In c# use the environment variable DOTNET_ROOT support to first check before using path which parallels modules/mono/editor/hostfxr_resolver.cpp's get_dotnet_root_from_env which does this check for c++ side.

Additionally supports editor settings for environments where environment variables dont work:

Fixes Issue: https://github.com/godotengine/godot/issues/97501
Implements Proposal: https://github.com/godotengine/godot-proposals/issues/1941

removed unused variable
This commit is contained in:
jpetersen 2024-10-09 11:43:12 -07:00
parent 4c4e673344
commit 4e2df6aa79
4 changed files with 123 additions and 14 deletions

View file

@ -20,13 +20,13 @@ namespace GodotTools.Build
private static Process LaunchBuild(BuildInfo buildInfo, Action<string?>? stdOutHandler,
Action<string?>? stdErrHandler)
{
string? dotnetPath = DotNetFinder.FindDotNetExe();
var editorSettings = EditorInterface.Singleton.GetEditorSettings();
string? dotnetPath = DotNetFinder.FindDotNetExe(editorSettings.GetSetting(GodotSharpEditor.Settings.OverrideDotnetExecutable).As<string>());
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var startInfo = new ProcessStartInfo(dotnetPath);
BuildArguments(buildInfo, startInfo.ArgumentList, editorSettings);
@ -91,13 +91,13 @@ namespace GodotTools.Build
private static Process LaunchPublish(BuildInfo buildInfo, Action<string?>? stdOutHandler,
Action<string?>? stdErrHandler)
{
string? dotnetPath = DotNetFinder.FindDotNetExe();
var editorSettings = EditorInterface.Singleton.GetEditorSettings();
string? dotnetPath = DotNetFinder.FindDotNetExe(editorSettings.GetSetting(GodotSharpEditor.Settings.OverrideDotnetExecutable).As<string>());
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var startInfo = new ProcessStartInfo(dotnetPath);
BuildPublishArguments(buildInfo, startInfo.ArgumentList, editorSettings);

View file

@ -11,8 +11,55 @@ namespace GodotTools.Build
{
public static class DotNetFinder
{
public static string? FindDotNetExe()
private static string DOTNET_ROOT_ENVIRONMENT_VARIABLE = "DOTNET_ROOT";
public static string? FindDotNetExe(string? overrideDotnetExecutable)
{
if (string.IsNullOrEmpty(overrideDotnetExecutable) == false)
{
if (File.Exists(overrideDotnetExecutable))
{
return overrideDotnetExecutable;
}
}
// Simplified parallel to modules/mono/editor/hostfxr_resolver.cpp's get_dotnet_root_from_env where
// DOTNET_ROOT is checked before path.
// https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#net-sdk-and-cli-environment-variables
// Further support would be to handle DOTNET_ROOT, DOTNET_ROOT(x86), DOTNET_ROOT_X86, DOTNET_ROOT_X64 for specific architectures.
string envVariableToDotnetPath = Environment.GetEnvironmentVariable(DOTNET_ROOT_ENVIRONMENT_VARIABLE);
if (string.IsNullOrEmpty(envVariableToDotnetPath) == false)
{
if (Directory.Exists(envVariableToDotnetPath))
{
string defaultDotnet = OS.PathWhich("dotnet");
if (string.IsNullOrEmpty(defaultDotnet) == false)
{
string fileName = Path.GetFileName(defaultDotnet);
string dotnetExecutable = Path.Join(envVariableToDotnetPath, fileName);
if (File.Exists(dotnetExecutable))
{
return dotnetExecutable;
}
else if (Godot.OS.IsStdOutVerbose())
{
Console.WriteLine(
$"Environment Variable \"{DOTNET_ROOT_ENVIRONMENT_VARIABLE}\"= \"{envVariableToDotnetPath}\" but does not contain a valid path to \"{dotnetExecutable}\".");
}
}
else if (Godot.OS.IsStdOutVerbose())
{
Console.WriteLine(
$"No default dotnet to use as method to connect \"{DOTNET_ROOT_ENVIRONMENT_VARIABLE}\" with correct executable name for platform.");
}
}
else if (Godot.OS.IsStdOutVerbose())
{
Console.WriteLine(
$"Environment Variable \"{DOTNET_ROOT_ENVIRONMENT_VARIABLE}\" = \"{envVariableToDotnetPath}\" but is not a directory.");
}
}
// In the future, this method may do more than just search in PATH. We could look in
// known locations or use Godot's linked nethost to search from the hostfxr location.
@ -38,6 +85,7 @@ namespace GodotTools.Build
public static bool TryFindDotNetSdk(
Version expectedVersion,
string? overrideDotnetExecutable,
[NotNullWhen(true)] out Version? version,
[NotNullWhen(true)] out string? path
)
@ -45,7 +93,7 @@ namespace GodotTools.Build
version = null;
path = null;
string? dotNetExe = FindDotNetExe();
string? dotNetExe = FindDotNetExe(overrideDotnetExecutable);
if (string.IsNullOrEmpty(dotNetExe))
return false;

View file

@ -34,6 +34,7 @@ namespace GodotTools
public const string NoConsoleLogging = "dotnet/build/no_console_logging";
public const string CreateBinaryLog = "dotnet/build/create_binary_log";
public const string ProblemsLayout = "dotnet/build/problems_layout";
public const string OverrideDotnetExecutable = "dotnet/build/override_dotnet_executable";
}
#nullable disable
@ -264,8 +265,7 @@ namespace GodotTools
GodotSharpDirs.ProjectSlnPath,
line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath
};
string command = DotNetFinder.FindDotNetExe() ?? "dotnet";
string command = DotNetFinder.FindDotNetExe(_editorSettings.GetSetting(GodotSharpEditor.Settings.OverrideDotnetExecutable).As<string>()) ?? "dotnet";
try
{
@ -456,9 +456,14 @@ namespace GodotTools
var dotNetSdkSearchVersion = Environment.Version;
_editorSettings = EditorInterface.Singleton.GetEditorSettings();
string? dotnetExecutableEditorSettings = _editorSettings.GetSetting(GodotSharpEditor.Settings.OverrideDotnetExecutable)
.As<string>();
// First we try to find the .NET Sdk ourselves to make sure we get the
// correct version first, otherwise pick the latest.
if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string? sdkPath))
if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, dotnetExecutableEditorSettings, out var sdkVersion, out string? sdkPath))
{
if (Godot.OS.IsStdOutVerbose())
Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
@ -483,8 +488,6 @@ namespace GodotTools
var editorBaseControl = EditorInterface.Singleton.GetBaseControl();
_editorSettings = EditorInterface.Singleton.GetEditorSettings();
_errorDialog = new AcceptDialog();
_errorDialog.SetUnparentWhenInvisible(true);
@ -542,6 +545,7 @@ namespace GodotTools
EditorDef(Settings.VerbosityLevel, Variant.From(VerbosityLevelId.Normal));
EditorDef(Settings.NoConsoleLogging, false);
EditorDef(Settings.CreateBinaryLog, false);
EditorDef(Settings.OverrideDotnetExecutable, "");
EditorDef(Settings.ProblemsLayout, Variant.From(BuildProblemsView.ProblemsLayout.Tree));
string settingsHintStr = "Disabled";

View file

@ -67,6 +67,7 @@ SOFTWARE.
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_settings.h"
#ifdef WINDOWS_ENABLED
#define WIN32_LEAN_AND_MEAN
@ -318,6 +319,61 @@ bool get_dotnet_root_from_env(String &r_dotnet_root) {
return get_file_path_from_env(dotnet_root_env, r_dotnet_root);
}
const StringName get_dotnet_editor_settings_name() {
return StringName("dotnet/build/override_dotnet_executable");
}
bool get_dotnet_exe_from_settings(String &r_dotnet_exe) {
const EditorSettings *editor_settings = EditorSettings::get_singleton();
if (editor_settings == nullptr) {
// load editor settings.
EditorSettings::create();
editor_settings = EditorSettings::get_singleton();
if (editor_settings == nullptr) {
return false;
}
}
const StringName editor_settings_name = get_dotnet_editor_settings_name();
// Use EDITOR_DEF to default it to "", if not found.
const Variant property_val = EDITOR_DEF(editor_settings_name, "");
if (property_val.is_string() == false) {
if (OS::get_singleton()->is_stdout_verbose()) {
ERR_PRINT("The editor setting (" + editor_settings_name + ") is not referencing a valid string path.");
}
return false;
}
String dotnet_exe = property_val;
if (dotnet_exe.is_empty()) {
return false;
}
r_dotnet_exe = path::abspath(path::realpath(dotnet_exe));
return true;
}
bool get_dotnet_root_from_settings(String &r_dotnet_root) {
String dotnet_exe;
if (!get_dotnet_exe_from_settings(dotnet_exe)) {
return false;
}
r_dotnet_root = dotnet_exe.get_base_dir();
if (!DirAccess::exists(r_dotnet_root)) {
if (OS::get_singleton()->is_stdout_verbose()) {
ERR_PRINT("The editor setting (" + get_dotnet_editor_settings_name() + ") is not referencing a valid string path (" + r_dotnet_root + ").");
}
return false;
}
return true;
}
} //namespace
bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {
@ -332,7 +388,8 @@ bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p
}
bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {
if (!get_dotnet_root_from_env(r_dotnet_root) &&
if (!get_dotnet_root_from_settings(r_dotnet_root) &&
!get_dotnet_root_from_env(r_dotnet_root) &&
!get_dotnet_self_registered_dir(r_dotnet_root) &&
!get_default_installation_dir(r_dotnet_root)) {
return false;