3.2 C#: Upgrade GodotTools to nuget Microsoft.Build

This upgrade is needed in order to support
reading and editing project files that use Sdks
as well as other new features. A common example
in 3.2 is having to specify a PackageReference
version with a child element rather than the
attribute. This is no longer the case now.

Partial cherry-pick of f3bcd5f8dd
Most of the other changes from that commit were already partially
cherry-picked in 3928fe200f.
This commit is contained in:
Ignacio Etcheverry 2020-08-20 12:56:36 +02:00
parent e1f17a0b35
commit 4d7b7d9b73
15 changed files with 71 additions and 84 deletions

View file

@ -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;
}
}
}

View file

@ -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" />

View file

@ -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>

View file

@ -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)

View file

@ -1,16 +1,24 @@
<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="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>

View file

@ -2,8 +2,8 @@ using GodotTools.Core;
using System;
using System.Collections.Generic;
using System.IO;
using DotNet.Globbing;
using Microsoft.Build.Construction;
using Microsoft.Build.Globbing;
namespace GodotTools.ProjectEditor
{
@ -11,8 +11,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 +23,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;
@ -36,8 +34,6 @@ namespace GodotTools.ProjectEditor
}
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 +46,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;

View file

@ -4,8 +4,8 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using DotNet.Globbing;
using Microsoft.Build.Construction;
using Microsoft.Build.Globbing;
namespace GodotTools.ProjectEditor
{
@ -133,9 +133,6 @@ 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);
@ -151,7 +148,7 @@ namespace GodotTools.ProjectEditor
string normalizedInclude = item.Include.NormalizePath();
var glob = Glob.Parse(normalizedInclude, globOptions);
var glob = MSBuildGlob.Parse(normalizedInclude);
// TODO Check somehow if path has no blob to avoid the following loop...

View file

@ -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("\\"))

View file

@ -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;

View file

@ -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;
@ -551,7 +551,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}" +

View file

@ -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>

View file

@ -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>
{

View file

@ -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";

View file

@ -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();