Merge pull request #40595 from neikeq/godot-net-sdk-and-net-standard
C#: Switch games to MSBuild Sdks and .NET Standard
This commit is contained in:
commit
dc456059a4
|
@ -20,3 +20,7 @@ indent_size = 4
|
|||
[.travis.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{csproj,props,targets,nuspec}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/bindings_generator.h"
|
||||
#include "editor/csharp_project.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/node_dock.h"
|
||||
#endif
|
||||
|
@ -3724,13 +3723,9 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!FileAccess::exists(p_path)) {
|
||||
// The file does not yet exists, let's assume the user just created this script
|
||||
|
||||
if (_create_project_solution_if_needed()) {
|
||||
CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
|
||||
"Compile",
|
||||
ProjectSettings::get_singleton()->globalize_path(p_path));
|
||||
} else {
|
||||
// The file does not yet exist, let's assume the user just created this script. In such
|
||||
// cases we need to check whether the solution and csproj were already created or not.
|
||||
if (!_create_project_solution_if_needed()) {
|
||||
ERR_PRINT("C# project could not be created; cannot add file: '" + p_path + "'.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,35 @@
|
|||
<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
||||
<Description>MSBuild .NET Sdk for Godot projects.</Description>
|
||||
<Authors>Godot Engine contributors</Authors>
|
||||
|
||||
<PackageId>Godot.NET.Sdk</PackageId>
|
||||
<Version>4.0.0</Version>
|
||||
<PackageVersion>4.0.0-dev2</PackageVersion>
|
||||
<PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
|
||||
<PackageType>MSBuildSdk</PackageType>
|
||||
<PackageTags>MSBuildSdk</PackageTags>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NuspecFile>Godot.NET.Sdk.nuspec</NuspecFile>
|
||||
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
|
||||
<PropertyGroup>
|
||||
<NuspecProperties>
|
||||
id=$(PackageId);
|
||||
description=$(Description);
|
||||
authors=$(Authors);
|
||||
version=$(PackageVersion);
|
||||
packagetype=$(PackageType);
|
||||
tags=$(PackageTags);
|
||||
projecturl=$(PackageProjectUrl)
|
||||
</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>$id$</id>
|
||||
<version>$version$</version>
|
||||
<description>$description$</description>
|
||||
<authors>$authors$</authors>
|
||||
<owners>$authors$</owners>
|
||||
<projectUrl>$projecturl$</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
|
||||
<tags>$tags$</tags>
|
||||
<packageTypes>
|
||||
<packageType name="$packagetype$" />
|
||||
</packageTypes>
|
||||
<repository url="$projecturl$" />
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="Sdk\**" target="Sdk" />\
|
||||
</files>
|
||||
</package>
|
|
@ -0,0 +1,112 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- Determines if we should import Microsoft.NET.Sdk, if it wasn't already imported. -->
|
||||
<GodotSdkImportsMicrosoftNetSdk Condition=" '$(UsingMicrosoftNETSdk)' != 'true' ">true</GodotSdkImportsMicrosoftNetSdk>
|
||||
|
||||
<GodotProjectTypeGuid>{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}</GodotProjectTypeGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Configurations>Debug;ExportDebug;ExportRelease</Configurations>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
||||
<GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
|
||||
<GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
|
||||
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
|
||||
|
||||
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.mono\temp\'. -->
|
||||
<BaseOutputPath>$(GodotProjectDir).mono\temp\bin\</BaseOutputPath>
|
||||
<OutputPath>$(GodotProjectDir).mono\temp\bin\$(Configuration)\</OutputPath>
|
||||
<!--
|
||||
Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set.
|
||||
Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet.
|
||||
-->
|
||||
<IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\</BaseIntermediateOutputPath>
|
||||
|
||||
<!-- Do not append the target framework name to the output path. -->
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
The Microsoft.NET.Sdk only understands of the Debug and Release configurations.
|
||||
We need to set the following properties manually for ExportDebug and ExportRelease.
|
||||
-->
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' or '$(Configuration)' == 'ExportDebug' ">
|
||||
<DebugSymbols Condition=" '$(DebugSymbols)' == '' ">true</DebugSymbols>
|
||||
<Optimize Condition=" '$(Optimize)' == '' ">false</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'ExportRelease' ">
|
||||
<Optimize Condition=" '$(Optimize)' == '' ">true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GodotApiConfiguration Condition=" '$(Configuration)' != 'ExportRelease' ">Debug</GodotApiConfiguration>
|
||||
<GodotApiConfiguration Condition=" '$(Configuration)' == 'ExportRelease' ">Release</GodotApiConfiguration>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Auto-detect the target Godot platform if it was not specified. -->
|
||||
<PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
|
||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
|
||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
|
||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">osx</GodotTargetPlatform>
|
||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Windows))' ">windows</GodotTargetPlatform>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GodotRealTIsDouble Condition=" '$(GodotRealTIsDouble)' == '' ">false</GodotRealTIsDouble>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Godot DefineConstants. -->
|
||||
<PropertyGroup>
|
||||
<!-- Define constant to identify Godot builds. -->
|
||||
<GodotDefineConstants>GODOT</GodotDefineConstants>
|
||||
|
||||
<!--
|
||||
Define constant to determine the target Godot platform. This includes the
|
||||
recognized platform names and the platform category (PC, MOBILE or WEB).
|
||||
-->
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'osx' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'server' ">GODOT_SERVER;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'uwp' ">GODOT_UWP;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'haiku' ">GODOT_HAIKU;GODOT_PC</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'android' ">GODOT_ANDROID;GODOT_MOBILE</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'iphone' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
|
||||
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
|
||||
|
||||
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- ExportDebug also defines DEBUG like Debug does. -->
|
||||
<DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants>
|
||||
<!-- Debug defines TOOLS to differenciate between Debug and ExportDebug configurations. -->
|
||||
<DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants>
|
||||
|
||||
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
TODO:
|
||||
We should consider a nuget package for reference assemblies. This is difficult because the
|
||||
Godot scripting API is continuaslly breaking backwards compatibility even in patch releases.
|
||||
-->
|
||||
<Reference Include="GodotSharp">
|
||||
<Private>false</Private>
|
||||
<HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<Private>false</Private>
|
||||
<HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,17 @@
|
|||
<Project>
|
||||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableGodotProjectTypeGuid Condition=" '$(EnableGodotProjectTypeGuid)' == '' ">true</EnableGodotProjectTypeGuid>
|
||||
<ProjectTypeGuids Condition=" '$(EnableGodotProjectTypeGuid)' == 'true' ">$(GodotProjectTypeGuid);$(DefaultProjectTypeGuid)</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
Define constant to determine whether the real_t type in Godot is double precision or not.
|
||||
By default this is false, like the official Godot builds. If someone is using a custom
|
||||
Godot build where real_t is double, they can override the GodotRealTIsDouble property.
|
||||
-->
|
||||
<DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -19,7 +19,10 @@ namespace GodotTools.Core
|
|||
}
|
||||
|
||||
if (attempt > maxAttempts + 1)
|
||||
return;
|
||||
{
|
||||
// Overwrite the oldest one
|
||||
backupPath = backupPathBase;
|
||||
}
|
||||
|
||||
File.Copy(filePath, backupPath, overwrite: true);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,37 @@ namespace GodotTools.ProjectEditor
|
|||
return string.Join(".", identifiers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips invalid identifier characters including decimal digit numbers at the start of the identifier.
|
||||
/// </summary>
|
||||
private static void SkipInvalidCharacters(string source, int startIndex, StringBuilder outputBuilder)
|
||||
{
|
||||
for (int i = startIndex; i < source.Length; i++)
|
||||
{
|
||||
char @char = source[i];
|
||||
|
||||
switch (char.GetUnicodeCategory(@char))
|
||||
{
|
||||
case UnicodeCategory.UppercaseLetter:
|
||||
case UnicodeCategory.LowercaseLetter:
|
||||
case UnicodeCategory.TitlecaseLetter:
|
||||
case UnicodeCategory.ModifierLetter:
|
||||
case UnicodeCategory.LetterNumber:
|
||||
case UnicodeCategory.OtherLetter:
|
||||
outputBuilder.Append(@char);
|
||||
break;
|
||||
case UnicodeCategory.NonSpacingMark:
|
||||
case UnicodeCategory.SpacingCombiningMark:
|
||||
case UnicodeCategory.ConnectorPunctuation:
|
||||
case UnicodeCategory.DecimalDigitNumber:
|
||||
// Identifiers may start with underscore
|
||||
if (outputBuilder.Length > startIndex || @char == '_')
|
||||
outputBuilder.Append(@char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string SanitizeIdentifier(string identifier, bool allowEmpty)
|
||||
{
|
||||
if (string.IsNullOrEmpty(identifier))
|
||||
|
@ -44,30 +75,7 @@ namespace GodotTools.ProjectEditor
|
|||
startIndex += 1;
|
||||
}
|
||||
|
||||
for (int i = startIndex; i < identifier.Length; i++)
|
||||
{
|
||||
char @char = identifier[i];
|
||||
|
||||
switch (Char.GetUnicodeCategory(@char))
|
||||
{
|
||||
case UnicodeCategory.UppercaseLetter:
|
||||
case UnicodeCategory.LowercaseLetter:
|
||||
case UnicodeCategory.TitlecaseLetter:
|
||||
case UnicodeCategory.ModifierLetter:
|
||||
case UnicodeCategory.LetterNumber:
|
||||
case UnicodeCategory.OtherLetter:
|
||||
identifierBuilder.Append(@char);
|
||||
break;
|
||||
case UnicodeCategory.NonSpacingMark:
|
||||
case UnicodeCategory.SpacingCombiningMark:
|
||||
case UnicodeCategory.ConnectorPunctuation:
|
||||
case UnicodeCategory.DecimalDigitNumber:
|
||||
// Identifiers may start with underscore
|
||||
if (identifierBuilder.Length > startIndex || @char == '_')
|
||||
identifierBuilder.Append(@char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SkipInvalidCharacters(identifier, startIndex, identifierBuilder);
|
||||
|
||||
if (identifierBuilder.Length == startIndex)
|
||||
{
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Globbing;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectExtensions
|
||||
{
|
||||
public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
|
||||
{
|
||||
string normalizedInclude = include.NormalizePath();
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
{
|
||||
if (noCondition && itemGroup.Condition.Length != 0)
|
||||
continue;
|
||||
|
||||
foreach (var item in itemGroup.Items)
|
||||
{
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
//var glob = Glob.Parse(item.Include.NormalizePath(), globOptions);
|
||||
var glob = MSBuildGlob.Parse(item.Include.NormalizePath());
|
||||
|
||||
if (glob.IsMatch(normalizedInclude))
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
|
||||
{
|
||||
string normalizedInclude = Path.GetFullPath(include).NormalizePath();
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
{
|
||||
if (noCondition && itemGroup.Condition.Length != 0)
|
||||
continue;
|
||||
|
||||
foreach (var item in itemGroup.Items)
|
||||
{
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
var glob = MSBuildGlob.Parse(Path.GetFullPath(item.Include).NormalizePath());
|
||||
|
||||
if (glob.IsMatch(normalizedInclude))
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<ProjectItemElement> FindAllItemsInFolder(this ProjectRootElement root, string itemType, string folder)
|
||||
{
|
||||
string absFolderNormalizedWithSep = Path.GetFullPath(folder).NormalizePath() + Path.DirectorySeparatorChar;
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
{
|
||||
foreach (var item in itemGroup.Items)
|
||||
{
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
|
||||
|
||||
if (absPathNormalized.StartsWith(absFolderNormalizedWithSep))
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasItem(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
|
||||
{
|
||||
return root.FindItemOrNull(itemType, include, noCondition) != null;
|
||||
}
|
||||
|
||||
public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include)
|
||||
{
|
||||
if (!root.HasItem(itemType, include, noCondition: true))
|
||||
{
|
||||
root.AddItem(itemType, include);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool RemoveItemChecked(this ProjectRootElement root, string itemType, string include)
|
||||
{
|
||||
var item = root.FindItemOrNullAbs(itemType, include);
|
||||
if (item != null)
|
||||
{
|
||||
item.Parent.RemoveChild(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Guid GetGuid(this ProjectRootElement root)
|
||||
{
|
||||
foreach (var property in root.Properties)
|
||||
{
|
||||
if (property.Name == "ProjectGuid")
|
||||
return Guid.Parse(property.Value);
|
||||
}
|
||||
|
||||
return Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,174 +1,49 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Evaluation;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectGenerator
|
||||
{
|
||||
private const string CoreApiProjectName = "GodotSharp";
|
||||
private const string EditorApiProjectName = "GodotSharpEditor";
|
||||
public const string GodotSdkVersionToUse = "4.0.0-dev2";
|
||||
|
||||
public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
|
||||
public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}";
|
||||
public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
|
||||
|
||||
public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}";
|
||||
|
||||
public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
|
||||
public static ProjectRootElement GenGameProject(string name)
|
||||
{
|
||||
string path = Path.Combine(dir, name + ".csproj");
|
||||
if (name.Length == 0)
|
||||
throw new ArgumentException("Project name is empty", nameof(name));
|
||||
|
||||
ProjectPropertyGroupElement mainGroup;
|
||||
var root = CreateLibraryProject(name, "Debug", out mainGroup);
|
||||
var root = ProjectRootElement.Create(NewProjectFileOptions.None);
|
||||
|
||||
mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);
|
||||
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
|
||||
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
|
||||
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
|
||||
mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'ExportRelease' ";
|
||||
mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'ExportRelease' ";
|
||||
root.Sdk = GodotSdkAttrValue;
|
||||
|
||||
var debugGroup = root.AddPropertyGroup();
|
||||
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
|
||||
debugGroup.AddProperty("DebugSymbols", "true");
|
||||
debugGroup.AddProperty("DebugType", "portable");
|
||||
debugGroup.AddProperty("Optimize", "false");
|
||||
debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
|
||||
debugGroup.AddProperty("ErrorReport", "prompt");
|
||||
debugGroup.AddProperty("WarningLevel", "4");
|
||||
debugGroup.AddProperty("ConsolePause", "false");
|
||||
var mainGroup = root.AddPropertyGroup();
|
||||
mainGroup.AddProperty("TargetFramework", "netstandard2.1");
|
||||
|
||||
var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
|
||||
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
|
||||
coreApiRef.AddMetadata("Private", "False");
|
||||
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
|
||||
|
||||
var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
|
||||
editorApiRef.Condition = " '$(Configuration)' == 'Debug' ";
|
||||
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
|
||||
editorApiRef.AddMetadata("Private", "False");
|
||||
|
||||
GenAssemblyInfoFile(root, dir, name);
|
||||
|
||||
foreach (var item in compileItems)
|
||||
{
|
||||
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
|
||||
}
|
||||
|
||||
root.Save(path);
|
||||
|
||||
return root.GetGuid().ToString().ToUpper();
|
||||
}
|
||||
|
||||
private static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
|
||||
{
|
||||
string propertiesDir = Path.Combine(dir, "Properties");
|
||||
if (!Directory.Exists(propertiesDir))
|
||||
Directory.CreateDirectory(propertiesDir);
|
||||
|
||||
string usingDirectivesText = string.Empty;
|
||||
|
||||
if (usingDirectives != null)
|
||||
{
|
||||
foreach (var usingDirective in usingDirectives)
|
||||
usingDirectivesText += "\nusing " + usingDirective + ";";
|
||||
}
|
||||
|
||||
string assemblyLinesText = string.Empty;
|
||||
|
||||
if (assemblyLines != null)
|
||||
assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
|
||||
|
||||
string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
|
||||
|
||||
string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
|
||||
|
||||
File.WriteAllText(assemblyInfoFile, content);
|
||||
|
||||
root.AddItem("Compile", assemblyInfoFile.RelativeToPath(dir).Replace("/", "\\"));
|
||||
}
|
||||
|
||||
public static ProjectRootElement CreateLibraryProject(string name, string defaultConfig, out ProjectPropertyGroupElement mainGroup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name));
|
||||
|
||||
var root = ProjectRootElement.Create();
|
||||
root.DefaultTargets = "Build";
|
||||
|
||||
mainGroup = root.AddPropertyGroup();
|
||||
mainGroup.AddProperty("Configuration", defaultConfig).Condition = " '$(Configuration)' == '' ";
|
||||
mainGroup.AddProperty("Platform", "AnyCPU").Condition = " '$(Platform)' == '' ";
|
||||
mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
|
||||
mainGroup.AddProperty("OutputType", "Library");
|
||||
mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
|
||||
mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true));
|
||||
mainGroup.AddProperty("AssemblyName", name);
|
||||
mainGroup.AddProperty("TargetFrameworkVersion", "v4.7");
|
||||
mainGroup.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
|
||||
var exportDebugGroup = root.AddPropertyGroup();
|
||||
exportDebugGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportDebug|AnyCPU' ";
|
||||
exportDebugGroup.AddProperty("DebugSymbols", "true");
|
||||
exportDebugGroup.AddProperty("DebugType", "portable");
|
||||
exportDebugGroup.AddProperty("Optimize", "false");
|
||||
exportDebugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
|
||||
exportDebugGroup.AddProperty("ErrorReport", "prompt");
|
||||
exportDebugGroup.AddProperty("WarningLevel", "4");
|
||||
exportDebugGroup.AddProperty("ConsolePause", "false");
|
||||
|
||||
var exportReleaseGroup = root.AddPropertyGroup();
|
||||
exportReleaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportRelease|AnyCPU' ";
|
||||
exportReleaseGroup.AddProperty("DebugType", "portable");
|
||||
exportReleaseGroup.AddProperty("Optimize", "true");
|
||||
exportReleaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
|
||||
exportReleaseGroup.AddProperty("ErrorReport", "prompt");
|
||||
exportReleaseGroup.AddProperty("WarningLevel", "4");
|
||||
exportReleaseGroup.AddProperty("ConsolePause", "false");
|
||||
|
||||
// References
|
||||
var referenceGroup = root.AddItemGroup();
|
||||
referenceGroup.AddItem("Reference", "System");
|
||||
var frameworkRefAssembliesItem = referenceGroup.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
|
||||
|
||||
// Use metadata (child nodes) instead of attributes for the PackageReference.
|
||||
// This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
|
||||
frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
|
||||
frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
|
||||
|
||||
root.AddImport(Path.Combine("$(MSBuildBinPath)", "Microsoft.CSharp.targets").Replace("/", "\\"));
|
||||
// If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
|
||||
if (sanitizedName != name)
|
||||
mainGroup.AddProperty("RootNamespace", sanitizedName);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private const string AssemblyInfoTemplate =
|
||||
@"using System.Reflection;{0}
|
||||
public static string GenAndSaveGameProject(string dir, string name)
|
||||
{
|
||||
if (name.Length == 0)
|
||||
throw new ArgumentException("Project name is empty", nameof(name));
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
string path = Path.Combine(dir, name + ".csproj");
|
||||
|
||||
[assembly: AssemblyTitle(""{1}"")]
|
||||
[assembly: AssemblyDescription("""")]
|
||||
[assembly: AssemblyConfiguration("""")]
|
||||
[assembly: AssemblyCompany("""")]
|
||||
[assembly: AssemblyProduct("""")]
|
||||
[assembly: AssemblyCopyright("""")]
|
||||
[assembly: AssemblyTrademark("""")]
|
||||
[assembly: AssemblyCulture("""")]
|
||||
var root = GenGameProject(name);
|
||||
|
||||
// The assembly version has the format ""{{Major}}.{{Minor}}.{{Build}}.{{Revision}}"".
|
||||
// The form ""{{Major}}.{{Minor}}.*"" will automatically update the build and revision,
|
||||
// and ""{{Major}}.{{Minor}}.{{Build}}.*"" will update just the revision.
|
||||
root.Save(path);
|
||||
|
||||
[assembly: AssemblyVersion(""1.0.*"")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("""")]
|
||||
{2}";
|
||||
return Guid.NewGuid().ToString().ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using GodotTools.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Globbing;
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace GodotTools.ProjectEditor
|
|||
{
|
||||
public sealed class MSBuildProject
|
||||
{
|
||||
public ProjectRootElement Root { get; }
|
||||
internal ProjectRootElement Root { get; set; }
|
||||
|
||||
public bool HasUnsavedChanges { get; set; }
|
||||
|
||||
|
@ -31,91 +31,7 @@ namespace GodotTools.ProjectEditor
|
|||
return root != null ? new MSBuildProject(root) : null;
|
||||
}
|
||||
|
||||
public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
|
||||
{
|
||||
var dir = Directory.GetParent(projectPath).FullName;
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\");
|
||||
|
||||
if (root.AddItemChecked(itemType, normalizedInclude))
|
||||
root.Save();
|
||||
}
|
||||
|
||||
public static void RenameItemInProjectChecked(string projectPath, string itemType, string oldInclude, string newInclude)
|
||||
{
|
||||
var dir = Directory.GetParent(projectPath).FullName;
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
var normalizedOldInclude = oldInclude.NormalizePath();
|
||||
var normalizedNewInclude = newInclude.NormalizePath();
|
||||
|
||||
var item = root.FindItemOrNullAbs(itemType, normalizedOldInclude);
|
||||
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
item.Include = normalizedNewInclude.RelativeToPath(dir).Replace("/", "\\");
|
||||
root.Save();
|
||||
}
|
||||
|
||||
public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
|
||||
{
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
var normalizedInclude = include.NormalizePath();
|
||||
|
||||
if (root.RemoveItemChecked(itemType, normalizedInclude))
|
||||
root.Save();
|
||||
}
|
||||
|
||||
public static void RenameItemsToNewFolderInProjectChecked(string projectPath, string itemType, string oldFolder, string newFolder)
|
||||
{
|
||||
var dir = Directory.GetParent(projectPath).FullName;
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
var oldFolderNormalized = oldFolder.NormalizePath();
|
||||
var newFolderNormalized = newFolder.NormalizePath();
|
||||
string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
|
||||
string absNewFolderNormalized = Path.GetFullPath(newFolderNormalized).NormalizePath();
|
||||
|
||||
foreach (var item in root.FindAllItemsInFolder(itemType, oldFolderNormalized))
|
||||
{
|
||||
string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
|
||||
string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
|
||||
item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
root.Save();
|
||||
}
|
||||
|
||||
public static void RemoveItemsInFolderFromProjectChecked(string projectPath, string itemType, string folder)
|
||||
{
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
var folderNormalized = folder.NormalizePath();
|
||||
|
||||
var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList();
|
||||
|
||||
if (itemsToRemove.Count > 0)
|
||||
{
|
||||
foreach (var item in itemsToRemove)
|
||||
item.Parent.RemoveChild(item);
|
||||
|
||||
root.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] GetAllFilesRecursive(string rootDirectory, string mask)
|
||||
private static List<string> GetAllFilesRecursive(string rootDirectory, string mask)
|
||||
{
|
||||
string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
|
||||
|
||||
|
@ -125,262 +41,59 @@ namespace GodotTools.ProjectEditor
|
|||
files[i] = files[i].RelativeToPath(rootDirectory);
|
||||
}
|
||||
|
||||
return files;
|
||||
return new List<string>(files);
|
||||
}
|
||||
|
||||
public static string[] GetIncludeFiles(string projectPath, string itemType)
|
||||
// NOTE: Assumes auto-including items. Only used by the scripts metadata generator, which will be replaced with source generators in the future.
|
||||
public static IEnumerable<string> GetIncludeFiles(string projectPath, string itemType)
|
||||
{
|
||||
var result = new List<string>();
|
||||
var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
|
||||
var excluded = new List<string>();
|
||||
var includedFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
|
||||
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
foreach (var item in root.Items)
|
||||
{
|
||||
if (itemGroup.Condition.Length != 0)
|
||||
if (string.IsNullOrEmpty(item.Condition))
|
||||
continue;
|
||||
|
||||
foreach (var item in itemGroup.Items)
|
||||
{
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
string normalizedInclude = item.Include.NormalizePath();
|
||||
string normalizedExclude = item.Exclude.NormalizePath();
|
||||
|
||||
var glob = MSBuildGlob.Parse(normalizedInclude);
|
||||
var glob = MSBuildGlob.Parse(normalizedExclude);
|
||||
|
||||
// TODO Check somehow if path has no blob to avoid the following loop...
|
||||
|
||||
foreach (var existingFile in existingFiles)
|
||||
{
|
||||
if (glob.IsMatch(existingFile))
|
||||
{
|
||||
result.Add(existingFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile)));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
includedFiles.RemoveAll(f => excluded.Contains(f));
|
||||
|
||||
return includedFiles;
|
||||
}
|
||||
|
||||
public static void EnsureHasProjectTypeGuids(MSBuildProject project)
|
||||
public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
|
||||
{
|
||||
var root = project.Root;
|
||||
var origRoot = project.Root;
|
||||
|
||||
bool found = root.PropertyGroups.Any(pg =>
|
||||
string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
|
||||
|
||||
if (found)
|
||||
if (!string.IsNullOrEmpty(origRoot.Sdk))
|
||||
return;
|
||||
|
||||
root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
|
||||
|
||||
project.Root = ProjectGenerator.GenGameProject(projectName);
|
||||
project.Root.FullPath = origRoot.FullPath;
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
/// Simple function to make sure the Api assembly references are configured correctly
|
||||
public static void FixApiHintPath(MSBuildProject project)
|
||||
public static void EnsureGodotSdkIsUpToDate(MSBuildProject project)
|
||||
{
|
||||
var root = project.Root;
|
||||
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
|
||||
|
||||
void AddPropertyIfNotPresent(string name, string condition, string value)
|
||||
{
|
||||
if (root.PropertyGroups
|
||||
.Any(g => (string.IsNullOrEmpty(g.Condition) || g.Condition.Trim() == condition) &&
|
||||
g.Properties
|
||||
.Any(p => p.Name == name &&
|
||||
p.Value == value &&
|
||||
(p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
root.AddProperty(name, value).Condition = " " + condition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
AddPropertyIfNotPresent(name: "ApiConfiguration",
|
||||
condition: "'$(Configuration)' != 'ExportRelease'",
|
||||
value: "Debug");
|
||||
AddPropertyIfNotPresent(name: "ApiConfiguration",
|
||||
condition: "'$(Configuration)' == 'ExportRelease'",
|
||||
value: "Release");
|
||||
|
||||
void SetReferenceHintPath(string referenceName, string condition, string hintPath)
|
||||
{
|
||||
foreach (var itemGroup in root.ItemGroups.Where(g =>
|
||||
g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
|
||||
{
|
||||
var references = itemGroup.Items.Where(item =>
|
||||
item.ItemType == "Reference" &&
|
||||
item.Include == referenceName &&
|
||||
(item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));
|
||||
|
||||
var referencesWithHintPath = references.Where(reference =>
|
||||
reference.Metadata.Any(m => m.Name == "HintPath"));
|
||||
|
||||
if (referencesWithHintPath.Any(reference => reference.Metadata
|
||||
.Any(m => m.Name == "HintPath" && m.Value == hintPath)))
|
||||
{
|
||||
// Found a Reference item with the right HintPath
|
||||
return;
|
||||
}
|
||||
|
||||
var referenceWithHintPath = referencesWithHintPath.FirstOrDefault();
|
||||
if (referenceWithHintPath != null)
|
||||
{
|
||||
// Found a Reference item with a wrong HintPath
|
||||
foreach (var metadata in referenceWithHintPath.Metadata.ToList()
|
||||
.Where(m => m.Name == "HintPath"))
|
||||
{
|
||||
// Safe to remove as we duplicate with ToList() to loop
|
||||
referenceWithHintPath.RemoveChild(metadata);
|
||||
}
|
||||
|
||||
referenceWithHintPath.AddMetadata("HintPath", hintPath);
|
||||
project.HasUnsavedChanges = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var referenceWithoutHintPath = references.FirstOrDefault();
|
||||
if (referenceWithoutHintPath != null)
|
||||
{
|
||||
// Found a Reference item without a HintPath
|
||||
referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
|
||||
project.HasUnsavedChanges = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Found no Reference item at all. Add it.
|
||||
root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
const string coreProjectName = "GodotSharp";
|
||||
const string editorProjectName = "GodotSharpEditor";
|
||||
|
||||
const string coreCondition = "";
|
||||
const string editorCondition = "'$(Configuration)' == 'Debug'";
|
||||
|
||||
var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
|
||||
var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";
|
||||
|
||||
SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
|
||||
SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
|
||||
}
|
||||
|
||||
public static void MigrateFromOldConfigNames(MSBuildProject project)
|
||||
{
|
||||
var root = project.Root;
|
||||
|
||||
bool hasGodotProjectGeneratorVersion = false;
|
||||
bool foundOldConfiguration = false;
|
||||
|
||||
foreach (var propertyGroup in root.PropertyGroups.Where(g => string.IsNullOrEmpty(g.Condition)))
|
||||
{
|
||||
if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
|
||||
hasGodotProjectGeneratorVersion = true;
|
||||
|
||||
foreach (var configItem in propertyGroup.Properties
|
||||
.Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
|
||||
{
|
||||
configItem.Value = "Debug";
|
||||
foundOldConfiguration = true;
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasGodotProjectGeneratorVersion)
|
||||
{
|
||||
root.PropertyGroups.First(g => string.IsNullOrEmpty(g.Condition))?
|
||||
.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
if (!foundOldConfiguration)
|
||||
{
|
||||
var toolsConditions = new[]
|
||||
{
|
||||
"'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
|
||||
"'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
|
||||
"'$(Configuration)' == 'Tools'",
|
||||
"'$(Configuration)' != 'Tools'"
|
||||
};
|
||||
|
||||
foundOldConfiguration = root.PropertyGroups
|
||||
.Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
|
||||
}
|
||||
|
||||
if (foundOldConfiguration)
|
||||
{
|
||||
void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
|
||||
{
|
||||
void MigrateConditions(string oldCondition, string newCondition)
|
||||
{
|
||||
foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
|
||||
{
|
||||
propertyGroup.Condition = " " + newCondition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
foreach (var propertyGroup in root.PropertyGroups)
|
||||
{
|
||||
foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
|
||||
{
|
||||
prop.Condition = " " + newCondition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
|
||||
{
|
||||
itemGroup.Condition = " " + newCondition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
{
|
||||
foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
|
||||
{
|
||||
item.Condition = " " + newCondition + " ";
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var op in new[] {"==", "!="})
|
||||
{
|
||||
MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
|
||||
MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
|
||||
}
|
||||
}
|
||||
|
||||
MigrateConfigurationConditions("Debug", "ExportDebug");
|
||||
MigrateConfigurationConditions("Release", "ExportRelease");
|
||||
MigrateConfigurationConditions("Tools", "Debug"); // Must be last
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureHasNugetNetFrameworkRefAssemblies(MSBuildProject project)
|
||||
{
|
||||
var root = project.Root;
|
||||
|
||||
bool found = root.ItemGroups.Any(g => string.IsNullOrEmpty(g.Condition) && g.Items.Any(
|
||||
item => item.ItemType == "PackageReference" && item.Include == "Microsoft.NETFramework.ReferenceAssemblies"));
|
||||
|
||||
if (found)
|
||||
if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
var frameworkRefAssembliesItem = root.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
|
||||
|
||||
// Use metadata (child nodes) instead of attributes for the PackageReference.
|
||||
// This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
|
||||
frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
|
||||
frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
|
||||
|
||||
root.Sdk = godotSdkAttrValue;
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,48 +24,50 @@ namespace GodotTools
|
|||
private Button errorsBtn;
|
||||
private Button viewLogBtn;
|
||||
|
||||
private void _UpdateBuildTab(int index, int? currentTab)
|
||||
{
|
||||
var tab = (BuildTab)buildTabs.GetChild(index);
|
||||
|
||||
string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
|
||||
itemName += " [" + tab.BuildInfo.Configuration + "]";
|
||||
|
||||
buildTabsList.AddItem(itemName, tab.IconTexture);
|
||||
|
||||
string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
|
||||
itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
|
||||
itemTooltip += "\nStatus: ";
|
||||
|
||||
if (tab.BuildExited)
|
||||
itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
|
||||
else
|
||||
itemTooltip += "Running";
|
||||
|
||||
if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
|
||||
itemTooltip += $"\nErrors: {tab.ErrorCount}";
|
||||
|
||||
itemTooltip += $"\nWarnings: {tab.WarningCount}";
|
||||
|
||||
buildTabsList.SetItemTooltip(index, itemTooltip);
|
||||
|
||||
// If this tab was already selected before the changes or if no tab was selected
|
||||
if (currentTab == null || currentTab == index)
|
||||
{
|
||||
buildTabsList.Select(index);
|
||||
_BuildTabsItemSelected(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateBuildTabsList()
|
||||
{
|
||||
buildTabsList.Clear();
|
||||
|
||||
int currentTab = buildTabs.CurrentTab;
|
||||
int? currentTab = buildTabs.CurrentTab;
|
||||
|
||||
bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount();
|
||||
if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
|
||||
currentTab = null;
|
||||
|
||||
for (int i = 0; i < buildTabs.GetChildCount(); i++)
|
||||
{
|
||||
var tab = (BuildTab)buildTabs.GetChild(i);
|
||||
|
||||
if (tab == null)
|
||||
continue;
|
||||
|
||||
string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
|
||||
itemName += " [" + tab.BuildInfo.Configuration + "]";
|
||||
|
||||
buildTabsList.AddItem(itemName, tab.IconTexture);
|
||||
|
||||
string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
|
||||
itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
|
||||
itemTooltip += "\nStatus: ";
|
||||
|
||||
if (tab.BuildExited)
|
||||
itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
|
||||
else
|
||||
itemTooltip += "Running";
|
||||
|
||||
if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
|
||||
itemTooltip += $"\nErrors: {tab.ErrorCount}";
|
||||
|
||||
itemTooltip += $"\nWarnings: {tab.WarningCount}";
|
||||
|
||||
buildTabsList.SetItemTooltip(i, itemTooltip);
|
||||
|
||||
if (noCurrentTab || currentTab == i)
|
||||
{
|
||||
buildTabsList.Select(i);
|
||||
_BuildTabsItemSelected(i);
|
||||
}
|
||||
}
|
||||
_UpdateBuildTab(i, currentTab);
|
||||
}
|
||||
|
||||
public BuildTab GetBuildTabFor(BuildInfo buildInfo)
|
||||
|
@ -160,13 +162,7 @@ namespace GodotTools
|
|||
}
|
||||
}
|
||||
|
||||
var godotDefines = new[]
|
||||
{
|
||||
OS.GetName(),
|
||||
Internal.GodotIs32Bits() ? "32" : "64"
|
||||
};
|
||||
|
||||
bool buildSuccess = BuildManager.BuildProjectBlocking("Debug", godotDefines);
|
||||
bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
|
||||
|
||||
if (!buildSuccess)
|
||||
return;
|
||||
|
@ -272,7 +268,7 @@ namespace GodotTools
|
|||
};
|
||||
panelTabs.AddChild(panelBuildsTab);
|
||||
|
||||
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
|
||||
var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
|
||||
panelBuildsTab.AddChild(toolBarHBox);
|
||||
|
||||
var buildProjectBtn = new Button
|
||||
|
@ -325,7 +321,7 @@ namespace GodotTools
|
|||
};
|
||||
panelBuildsTab.AddChild(hsc);
|
||||
|
||||
buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
|
||||
buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
|
||||
buildTabsList.ItemSelected += _BuildTabsItemSelected;
|
||||
buildTabsList.NothingSelected += _BuildTabsNothingSelected;
|
||||
hsc.AddChild(buildTabsList);
|
||||
|
|
|
@ -6,6 +6,7 @@ using GodotTools.Build;
|
|||
using GodotTools.Ides.Rider;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.Utils;
|
||||
using JetBrains.Annotations;
|
||||
using static GodotTools.Internals.Globals;
|
||||
using File = GodotTools.Utils.File;
|
||||
|
||||
|
@ -152,7 +153,7 @@ namespace GodotTools
|
|||
}
|
||||
}
|
||||
|
||||
public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
|
||||
public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return true; // No solution to build
|
||||
|
@ -168,29 +169,18 @@ namespace GodotTools
|
|||
return false;
|
||||
}
|
||||
|
||||
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
|
||||
|
||||
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
|
||||
{
|
||||
pr.Step("Building project solution", 0);
|
||||
|
||||
var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
|
||||
|
||||
bool escapeNeedsDoubleBackslash = buildTool == BuildTool.MsBuildMono || buildTool == BuildTool.DotnetCli;
|
||||
|
||||
// Add Godot defines
|
||||
string constants = !escapeNeedsDoubleBackslash ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
|
||||
|
||||
foreach (var godotDefine in godotDefines)
|
||||
constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
|
||||
// If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
|
||||
if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
|
||||
buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
|
||||
|
||||
if (Internal.GodotIsRealTDouble())
|
||||
constants += "GODOT_REAL_T_IS_DOUBLE;";
|
||||
|
||||
constants += !escapeNeedsDoubleBackslash ? "\"" : "\\\"";
|
||||
|
||||
buildInfo.CustomProperties.Add(constants);
|
||||
buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
|
||||
|
||||
if (!Build(buildInfo))
|
||||
{
|
||||
|
@ -233,13 +223,7 @@ namespace GodotTools
|
|||
return true; // Requested play from an external editor/IDE which already built the project
|
||||
}
|
||||
|
||||
var godotDefines = new[]
|
||||
{
|
||||
Godot.OS.GetName(),
|
||||
Internal.GodotIs32Bits() ? "32" : "64"
|
||||
};
|
||||
|
||||
return BuildProjectBlocking("Debug", godotDefines);
|
||||
return BuildProjectBlocking("Debug");
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.ProjectEditor;
|
||||
using static GodotTools.Internals.Globals;
|
||||
using File = GodotTools.Utils.File;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace GodotTools
|
|||
{
|
||||
try
|
||||
{
|
||||
return ProjectGenerator.GenGameProject(dir, name, compileItems: new string[] { });
|
||||
return ProjectGenerator.GenAndSaveGameProject(dir, name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -24,14 +24,6 @@ namespace GodotTools
|
|||
}
|
||||
}
|
||||
|
||||
public static void AddItem(string projectPath, string itemType, string include)
|
||||
{
|
||||
if (!(bool)GlobalDef("mono/project/auto_update_project", true))
|
||||
return;
|
||||
|
||||
ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
|
||||
}
|
||||
|
||||
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private static ulong ConvertToTimestamp(this DateTime value)
|
||||
|
@ -40,81 +32,77 @@ namespace GodotTools
|
|||
return (ulong)elapsedTime.TotalSeconds;
|
||||
}
|
||||
|
||||
private static bool TryParseFileMetadata(string includeFile, ulong modifiedTime, out Dictionary fileMetadata)
|
||||
{
|
||||
fileMetadata = null;
|
||||
|
||||
var parseError = ScriptClassParser.ParseFile(includeFile, out var classes, out string errorStr);
|
||||
|
||||
if (parseError != Error.Ok)
|
||||
{
|
||||
GD.PushError($"Failed to determine namespace and class for script: {includeFile}. Parse error: {errorStr ?? parseError.ToString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string searchName = System.IO.Path.GetFileNameWithoutExtension(includeFile);
|
||||
|
||||
var firstMatch = classes.FirstOrDefault(classDecl =>
|
||||
classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object.
|
||||
classDecl.SearchName != searchName // Filter by the name we're looking for
|
||||
);
|
||||
|
||||
if (firstMatch == null)
|
||||
return false; // Not found
|
||||
|
||||
fileMetadata = new Dictionary
|
||||
{
|
||||
["modified_time"] = $"{modifiedTime}",
|
||||
["class"] = new Dictionary
|
||||
{
|
||||
["namespace"] = firstMatch.Namespace,
|
||||
["class_name"] = firstMatch.Name,
|
||||
["nested"] = firstMatch.Nested
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void GenerateScriptsMetadata(string projectPath, string outputPath)
|
||||
{
|
||||
if (File.Exists(outputPath))
|
||||
File.Delete(outputPath);
|
||||
var metadataDict = Internal.GetScriptsMetadataOrNothing().Duplicate();
|
||||
|
||||
var oldDict = Internal.GetScriptsMetadataOrNothing();
|
||||
var newDict = new Godot.Collections.Dictionary<string, object>();
|
||||
|
||||
foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile"))
|
||||
bool IsUpToDate(string includeFile, ulong modifiedTime)
|
||||
{
|
||||
string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath();
|
||||
|
||||
ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp();
|
||||
|
||||
if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar))
|
||||
{
|
||||
var oldFileDict = (Dictionary)oldFileVar;
|
||||
|
||||
if (ulong.TryParse(oldFileDict["modified_time"] as string, out ulong storedModifiedTime))
|
||||
{
|
||||
if (storedModifiedTime == modifiedTime)
|
||||
{
|
||||
// No changes so no need to parse again
|
||||
newDict[projectIncludeFile] = oldFileDict;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error parseError = ScriptClassParser.ParseFile(projectIncludeFile, out var classes, out string errorStr);
|
||||
if (parseError != Error.Ok)
|
||||
{
|
||||
GD.PushError($"Failed to determine namespace and class for script: {projectIncludeFile}. Parse error: {errorStr ?? parseError.ToString()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile);
|
||||
|
||||
var classDict = new Dictionary();
|
||||
|
||||
foreach (var classDecl in classes)
|
||||
{
|
||||
if (classDecl.BaseCount == 0)
|
||||
continue; // Does not inherit nor implement anything, so it can't be a script class
|
||||
|
||||
string classCmp = classDecl.Nested ?
|
||||
classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
|
||||
classDecl.Name;
|
||||
|
||||
if (classCmp != searchName)
|
||||
continue;
|
||||
|
||||
classDict["namespace"] = classDecl.Namespace;
|
||||
classDict["class_name"] = classDecl.Name;
|
||||
classDict["nested"] = classDecl.Nested;
|
||||
break;
|
||||
}
|
||||
|
||||
if (classDict.Count == 0)
|
||||
continue; // Not found
|
||||
|
||||
newDict[projectIncludeFile] = new Dictionary { ["modified_time"] = $"{modifiedTime}", ["class"] = classDict };
|
||||
return metadataDict.TryGetValue(includeFile, out var oldFileVar) &&
|
||||
ulong.TryParse(((Dictionary)oldFileVar)["modified_time"] as string,
|
||||
out ulong storedModifiedTime) && storedModifiedTime == modifiedTime;
|
||||
}
|
||||
|
||||
if (newDict.Count > 0)
|
||||
var outdatedFiles = ProjectUtils.GetIncludeFiles(projectPath, "Compile")
|
||||
.Select(path => ("res://" + path).SimplifyGodotPath())
|
||||
.ToDictionary(path => path, path => File.GetLastWriteTime(path).ConvertToTimestamp())
|
||||
.Where(pair => !IsUpToDate(includeFile: pair.Key, modifiedTime: pair.Value))
|
||||
.ToArray();
|
||||
|
||||
foreach (var pair in outdatedFiles)
|
||||
{
|
||||
string json = JSON.Print(newDict);
|
||||
metadataDict.Remove(pair.Key);
|
||||
|
||||
string baseDir = outputPath.GetBaseDir();
|
||||
string includeFile = pair.Key;
|
||||
|
||||
if (!Directory.Exists(baseDir))
|
||||
Directory.CreateDirectory(baseDir);
|
||||
|
||||
File.WriteAllText(outputPath, json);
|
||||
if (TryParseFileMetadata(includeFile, modifiedTime: pair.Value, out var fileMetadata))
|
||||
metadataDict[includeFile] = fileMetadata;
|
||||
}
|
||||
|
||||
string json = metadataDict.Count <= 0 ? "{}" : JSON.Print(metadataDict);
|
||||
|
||||
string baseDir = outputPath.GetBaseDir();
|
||||
|
||||
if (!Directory.Exists(baseDir))
|
||||
Directory.CreateDirectory(baseDir);
|
||||
|
||||
File.WriteAllText(outputPath, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Runtime.CompilerServices;
|
||||
using GodotTools.Core;
|
||||
using GodotTools.Internals;
|
||||
using JetBrains.Annotations;
|
||||
using static GodotTools.Internals.Globals;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
using File = GodotTools.Utils.File;
|
||||
|
@ -145,9 +146,7 @@ namespace GodotTools.Export
|
|||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return;
|
||||
|
||||
string platform = DeterminePlatformFromFeatures(features);
|
||||
|
||||
if (platform == null)
|
||||
if (!DeterminePlatformFromFeatures(features, out string platform))
|
||||
throw new NotSupportedException("Target platform not supported");
|
||||
|
||||
string outputDir = new FileInfo(path).Directory?.FullName ??
|
||||
|
@ -160,10 +159,7 @@ namespace GodotTools.Export
|
|||
|
||||
AddFile(scriptsMetadataPath, scriptsMetadataPath);
|
||||
|
||||
// Turn export features into defines
|
||||
var godotDefines = features;
|
||||
|
||||
if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
|
||||
if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
|
||||
throw new Exception("Failed to build project");
|
||||
|
||||
// Add dependency assemblies
|
||||
|
@ -289,6 +285,7 @@ namespace GodotTools.Export
|
|||
}
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
|
||||
{
|
||||
string target = isDebug ? "release_debug" : "release";
|
||||
|
@ -343,18 +340,19 @@ namespace GodotTools.Export
|
|||
private static bool PlatformHasTemplateDir(string platform)
|
||||
{
|
||||
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
|
||||
return !new[] { OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
|
||||
return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
|
||||
}
|
||||
|
||||
private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
|
||||
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
|
||||
{
|
||||
foreach (var feature in features)
|
||||
{
|
||||
if (OS.PlatformNameMap.TryGetValue(feature, out string platform))
|
||||
return platform;
|
||||
if (OS.PlatformNameMap.TryGetValue(feature, out platform))
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
platform = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetBclProfileDir(string profile)
|
||||
|
@ -391,7 +389,7 @@ namespace GodotTools.Export
|
|||
/// </summary>
|
||||
private static bool PlatformRequiresCustomBcl(string platform)
|
||||
{
|
||||
if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
|
||||
if (new[] {OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform))
|
||||
return true;
|
||||
|
||||
// The 'net_4_x' BCL is not compatible between Windows and the other platforms.
|
||||
|
|
|
@ -175,36 +175,6 @@ namespace GodotTools
|
|||
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
|
||||
aboutDialog.Exclusive = false;
|
||||
}
|
||||
|
||||
var fileSystemDock = GetEditorInterface().GetFileSystemDock();
|
||||
|
||||
fileSystemDock.FilesMoved += (file, newFile) =>
|
||||
{
|
||||
if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
|
||||
{
|
||||
ProjectUtils.RenameItemInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
|
||||
ProjectSettings.GlobalizePath(file), ProjectSettings.GlobalizePath(newFile));
|
||||
}
|
||||
};
|
||||
|
||||
fileSystemDock.FileRemoved += file =>
|
||||
{
|
||||
if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
|
||||
ProjectUtils.RemoveItemFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
|
||||
ProjectSettings.GlobalizePath(file));
|
||||
};
|
||||
|
||||
fileSystemDock.FolderMoved += (oldFolder, newFolder) =>
|
||||
{
|
||||
ProjectUtils.RenameItemsToNewFolderInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
|
||||
ProjectSettings.GlobalizePath(oldFolder), ProjectSettings.GlobalizePath(newFolder));
|
||||
};
|
||||
|
||||
fileSystemDock.FolderRemoved += oldFolder =>
|
||||
{
|
||||
ProjectUtils.RemoveItemsInFolderFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
|
||||
ProjectSettings.GlobalizePath(oldFolder));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,6 +359,37 @@ namespace GodotTools
|
|||
return BuildManager.EditorBuildCallback();
|
||||
}
|
||||
|
||||
private void ApplyNecessaryChangesToSolution()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
|
||||
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
|
||||
|
||||
var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
|
||||
?? throw new Exception("Cannot open C# project");
|
||||
|
||||
// NOTE: The order in which changes are made to the project is important
|
||||
|
||||
// Migrate to MSBuild project Sdks style if using the old style
|
||||
ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, ProjectAssemblyName);
|
||||
|
||||
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
|
||||
|
||||
if (msbuildProject.HasUnsavedChanges)
|
||||
{
|
||||
// Save a copy of the project before replacing it
|
||||
FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
|
||||
|
||||
msbuildProject.Save();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public override void EnablePlugin()
|
||||
{
|
||||
base.EnablePlugin();
|
||||
|
@ -468,42 +469,7 @@ namespace GodotTools
|
|||
|
||||
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
|
||||
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
|
||||
|
||||
var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
|
||||
?? throw new Exception("Cannot open C# project");
|
||||
|
||||
// NOTE: The order in which changes are made to the project is important
|
||||
|
||||
// Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
|
||||
ProjectUtils.MigrateFromOldConfigNames(msbuildProject);
|
||||
|
||||
// Apply the other fixes only after configurations have been migrated
|
||||
|
||||
// Make sure the existing project has the ProjectTypeGuids property (for VisualStudio)
|
||||
ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject);
|
||||
|
||||
// Make sure the existing project has Api assembly references configured correctly
|
||||
ProjectUtils.FixApiHintPath(msbuildProject);
|
||||
|
||||
// Make sure the existing project references the Microsoft.NETFramework.ReferenceAssemblies nuget package
|
||||
ProjectUtils.EnsureHasNugetNetFrameworkRefAssemblies(msbuildProject);
|
||||
|
||||
if (msbuildProject.HasUnsavedChanges)
|
||||
{
|
||||
// Save a copy of the project before replacing it
|
||||
FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
|
||||
|
||||
msbuildProject.Save();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError(e.ToString());
|
||||
}
|
||||
ApplyNecessaryChangesToSolution();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -15,6 +15,10 @@ namespace GodotTools.Internals
|
|||
public bool Nested { get; }
|
||||
public long BaseCount { get; }
|
||||
|
||||
public string SearchName => Nested ?
|
||||
Name.Substring(Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
|
||||
Name;
|
||||
|
||||
public ClassDecl(string name, string @namespace, bool nested, long baseCount)
|
||||
{
|
||||
Name = name;
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "../utils/path_utils.h"
|
||||
#include "../utils/string_utils.h"
|
||||
#include "csharp_project.h"
|
||||
|
||||
#define CS_INDENT " " // 4 whitespaces
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* csharp_project.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "csharp_project.h"
|
||||
|
||||
#include "core/io/json.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
#include "../utils/string_utils.h"
|
||||
#include "script_class_parser.h"
|
||||
|
||||
namespace CSharpProject {
|
||||
|
||||
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
|
||||
if (!GLOBAL_DEF("mono/project/auto_update_project", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly();
|
||||
|
||||
GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils");
|
||||
|
||||
Variant project_path = p_project_path;
|
||||
Variant item_type = p_item_type;
|
||||
Variant include = p_include;
|
||||
const Variant *args[3] = { &project_path, &item_type, &include };
|
||||
MonoException *exc = nullptr;
|
||||
klass->get_method("AddItemToProjectChecked", 3)->invoke(nullptr, args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CSharpProject
|
|
@ -1,42 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* csharp_project.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef CSHARP_PROJECT_H
|
||||
#define CSHARP_PROJECT_H
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
namespace CSharpProject {
|
||||
|
||||
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
|
||||
|
||||
} // namespace CSharpProject
|
||||
|
||||
#endif // CSHARP_PROJECT_H
|
|
@ -1,38 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{AEBF0036-DA76-4341-B651-A3F2856AB2FA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>bin/$(Configuration)</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<RootNamespace>Godot</RootNamespace>
|
||||
<AssemblyName>GodotSharp</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
|
||||
<BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
|
||||
<EnableDefaultItems>false</EnableDefaultItems>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Core\AABB.cs" />
|
||||
<Compile Include="Core\Array.cs" />
|
||||
|
@ -90,5 +68,4 @@
|
|||
Fortunately code completion, go to definition and such still work.
|
||||
-->
|
||||
<Import Project="Generated\GeneratedIncludes.props" />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
|
|
|
@ -1,27 +1,3 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("GodotSharp")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
[assembly: InternalsVisibleTo("GodotSharpEditor")]
|
||||
|
|
|
@ -1,46 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8FBEC238-D944-4074-8548-B3B524305905}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>bin/$(Configuration)</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<RootNamespace>Godot</RootNamespace>
|
||||
<AssemblyName>GodotSharpEditor</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
|
||||
<BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
|
||||
<EnableDefaultItems>false</EnableDefaultItems>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="Generated\GeneratedIncludes.props" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj">
|
||||
<Private>False</Private>
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!--
|
||||
We import a props file with auto-generated includes. This works well with Rider.
|
||||
However, Visual Studio and MonoDevelop won't list them in the solution explorer.
|
||||
We can't use wildcards as there may be undesired old files still hanging around.
|
||||
Fortunately code completion, go to definition and such still work.
|
||||
-->
|
||||
<Import Project="Generated\GeneratedIncludes.props" />
|
||||
</Project>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
using System.Reflection;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("GodotSharpEditor")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
Loading…
Reference in New Issue