[3.2] C#: Support for building with the dotnet CLI

By adding a reference to the 'Microsoft.NETFramework.ReferenceAssemblies' nuget
package, we can build projects targeting .NET Framework with the dotnet CLI.
By referencing this package we also don't need to install Mono on Linux/macOS
or .NET Framework on Windows, as the assemblies are taken from the package.
This commit is contained in:
Ignacio Etcheverry 2020-05-10 22:56:35 +02:00
parent a9bcd8ba26
commit 3928fe200f
22 changed files with 232 additions and 436 deletions

View file

@ -15,7 +15,7 @@ def build_godot_tools(source, target, env):
from .solution_builder import build_solution
build_solution(env, solution_path, build_config, restore=True)
build_solution(env, solution_path, build_config)
# No need to copy targets. The GodotTools csproj takes care of copying them.

View file

@ -5,7 +5,29 @@ import os
verbose = False
def find_msbuild_unix(filename):
def find_dotnet_cli():
import os.path
if os.name == "nt":
windows_exts = os.environ["PATHEXT"]
windows_exts = windows_exts.split(os.pathsep) if windows_exts else []
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
return hint_path + ".exe"
else:
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
def find_msbuild_unix():
import os.path
import sys
@ -86,15 +108,7 @@ def run_command(command, args, env_override=None, name=None):
raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
def nuget_restore(env, *args):
global verbose
verbose = env['verbose']
# Do NuGet restore
run_command(nuget_path, ['restore'] + list(args), name='nuget restore')
def build_solution(env, solution_path, build_config, extra_msbuild_args=[], restore=False):
def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
global verbose
verbose = env['verbose']
@ -104,27 +118,33 @@ def build_solution(env, solution_path, build_config, extra_msbuild_args=[], rest
if 'PLATFORM' in msbuild_env:
del msbuild_env['PLATFORM']
# Find MSBuild
if os.name == 'nt':
msbuild_info = find_msbuild_windows(env)
if msbuild_info is None:
raise RuntimeError('Cannot find MSBuild executable')
msbuild_path = msbuild_info[0]
msbuild_env.update(msbuild_info[1])
msbuild_args = []
dotnet_cli = find_dotnet_cli()
if dotnet_cli:
msbuild_path = dotnet_cli
msbuild_args += ["msbuild"] # `dotnet msbuild` command
else:
msbuild_path = find_msbuild_unix('msbuild')
if msbuild_path is None:
raise RuntimeError("Cannot find MSBuild executable")
# Find MSBuild
if os.name == "nt":
msbuild_info = find_msbuild_windows(env)
if msbuild_info is None:
raise RuntimeError("Cannot find MSBuild executable")
msbuild_path = msbuild_info[0]
msbuild_env.update(msbuild_info[1])
else:
msbuild_path = find_msbuild_unix()
if msbuild_path is None:
raise RuntimeError("Cannot find MSBuild executable")
print('MSBuild path: ' + msbuild_path)
# Build solution
targets = ["Build"]
if restore:
targets.insert(0, "Restore")
targets = ["Restore", "Build"]
msbuild_args = [solution_path, "/t:%s" % ",".join(targets), "/p:Configuration=" + build_config]
msbuild_args += [solution_path, "/t:%s" % ",".join(targets), "/p:Configuration=" + build_config]
msbuild_args += extra_msbuild_args
run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild')

View file

@ -1,60 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GodotTools.BuildLogger</RootNamespace>
<AssemblyName>GodotTools.BuildLogger</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFramework>net472</TargetFramework>
<LangVersion>7</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Compile Include="GodotBuildLogger.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
<Project>{639e48bd-44e5-4091-8edd-22d36dc0768d}</Project>
<Name>GodotTools.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -1,35 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GodotTools.BuildLogger")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Godot Engine contributors")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("6CE9A984-37B1-4F8A-8FE9-609F05F071B3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -1,40 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" 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>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GodotTools.Core</RootNamespace>
<AssemblyName>GodotTools.Core</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFramework>net472</TargetFramework>
<LangVersion>7</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileUtils.cs" />
<Compile Include="ProcessExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StringExtensions.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -1,26 +0,0 @@
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("GodotTools.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Godot Engine contributors")]
[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("")]

View file

@ -1,57 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" 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>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GodotTools.ProjectEditor</RootNamespace>
<AssemblyName>GodotTools.ProjectEditor</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
<TargetFramework>net472</TargetFramework>
<LangVersion>7</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.Build" />
<Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
</Reference>
<PackageReference Include="DotNet.Glob" Version="2.1.1" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Compile Include="ApiAssembliesInfo.cs" />
<Compile Include="DotNetSolution.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IdentifierUtils.cs" />
<Compile Include="ProjectExtensions.cs" />
<Compile Include="ProjectGenerator.cs" />
<Compile Include="ProjectUtils.cs" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
<Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
<Name>GodotTools.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -125,6 +125,12 @@ namespace GodotTools.ProjectEditor
// 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("/", "\\"));

View file

@ -176,7 +176,7 @@ namespace GodotTools.ProjectEditor
void AddPropertyIfNotPresent(string name, string condition, string value)
{
if (root.PropertyGroups
.Any(g => (g.Condition == string.Empty || g.Condition.Trim() == condition) &&
.Any(g => (string.IsNullOrEmpty(g.Condition) || g.Condition.Trim() == condition) &&
g.Properties
.Any(p => p.Name == name &&
p.Value == value &&
@ -267,7 +267,7 @@ namespace GodotTools.ProjectEditor
bool hasGodotProjectGeneratorVersion = false;
bool foundOldConfiguration = false;
foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition == string.Empty))
foreach (var propertyGroup in root.PropertyGroups.Where(g => string.IsNullOrEmpty(g.Condition)))
{
if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
hasGodotProjectGeneratorVersion = true;
@ -283,7 +283,7 @@ namespace GodotTools.ProjectEditor
if (!hasGodotProjectGeneratorVersion)
{
root.PropertyGroups.First(g => g.Condition == string.Empty)?
root.PropertyGroups.First(g => string.IsNullOrEmpty(g.Condition))?
.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
project.HasUnsavedChanges = true;
}
@ -351,5 +351,25 @@ namespace GodotTools.ProjectEditor
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)
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");
project.HasUnsavedChanges = true;
}
}
}

View file

@ -1,27 +0,0 @@
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("GodotTools.ProjectEditor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Godot Engine contributors")]
[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("")]

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNet.Glob" version="2.1.1" targetFramework="net45" />
</packages>

View file

@ -14,16 +14,6 @@ namespace GodotTools.Build
{
public static class BuildSystem
{
private static string GetMsBuildPath()
{
string msbuildPath = MsBuildFinder.FindMsBuild();
if (msbuildPath == null)
throw new FileNotFoundException("Cannot find the MSBuild executable.");
return msbuildPath;
}
private static string MonoWindowsBinDir
{
get
@ -46,8 +36,8 @@ namespace GodotTools.Build
{
if (OS.IsWindows)
{
return (BuildManager.BuildTool)EditorSettings.GetSetting("mono/builds/build_tool")
== BuildManager.BuildTool.MsBuildMono;
return (BuildTool)EditorSettings.GetSetting("mono/builds/build_tool")
== BuildTool.MsBuildMono;
}
return false;
@ -57,16 +47,21 @@ namespace GodotTools.Build
private static bool PrintBuildOutput =>
(bool)EditorSettings.GetSetting("mono/builds/print_build_output");
private static Process LaunchBuild(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
private static Process LaunchBuild(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
if (msbuildPath == null)
throw new FileNotFoundException("Cannot find the MSBuild executable.");
var customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
string compilerArgs = BuildArguments(solution, config, loggerOutputDir, customPropertiesList);
string compilerArgs = BuildArguments(buildTool, solution, targets, config, loggerOutputDir, customPropertiesList);
var startInfo = new ProcessStartInfo(GetMsBuildPath(), compilerArgs);
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
@ -90,7 +85,7 @@ namespace GodotTools.Build
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
var process = new Process { StartInfo = startInfo };
var process = new Process {StartInfo = startInfo};
process.Start();
@ -105,19 +100,19 @@ namespace GodotTools.Build
public static int Build(BuildInfo buildInfo)
{
return Build(buildInfo.Solution, buildInfo.Configuration,
return Build(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
buildInfo.LogsDirPath, buildInfo.CustomProperties);
}
public static async Task<int> BuildAsync(BuildInfo buildInfo)
public static Task<int> BuildAsync(BuildInfo buildInfo)
{
return await BuildAsync(buildInfo.Solution, buildInfo.Configuration,
return BuildAsync(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
buildInfo.LogsDirPath, buildInfo.CustomProperties);
}
public static int Build(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
public static int Build(string solution, string[] targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
{
using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
{
process.WaitForExit();
@ -125,9 +120,9 @@ namespace GodotTools.Build
}
}
public static async Task<int> BuildAsync(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
public static async Task<int> BuildAsync(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
{
using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
{
await process.WaitForExitAsync();
@ -135,10 +130,15 @@ namespace GodotTools.Build
}
}
private static string BuildArguments(string solution, string config, string loggerOutputDir, List<string> customProperties)
private static string BuildArguments(BuildTool buildTool, string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties)
{
string arguments = $@"""{solution}"" /v:normal /t:Build ""/p:{"Configuration=" + config}"" " +
$@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
string arguments = string.Empty;
if (buildTool == BuildTool.DotnetCli)
arguments += "msbuild "; // `dotnet msbuild` command
arguments += $@"""{solution}"" /v:normal /t:{string.Join(",", targets)} ""/p:{"Configuration=" + config}"" " +
$@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
foreach (string customProperty in customProperties)
{

View file

@ -0,0 +1,10 @@
namespace GodotTools.Build
{
public enum BuildTool
{
MsBuildMono,
MsBuildVs,
JetBrainsMsBuild,
DotnetCli
}
}

View file

@ -17,16 +17,24 @@ namespace GodotTools.Build
private static string _msbuildToolsPath = string.Empty;
private static string _msbuildUnixPath = string.Empty;
public static string FindMsBuild()
public static (string, BuildTool) FindMsBuild()
{
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var buildTool = (BuildManager.BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
if (OS.IsWindows)
{
switch (buildTool)
{
case BuildManager.BuildTool.MsBuildVs:
case BuildTool.DotnetCli:
{
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Visual Studio.");
goto case BuildTool.MsBuildVs;
}
case BuildTool.MsBuildVs:
{
if (_msbuildToolsPath.Empty() || !File.Exists(_msbuildToolsPath))
{
@ -35,32 +43,41 @@ namespace GodotTools.Build
if (_msbuildToolsPath.Empty())
{
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMsbuildVs}'.");
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
}
}
if (!_msbuildToolsPath.EndsWith("\\"))
_msbuildToolsPath += "\\";
return Path.Combine(_msbuildToolsPath, "MSBuild.exe");
return (Path.Combine(_msbuildToolsPath, "MSBuild.exe"), BuildTool.MsBuildVs);
}
case BuildManager.BuildTool.MsBuildMono:
case BuildTool.MsBuildMono:
{
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}");
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildMono}'. Tried with path: {msbuildPath}");
}
return msbuildPath;
return (msbuildPath, BuildTool.MsBuildMono);
}
case BuildManager.BuildTool.JetBrainsMsBuild:
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}");
var riderDir = new FileInfo(editorPath).Directory.Parent;
return Path.Combine(riderDir.FullName, @"tools\MSBuild\Current\Bin\MSBuild.exe");
var riderDir = new FileInfo(editorPath).Directory?.Parent;
string msbuildPath = Path.Combine(riderDir.FullName, @"tools\MSBuild\Current\Bin\MSBuild.exe");
if (!File.Exists(msbuildPath))
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildJetBrains}'. Tried with path: {msbuildPath}");
return (msbuildPath, BuildTool.JetBrainsMsBuild);
}
default:
throw new IndexOutOfRangeException("Invalid build tool in editor settings");
}
@ -68,24 +85,31 @@ namespace GodotTools.Build
if (OS.IsUnixLike())
{
if (buildTool == BuildManager.BuildTool.MsBuildMono)
switch (buildTool)
{
if (_msbuildUnixPath.Empty() || !File.Exists(_msbuildUnixPath))
case BuildTool.DotnetCli:
{
// Try to search it again if it wasn't found last time or if it was removed from its location
_msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Mono.");
goto case BuildTool.MsBuildMono;
}
if (_msbuildUnixPath.Empty())
case BuildTool.MsBuildMono:
{
throw new FileNotFoundException($"Cannot find binary for '{BuildManager.PropNameMsbuildMono}'");
}
if (string.IsNullOrEmpty(_msbuildUnixPath) || !File.Exists(_msbuildUnixPath))
{
// Try to search it again if it wasn't found last time or if it was removed from its location
_msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
}
return _msbuildUnixPath;
}
else
{
throw new IndexOutOfRangeException("Invalid build tool in editor settings");
if (string.IsNullOrEmpty(_msbuildUnixPath))
throw new FileNotFoundException($"Cannot find binary for '{BuildManager.PropNameMSBuildMono}'");
return (_msbuildUnixPath, BuildTool.MsBuildMono);
}
default:
throw new IndexOutOfRangeException("Invalid build tool in editor settings");
}
}

View file

@ -10,6 +10,7 @@ namespace GodotTools
public sealed class BuildInfo : Reference // TODO Remove Reference once we have proper serialization
{
public string Solution { get; }
public string[] Targets { get; }
public string Configuration { get; }
public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
@ -38,9 +39,10 @@ namespace GodotTools
{
}
public BuildInfo(string solution, string configuration)
public BuildInfo(string solution, string[] targets, string configuration)
{
Solution = solution;
Targets = targets;
Configuration = configuration;
}
}

View file

@ -15,20 +15,14 @@ namespace GodotTools
{
private static readonly List<BuildInfo> BuildsInProgress = new List<BuildInfo>();
public const string PropNameMsbuildMono = "MSBuild (Mono)";
public const string PropNameMsbuildVs = "MSBuild (VS Build Tools)";
public const string PropNameMsbuildJetBrains = "MSBuild (JetBrains Rider)";
public const string PropNameMSBuildMono = "MSBuild (Mono)";
public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
public const string PropNameMSBuildJetBrains = "MSBuild (JetBrains Rider)";
public const string PropNameDotnetCli = "dotnet CLI";
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
public const string MsBuildLogFileName = "msbuild_log.txt";
public enum BuildTool
{
MsBuildMono,
MsBuildVs,
JetBrainsMsBuild
}
private static void RemoveOldIssuesFile(BuildInfo buildInfo)
{
var issuesFile = GetIssuesFilePath(buildInfo);
@ -181,10 +175,12 @@ namespace GodotTools
{
pr.Step("Building project solution", 0);
var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, config);
var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, new[] {"Restore", "Build"}, config);
bool escapeNeedsDoubleBackslash = buildTool == BuildTool.MsBuildMono || buildTool == BuildTool.DotnetCli;
// Add Godot defines
string constants = buildTool != BuildTool.MsBuildMono ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
string constants = !escapeNeedsDoubleBackslash ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
foreach (var godotDefine in godotDefines)
constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
@ -192,7 +188,7 @@ namespace GodotTools
if (Internal.GodotIsRealTDouble())
constants += "GODOT_REAL_T_IS_DOUBLE;";
constants += buildTool != BuildTool.MsBuildMono ? "\"" : "\\\"";
constants += !escapeNeedsDoubleBackslash ? "\"" : "\\\"";
buildInfo.CustomProperties.Add(constants);
@ -250,22 +246,44 @@ namespace GodotTools
{
// Build tool settings
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var msbuild = BuildTool.MsBuildMono;
if (OS.IsWindows)
msbuild = RiderPathManager.IsExternalEditorSetToRider(editorSettings)
? BuildTool.JetBrainsMsBuild
: BuildTool.MsBuildVs;
EditorDef("mono/builds/build_tool", msbuild);
BuildTool msbuildDefault;
if (OS.IsWindows)
{
if (RiderPathManager.IsExternalEditorSetToRider(editorSettings))
msbuildDefault = BuildTool.JetBrainsMsBuild;
else
msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildVs;
}
else
{
msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildMono;
}
EditorDef("mono/builds/build_tool", msbuildDefault);
string hintString;
if (OS.IsWindows)
{
hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
$"{PropNameMSBuildVs}:{(int)BuildTool.MsBuildVs}," +
$"{PropNameMSBuildJetBrains}:{(int)BuildTool.JetBrainsMsBuild}," +
$"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
}
else
{
hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
$"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
}
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Godot.Variant.Type.Int,
["name"] = "mono/builds/build_tool",
["hint"] = Godot.PropertyHint.Enum,
["hint_string"] = OS.IsWindows ?
$"{PropNameMsbuildMono},{PropNameMsbuildVs},{PropNameMsbuildJetBrains}" :
$"{PropNameMsbuildMono}"
["hint_string"] = hintString
});
EditorDef("mono/builds/print_build_output", false);

View file

@ -471,6 +471,9 @@ namespace GodotTools
// 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

View file

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" 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>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GodotTools</RootNamespace>
<AssemblyName>GodotTools</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFramework>net472</TargetFramework>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<DataDirToolsOutputPath>$(GodotSourceRootPath)/bin/GodotSharp/Tools</DataDirToolsOutputPath>
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<LangVersion>7</LangVersion>
<GodotApiConfiguration>Debug</GodotApiConfiguration> <!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
@ -18,36 +16,14 @@
<!-- The project is part of the Godot source tree -->
<!-- Use the Godot source tree output folder instead of '$(ProjectDir)/bin' -->
<OutputPath>$(GodotOutputDataDir)/Tools</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<!-- Must not append '$(TargetFramework)' to the output path in this case -->
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="GodotTools.IdeMessaging, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\packages\GodotTools.IdeMessaging.1.1.0\lib\netstandard2.0\GodotTools.IdeMessaging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="JetBrains.Annotations, Version=2019.1.3.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325">
<HintPath>..\packages\JetBrains.Annotations.2019.1.3\lib\net20\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<PackageReference Include="GodotTools.IdeMessaging" Version="1.1.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" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
@ -58,54 +34,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Build\MsBuildFinder.cs" />
<Compile Include="Export\AotBuilder.cs" />
<Compile Include="Export\ExportPlugin.cs" />
<Compile Include="Export\XcodeHelper.cs" />
<Compile Include="ExternalEditorId.cs" />
<Compile Include="Ides\GodotIdeManager.cs" />
<Compile Include="Ides\MessagingServer.cs" />
<Compile Include="Ides\MonoDevelop\EditorId.cs" />
<Compile Include="Ides\MonoDevelop\Instance.cs" />
<Compile Include="Ides\Rider\RiderPathLocator.cs" />
<Compile Include="Ides\Rider\RiderPathManager.cs" />
<Compile Include="Internals\EditorProgress.cs" />
<Compile Include="Internals\GodotSharpDirs.cs" />
<Compile Include="Internals\Internal.cs" />
<Compile Include="Internals\ScriptClassParser.cs" />
<Compile Include="Internals\Globals.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Build\BuildSystem.cs" />
<Compile Include="Utils\Directory.cs" />
<Compile Include="Utils\File.cs" />
<Compile Include="Utils\OS.cs" />
<Compile Include="GodotSharpEditor.cs" />
<Compile Include="BuildManager.cs" />
<Compile Include="HotReloadAssemblyWatcher.cs" />
<Compile Include="BuildInfo.cs" />
<Compile Include="BuildTab.cs" />
<Compile Include="BottomPanel.cs" />
<Compile Include="CsProjOperations.cs" />
<Compile Include="PlaySettings.cs" />
<Compile Include="Utils\CollectionExtensions.cs" />
<Compile Include="Utils\User32Dll.cs" />
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj">
<Project>{6ce9a984-37b1-4f8a-8fe9-609f05f071b3}</Project>
<Name>GodotTools.BuildLogger</Name>
</ProjectReference>
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj">
<Project>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</Project>
<Name>GodotTools.ProjectEditor</Name>
</ProjectReference>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
<Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
<Name>GodotTools.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -1,26 +0,0 @@
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("GodotTools")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Godot Engine contributors")]
[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("")]

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GodotTools.IdeMessaging" version="1.1.0" targetFramework="net47" />
<package id="JetBrains.Annotations" version="2019.1.3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
</packages>

View file

@ -30,6 +30,7 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>

View file

@ -30,6 +30,7 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>