diff options
| -rw-r--r-- | modules/mono/SCsub | 139 | ||||
| -rw-r--r-- | modules/mono/config.py | 80 | ||||
| -rw-r--r-- | modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs | 73 | ||||
| -rw-r--r-- | modules/mono/editor/godotsharp_builds.cpp | 61 | ||||
| -rw-r--r-- | modules/mono/editor/godotsharp_builds.h | 7 | ||||
| -rw-r--r-- | modules/mono/mono_reg_utils.py | 92 | 
6 files changed, 339 insertions, 113 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index caf4fdb3ca..13212636e3 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -53,68 +53,149 @@ if env['tools']:  vars = Variables()  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)  # Glue sources  if env['mono_glue']:      env.add_source_files(env.modules_sources, 'glue/*.cpp')  else: -    env.Append(CPPDEFINES = [ 'MONO_GLUE_DISABLED' ]) +    env.Append(CPPDEFINES=['MONO_GLUE_DISABLED'])  if ARGUMENTS.get('yolo_copy', False): -    env.Append(CPPDEFINES = [ 'YOLO_COPY' ]) +    env.Append(CPPDEFINES=['YOLO_COPY']) +  # Build GodotSharpTools solution +  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): +    import subprocess +    import mono_reg_utils as monoreg +    from shutil import copyfile + +    framework_path_override = '' +      if os.name == 'nt': -        msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() -        if not msbuild_tools_path: -            raise RuntimeError('Cannot find MSBuild Tools Path in the registry') -        msbuild_path = os.path.join(msbuild_tools_path, 'MSBuild.exe') +        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 = 'msbuild' +        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) -    output_path = os.path.abspath(os.path.join(str(target[0]), os.pardir)) +    build_config = 'Release'      msbuild_args = [          msbuild_path,          os.path.abspath(str(source[0])), -        '/p:Configuration=Release', -        '/p:OutputPath=' + output_path +        '/p:Configuration=' + build_config,      ] +    if framework_path_override: +        msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override] +      msbuild_env = os.environ.copy()      # Needed when running from Developer Command Prompt for VS      if 'PLATFORM' in msbuild_env:          del msbuild_env['PLATFORM'] -    msbuild_alt_paths = [ 'xbuild' ] - -    while True: -        try: -            subprocess.check_call(msbuild_args, env = msbuild_env) -            break -        except subprocess.CalledProcessError: -            raise RuntimeError('GodotSharpTools build failed') -        except OSError: -            if os.name != 'nt': -                if not msbuild_alt_paths: -                    raise RuntimeError('Could not find commands msbuild or xbuild') -                # Try xbuild -                msbuild_args[0] = msbuild_alt_paths.pop(0) -            else: -                raise RuntimeError('Could not find command MSBuild.exe') +    try: +        subprocess.check_call(msbuild_args, env=msbuild_env) +    except subprocess.CalledProcessError: +        raise RuntimeError('GodotSharpTools build failed') + +    src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config)) +    dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + +    if not os.path.isdir(dst_dir): +        if os.path.exists(dst_dir): +            raise RuntimeError('Target directory is a file') +        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) -env.Append(BUILDERS = { 'MonoBuildSolution' : mono_sln_builder }) +env.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})  env.MonoBuildSolution(      os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),      'editor/GodotSharpTools/GodotSharpTools.sln' diff --git a/modules/mono/config.py b/modules/mono/config.py index 0833d30ce1..44eef45f76 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,7 +2,6 @@  import imp  import os  import sys -from shutil import copyfile  from SCons.Script import BoolVariable, Environment, Variables @@ -16,8 +15,7 @@ def find_file_in_dir(directory, files, prefix='', extension=''):      for curfile in files:          if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):              return curfile - -    return None +    return ''  def can_build(platform): @@ -31,6 +29,22 @@ def is_enabled():      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):      env.use_ptrcall = True @@ -38,6 +52,8 @@ def configure(env):      envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))      envvars.Update(env) +    bits = env['bits'] +      mono_static = env['mono_static']      mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] @@ -46,18 +62,18 @@ def configure(env):          if mono_static:              raise RuntimeError('mono-static: Not supported on Windows') -        if env['bits'] == '32': +        if bits == '32':              if os.getenv('MONO32_PREFIX'):                  mono_root = os.getenv('MONO32_PREFIX')              elif os.name == 'nt': -                mono_root = monoreg.find_mono_root_dir() +                mono_root = monoreg.find_mono_root_dir(bits)          else:              if os.getenv('MONO64_PREFIX'):                  mono_root = os.getenv('MONO64_PREFIX')              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')          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') -        if mono_lib_name is None: +        if not mono_lib_name:              raise RuntimeError('Could not find mono library in: ' + mono_lib_path)          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_src = os.path.join(mono_bin_path, mono_dll_name + '.dll') -        mono_dll_dst = os.path.join('bin', mono_dll_name + '.dll') -        copy_mono_dll = True - -        if not os.path.isdir('bin'): -            os.mkdir('bin') -        elif os.path.exists(mono_dll_dst): -            copy_mono_dll = False +        if not mono_dll_name: +            raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) -        if copy_mono_dll: -            copyfile(mono_dll_src, mono_dll_dst) +        copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll')      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'):                  mono_root = os.getenv('MONO32_PREFIX')          else:              if 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')              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') -            if mono_lib is None: +            if not mono_lib:                  raise RuntimeError('Could not find mono library in: ' + mono_lib_path)              env.Append(CPPFLAGS=['-D_REENTRANT']) @@ -130,12 +141,37 @@ def configure(env):              elif sys.platform == "linux" or sys.platform == "linux2":                  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:              if mono_static:                  raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')              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') diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs index 5544233eb7..bdbef82aad 100644 --- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized;  using System.Diagnostics;  using System.IO;  using System.Runtime.CompilerServices; +using System.Runtime.InteropServices;  using System.Security;  using Microsoft.Build.Framework; @@ -12,24 +13,38 @@ namespace GodotSharpTools.Build      public class BuildInstance : IDisposable      {          [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)] -        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 ret = godot_icall_BuildInstance_get_MSBuildPath(); +            string path; +            string frameworkPathOverride; -                if (ret == null) -                    throw new FileNotFoundException("Cannot find the MSBuild executable."); +            public string MSBuildPath +            { +                get { return path; } +            } -                return ret; +            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."); + +            return ret; +        } +          private string solution;          private string config; @@ -48,9 +63,19 @@ namespace GodotSharpTools.Build          public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)          { -            string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties); +            MSBuildInfo msbuildInfo = GetMSBuildInfo(); + +            List<string> customPropertiesList = new List<string>(); + +            if (customProperties != null) +                customPropertiesList.AddRange(customProperties); -            ProcessStartInfo startInfo = new ProcessStartInfo(MSBuildPath, compilerArgs); +            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              startInfo.RedirectStandardOutput = true; @@ -82,9 +107,19 @@ namespace GodotSharpTools.Build              if (process != null)                  throw new InvalidOperationException("Already in use"); -            string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties); +            MSBuildInfo msbuildInfo = GetMSBuildInfo(); + +            List<string> customPropertiesList = new List<string>(); + +            if (customProperties != null) +                customPropertiesList.AddRange(customProperties); + +            if (msbuildInfo.FrameworkPathOverride.Length > 0) +                customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.FrameworkPathOverride); -            ProcessStartInfo startInfo = new ProcessStartInfo("msbuild", compilerArgs); +            string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); + +            ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.MSBuildPath, compilerArgs);              // No console output, thanks              startInfo.RedirectStandardOutput = true; @@ -101,10 +136,13 @@ namespace GodotSharpTools.Build              process.Start(); +            process.BeginOutputReadLine(); +            process.BeginErrorReadLine(); +              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}""",                  solution, @@ -114,12 +152,9 @@ namespace GodotSharpTools.Build                  loggerOutputDir              ); -            if (customProperties != null) +            foreach (string customProperty in customProperties)              { -                foreach (string customProperty in customProperties) -                { -                    arguments += " /p:" + customProperty; -                } +                arguments += " \"/p:" + customProperty + "\"";              }              return arguments; diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index d3808769fb..c3b9c140e4 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -71,10 +71,15 @@ String _find_build_engine_on_unix(const String &p_name) {  }  #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"))); +	MonoString *res[2] = { +		NULL, // MSBuildPath +		NULL // FrameworkPathOverride +	}; +  #if defined(WINDOWS_ENABLED)  	switch (build_tool) {  		case GodotSharpBuilds::MSBUILD: { @@ -84,11 +89,17 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {  				if (!msbuild_tools_path.ends_with("\\"))  					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;  			} -			OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n"); -		} +			if (OS::get_singleton()->is_stdout_verbose()) +				OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n"); +		} // fall through  		case GodotSharpBuilds::MSBUILD_MONO: {  			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);  			} -			return GDMonoMarshal::mono_string_from_godot(msbuild_path); -		} -		case GodotSharpBuilds::XBUILD: { -			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); -		} +			res[0] = GDMonoMarshal::mono_string_from_godot(msbuild_path); +			return res; +		} break;  		default:  			ERR_EXPLAIN("You don't deserve to live");  			CRASH_NOW(); @@ -118,25 +121,26 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {  	if (build_tool != GodotSharpBuilds::XBUILD) {  		if (msbuild_path.empty()) {  			WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool')."); -			return NULL; +			return res;  		}  	} else {  		if (xbuild_path.empty()) {  			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 -	return NULL; +	return res;  #endif  }  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.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) { @@ -353,9 +357,22 @@ GodotSharpBuilds::GodotSharpBuilds() {  	// Build tool settings  	EditorSettings *ed_settings = EditorSettings::get_singleton();  	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() { diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 6d5fa3b44a..3486f9f3e9 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -67,9 +67,12 @@ public:  	};  	enum BuildTool { -		MSBUILD,  		MSBUILD_MONO, -		XBUILD +#ifdef WINDOWS_ENABLED +		MSBUILD +#else +		XBUILD // Deprecated +#endif  	};  	_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index e9988625f5..22c4aeaf4c 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/mono_reg_utils.py @@ -1,4 +1,5 @@  import os +import platform  if os.name == 'nt':      import sys @@ -11,8 +12,7 @@ if os.name == 'nt':  def _reg_open_key(key, subkey):      try:          return winreg.OpenKey(key, subkey) -    except (WindowsError, EnvironmentError) as e: -        import platform +    except WindowsError, OSError:          if platform.architecture()[0] == '32bit':              bitness_sam = winreg.KEY_WOW64_64KEY          else: @@ -20,39 +20,93 @@ def _reg_open_key(key, subkey):          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: -        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')              return value -    except (WindowsError, EnvironmentError) as e: +    except WindowsError, OSError:          return None -def _find_mono_in_reg_old(subkey): + +def _find_mono_in_reg_old(subkey, bits):      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')              if default_clr: -                return _find_mono_in_reg(subkey + '\\' + default_clr) +                return _find_mono_in_reg(subkey + '\\' + default_clr, bits)              return None      except (WindowsError, EnvironmentError):          return None -def find_mono_root_dir(): -    dir = _find_mono_in_reg(r'SOFTWARE\Mono') -    if dir: -        return dir -    dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono') -    if dir: -        return dir -    return None +def find_mono_root_dir(bits): +    root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits) +    if root_dir is not None: +        return root_dir +    root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits) +    if root_dir is not None: +        return root_dir +    return ''  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: -        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')              return value -    except (WindowsError, EnvironmentError) as e: -        return None +    except WindowsError, OSError: +        return '' + +    return ''  |