Merge pull request #41408 from neikeq/senkyu
3.2 New csproj style with backport of Godot.NET.Sdk
This commit is contained in:
commit
20e411545c
|
@ -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
|
||||
|
|
|
@ -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>3.2.3</Version>
|
||||
<PackageVersion>3.2.3</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,119 @@
|
|||
<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))' ">x11</GodotTargetPlatform>
|
||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">x11</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)' == 'x11' ">GODOT_X11;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>
|
||||
|
||||
<PropertyGroup Condition=" '$(AutomaticallyUseReferenceAssemblyPackages)' == '' and '$(MicrosoftNETFrameworkReferenceAssembliesLatestPackageVersion)' == '' ">
|
||||
<!-- Old 'Microsoft.NET.Sdk' so we reference the 'Microsoft.NETFramework.ReferenceAssemblies' package ourselves. -->
|
||||
<AutomaticallyUseReferenceAssemblyPackages>true</AutomaticallyUseReferenceAssemblyPackages>
|
||||
<MicrosoftNETFrameworkReferenceAssembliesLatestPackageVersion>1.0.0</MicrosoftNETFrameworkReferenceAssembliesLatestPackageVersion>
|
||||
<GodotUseNETFrameworkRefAssemblies>true</GodotUseNETFrameworkRefAssemblies>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,34 @@
|
|||
<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>
|
||||
|
||||
<!-- Backported from newer Microsoft.NET.Sdk version -->
|
||||
<Target Name="IncludeTargetingPackReference" BeforeTargets="_GetRestoreSettingsPerFramework;_CheckForInvalidConfigurationAndPlatform"
|
||||
Condition=" '$(GodotUseNETFrameworkRefAssemblies)' == 'true' and '$(TargetFrameworkMoniker)' != '' and '$(TargetFrameworkIdentifier)' == '.NETFramework' and '$(AutomaticallyUseReferenceAssemblyPackages)' == 'true' ">
|
||||
<GetReferenceAssemblyPaths
|
||||
TargetFrameworkMoniker="$(TargetFrameworkMoniker)"
|
||||
RootPath="$(TargetFrameworkRootPath)"
|
||||
TargetFrameworkFallbackSearchPaths="$(TargetFrameworkFallbackSearchPaths)"
|
||||
BypassFrameworkInstallChecks="$(BypassFrameworkInstallChecks)"
|
||||
SuppressNotFoundError="true">
|
||||
<Output TaskParameter="FullFrameworkReferenceAssemblyPaths" PropertyName="_FullFrameworkReferenceAssemblyPaths"/>
|
||||
</GetReferenceAssemblyPaths>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="$(MicrosoftNETFrameworkReferenceAssembliesLatestPackageVersion)" IsImplicitlyDefined="true" Condition="'$(_FullFrameworkReferenceAssemblyPaths)' == ''"/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.IO;
|
||||
using System.Security;
|
||||
using Microsoft.Build.Framework;
|
||||
using GodotTools.Core;
|
||||
|
||||
namespace GodotTools.BuildLogger
|
||||
{
|
||||
|
@ -18,7 +17,7 @@ namespace GodotTools.BuildLogger
|
|||
if (null == Parameters)
|
||||
throw new LoggerException("Log directory was not set.");
|
||||
|
||||
var parameters = Parameters.Split(new[] { ';' });
|
||||
var parameters = Parameters.Split(new[] {';'});
|
||||
|
||||
string logDir = parameters[0];
|
||||
|
||||
|
@ -183,4 +182,17 @@ namespace GodotTools.BuildLogger
|
|||
private StreamWriter issuesStreamWriter;
|
||||
private int indent;
|
||||
}
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string CsvEscape(this string value, char delimiter = ',')
|
||||
{
|
||||
bool hasSpecialChar = value.IndexOfAny(new[] {'\"', '\n', '\r', delimiter}) != -1;
|
||||
|
||||
if (hasSpecialChar)
|
||||
return "\"" + value.Replace("\"", "\"\"") + "\"";
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>7</LangVersion>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
|
||||
|
|
|
@ -19,7 +19,10 @@ namespace GodotTools.Core
|
|||
}
|
||||
|
||||
if (attempt > maxAttempts + 1)
|
||||
return;
|
||||
{
|
||||
// Overwrite the oldest one
|
||||
backupPath = backupPathBase;
|
||||
}
|
||||
|
||||
File.Copy(filePath, backupPath, overwrite: true);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>7</LangVersion>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -34,23 +34,13 @@ namespace GodotTools.Core
|
|||
return rooted ? Path.DirectorySeparatorChar + path : path;
|
||||
}
|
||||
|
||||
private static readonly string driveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
|
||||
private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
|
||||
|
||||
public static bool IsAbsolutePath(this string path)
|
||||
{
|
||||
return path.StartsWith("/", StringComparison.Ordinal) ||
|
||||
path.StartsWith("\\", StringComparison.Ordinal) ||
|
||||
path.StartsWith(driveRoot, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string CsvEscape(this string value, char delimiter = ',')
|
||||
{
|
||||
bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1;
|
||||
|
||||
if (hasSpecialChar)
|
||||
return "\"" + value.Replace("\"", "\"\"") + "\"";
|
||||
|
||||
return value;
|
||||
path.StartsWith(DriveRoot, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>7</LangVersion>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build" />
|
||||
<PackageReference Include="DotNet.Glob" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Build" Version="16.5.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!--
|
||||
The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
|
||||
here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
|
||||
We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
|
||||
searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
|
||||
-->
|
||||
<None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -2,8 +2,9 @@ using GodotTools.Core;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DotNet.Globbing;
|
||||
using System.Linq;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Globbing;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
|
@ -11,8 +12,6 @@ namespace GodotTools.ProjectEditor
|
|||
{
|
||||
public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
|
||||
{
|
||||
GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}};
|
||||
|
||||
string normalizedInclude = include.NormalizePath();
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
|
@ -25,7 +24,7 @@ namespace GodotTools.ProjectEditor
|
|||
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;
|
||||
|
@ -34,10 +33,9 @@ namespace GodotTools.ProjectEditor
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
|
||||
{
|
||||
GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}};
|
||||
|
||||
string normalizedInclude = Path.GetFullPath(include).NormalizePath();
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
|
@ -50,7 +48,7 @@ namespace GodotTools.ProjectEditor
|
|||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
var glob = Glob.Parse(Path.GetFullPath(item.Include).NormalizePath(), globOptions);
|
||||
var glob = MSBuildGlob.Parse(Path.GetFullPath(item.Include).NormalizePath());
|
||||
|
||||
if (glob.IsMatch(normalizedInclude))
|
||||
return item;
|
||||
|
@ -117,5 +115,19 @@ namespace GodotTools.ProjectEditor
|
|||
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
public static bool AreDefaultCompileItemsEnabled(this ProjectRootElement root)
|
||||
{
|
||||
var enableDefaultCompileItemsProps = root.PropertyGroups
|
||||
.Where(g => string.IsNullOrEmpty(g.Condition))
|
||||
.SelectMany(g => g.Properties
|
||||
.Where(p => p.Name == "EnableDefaultCompileItems" && string.IsNullOrEmpty(p.Condition)));
|
||||
|
||||
bool enableDefaultCompileItems = true;
|
||||
foreach (var prop in enableDefaultCompileItemsProps)
|
||||
enableDefaultCompileItems = prop.Value.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return enableDefaultCompileItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "3.2.3";
|
||||
|
||||
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", "net472");
|
||||
|
||||
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,17 +1,20 @@
|
|||
using System;
|
||||
using GodotTools.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using DotNet.Globbing;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Globbing;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public sealed class MSBuildProject
|
||||
{
|
||||
public ProjectRootElement Root { get; }
|
||||
internal ProjectRootElement Root { get; set; }
|
||||
|
||||
public bool HasUnsavedChanges { get; set; }
|
||||
|
||||
|
@ -31,12 +34,20 @@ namespace GodotTools.ProjectEditor
|
|||
return root != null ? new MSBuildProject(root) : null;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
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);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
// No need to add. It's already included automatically by the MSBuild Sdk.
|
||||
// This assumes the source file is inside the project directory and not manually excluded in the csproj
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\");
|
||||
|
||||
if (root.AddItemChecked(itemType, normalizedInclude))
|
||||
|
@ -49,6 +60,13 @@ namespace GodotTools.ProjectEditor
|
|||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
// No need to add. It's already included automatically by the MSBuild Sdk.
|
||||
// This assumes the source file is inside the project directory and not manually excluded in the csproj
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedOldInclude = oldInclude.NormalizePath();
|
||||
var normalizedNewInclude = newInclude.NormalizePath();
|
||||
|
||||
|
@ -66,6 +84,13 @@ namespace GodotTools.ProjectEditor
|
|||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
// No need to add. It's already included automatically by the MSBuild Sdk.
|
||||
// This assumes the source file is inside the project directory and not manually excluded in the csproj
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedInclude = include.NormalizePath();
|
||||
|
||||
if (root.RemoveItemChecked(itemType, normalizedInclude))
|
||||
|
@ -78,6 +103,13 @@ namespace GodotTools.ProjectEditor
|
|||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
// No need to add. It's already included automatically by the MSBuild Sdk.
|
||||
// This assumes the source file is inside the project directory and not manually excluded in the csproj
|
||||
return;
|
||||
}
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
var oldFolderNormalized = oldFolder.NormalizePath();
|
||||
|
@ -102,6 +134,13 @@ namespace GodotTools.ProjectEditor
|
|||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
// No need to add. It's already included automatically by the MSBuild Sdk.
|
||||
// This assumes the source file is inside the project directory and not manually excluded in the csproj
|
||||
return;
|
||||
}
|
||||
|
||||
var folderNormalized = folder.NormalizePath();
|
||||
|
||||
var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList();
|
||||
|
@ -133,12 +172,32 @@ namespace GodotTools.ProjectEditor
|
|||
var result = new List<string>();
|
||||
var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
|
||||
|
||||
var globOptions = new GlobOptions();
|
||||
globOptions.Evaluation.CaseInsensitive = false;
|
||||
|
||||
var root = ProjectRootElement.Open(projectPath);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
if (root.AreDefaultCompileItemsEnabled())
|
||||
{
|
||||
var excluded = new List<string>();
|
||||
result = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs").ToList();
|
||||
|
||||
foreach (var item in root.Items)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.Condition))
|
||||
continue;
|
||||
|
||||
if (item.ItemType != itemType)
|
||||
continue;
|
||||
|
||||
string normalizedExclude = item.Exclude.NormalizePath();
|
||||
|
||||
var glob = MSBuildGlob.Parse(normalizedExclude);
|
||||
|
||||
excluded.AddRange(result.Where(includedFile => glob.IsMatch(includedFile)));
|
||||
}
|
||||
|
||||
result.RemoveAll(f => excluded.Contains(f));
|
||||
}
|
||||
|
||||
foreach (var itemGroup in root.ItemGroups)
|
||||
{
|
||||
if (itemGroup.Condition.Length != 0)
|
||||
|
@ -151,9 +210,7 @@ namespace GodotTools.ProjectEditor
|
|||
|
||||
string normalizedInclude = item.Include.NormalizePath();
|
||||
|
||||
var glob = Glob.Parse(normalizedInclude, globOptions);
|
||||
|
||||
// TODO Check somehow if path has no blob to avoid the following loop...
|
||||
var glob = MSBuildGlob.Parse(normalizedInclude);
|
||||
|
||||
foreach (var existingFile in existingFiles)
|
||||
{
|
||||
|
@ -168,222 +225,186 @@ namespace GodotTools.ProjectEditor
|
|||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static void EnsureHasProjectTypeGuids(MSBuildProject project)
|
||||
public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
|
||||
{
|
||||
var root = project.Root;
|
||||
|
||||
bool found = root.PropertyGroups.Any(pg =>
|
||||
string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
|
||||
|
||||
if (found)
|
||||
if (!string.IsNullOrEmpty(root.Sdk))
|
||||
return;
|
||||
|
||||
root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
|
||||
root.Sdk = ProjectGenerator.GodotSdkAttrValue;
|
||||
|
||||
root.ToolsVersion = null;
|
||||
root.DefaultTargets = null;
|
||||
|
||||
root.AddProperty("TargetFramework", "net472");
|
||||
|
||||
// Remove obsolete properties, items and elements. We're going to be conservative
|
||||
// here to minimize the chances of introducing breaking changes. As such we will
|
||||
// only remove elements that could potentially cause issues with the Godot.NET.Sdk.
|
||||
|
||||
void RemoveElements(IEnumerable<ProjectElement> elements)
|
||||
{
|
||||
foreach (var element in elements)
|
||||
element.Parent.RemoveChild(element);
|
||||
}
|
||||
|
||||
// Default Configuration
|
||||
|
||||
RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
|
||||
.Where(p => p.Name == "Configuration" && p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Debug"));
|
||||
|
||||
// Default Platform
|
||||
|
||||
RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
|
||||
.Where(p => p.Name == "Platform" && p.Condition.Trim() == "'$(Platform)' == ''" && p.Value == "AnyCPU"));
|
||||
|
||||
// Simple properties
|
||||
|
||||
var yabaiProperties = new[]
|
||||
{
|
||||
"OutputPath",
|
||||
"BaseIntermediateOutputPath",
|
||||
"IntermediateOutputPath",
|
||||
"TargetFrameworkVersion",
|
||||
"ProjectTypeGuids",
|
||||
"ApiConfiguration"
|
||||
};
|
||||
|
||||
RemoveElements(root.PropertyGroups.SelectMany(g => g.Properties)
|
||||
.Where(p => yabaiProperties.Contains(p.Name)));
|
||||
|
||||
// Configuration dependent properties
|
||||
|
||||
var yabaiPropertiesForConfigs = new[]
|
||||
{
|
||||
"DebugSymbols",
|
||||
"DebugType",
|
||||
"Optimize",
|
||||
"DefineConstants",
|
||||
"ErrorReport",
|
||||
"WarningLevel",
|
||||
"ConsolePause"
|
||||
};
|
||||
|
||||
foreach (var config in new[] {"ExportDebug", "ExportRelease", "Debug"})
|
||||
{
|
||||
var group = root.PropertyGroups
|
||||
.First(g => g.Condition.Trim() == $"'$(Configuration)|$(Platform)' == '{config}|AnyCPU'");
|
||||
|
||||
RemoveElements(group.Properties.Where(p => yabaiPropertiesForConfigs.Contains(p.Name)));
|
||||
|
||||
if (group.Count == 0)
|
||||
{
|
||||
// No more children, safe to delete the group
|
||||
group.Parent.RemoveChild(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Godot API References
|
||||
|
||||
var apiAssemblies = new[] {ApiAssemblyNames.Core, ApiAssemblyNames.Editor};
|
||||
|
||||
RemoveElements(root.ItemGroups.SelectMany(g => g.Items)
|
||||
.Where(i => i.ItemType == "Reference" && apiAssemblies.Contains(i.Include)));
|
||||
|
||||
// Microsoft.NETFramework.ReferenceAssemblies PackageReference
|
||||
|
||||
RemoveElements(root.ItemGroups.SelectMany(g => g.Items).Where(i =>
|
||||
i.ItemType == "PackageReference" &&
|
||||
i.Include.Equals("Microsoft.NETFramework.ReferenceAssemblies", StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
// Imports
|
||||
|
||||
var yabaiImports = new[]
|
||||
{
|
||||
"$(MSBuildBinPath)/Microsoft.CSharp.targets",
|
||||
"$(MSBuildBinPath)Microsoft.CSharp.targets"
|
||||
};
|
||||
|
||||
RemoveElements(root.Imports.Where(import => yabaiImports.Contains(
|
||||
import.Project.Replace("\\", "/").Replace("//", "/"))));
|
||||
|
||||
// 'EnableDefaultCompileItems' and 'GenerateAssemblyInfo' are kept enabled by default
|
||||
// on new projects, but when migrating old projects we disable them to avoid errors.
|
||||
root.AddProperty("EnableDefaultCompileItems", "false");
|
||||
root.AddProperty("GenerateAssemblyInfo", "false");
|
||||
|
||||
// Older AssemblyInfo.cs cause the following error:
|
||||
// 'Properties/AssemblyInfo.cs(19,28): error CS8357:
|
||||
// The specified version string contains wildcards, which are not compatible with determinism.
|
||||
// Either remove wildcards from the version string, or disable determinism for this compilation.'
|
||||
// We disable deterministic builds to prevent this. The user can then fix this manually when desired
|
||||
// by fixing 'AssemblyVersion("1.0.*")' to not use wildcards.
|
||||
root.AddProperty("Deterministic", "false");
|
||||
|
||||
project.HasUnsavedChanges = true;
|
||||
|
||||
var xDoc = XDocument.Parse(root.RawXml);
|
||||
|
||||
if (xDoc.Root == null)
|
||||
return; // Too bad, we will have to keep the xmlns/namespace and xml declaration
|
||||
|
||||
XElement GetElement(XDocument doc, string name, string value, string parentName)
|
||||
{
|
||||
foreach (var node in doc.DescendantNodes())
|
||||
{
|
||||
if (!(node is XElement element))
|
||||
continue;
|
||||
if (element.Name.LocalName.Equals(name) && element.Value == value &&
|
||||
element.Parent != null && element.Parent.Name.LocalName.Equals(parentName))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add comment about Microsoft.NET.Sdk properties disabled during migration
|
||||
|
||||
GetElement(xDoc, name: "EnableDefaultCompileItems", value: "false", parentName: "PropertyGroup")
|
||||
.AddBeforeSelf(new XComment("The following properties were overriden during migration to prevent errors.\n" +
|
||||
" Enabling them may require other manual changes to the project and its files."));
|
||||
|
||||
void RemoveNamespace(XElement element)
|
||||
{
|
||||
element.Attributes().Where(x => x.IsNamespaceDeclaration).Remove();
|
||||
element.Name = element.Name.LocalName;
|
||||
|
||||
foreach (var node in element.DescendantNodes())
|
||||
{
|
||||
if (node is XElement xElement)
|
||||
{
|
||||
// Need to do the same for all children recursively as it adds it to them for some reason...
|
||||
RemoveNamespace(xElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove xmlns/namespace
|
||||
RemoveNamespace(xDoc.Root);
|
||||
|
||||
// Remove xml declaration
|
||||
xDoc.Nodes().FirstOrDefault(node => node.NodeType == XmlNodeType.XmlDeclaration)?.Remove();
|
||||
|
||||
string projectFullPath = root.FullPath;
|
||||
|
||||
root = ProjectRootElement.Create(xDoc.CreateReader());
|
||||
root.FullPath = projectFullPath;
|
||||
|
||||
project.Root = root;
|
||||
}
|
||||
|
||||
/// 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 ToolButton 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
|
||||
|
|
|
@ -36,15 +36,13 @@ namespace GodotTools.Build
|
|||
}
|
||||
case BuildTool.MsBuildVs:
|
||||
{
|
||||
if (_msbuildToolsPath.Empty() || !File.Exists(_msbuildToolsPath))
|
||||
if (string.IsNullOrEmpty(_msbuildToolsPath) || !File.Exists(_msbuildToolsPath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_msbuildToolsPath = FindMsBuildToolsPathOnWindows();
|
||||
|
||||
if (_msbuildToolsPath.Empty())
|
||||
{
|
||||
if (string.IsNullOrEmpty(_msbuildToolsPath))
|
||||
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!_msbuildToolsPath.EndsWith("\\"))
|
||||
|
@ -57,15 +55,14 @@ namespace GodotTools.Build
|
|||
string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
|
||||
|
||||
if (!File.Exists(msbuildPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildMono}'. Tried with path: {msbuildPath}");
|
||||
}
|
||||
|
||||
return (msbuildPath, BuildTool.MsBuildMono);
|
||||
}
|
||||
case BuildTool.JetBrainsMsBuild:
|
||||
{
|
||||
var editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
|
||||
|
||||
if (!File.Exists(editorPath))
|
||||
throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}");
|
||||
|
||||
|
@ -83,7 +80,7 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
if (OS.IsUnixLike())
|
||||
if (OS.IsUnixLike)
|
||||
{
|
||||
switch (buildTool)
|
||||
{
|
||||
|
@ -138,12 +135,12 @@ namespace GodotTools.Build
|
|||
{
|
||||
string ret = OS.PathWhich(name);
|
||||
|
||||
if (!ret.Empty())
|
||||
if (!string.IsNullOrEmpty(ret))
|
||||
return ret;
|
||||
|
||||
string retFallback = OS.PathWhich($"{name}.exe");
|
||||
|
||||
if (!retFallback.Empty())
|
||||
if (!string.IsNullOrEmpty(retFallback))
|
||||
return retFallback;
|
||||
|
||||
foreach (string hintDir in MsBuildHintDirs)
|
||||
|
@ -195,7 +192,7 @@ namespace GodotTools.Build
|
|||
|
||||
string value = line.Substring(sepIdx + 1).StripEdges();
|
||||
|
||||
if (value.Empty())
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new FormatException("installationPath value is empty");
|
||||
|
||||
if (!value.EndsWith("\\"))
|
||||
|
|
|
@ -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, 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()
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace GodotTools
|
|||
{
|
||||
string[] csvColumns = file.GetCsvLine();
|
||||
|
||||
if (csvColumns.Length == 1 && csvColumns[0].Empty())
|
||||
if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
|
||||
return;
|
||||
|
||||
if (csvColumns.Length != 7)
|
||||
|
@ -115,12 +115,12 @@ namespace GodotTools
|
|||
// Get correct issue idx from issue list
|
||||
int issueIndex = (int)issuesList.GetItemMetadata(idx);
|
||||
|
||||
if (idx < 0 || idx >= issues.Count)
|
||||
if (issueIndex < 0 || issueIndex >= issues.Count)
|
||||
throw new IndexOutOfRangeException("Issue index out of range");
|
||||
|
||||
BuildIssue issue = issues[issueIndex];
|
||||
|
||||
if (issue.ProjectFile.Empty() && issue.File.Empty())
|
||||
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
|
||||
return;
|
||||
|
||||
string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir();
|
||||
|
@ -158,14 +158,14 @@ namespace GodotTools
|
|||
string tooltip = string.Empty;
|
||||
tooltip += $"Message: {issue.Message}";
|
||||
|
||||
if (!issue.Code.Empty())
|
||||
if (!string.IsNullOrEmpty(issue.Code))
|
||||
tooltip += $"\nCode: {issue.Code}";
|
||||
|
||||
tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
|
||||
|
||||
string text = string.Empty;
|
||||
|
||||
if (!issue.File.Empty())
|
||||
if (!string.IsNullOrEmpty(issue.File))
|
||||
{
|
||||
text += $"{issue.File}({issue.Line},{issue.Column}): ";
|
||||
|
||||
|
@ -174,7 +174,7 @@ namespace GodotTools
|
|||
tooltip += $"\nColumn: {issue.Column}";
|
||||
}
|
||||
|
||||
if (!issue.ProjectFile.Empty())
|
||||
if (!string.IsNullOrEmpty(issue.ProjectFile))
|
||||
tooltip += $"\nProject: {issue.ProjectFile}";
|
||||
|
||||
text += issue.Message;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -302,7 +302,7 @@ namespace GodotTools
|
|||
|
||||
case ExternalEditorId.VsCode:
|
||||
{
|
||||
if (_vsCodePath.Empty() || !File.Exists(_vsCodePath))
|
||||
if (string.IsNullOrEmpty(_vsCodePath) || !File.Exists(_vsCodePath))
|
||||
{
|
||||
// Try to search it again if it wasn't found last time or if it was removed from its location
|
||||
_vsCodePath = VsCodeNames.SelectFirstNotNull(OS.PathWhich, orElse: string.Empty);
|
||||
|
@ -354,7 +354,7 @@ namespace GodotTools
|
|||
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
if (!osxAppBundleInstalled && _vsCodePath.Empty())
|
||||
if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
|
||||
{
|
||||
GD.PushError("Cannot find code editor: VSCode");
|
||||
return Error.FileNotFound;
|
||||
|
@ -364,7 +364,7 @@ namespace GodotTools
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_vsCodePath.Empty())
|
||||
if (string.IsNullOrEmpty(_vsCodePath))
|
||||
{
|
||||
GD.PushError("Cannot find code editor: VSCode");
|
||||
return Error.FileNotFound;
|
||||
|
@ -403,6 +403,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();
|
||||
|
@ -478,42 +509,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
|
||||
{
|
||||
|
@ -551,7 +547,7 @@ namespace GodotTools
|
|||
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
|
||||
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
|
||||
}
|
||||
else if (OS.IsUnixLike())
|
||||
else if (OS.IsUnixLike)
|
||||
{
|
||||
settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
|
||||
<DataDirToolsOutputPath>$(GodotSourceRootPath)/bin/GodotSharp/Tools</DataDirToolsOutputPath>
|
||||
<GodotApiConfiguration>Debug</GodotApiConfiguration>
|
||||
<LangVersion>7</LangVersion>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<GodotApiConfiguration>Debug</GodotApiConfiguration> <!-- The Godot editor uses the Debug Godot API assemblies -->
|
||||
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
|
||||
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
|
||||
|
|
|
@ -128,7 +128,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
{EditorId.MonoDevelop, "MonoDevelop.exe"}
|
||||
};
|
||||
}
|
||||
else if (OS.IsUnixLike())
|
||||
else if (OS.IsUnixLike)
|
||||
{
|
||||
ExecutableNames = new Dictionary<EditorId, string>
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace GodotTools.Ides.Rider
|
|||
{
|
||||
return CollectRiderInfosMac();
|
||||
}
|
||||
if (OS.IsUnixLike())
|
||||
if (OS.IsUnixLike)
|
||||
{
|
||||
return CollectAllRiderPathsLinux();
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ namespace GodotTools.Ides.Rider
|
|||
return GetToolboxRiderRootPath(localAppData);
|
||||
}
|
||||
|
||||
if (OS.IsUnixLike())
|
||||
if (OS.IsUnixLike)
|
||||
{
|
||||
var home = Environment.GetEnvironmentVariable("HOME");
|
||||
if (string.IsNullOrEmpty(home))
|
||||
|
@ -209,7 +209,7 @@ namespace GodotTools.Ides.Rider
|
|||
|
||||
private static string GetRelativePathToBuildTxt()
|
||||
{
|
||||
if (OS.IsWindows || OS.IsUnixLike())
|
||||
if (OS.IsWindows || OS.IsUnixLike)
|
||||
return "../../build.txt";
|
||||
if (OS.IsOSX)
|
||||
return "Contents/Resources/build.txt";
|
||||
|
|
|
@ -15,6 +15,10 @@ namespace GodotTools.Internals
|
|||
public bool Nested { get; }
|
||||
public int BaseCount { get; }
|
||||
|
||||
public string SearchName => Nested ?
|
||||
Name.Substring(Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
|
||||
Name;
|
||||
|
||||
public ClassDecl(string name, string @namespace, bool nested, int baseCount)
|
||||
{
|
||||
Name = name;
|
||||
|
|
|
@ -62,6 +62,11 @@ namespace GodotTools.Utils
|
|||
return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsAnyOS(IEnumerable<string> names)
|
||||
{
|
||||
return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
|
||||
private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX));
|
||||
private static readonly Lazy<bool> _isX11 = new Lazy<bool>(() => IsOS(Names.X11));
|
||||
|
@ -71,6 +76,7 @@ namespace GodotTools.Utils
|
|||
private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
|
||||
private static readonly Lazy<bool> _isiOS = new Lazy<bool>(() => IsOS(Names.iOS));
|
||||
private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
|
||||
private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
|
||||
|
||||
public static bool IsWindows => _isWindows.Value || IsUWP;
|
||||
public static bool IsOSX => _isOSX.Value;
|
||||
|
@ -82,18 +88,9 @@ namespace GodotTools.Utils
|
|||
public static bool IsiOS => _isiOS.Value;
|
||||
public static bool IsHTML5 => _isHTML5.Value;
|
||||
|
||||
private static bool? _isUnixCache;
|
||||
private static readonly string[] UnixLikePlatforms = { Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android, Names.iOS };
|
||||
private static readonly string[] UnixLikePlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android, Names.iOS};
|
||||
|
||||
public static bool IsUnixLike()
|
||||
{
|
||||
if (_isUnixCache.HasValue)
|
||||
return _isUnixCache.Value;
|
||||
|
||||
string osName = GetPlatformName();
|
||||
_isUnixCache = UnixLikePlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
|
||||
return _isUnixCache.Value;
|
||||
}
|
||||
public static bool IsUnixLike => _isUnixLike.Value;
|
||||
|
||||
public static char PathSep => IsWindows ? ';' : ':';
|
||||
|
||||
|
@ -121,10 +118,10 @@ namespace GodotTools.Utils
|
|||
return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
|
||||
|
||||
return (from dir in searchDirs
|
||||
select Path.Combine(dir, name)
|
||||
select Path.Combine(dir, name)
|
||||
into path
|
||||
from ext in windowsExts
|
||||
select path + ext).FirstOrDefault(File.Exists);
|
||||
from ext in windowsExts
|
||||
select path + ext).FirstOrDefault(File.Exists);
|
||||
}
|
||||
|
||||
private static string PathWhichUnix([NotNull] string name)
|
||||
|
@ -189,7 +186,7 @@ namespace GodotTools.Utils
|
|||
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
using (var process = new Process { StartInfo = startInfo })
|
||||
using (var process = new Process {StartInfo = startInfo})
|
||||
{
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -32,69 +32,71 @@
|
|||
|
||||
#include <mono/metadata/image.h>
|
||||
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
#include "../mono_gd/gd_mono.h"
|
||||
#include "../mono_gd/gd_mono_assembly.h"
|
||||
#include "../mono_gd/gd_mono_cache.h"
|
||||
#include "../utils/macros.h"
|
||||
|
||||
namespace GodotSharpExport {
|
||||
|
||||
String get_assemblyref_name(MonoImage *p_image, int index) {
|
||||
struct AssemblyRefInfo {
|
||||
String name;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t build;
|
||||
uint16_t revision;
|
||||
};
|
||||
|
||||
AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
|
||||
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
|
||||
|
||||
uint32_t cols[MONO_ASSEMBLYREF_SIZE];
|
||||
|
||||
mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
|
||||
|
||||
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
|
||||
return {
|
||||
String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])),
|
||||
(uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION],
|
||||
(uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION],
|
||||
(uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER],
|
||||
(uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER]
|
||||
};
|
||||
}
|
||||
|
||||
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
|
||||
MonoImage *image = p_assembly->get_image();
|
||||
|
||||
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
||||
String ref_name = get_assemblyref_name(image, i);
|
||||
AssemblyRefInfo ref_info = get_assemblyref_name(image, i);
|
||||
|
||||
const String &ref_name = ref_info.name;
|
||||
|
||||
if (r_assembly_dependencies.has(ref_name))
|
||||
continue;
|
||||
|
||||
GDMonoAssembly *ref_assembly = NULL;
|
||||
String path;
|
||||
bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
|
||||
|
||||
for (int j = 0; j < p_search_dirs.size(); j++) {
|
||||
const String &search_dir = p_search_dirs[j];
|
||||
{
|
||||
MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A"
|
||||
CRASH_COND(ref_aname == nullptr);
|
||||
SCOPE_EXIT {
|
||||
mono_assembly_name_free(ref_aname);
|
||||
mono_free(ref_aname);
|
||||
};
|
||||
|
||||
if (has_extension) {
|
||||
path = search_dir.plus_file(ref_name);
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(ref_name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
mono_assembly_get_assemblyref(image, i, ref_aname);
|
||||
|
||||
path = search_dir.plus_file(ref_name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
|
||||
if (ref_assembly != NULL)
|
||||
break;
|
||||
}
|
||||
if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
|
||||
ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
||||
}
|
||||
|
||||
r_assembly_dependencies[ref_name] = ref_assembly->get_path();
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
||||
|
||||
// Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
|
||||
r_assembly_dependencies[ref_name] = path;
|
||||
|
||||
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
|
||||
}
|
||||
|
@ -113,6 +115,11 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
|
|||
Vector<String> search_dirs;
|
||||
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
|
||||
|
||||
if (p_custom_bcl_dir.length()) {
|
||||
// Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory.
|
||||
r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path();
|
||||
}
|
||||
|
||||
for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
|
||||
String assembly_name = *key;
|
||||
String assembly_path = p_initial_assemblies[*key];
|
||||
|
|
|
@ -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.0</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" />
|
||||
|
@ -82,5 +60,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.0</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("")]
|
|
@ -521,8 +521,8 @@ void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
|
|||
|
||||
GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
|
||||
|
||||
if (p_name == "mscorlib")
|
||||
return get_corlib_assembly();
|
||||
if (p_name == "mscorlib" && corlib_assembly)
|
||||
return corlib_assembly;
|
||||
|
||||
MonoDomain *domain = mono_domain_get();
|
||||
uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
|
||||
|
@ -532,7 +532,9 @@ GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
|
|||
|
||||
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(!r_assembly);
|
||||
#endif
|
||||
|
||||
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
||||
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
|
||||
|
@ -544,26 +546,27 @@ bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bo
|
|||
|
||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(!r_assembly);
|
||||
#endif
|
||||
|
||||
return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs());
|
||||
}
|
||||
|
||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(!r_assembly);
|
||||
#endif
|
||||
|
||||
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
|
||||
|
||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
||||
MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
|
||||
GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
|
||||
|
||||
if (!assembly)
|
||||
return false;
|
||||
|
||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
|
||||
|
||||
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
|
||||
|
||||
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
|
||||
|
||||
ERR_FAIL_COND_V(stored_assembly == NULL, false);
|
||||
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
|
||||
|
||||
*r_assembly = *stored_assembly;
|
||||
*r_assembly = assembly;
|
||||
|
||||
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
|
||||
|
||||
|
|
|
@ -241,6 +241,7 @@ public:
|
|||
|
||||
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
|
||||
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <mono/metadata/mono-debug.h>
|
||||
#include <mono/metadata/tokentype.h>
|
||||
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/list.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
|
@ -99,7 +100,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
|
|||
// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
|
||||
// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
|
||||
|
||||
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
|
||||
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) {
|
||||
|
||||
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
|
||||
|
||||
|
@ -133,9 +134,7 @@ MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *an
|
|||
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) {
|
||||
|
||||
(void)user_data; // UNUSED
|
||||
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) {
|
||||
|
||||
String name = String::utf8(mono_assembly_name_get_name(aname));
|
||||
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
|
||||
|
@ -147,15 +146,13 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
return NULL;
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
|
||||
|
||||
(void)user_data; // UNUSED
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) {
|
||||
|
||||
String name = String::utf8(mono_assembly_name_get_name(aname));
|
||||
return _load_assembly_search(name, search_dirs, refonly);
|
||||
return _load_assembly_search(name, aname, refonly, search_dirs);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
|
||||
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
|
||||
|
||||
MonoAssembly *res = NULL;
|
||||
String path;
|
||||
|
@ -168,21 +165,21 @@ MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const
|
|||
if (has_extension) {
|
||||
path = search_dir.plus_file(p_name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _real_load_assembly_from(path, p_refonly);
|
||||
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(p_name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _real_load_assembly_from(path, p_refonly);
|
||||
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(p_name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _real_load_assembly_from(path, p_refonly);
|
||||
res = _real_load_assembly_from(path, p_refonly, p_aname);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
|
@ -230,7 +227,7 @@ void GDMonoAssembly::initialize() {
|
|||
mono_install_assembly_load_hook(&assembly_load_hook, NULL);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly) {
|
||||
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
|
||||
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
|
||||
ERR_FAIL_COND_V_MSG(data.empty(), NULL, "Could read the assembly in the specified location");
|
||||
|
@ -255,7 +252,33 @@ MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, boo
|
|||
true, &status, p_refonly,
|
||||
image_filename.utf8());
|
||||
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, NULL, "Failed to open assembly image from the loaded data");
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, NULL, "Failed to open assembly image from memory: '" + p_path + "'.");
|
||||
|
||||
if (p_aname != nullptr) {
|
||||
// Check assembly version
|
||||
const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY);
|
||||
|
||||
ERR_FAIL_NULL_V(table, nullptr);
|
||||
|
||||
if (mono_table_info_get_rows(table)) {
|
||||
uint32_t cols[MONO_ASSEMBLY_SIZE];
|
||||
mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE);
|
||||
|
||||
// Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision.
|
||||
uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION];
|
||||
uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION];
|
||||
|
||||
uint16_t required_minor;
|
||||
uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr);
|
||||
|
||||
if (required_major != 0) {
|
||||
if (major != required_major && minor != required_minor) {
|
||||
mono_image_close(image);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Vector<uint8_t> pdb_data;
|
||||
|
@ -283,7 +306,7 @@ no_pdb:
|
|||
|
||||
MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, NULL, "Failed to load assembly for image");
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
|
||||
|
||||
if (need_manual_load_hook) {
|
||||
// For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else),
|
||||
|
@ -425,6 +448,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
|
|||
return match;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
|
||||
|
||||
if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll"))
|
||||
return GDMono::get_singleton()->get_corlib_assembly();
|
||||
|
||||
// We need to manually call the search hook in this case, as it won't be called in the next step
|
||||
MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname);
|
||||
|
||||
if (!assembly) {
|
||||
assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
|
||||
ERR_FAIL_NULL_V(assembly, nullptr);
|
||||
}
|
||||
|
||||
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
|
||||
ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
|
||||
ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr);
|
||||
|
||||
return loaded_asm;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||
|
||||
if (p_name == "mscorlib" || p_name == "mscorlib.dll")
|
||||
|
|
|
@ -93,8 +93,8 @@ class GDMonoAssembly {
|
|||
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
|
||||
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
|
||||
|
||||
static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly);
|
||||
static MonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
|
||||
static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr);
|
||||
static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||
|
||||
friend class GDMono;
|
||||
static void initialize();
|
||||
|
@ -120,7 +120,9 @@ public:
|
|||
static String find_assembly(const String &p_name);
|
||||
|
||||
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
|
||||
static const Vector<String> &get_default_search_dirs() { return search_dirs; }
|
||||
|
||||
static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
|
||||
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||
|
||||
GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly);
|
||||
|
|
|
@ -175,7 +175,7 @@ void GDMonoLog::initialize() {
|
|||
log_level_id = get_log_level_id(log_level.get_data());
|
||||
|
||||
if (log_file) {
|
||||
OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data());
|
||||
OS::get_singleton()->print("Mono: Log file is: '%s'\n", log_file_path.utf8().get_data());
|
||||
mono_trace_set_log_handler(mono_log_callback, this);
|
||||
} else {
|
||||
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
|
||||
|
|
|
@ -81,4 +81,25 @@
|
|||
} while (true);
|
||||
#endif
|
||||
|
||||
namespace gdmono {
|
||||
|
||||
template <typename F>
|
||||
struct ScopeExit {
|
||||
ScopeExit(F p_exit_func) :
|
||||
exit_func(p_exit_func) {}
|
||||
~ScopeExit() { exit_func(); }
|
||||
F exit_func;
|
||||
};
|
||||
|
||||
class ScopeExitAux {
|
||||
public:
|
||||
template <typename F>
|
||||
ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); }
|
||||
};
|
||||
|
||||
} // namespace gdmono
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]() -> void
|
||||
|
||||
#endif // UTIL_MACROS_H
|
||||
|
|
Loading…
Reference in New Issue