GN: Autodetect Visual Studio versions
This searches the local system for Visual Studio versions like GYP. It enables specifically requesting one version, as well as overriding the directory like GYP. BUG= R=scottmg@chromium.org Review URL: https://codereview.chromium.org/126073005 git-svn-id: http://src.chromium.org/svn/trunk/src/build@243612 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
This commit is contained in:
Родитель
c4dfd98310
Коммит
d26952a6e6
|
@ -2,13 +2,13 @@
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import("//build/config/win/visual_studio_version.gni")
|
||||||
|
|
||||||
declare_args() {
|
declare_args() {
|
||||||
# Full path to the Windows SDK, not including a backslash at the end.
|
# Full path to the Windows SDK, not including a backslash at the end.
|
||||||
|
# This value is the default location, override if you have a different
|
||||||
|
# installation location.
|
||||||
windows_sdk_path = "C:\Program Files (x86)\Windows Kits\8.0"
|
windows_sdk_path = "C:\Program Files (x86)\Windows Kits\8.0"
|
||||||
|
|
||||||
# Full path to the Visual Studio installation, not including a backslash
|
|
||||||
# at the end.
|
|
||||||
visual_studio_path = "C:\Program Files (x86)\Microsoft Visual Studio 10.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Compiler setup for the Windows SDK. Applied to all targets.
|
# Compiler setup for the Windows SDK. Applied to all targets.
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
"""
|
||||||
|
This script searches for Visual Studio versions on the current system.
|
||||||
|
|
||||||
|
Pass in the preferred VS version on the command line, or pass "auto" for
|
||||||
|
autodetect.
|
||||||
|
|
||||||
|
This script prints a string containing the VS root directory. On failure it
|
||||||
|
returns the empty string.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _ConvertToCygpath(path):
|
||||||
|
"""Convert to cygwin path if we are using cygwin."""
|
||||||
|
if sys.platform == 'cygwin':
|
||||||
|
p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
|
||||||
|
path = p.communicate()[0].strip()
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _RegistryQueryBase(sysdir, key, value):
|
||||||
|
"""Use reg.exe to read a particular key.
|
||||||
|
|
||||||
|
While ideally we might use the win32 module, we would like gyp to be
|
||||||
|
python neutral, so for instance cygwin python lacks this module.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
sysdir: The system subdirectory to attempt to launch reg.exe from.
|
||||||
|
key: The registry key to read from.
|
||||||
|
value: The particular value to read.
|
||||||
|
Return:
|
||||||
|
stdout from reg.exe, or None for failure.
|
||||||
|
"""
|
||||||
|
# Setup params to pass to and attempt to launch reg.exe
|
||||||
|
cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
|
||||||
|
'query', key]
|
||||||
|
if value:
|
||||||
|
cmd.extend(['/v', value])
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
# Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
|
||||||
|
# Note that the error text may be in [1] in some cases
|
||||||
|
text = p.communicate()[0]
|
||||||
|
# Check return code from reg.exe; officially 0==success and 1==error
|
||||||
|
if p.returncode:
|
||||||
|
return None
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _RegistryQuery(key, value=None):
|
||||||
|
"""Use reg.exe to read a particular key through _RegistryQueryBase.
|
||||||
|
|
||||||
|
First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
|
||||||
|
that fails, it falls back to System32. Sysnative is available on Vista and
|
||||||
|
up and available on Windows Server 2003 and XP through KB patch 942589. Note
|
||||||
|
that Sysnative will always fail if using 64-bit python due to it being a
|
||||||
|
virtual directory and System32 will work correctly in the first place.
|
||||||
|
|
||||||
|
KB 942589 - http://support.microsoft.com/kb/942589/en-us.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
key: The registry key.
|
||||||
|
value: The particular registry value to read (optional).
|
||||||
|
Return:
|
||||||
|
stdout from reg.exe, or None for failure.
|
||||||
|
"""
|
||||||
|
text = None
|
||||||
|
try:
|
||||||
|
text = _RegistryQueryBase('Sysnative', key, value)
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
text = _RegistryQueryBase('System32', key, value)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _RegistryGetValue(key, value):
|
||||||
|
"""Use reg.exe to obtain the value of a registry key.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: The registry key.
|
||||||
|
value: The particular registry value to read.
|
||||||
|
Return:
|
||||||
|
contents of the registry key's value, or None on failure.
|
||||||
|
"""
|
||||||
|
text = _RegistryQuery(key, value)
|
||||||
|
if not text:
|
||||||
|
return None
|
||||||
|
# Extract value.
|
||||||
|
match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _DetectVisualStudioVersion(versions_to_check, force_express):
|
||||||
|
"""Gets the path of the preferred Visual Studio version.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The base path of Visual Studio based on the registry and a quick check if
|
||||||
|
devenv.exe exists.
|
||||||
|
|
||||||
|
Possibilities are:
|
||||||
|
2010(e) - Visual Studio 2010 (10)
|
||||||
|
2012(e) - Visual Studio 2012 (11)
|
||||||
|
2013(e) - Visual Studio 2013 (12)
|
||||||
|
Where (e) is e for express editions of MSVS and blank otherwise.
|
||||||
|
"""
|
||||||
|
versions = []
|
||||||
|
for version in versions_to_check:
|
||||||
|
# Old method of searching for which VS version is installed
|
||||||
|
# We don't use the 2010-encouraged-way because we also want to get the
|
||||||
|
# path to the binaries, which it doesn't offer.
|
||||||
|
keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
|
||||||
|
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
|
||||||
|
r'HKLM\Software\Microsoft\VCExpress\%s' % version,
|
||||||
|
r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
|
||||||
|
for index in range(len(keys)):
|
||||||
|
path = _RegistryGetValue(keys[index], 'InstallDir')
|
||||||
|
if not path:
|
||||||
|
continue
|
||||||
|
path = _ConvertToCygpath(path)
|
||||||
|
# Check for full.
|
||||||
|
full_path = os.path.join(path, 'devenv.exe')
|
||||||
|
express_path = os.path.join(path, '*express.exe')
|
||||||
|
if not force_express and os.path.exists(full_path):
|
||||||
|
return os.path.normpath(os.path.join(path, '..', '..'))
|
||||||
|
# Check for express.
|
||||||
|
elif glob.glob(express_path):
|
||||||
|
return os.path.normpath(os.path.join(path, '..', '..'))
|
||||||
|
|
||||||
|
# The old method above does not work when only SDK is installed.
|
||||||
|
keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
|
||||||
|
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
|
||||||
|
for index in range(len(keys)):
|
||||||
|
path = _RegistryGetValue(keys[index], version)
|
||||||
|
if not path:
|
||||||
|
continue
|
||||||
|
path = _ConvertToCygpath(path)
|
||||||
|
return os.path.normpath(os.path.join(path, '..'))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print 'Usage: get_visual_studio_path.py <version>'
|
||||||
|
print 'Use "auto" for the version to autodetect.'
|
||||||
|
sys.exit(2)
|
||||||
|
version_map = {
|
||||||
|
'auto': ('10.0', '12.0', '11.0'),
|
||||||
|
'2010': ('10.0',),
|
||||||
|
'2010e': ('10.0',),
|
||||||
|
'2012': ('11.0',),
|
||||||
|
'2012e': ('11.0',),
|
||||||
|
'2013': ('12.0',),
|
||||||
|
'2013e': ('12.0',),
|
||||||
|
}
|
||||||
|
|
||||||
|
requested_version = sys.argv[1]
|
||||||
|
vs_path = _DetectVisualStudioVersion(version_map[requested_version],
|
||||||
|
'e' in requested_version)
|
||||||
|
if not vs_path:
|
||||||
|
# No Visual Studio version detected.
|
||||||
|
print '""' # Return empty string to .gn file.
|
||||||
|
sys.exit(1);
|
||||||
|
|
||||||
|
# Return Visual Studio path to the .gn file.
|
||||||
|
print '"%s"' % vs_path
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright 2014 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
declare_args() {
|
||||||
|
# Version of Visual Studio to use.
|
||||||
|
#
|
||||||
|
# Possible values:
|
||||||
|
# - "auto"
|
||||||
|
# - "2010"
|
||||||
|
# - "2010e"
|
||||||
|
# - "2012"
|
||||||
|
# - "2012e"
|
||||||
|
# - "2013"
|
||||||
|
# - "2013e"
|
||||||
|
#
|
||||||
|
# The "e" variants denote "Express". The default, "auto" will find the
|
||||||
|
# preferred one of all Visual Studios installed on the current system.
|
||||||
|
visual_studio_version = "auto"
|
||||||
|
|
||||||
|
# The path to use as the root of a Visual Studio install. If nonempty,
|
||||||
|
# this path will be used instead of any autodetected install locations,
|
||||||
|
# which allows you to have the Visual Studio files in a directory without
|
||||||
|
# actually "installing" it.
|
||||||
|
#
|
||||||
|
# When empty, we will autodetect the best Visual Studio path to use. The
|
||||||
|
# autodetected one is usually something like
|
||||||
|
# "C:\Program Files (x86)\Microsoft Visual Studio 10.0".
|
||||||
|
visual_studio_path = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visual_studio_path == "") {
|
||||||
|
visual_studio_path = exec_script("get_visual_studio_path.py",
|
||||||
|
[ visual_studio_version ], "value")
|
||||||
|
}
|
||||||
|
|
|
@ -177,6 +177,15 @@ def GetArgsStringForGN(supplemental_files):
|
||||||
if v in vars_dict:
|
if v in vars_dict:
|
||||||
gn_args += ' ' + v + '=' + EscapeStringForGN(vars_dict[v])
|
gn_args += ' ' + v + '=' + EscapeStringForGN(vars_dict[v])
|
||||||
|
|
||||||
|
# Some other flags come from GYP environment variables.
|
||||||
|
gyp_msvs_version = os.environ.get('GYP_MSVS_VERSION', '')
|
||||||
|
if gyp_msvs_version:
|
||||||
|
gn_args += ' visual_studio_version=' + EscapeStringForGN(gyp_msvs_version)
|
||||||
|
gyp_msvs_override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH', '')
|
||||||
|
if gyp_msvs_override_path:
|
||||||
|
gn_args += ' visual_studio_override_path=' + \
|
||||||
|
EscapeStringForGN(gyp_msvs_override_path)
|
||||||
|
|
||||||
# Set the GYP flag so BUILD files know they're being invoked in GYP mode.
|
# Set the GYP flag so BUILD files know they're being invoked in GYP mode.
|
||||||
gn_args += ' is_gyp=true'
|
gn_args += ' is_gyp=true'
|
||||||
return gn_args.strip()
|
return gn_args.strip()
|
||||||
|
|
|
@ -7,23 +7,17 @@ import("//build/toolchain/goma.gni")
|
||||||
# Should only be running on Windows.
|
# Should only be running on Windows.
|
||||||
assert(is_win)
|
assert(is_win)
|
||||||
|
|
||||||
|
import("//build/config/win/visual_studio_version.gni")
|
||||||
|
|
||||||
# Setup the Visual Studio state.
|
# Setup the Visual Studio state.
|
||||||
#
|
#
|
||||||
# Its argument is the location to write the environment files.
|
# Its arguments are the VS path and the compiler wrapper tool. It will write
|
||||||
# It will write "environment.x86" and "environment.x64" to this directory,
|
# "environment.x86" and "environment.x64" to the build directory and return a
|
||||||
# and return a list to us.
|
# list to us.
|
||||||
#
|
gyp_win_tool_path = rebase_path("//tools/gyp/pylib/gyp/win_tool.py", ".",
|
||||||
# The list contains the include path as its only element. (I'm expecting to
|
root_build_dir)
|
||||||
# add more so it's currently a list inside a list.)
|
exec_script("setup_toolchain.py", [ visual_studio_path, gyp_win_tool_path ],
|
||||||
#exec_script("get_msvc_config.py",
|
"string")
|
||||||
# [relative_root_output_dir],
|
|
||||||
# "value")
|
|
||||||
|
|
||||||
# This will save the environment block and and copy the gyp-win-tool to the
|
|
||||||
# build directory. We pass in the source file of the win tool.
|
|
||||||
gyp_win_tool_source =
|
|
||||||
rebase_path("//tools/gyp/pylib/gyp/win_tool.py", ".", root_build_dir)
|
|
||||||
exec_script("setup_toolchain.py", [ gyp_win_tool_source ], "value")
|
|
||||||
|
|
||||||
stamp_command = "$python_path gyp-win-tool stamp \$out"
|
stamp_command = "$python_path gyp-win-tool stamp \$out"
|
||||||
copy_command = "$python_path gyp-win-tool recursive-mirror \$in \$out"
|
copy_command = "$python_path gyp-win-tool recursive-mirror \$in \$out"
|
||||||
|
|
|
@ -2,10 +2,23 @@
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import errno
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copies the given "win tool" (which the toolchain uses to wrap compiler
|
||||||
|
invocations) and the environment blocks for the 32-bit and 64-bit builds on
|
||||||
|
Windows to the build directory.
|
||||||
|
|
||||||
|
The arguments are the visual studio install location and the location of the
|
||||||
|
win tool. The script assumes that the root build directory is the current dir
|
||||||
|
and the files will be written to the current directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def ExtractImportantEnvironment():
|
def ExtractImportantEnvironment():
|
||||||
"""Extracts environment variables required for the toolchain from the
|
"""Extracts environment variables required for the toolchain from the
|
||||||
current environment."""
|
current environment."""
|
||||||
|
@ -36,48 +49,6 @@ def ExtractImportantEnvironment():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# VC setup will add a path like this in 32-bit mode:
|
|
||||||
# c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN
|
|
||||||
# And this in 64-bit mode:
|
|
||||||
# c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN\amd64
|
|
||||||
# Note that in 64-bit it's duplicated but the 64-bit one comes first.
|
|
||||||
#
|
|
||||||
# What we get as the path when running this will depend on which VS setup
|
|
||||||
# script you've run. The following two functions try to do this.
|
|
||||||
|
|
||||||
# For 32-bit compiles remove anything that ends in "\VC\WIN\amd64".
|
|
||||||
def FixupPath32(path):
|
|
||||||
find_64 = re.compile("VC\\\\BIN\\\\amd64\\\\*$", flags=re.IGNORECASE)
|
|
||||||
|
|
||||||
for i in range(len(path)):
|
|
||||||
if find_64.search(path[i]):
|
|
||||||
# Found 32-bit path, insert the 64-bit one immediately before it.
|
|
||||||
dir_64 = path[i].rstrip("\\")
|
|
||||||
dir_64 = dir_64[:len(dir_64) - 6] # Trim off "\amd64".
|
|
||||||
path[i] = dir_64
|
|
||||||
break
|
|
||||||
return path
|
|
||||||
|
|
||||||
# For 64-bit compiles, append anything ending in "\VC\BIN" with "\amd64" as
|
|
||||||
# long as that thing isn't already in the list, and append it immediately
|
|
||||||
# before the non-amd64-one.
|
|
||||||
def FixupPath64(path):
|
|
||||||
find_32 = re.compile("VC\\\\BIN\\\\*$", flags=re.IGNORECASE)
|
|
||||||
|
|
||||||
for i in range(len(path)):
|
|
||||||
if find_32.search(path[i]):
|
|
||||||
# Found 32-bit path, insert the 64-bit one immediately before it.
|
|
||||||
dir_32 = path[i]
|
|
||||||
if dir_32[len(dir_32) - 1] == '\\':
|
|
||||||
dir_64 = dir_32 + "amd64"
|
|
||||||
else:
|
|
||||||
dir_64 = dir_32 + "\\amd64"
|
|
||||||
path.insert(i, dir_64)
|
|
||||||
break
|
|
||||||
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def FormatAsEnvironmentBlock(envvar_dict):
|
def FormatAsEnvironmentBlock(envvar_dict):
|
||||||
"""Format as an 'environment block' directly suitable for CreateProcess.
|
"""Format as an 'environment block' directly suitable for CreateProcess.
|
||||||
Briefly this is a list of key=value\0, terminated by an additional \0. See
|
Briefly this is a list of key=value\0, terminated by an additional \0. See
|
||||||
|
@ -89,6 +60,7 @@ def FormatAsEnvironmentBlock(envvar_dict):
|
||||||
block += nul
|
block += nul
|
||||||
return block
|
return block
|
||||||
|
|
||||||
|
|
||||||
def CopyTool(source_path):
|
def CopyTool(source_path):
|
||||||
"""Copies the given tool to the current directory, including a warning not
|
"""Copies the given tool to the current directory, including a warning not
|
||||||
to edit it."""
|
to edit it."""
|
||||||
|
@ -102,22 +74,27 @@ def CopyTool(source_path):
|
||||||
'# Generated by setup_toolchain.py do not edit.\n']
|
'# Generated by setup_toolchain.py do not edit.\n']
|
||||||
+ tool_source[1:]))
|
+ tool_source[1:]))
|
||||||
|
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print 'Usage setup_toolchain.py <visual studio path> <win tool path>'
|
||||||
|
sys.exit(2)
|
||||||
|
vs_path = sys.argv[1]
|
||||||
|
tool_source = sys.argv[2]
|
||||||
|
|
||||||
# Find the tool source, it's the first argument, and copy it.
|
CopyTool(tool_source)
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print "Need one argument (win_tool source path)."
|
|
||||||
sys.exit(1)
|
|
||||||
CopyTool(sys.argv[1])
|
|
||||||
|
|
||||||
important_env_vars = ExtractImportantEnvironment()
|
important_env_vars = ExtractImportantEnvironment()
|
||||||
path = important_env_vars["PATH"].split(";")
|
path = important_env_vars["PATH"].split(";")
|
||||||
|
|
||||||
important_env_vars["PATH"] = ";".join(FixupPath32(path))
|
# Add 32-bit compiler path to the beginning and write the block.
|
||||||
|
path32 = [os.path.join(vs_path, "VC\\BIN")] + path
|
||||||
|
important_env_vars["PATH"] = ";".join(path32)
|
||||||
environ = FormatAsEnvironmentBlock(important_env_vars)
|
environ = FormatAsEnvironmentBlock(important_env_vars)
|
||||||
with open('environment.x86', 'wb') as env_file:
|
with open('environment.x86', 'wb') as env_file:
|
||||||
env_file.write(environ)
|
env_file.write(environ)
|
||||||
|
|
||||||
important_env_vars["PATH"] = ";".join(FixupPath64(path))
|
# Add 64-bit compiler path to the beginning and write the block.
|
||||||
|
path64 = [os.path.join(vs_path, "VC\\BIN\\amd64")] + path
|
||||||
|
important_env_vars["PATH"] = ";".join(path64)
|
||||||
environ = FormatAsEnvironmentBlock(important_env_vars)
|
environ = FormatAsEnvironmentBlock(important_env_vars)
|
||||||
with open('environment.x64', 'wb') as env_file:
|
with open('environment.x64', 'wb') as env_file:
|
||||||
env_file.write(environ)
|
env_file.write(environ)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче