232 lines
8.9 KiB
C#
232 lines
8.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Godot;
|
|
using GodotTools.Ides.Rider;
|
|
using GodotTools.Internals;
|
|
using Directory = System.IO.Directory;
|
|
using Environment = System.Environment;
|
|
using File = System.IO.File;
|
|
using Path = System.IO.Path;
|
|
using OS = GodotTools.Utils.OS;
|
|
|
|
namespace GodotTools.Build
|
|
{
|
|
public static class MsBuildFinder
|
|
{
|
|
private static string _msbuildToolsPath = string.Empty;
|
|
private static string _msbuildUnixPath = string.Empty;
|
|
|
|
public static (string, BuildTool) FindMsBuild()
|
|
{
|
|
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
|
var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
|
|
|
|
if (OS.IsWindows)
|
|
{
|
|
switch (buildTool)
|
|
{
|
|
case BuildTool.DotnetCli:
|
|
{
|
|
string dotnetCliPath = OS.PathWhich("dotnet");
|
|
if (!string.IsNullOrEmpty(dotnetCliPath))
|
|
return (dotnetCliPath, BuildTool.DotnetCli);
|
|
GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
|
|
goto case BuildTool.MsBuildVs;
|
|
}
|
|
case BuildTool.MsBuildVs:
|
|
{
|
|
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 (string.IsNullOrEmpty(_msbuildToolsPath))
|
|
throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
|
|
}
|
|
|
|
if (!_msbuildToolsPath.EndsWith("\\"))
|
|
_msbuildToolsPath += "\\";
|
|
|
|
return (Path.Combine(_msbuildToolsPath, "MSBuild.exe"), BuildTool.MsBuildVs);
|
|
}
|
|
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}");
|
|
|
|
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}");
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
if (OS.IsUnixLike)
|
|
{
|
|
switch (buildTool)
|
|
{
|
|
case BuildTool.DotnetCli:
|
|
{
|
|
string dotnetCliPath = FindBuildEngineOnUnix("dotnet");
|
|
if (!string.IsNullOrEmpty(dotnetCliPath))
|
|
return (dotnetCliPath, BuildTool.DotnetCli);
|
|
GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
|
|
goto case BuildTool.MsBuildMono;
|
|
}
|
|
case BuildTool.MsBuildMono:
|
|
{
|
|
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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
throw new PlatformNotSupportedException();
|
|
}
|
|
|
|
private static IEnumerable<string> MsBuildHintDirs
|
|
{
|
|
get
|
|
{
|
|
var result = new List<string>();
|
|
|
|
if (OS.IsMacOS)
|
|
{
|
|
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
|
|
result.Add("/opt/local/bin/");
|
|
result.Add("/usr/local/var/homebrew/linked/mono/bin/");
|
|
result.Add("/usr/local/bin/");
|
|
result.Add("/usr/local/bin/dotnet/");
|
|
result.Add("/usr/local/share/dotnet/");
|
|
}
|
|
|
|
result.Add("/opt/novell/mono/bin/");
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private static string FindBuildEngineOnUnix(string name)
|
|
{
|
|
string ret = OS.PathWhich(name);
|
|
|
|
if (!string.IsNullOrEmpty(ret))
|
|
return ret;
|
|
|
|
string retFallback = OS.PathWhich($"{name}.exe");
|
|
|
|
if (!string.IsNullOrEmpty(retFallback))
|
|
return retFallback;
|
|
|
|
foreach (string hintDir in MsBuildHintDirs)
|
|
{
|
|
string hintPath = Path.Combine(hintDir, name);
|
|
|
|
if (File.Exists(hintPath))
|
|
return hintPath;
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
private static string FindMsBuildToolsPathOnWindows()
|
|
{
|
|
if (!OS.IsWindows)
|
|
throw new PlatformNotSupportedException();
|
|
|
|
// Try to find 15.0 with vswhere
|
|
|
|
var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"};
|
|
|
|
string vsWherePath = null;
|
|
foreach (var envName in envNames)
|
|
{
|
|
vsWherePath = Environment.GetEnvironmentVariable(envName);
|
|
if (!string.IsNullOrEmpty(vsWherePath))
|
|
{
|
|
vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
|
|
if (File.Exists(vsWherePath))
|
|
break;
|
|
}
|
|
|
|
vsWherePath = null;
|
|
}
|
|
|
|
var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
|
|
|
|
var outputArray = new Godot.Collections.Array<string>();
|
|
int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
|
|
output: (Godot.Collections.Array)outputArray);
|
|
|
|
if (exitCode != 0)
|
|
return string.Empty;
|
|
|
|
if (outputArray.Count == 0)
|
|
return string.Empty;
|
|
|
|
var lines = outputArray[0].Split('\n');
|
|
|
|
foreach (string line in lines)
|
|
{
|
|
int sepIdx = line.IndexOf(':');
|
|
|
|
if (sepIdx <= 0)
|
|
continue;
|
|
|
|
string key = line.Substring(0, sepIdx); // No need to trim
|
|
|
|
if (key != "installationPath")
|
|
continue;
|
|
|
|
string value = line.Substring(sepIdx + 1).StripEdges();
|
|
|
|
if (string.IsNullOrEmpty(value))
|
|
throw new FormatException("installationPath value is empty");
|
|
|
|
if (!value.EndsWith("\\"))
|
|
value += "\\";
|
|
|
|
// Since VS2019, the directory is simply named "Current"
|
|
string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
|
|
|
|
if (Directory.Exists(msbuildDir))
|
|
return msbuildDir;
|
|
|
|
// Directory name "15.0" is used in VS 2017
|
|
return Path.Combine(value, "MSBuild\\15.0\\Bin");
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|