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.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):
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')
else:
msbuild_path = 'msbuild'
import subprocess
import mono_reg_utils as monoreg
from shutil import copyfile
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_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' ]
try:
subprocess.check_call(msbuild_args, env=msbuild_env)
except subprocess.CalledProcessError:
raise RuntimeError('GodotSharpTools build failed')
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')
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'

View File

@ -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 mono_dll_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
if not os.path.isdir('bin'):
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)
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')

View File

@ -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,22 +13,36 @@ 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 path;
string frameworkPathOverride;
public string MSBuildPath
{
string ret = godot_icall_BuildInstance_get_MSBuildPath();
if (ret == null)
throw new FileNotFoundException("Cannot find the MSBuild executable.");
return ret;
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.");
return ret;
}
private string solution;
@ -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();
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
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();
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
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;

View File

@ -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() {

View File

@ -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; }

View File

@ -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 ''