Buildsystem improvements for the Mono module

- Make sure to search the mono installation directory for the right architecture in the windows registry.
- Do not build GodotSharpTools directly to #bin dir. Instead build to the default output path and copy it. This way we avoid MSBuild adding files we don't want to #bin.
- Add hint path for MSBuild in OSX.
- Copy shared library on Unix if not statically linking.
- Use vswhere to search MSBuild and search for 14.0 tools version in the registry instead of 4.0.
- SCons will only fallback xbuild when msbuild is not found if 'xbuild_fallback=yes' is passed to the command.
- Use mono's assembly path as FrameworkPathOverride if using with system's MSBuild (not mono's fork).
- Cleanup.
This commit is contained in:
Ignacio Etcheverry 2017-10-24 22:47:27 +02:00
parent 4396712137
commit 9f469887fc
6 changed files with 341 additions and 115 deletions

View file

@ -53,68 +53,149 @@ if env['tools']:
vars = Variables() vars = Variables()
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
vars.Update(env) vars.Update(env)
# Glue sources # Glue sources
if env['mono_glue']: if env['mono_glue']:
env.add_source_files(env.modules_sources, 'glue/*.cpp') env.add_source_files(env.modules_sources, 'glue/*.cpp')
else: else:
env.Append(CPPDEFINES = [ 'MONO_GLUE_DISABLED' ]) env.Append(CPPDEFINES=['MONO_GLUE_DISABLED'])
if ARGUMENTS.get('yolo_copy', False): if ARGUMENTS.get('yolo_copy', False):
env.Append(CPPDEFINES = [ 'YOLO_COPY' ]) env.Append(CPPDEFINES=['YOLO_COPY'])
# Build GodotSharpTools solution # Build GodotSharpTools solution
import os import os
import subprocess
import mono_reg_utils as monoreg
def find_msbuild_unix(filename):
import os.path
import sys
hint_dirs = ['/opt/novell/mono/bin']
if sys.platform == "darwin":
hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path):
return hint_path
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
return None
def find_msbuild_windows():
import mono_reg_utils as monoreg
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
if msbuild_tools_path:
return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), '')
else:
bits = env['bits']
if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
else:
mono_root = monoreg.find_mono_root_dir(bits)
else:
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
else:
mono_root = monoreg.find_mono_root_dir(bits)
if mono_root:
msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat')
if os.path.isfile(msbuild_mono):
return (msbuild_mono, os.path.join(mono_root, 'lib', 'mono', '4.5'))
return None
def mono_build_solution(source, target, env): def mono_build_solution(source, target, env):
if os.name == 'nt': import subprocess
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() import mono_reg_utils as monoreg
if not msbuild_tools_path: from shutil import copyfile
raise RuntimeError('Cannot find MSBuild Tools Path in the registry')
msbuild_path = os.path.join(msbuild_tools_path, 'MSBuild.exe')
else:
msbuild_path = 'msbuild'
output_path = os.path.abspath(os.path.join(str(target[0]), os.pardir)) framework_path_override = ''
if os.name == 'nt':
msbuild_info = find_msbuild_windows()
if msbuild_info is None:
raise RuntimeError('Cannot find MSBuild executable')
msbuild_path = msbuild_windows[0]
framework_path_override = msbuild_windows[1]
else:
msbuild_path = find_msbuild_unix('msbuild')
if msbuild_path is None:
xbuild_fallback = env['xbuild_fallback']
if xbuild_fallback and os.name == 'nt':
print("Option 'xbuild_fallback' not supported on Windows")
xbuild_fallback = False
if xbuild_fallback:
print('Cannot find MSBuild executable, trying with xbuild')
print('Warning: xbuild is deprecated')
msbuild_path = find_msbuild_unix('xbuild')
if msbuild_path is None:
raise RuntimeError('Cannot find xbuild executable')
else:
raise RuntimeError('Cannot find MSBuild executable')
print('MSBuild path: ' + msbuild_path)
build_config = 'Release'
msbuild_args = [ msbuild_args = [
msbuild_path, msbuild_path,
os.path.abspath(str(source[0])), os.path.abspath(str(source[0])),
'/p:Configuration=Release', '/p:Configuration=' + build_config,
'/p:OutputPath=' + output_path
] ]
if framework_path_override:
msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override]
msbuild_env = os.environ.copy() msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS # Needed when running from Developer Command Prompt for VS
if 'PLATFORM' in msbuild_env: if 'PLATFORM' in msbuild_env:
del msbuild_env['PLATFORM'] del msbuild_env['PLATFORM']
msbuild_alt_paths = [ 'xbuild' ]
while True:
try: try:
subprocess.check_call(msbuild_args, env = msbuild_env) subprocess.check_call(msbuild_args, env=msbuild_env)
break
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
raise RuntimeError('GodotSharpTools build failed') raise RuntimeError('GodotSharpTools build failed')
except OSError:
if os.name != 'nt': src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config))
if not msbuild_alt_paths: dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
raise RuntimeError('Could not find commands msbuild or xbuild')
# Try xbuild if not os.path.isdir(dst_dir):
msbuild_args[0] = msbuild_alt_paths.pop(0) if os.path.exists(dst_dir):
else: raise RuntimeError('Target directory is a file')
raise RuntimeError('Could not find command MSBuild.exe') os.makedirs(dst_dir)
asm_file = 'GodotSharpTools.dll'
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
mono_sln_builder = Builder(action = mono_build_solution) mono_sln_builder = Builder(action = mono_build_solution)
env.Append(BUILDERS = { 'MonoBuildSolution' : mono_sln_builder }) env.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
env.MonoBuildSolution( env.MonoBuildSolution(
os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'), os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),
'editor/GodotSharpTools/GodotSharpTools.sln' 'editor/GodotSharpTools/GodotSharpTools.sln'

View file

@ -2,7 +2,6 @@
import imp import imp
import os import os
import sys import sys
from shutil import copyfile
from SCons.Script import BoolVariable, Environment, Variables from SCons.Script import BoolVariable, Environment, Variables
@ -16,8 +15,7 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
for curfile in files: for curfile in files:
if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
return curfile return curfile
return ''
return None
def can_build(platform): def can_build(platform):
@ -31,6 +29,22 @@ def is_enabled():
return False return False
def copy_file_no_replace(src_dir, dst_dir, name):
from shutil import copyfile
src_path = os.path.join(src_dir, name)
dst_path = os.path.join(dst_dir, name)
need_copy = True
if not os.path.isdir(dst_dir):
os.mkdir(dst_dir)
elif os.path.exists(dst_path):
need_copy = False
if need_copy:
copyfile(src_path, dst_path)
def configure(env): def configure(env):
env.use_ptrcall = True env.use_ptrcall = True
@ -38,6 +52,8 @@ def configure(env):
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
envvars.Update(env) envvars.Update(env)
bits = env['bits']
mono_static = env['mono_static'] mono_static = env['mono_static']
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
@ -46,18 +62,18 @@ def configure(env):
if mono_static: if mono_static:
raise RuntimeError('mono-static: Not supported on Windows') raise RuntimeError('mono-static: Not supported on Windows')
if env['bits'] == '32': if bits == '32':
if os.getenv('MONO32_PREFIX'): if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX') mono_root = os.getenv('MONO32_PREFIX')
elif os.name == 'nt': elif os.name == 'nt':
mono_root = monoreg.find_mono_root_dir() mono_root = monoreg.find_mono_root_dir(bits)
else: else:
if os.getenv('MONO64_PREFIX'): if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX') mono_root = os.getenv('MONO64_PREFIX')
elif os.name == 'nt': elif os.name == 'nt':
mono_root = monoreg.find_mono_root_dir() mono_root = monoreg.find_mono_root_dir(bits)
if mono_root is None: if not mono_root:
raise RuntimeError('Mono installation directory not found') raise RuntimeError('Mono installation directory not found')
mono_lib_path = os.path.join(mono_root, 'lib') mono_lib_path = os.path.join(mono_root, 'lib')
@ -67,7 +83,7 @@ def configure(env):
mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
if mono_lib_name is None: if not mono_lib_name:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path) raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
if os.getenv('VCINSTALLDIR'): if os.getenv('VCINSTALLDIR'):
@ -79,28 +95,23 @@ def configure(env):
mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
mono_dll_src = os.path.join(mono_bin_path, mono_dll_name + '.dll') if not mono_dll_name:
mono_dll_dst = os.path.join('bin', mono_dll_name + '.dll') raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
copy_mono_dll = True
if not os.path.isdir('bin'): copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll')
os.mkdir('bin')
elif os.path.exists(mono_dll_dst):
copy_mono_dll = False
if copy_mono_dll:
copyfile(mono_dll_src, mono_dll_dst)
else: else:
mono_root = None sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
if env['bits'] == '32': mono_root = ''
if bits == '32':
if os.getenv('MONO32_PREFIX'): if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX') mono_root = os.getenv('MONO32_PREFIX')
else: else:
if os.getenv('MONO64_PREFIX'): if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX') mono_root = os.getenv('MONO64_PREFIX')
if mono_root is not None: if mono_root:
mono_lib_path = os.path.join(mono_root, 'lib') mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path) env.Append(LIBPATH=mono_lib_path)
@ -108,7 +119,7 @@ def configure(env):
mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
if mono_lib is None: if not mono_lib:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path) raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
env.Append(CPPFLAGS=['-D_REENTRANT']) env.Append(CPPFLAGS=['-D_REENTRANT'])
@ -130,12 +141,37 @@ def configure(env):
elif sys.platform == "linux" or sys.platform == "linux2": elif sys.platform == "linux" or sys.platform == "linux2":
env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
if not mono_static:
mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext)
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
else: else:
if mono_static: if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
env.ParseConfig('pkg-config monosgen-2 --cflags --libs') env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
mono_lib_path = ''
mono_so_name = ''
tmpenv = Environment()
tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
for hint_dir in tmpenv['LIBPATH']:
name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
if name_found:
mono_lib_path = hint_dir
mono_so_name = name_found
break
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
env.Append(LINKFLAGS='-rdynamic') env.Append(LINKFLAGS='-rdynamic')

View file

@ -4,6 +4,7 @@ using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security; using System.Security;
using Microsoft.Build.Framework; using Microsoft.Build.Framework;
@ -12,23 +13,37 @@ namespace GodotSharpTools.Build
public class BuildInstance : IDisposable public class BuildInstance : IDisposable
{ {
[MethodImpl(MethodImplOptions.InternalCall)] [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode); private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)] [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_BuildInstance_get_MSBuildPath(); private extern static MSBuildInfo godot_icall_BuildInstance_get_MSBuildInfo();
private static string MSBuildPath [StructLayout(LayoutKind.Sequential)]
private struct MSBuildInfo
{ {
get string path;
{ string frameworkPathOverride;
string ret = godot_icall_BuildInstance_get_MSBuildPath();
if (ret == null) public string MSBuildPath
{
get { return path; }
}
public string FrameworkPathOverride
{
get { return frameworkPathOverride; }
}
}
private static MSBuildInfo GetMSBuildInfo()
{
MSBuildInfo ret = godot_icall_BuildInstance_get_MSBuildInfo();
if (ret.MSBuildPath == null)
throw new FileNotFoundException("Cannot find the MSBuild executable."); throw new FileNotFoundException("Cannot find the MSBuild executable.");
return ret; return ret;
} }
}
private string solution; private string solution;
private string config; private string config;
@ -48,9 +63,19 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{ {
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties); MSBuildInfo msbuildInfo = GetMSBuildInfo();
ProcessStartInfo startInfo = new ProcessStartInfo(MSBuildPath, compilerArgs); List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
if (msbuildInfo.FrameworkPathOverride.Length > 0)
customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.FrameworkPathOverride);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.MSBuildPath, compilerArgs);
// No console output, thanks // No console output, thanks
startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardOutput = true;
@ -82,9 +107,19 @@ namespace GodotSharpTools.Build
if (process != null) if (process != null)
throw new InvalidOperationException("Already in use"); throw new InvalidOperationException("Already in use");
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties); MSBuildInfo msbuildInfo = GetMSBuildInfo();
ProcessStartInfo startInfo = new ProcessStartInfo("msbuild", compilerArgs); List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
if (msbuildInfo.FrameworkPathOverride.Length > 0)
customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.FrameworkPathOverride);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.MSBuildPath, compilerArgs);
// No console output, thanks // No console output, thanks
startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardOutput = true;
@ -101,10 +136,13 @@ namespace GodotSharpTools.Build
process.Start(); process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return true; return true;
} }
private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties) private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
{ {
string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""", string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""",
solution, solution,
@ -114,12 +152,9 @@ namespace GodotSharpTools.Build
loggerOutputDir loggerOutputDir
); );
if (customProperties != null)
{
foreach (string customProperty in customProperties) foreach (string customProperty in customProperties)
{ {
arguments += " /p:" + customProperty; arguments += " \"/p:" + customProperty + "\"";
}
} }
return arguments; return arguments;

View file

@ -71,10 +71,15 @@ String _find_build_engine_on_unix(const String &p_name) {
} }
#endif #endif
MonoString *godot_icall_BuildInstance_get_MSBuildPath() { MonoString **godot_icall_BuildInstance_get_MSBuildInfo() {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))); GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
MonoString *res[2] = {
NULL, // MSBuildPath
NULL // FrameworkPathOverride
};
#if defined(WINDOWS_ENABLED) #if defined(WINDOWS_ENABLED)
switch (build_tool) { switch (build_tool) {
case GodotSharpBuilds::MSBUILD: { case GodotSharpBuilds::MSBUILD: {
@ -84,11 +89,17 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (!msbuild_tools_path.ends_with("\\")) if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\"; msbuild_tools_path += "\\";
return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe"); res[0] = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
// FrameworkPathOverride
res[1] = GDMonoMarshal::mono_string_from_godot(GDMono::get_singleton()->get_mono_reg_info().assembly_dir);
return res;
} }
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n"); OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
} } // fall through
case GodotSharpBuilds::MSBUILD_MONO: { case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat"); String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
@ -96,17 +107,9 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path); WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path);
} }
return GDMonoMarshal::mono_string_from_godot(msbuild_path); res[0] = GDMonoMarshal::mono_string_from_godot(msbuild_path);
} return res;
case GodotSharpBuilds::XBUILD: { } break;
String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
if (!FileAccess::exists(xbuild_path)) {
WARN_PRINTS("Cannot find xbuild ('mono/builds/build_tool'). Tried with path: " + xbuild_path);
}
return GDMonoMarshal::mono_string_from_godot(xbuild_path);
}
default: default:
ERR_EXPLAIN("You don't deserve to live"); ERR_EXPLAIN("You don't deserve to live");
CRASH_NOW(); CRASH_NOW();
@ -118,25 +121,26 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (build_tool != GodotSharpBuilds::XBUILD) { if (build_tool != GodotSharpBuilds::XBUILD) {
if (msbuild_path.empty()) { if (msbuild_path.empty()) {
WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool')."); WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
return NULL; return res;
} }
} else { } else {
if (xbuild_path.empty()) { if (xbuild_path.empty()) {
WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool')."); WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool').");
return NULL; return res;
} }
} }
return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path); res[0] = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
return res;
#else #else
return NULL; return res;
#endif #endif
} }
void GodotSharpBuilds::_register_internal_calls() { void GodotSharpBuilds::_register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath); mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo);
} }
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) { void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@ -353,9 +357,22 @@ GodotSharpBuilds::GodotSharpBuilds() {
// Build tool settings // Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton(); EditorSettings *ed_settings = EditorSettings::get_singleton();
if (!ed_settings->has_setting("mono/builds/build_tool")) { if (!ed_settings->has_setting("mono/builds/build_tool")) {
ed_settings->set_setting("mono/builds/build_tool", MSBUILD); ed_settings->set_setting("mono/builds/build_tool",
#ifdef WINDOWS_ENABLED
// TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version
MSBUILD
#else
MSBUILD_MONO
#endif
);
} }
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, "MSBuild (System),MSBuild (Mono),xbuild")); ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
#ifdef WINDOWS_ENABLED
"MSBuild (Mono),MSBuild (System)"
#else
"MSBuild (Mono),xbuild (Deprecated)"
#endif
));
} }
GodotSharpBuilds::~GodotSharpBuilds() { GodotSharpBuilds::~GodotSharpBuilds() {

View file

@ -67,9 +67,12 @@ public:
}; };
enum BuildTool { enum BuildTool {
MSBUILD,
MSBUILD_MONO, MSBUILD_MONO,
XBUILD #ifdef WINDOWS_ENABLED
MSBUILD
#else
XBUILD // Deprecated
#endif
}; };
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }

View file

@ -1,4 +1,5 @@
import os import os
import platform
if os.name == 'nt': if os.name == 'nt':
import sys import sys
@ -11,8 +12,7 @@ if os.name == 'nt':
def _reg_open_key(key, subkey): def _reg_open_key(key, subkey):
try: try:
return winreg.OpenKey(key, subkey) return winreg.OpenKey(key, subkey)
except (WindowsError, EnvironmentError) as e: except WindowsError, OSError:
import platform
if platform.architecture()[0] == '32bit': if platform.architecture()[0] == '32bit':
bitness_sam = winreg.KEY_WOW64_64KEY bitness_sam = winreg.KEY_WOW64_64KEY
else: else:
@ -20,39 +20,93 @@ def _reg_open_key(key, subkey):
return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam) return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
def _find_mono_in_reg(subkey): def _reg_open_key_bits(key, subkey, bits):
sam = winreg.KEY_READ
if platform.architecture()[0] == '32bit':
if bits == '64':
# Force 32bit process to search in 64bit registry
sam |= winreg.KEY_WOW64_64KEY
else:
if bits == '32':
# Force 64bit process to search in 32bit registry
sam |= winreg.KEY_WOW64_32KEY
return winreg.OpenKey(key, subkey, 0, sam)
def _find_mono_in_reg(subkey, bits):
try: try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot') value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot')
return value return value
except (WindowsError, EnvironmentError) as e: except WindowsError, OSError:
return None return None
def _find_mono_in_reg_old(subkey):
def _find_mono_in_reg_old(subkey, bits):
try: try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR') default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR')
if default_clr: if default_clr:
return _find_mono_in_reg(subkey + '\\' + default_clr) return _find_mono_in_reg(subkey + '\\' + default_clr, bits)
return None return None
except (WindowsError, EnvironmentError): except (WindowsError, EnvironmentError):
return None return None
def find_mono_root_dir(): def find_mono_root_dir(bits):
dir = _find_mono_in_reg(r'SOFTWARE\Mono') root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
if dir: if root_dir is not None:
return dir return root_dir
dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono') root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
if dir: if root_dir is not None:
return dir return root_dir
return None return ''
def find_msbuild_tools_path_reg(): def find_msbuild_tools_path_reg():
import subprocess
vswhere = os.getenv('PROGRAMFILES(X86)')
if not vswhere:
vswhere = os.getenv('PROGRAMFILES')
vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild']
try: try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0') as hKey: lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
for line in lines:
parts = line.split(':', 1)
if len(parts) < 2 or parts[0] != 'installationPath':
continue
val = parts[1].strip()
if not val:
raise ValueError('Value of `installationPath` entry is empty')
return os.path.join(val, "MSBuild\\15.0\\Bin")
raise ValueError('Cannot find `installationPath` entry')
except ValueError as e:
print('Error reading output from vswhere: ' + e.message)
except WindowsError:
pass # Fine, vswhere not found
except subprocess.CalledProcessError, OSError:
pass
# Try to find 14.0 in the Registry
try:
subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0'
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath') value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')
return value return value
except (WindowsError, EnvironmentError) as e: except WindowsError, OSError:
return None return ''
return ''