mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:49:17 +00:00
[infra] Update windows build and toolchain setup python scripts.
The scripts originate from Chromium, this CL syncs Dart's copy with Chromium tip of the tree at 6f8f710079b3e363f4fd7ffe3d848384e4b7c816. Format toolchain/win/BUILD.gn Change-Id: Ice7ba48bdd102ffe0e25c6ae6068f83cb14169ba Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/253500 Reviewed-by: Alexander Thomas <athom@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
parent
63e170a242
commit
a6f56aec88
81
build/find_depot_tools.py
Normal file
81
build/find_depot_tools.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2011 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.
|
||||
#
|
||||
# For Dart/Flutter developers:
|
||||
# This file is copied from Chromium:
|
||||
# https://cs.chromium.org/chromium/src/build/find_depot_tools.py
|
||||
# When updating replace reference to python on the first line with python3.
|
||||
#
|
||||
"""Small utility function to find depot_tools and add it to the python path.
|
||||
|
||||
Will throw an ImportError exception if depot_tools can't be found since it
|
||||
imports breakpad.
|
||||
|
||||
This can also be used as a standalone script to print out the depot_tools
|
||||
directory location.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# Path to //src
|
||||
SRC = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
|
||||
def IsRealDepotTools(path):
|
||||
expanded_path = os.path.expanduser(path)
|
||||
return os.path.isfile(os.path.join(expanded_path, 'gclient.py'))
|
||||
|
||||
|
||||
def add_depot_tools_to_path():
|
||||
"""Search for depot_tools and add it to sys.path."""
|
||||
# First, check if we have a DEPS'd in "depot_tools".
|
||||
deps_depot_tools = os.path.join(SRC, 'third_party', 'depot_tools')
|
||||
if IsRealDepotTools(deps_depot_tools):
|
||||
# Put the pinned version at the start of the sys.path, in case there
|
||||
# are other non-pinned versions already on the sys.path.
|
||||
sys.path.insert(0, deps_depot_tools)
|
||||
return deps_depot_tools
|
||||
|
||||
# Then look if depot_tools is already in PYTHONPATH.
|
||||
for i in sys.path:
|
||||
if i.rstrip(os.sep).endswith('depot_tools') and IsRealDepotTools(i):
|
||||
return i
|
||||
# Then look if depot_tools is in PATH, common case.
|
||||
for i in os.environ['PATH'].split(os.pathsep):
|
||||
if IsRealDepotTools(i):
|
||||
sys.path.append(i.rstrip(os.sep))
|
||||
return i
|
||||
# Rare case, it's not even in PATH, look upward up to root.
|
||||
root_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
previous_dir = os.path.abspath(__file__)
|
||||
while root_dir and root_dir != previous_dir:
|
||||
i = os.path.join(root_dir, 'depot_tools')
|
||||
if IsRealDepotTools(i):
|
||||
sys.path.append(i)
|
||||
return i
|
||||
previous_dir = root_dir
|
||||
root_dir = os.path.dirname(root_dir)
|
||||
print('Failed to find depot_tools', file=sys.stderr)
|
||||
return None
|
||||
|
||||
DEPOT_TOOLS_PATH = add_depot_tools_to_path()
|
||||
|
||||
# pylint: disable=W0611
|
||||
import breakpad
|
||||
|
||||
|
||||
def main():
|
||||
if DEPOT_TOOLS_PATH is None:
|
||||
return 1
|
||||
print(DEPOT_TOOLS_PATH)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -146,9 +146,10 @@ template("msvc_toolchain") {
|
|||
}
|
||||
|
||||
tool("solink") {
|
||||
dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll
|
||||
libname =
|
||||
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib" # e.g. foo.dll.lib
|
||||
dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # e.g.
|
||||
# foo.dll
|
||||
libname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib" # e.g.
|
||||
# foo.dll.lib
|
||||
rspfile = "${dllname}.rsp"
|
||||
|
||||
link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:${dllname}.pdb @$rspfile"
|
||||
|
@ -175,7 +176,9 @@ template("msvc_toolchain") {
|
|||
}
|
||||
|
||||
tool("solink_module") {
|
||||
dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll
|
||||
dllname =
|
||||
"{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g.
|
||||
# foo.dll
|
||||
pdbname = "${dllname}.pdb"
|
||||
rspfile = "${dllname}.rsp"
|
||||
|
||||
|
@ -230,8 +233,7 @@ template("msvc_toolchain") {
|
|||
}
|
||||
|
||||
tool("copy") {
|
||||
command =
|
||||
"$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
|
||||
command = "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
|
||||
description = "COPY {{source}} {{output}}"
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +248,9 @@ template("win_toolchains") {
|
|||
visual_studio_path,
|
||||
windows_sdk_path,
|
||||
visual_studio_runtime_dirs,
|
||||
"win",
|
||||
toolchain_arch,
|
||||
"environment." + toolchain_arch,
|
||||
],
|
||||
"scope")
|
||||
|
||||
|
|
|
@ -10,63 +10,183 @@
|
|||
# win tool. The script assumes that the root build directory is the current dir
|
||||
# and the files will be written to the current directory.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
|
||||
import gn_helpers
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(__file__)
|
||||
|
||||
def _ExtractImportantEnvironment(output_of_set):
|
||||
"""Extracts environment variables required for the toolchain to run from
|
||||
a textual dump output by the cmd.exe 'set' command."""
|
||||
envvars_to_save = (
|
||||
'cipd_cache_dir', # needed by vpython
|
||||
'homedrive', # needed by vpython
|
||||
'homepath', # needed by vpython
|
||||
'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
|
||||
'include',
|
||||
'lib',
|
||||
'libpath',
|
||||
'luci_context', # needed by vpython
|
||||
'path',
|
||||
'pathext',
|
||||
'systemroot',
|
||||
'temp',
|
||||
'tmp',
|
||||
'userprofile', # needed by vpython
|
||||
'vpython_virtualenv_root' # needed by vpython
|
||||
)
|
||||
env = {}
|
||||
# This occasionally happens and leads to misleading SYSTEMROOT error messages
|
||||
# if not caught here.
|
||||
if output_of_set.count('=') == 0:
|
||||
raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set)
|
||||
for line in output_of_set.splitlines():
|
||||
for envvar in envvars_to_save:
|
||||
if re.match(envvar + '=', line.decode().lower()):
|
||||
var, setting = line.decode().split('=', 1)
|
||||
if re.match(envvar + '=', line.lower()):
|
||||
var, setting = line.split('=', 1)
|
||||
if envvar == 'path':
|
||||
# Our own rules (for running gyp-win-tool) and other actions in
|
||||
# Chromium rely on python being in the path. Add the path to this
|
||||
# python here so that if it's not in the path when ninja is run
|
||||
# later, python will still be found.
|
||||
# Our own rules and actions in Chromium rely on python being in the
|
||||
# path. Add the path to this python here so that if it's not in the
|
||||
# path when ninja is run later, python will still be found.
|
||||
setting = os.path.dirname(sys.executable) + os.pathsep + setting
|
||||
if envvar in ['include', 'lib']:
|
||||
# Make sure that the include and lib paths point to directories that
|
||||
# exist. This ensures a (relatively) clear error message if the
|
||||
# required SDK is not installed.
|
||||
for part in setting.split(';'):
|
||||
if not os.path.exists(part) and len(part) != 0:
|
||||
raise Exception(
|
||||
'Path "%s" from environment variable "%s" does not exist. '
|
||||
'Make sure the necessary SDK is installed.' % (part, envvar))
|
||||
env[var.upper()] = setting
|
||||
break
|
||||
for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
|
||||
if required not in env:
|
||||
raise Exception('Environment variable "%s" '
|
||||
'required to be set to valid path' % required)
|
||||
if sys.platform in ('win32', 'cygwin'):
|
||||
for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
|
||||
if required not in env:
|
||||
raise Exception('Environment variable "%s" '
|
||||
'required to be set to valid path' % required)
|
||||
return env
|
||||
|
||||
|
||||
def _SetupScript(target_cpu, sdk_dir):
|
||||
"""Returns a command (with arguments) to be used to set up the
|
||||
environment."""
|
||||
def _DetectVisualStudioPath():
|
||||
"""Return path to the installed Visual Studio.
|
||||
"""
|
||||
|
||||
# Use the code in build/vs_toolchain.py to avoid duplicating code.
|
||||
chromium_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..'))
|
||||
sys.path.append(os.path.join(chromium_dir, 'build'))
|
||||
import vs_toolchain
|
||||
return vs_toolchain.DetectVisualStudioPath()
|
||||
|
||||
|
||||
def _LoadEnvFromBat(args):
|
||||
"""Given a bat command, runs it and returns env vars set by it."""
|
||||
args = args[:]
|
||||
args.extend(('&&', 'set'))
|
||||
popen = subprocess.Popen(
|
||||
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
variables, _ = popen.communicate()
|
||||
if popen.returncode != 0:
|
||||
raise Exception('"%s" failed with error %d' % (args, popen.returncode))
|
||||
return variables.decode(errors='ignore')
|
||||
|
||||
|
||||
def _LoadToolchainEnv(cpu, toolchain_root, sdk_dir, target_store):
|
||||
"""Returns a dictionary with environment variables that must be set while
|
||||
running binaries from the toolchain (e.g. INCLUDE and PATH for cl.exe)."""
|
||||
# Check if we are running in the SDK command line environment and use
|
||||
# the setup script from the SDK if so.
|
||||
assert target_cpu in ('x86', 'x64', 'arm64')
|
||||
# the setup script from the SDK if so. |cpu| should be either
|
||||
# 'x86' or 'x64' or 'arm' or 'arm64'.
|
||||
assert cpu in ('x86', 'x64', 'arm', 'arm64')
|
||||
if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
|
||||
return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
|
||||
'/' + target_cpu]
|
||||
# Load environment from json file.
|
||||
env = os.path.normpath(os.path.join(sdk_dir, 'bin/SetEnv.%s.json' % cpu))
|
||||
env = json.load(open(env))['env']
|
||||
if env['VSINSTALLDIR'] == [["..", "..\\"]]:
|
||||
# Old-style paths were relative to the win_sdk\bin directory.
|
||||
json_relative_dir = os.path.join(sdk_dir, 'bin')
|
||||
else:
|
||||
# New-style paths are relative to the toolchain directory.
|
||||
json_relative_dir = toolchain_root
|
||||
for k in env:
|
||||
entries = [os.path.join(*([json_relative_dir] + e)) for e in env[k]]
|
||||
# clang-cl wants INCLUDE to be ;-separated even on non-Windows,
|
||||
# lld-link wants LIB to be ;-separated even on non-Windows. Path gets :.
|
||||
# The separator for INCLUDE here must match the one used in main() below.
|
||||
sep = os.pathsep if k == 'PATH' else ';'
|
||||
env[k] = sep.join(entries)
|
||||
# PATH is a bit of a special case, it's in addition to the current PATH.
|
||||
env['PATH'] = env['PATH'] + os.pathsep + os.environ['PATH']
|
||||
# Augment with the current env to pick up TEMP and friends.
|
||||
for k in os.environ:
|
||||
if k not in env:
|
||||
env[k] = os.environ[k]
|
||||
|
||||
varlines = []
|
||||
for k in sorted(env.keys()):
|
||||
varlines.append('%s=%s' % (str(k), str(env[k])))
|
||||
variables = '\n'.join(varlines)
|
||||
|
||||
# Check that the json file contained the same environment as the .cmd file.
|
||||
if sys.platform in ('win32', 'cygwin'):
|
||||
script = os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.cmd'))
|
||||
arg = '/' + cpu
|
||||
json_env = _ExtractImportantEnvironment(variables)
|
||||
cmd_env = _ExtractImportantEnvironment(_LoadEnvFromBat([script, arg]))
|
||||
assert _LowercaseDict(json_env) == _LowercaseDict(cmd_env)
|
||||
else:
|
||||
if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ:
|
||||
os.environ['GYP_MSVS_OVERRIDE_PATH'] = _DetectVisualStudioPath()
|
||||
# We only support x64-hosted tools.
|
||||
# TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual
|
||||
# Studio install location from registry.
|
||||
return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
|
||||
'VC/Auxiliary/Build/vcvarsall.bat')),
|
||||
'amd64_x86' if target_cpu == 'x86' else 'amd64']
|
||||
script_path = os.path.normpath(os.path.join(
|
||||
os.environ['GYP_MSVS_OVERRIDE_PATH'],
|
||||
'VC/vcvarsall.bat'))
|
||||
if not os.path.exists(script_path):
|
||||
# vcvarsall.bat for VS 2017 fails if run after running vcvarsall.bat from
|
||||
# VS 2013 or VS 2015. Fix this by clearing the vsinstalldir environment
|
||||
# variable. Since vcvarsall.bat appends to the INCLUDE, LIB, and LIBPATH
|
||||
# environment variables we need to clear those to avoid getting double
|
||||
# entries when vcvarsall.bat has been run before gn gen. vcvarsall.bat
|
||||
# also adds to PATH, but there is no clean way of clearing that and it
|
||||
# doesn't seem to cause problems.
|
||||
if 'VSINSTALLDIR' in os.environ:
|
||||
del os.environ['VSINSTALLDIR']
|
||||
if 'INCLUDE' in os.environ:
|
||||
del os.environ['INCLUDE']
|
||||
if 'LIB' in os.environ:
|
||||
del os.environ['LIB']
|
||||
if 'LIBPATH' in os.environ:
|
||||
del os.environ['LIBPATH']
|
||||
other_path = os.path.normpath(os.path.join(
|
||||
os.environ['GYP_MSVS_OVERRIDE_PATH'],
|
||||
'VC/Auxiliary/Build/vcvarsall.bat'))
|
||||
if not os.path.exists(other_path):
|
||||
raise Exception('%s is missing - make sure VC++ tools are installed.' %
|
||||
script_path)
|
||||
script_path = other_path
|
||||
cpu_arg = "amd64"
|
||||
if (cpu != 'x64'):
|
||||
# x64 is default target CPU thus any other CPU requires a target set
|
||||
cpu_arg += '_' + cpu
|
||||
args = [script_path, cpu_arg, ]
|
||||
# Store target must come before any SDK version declaration
|
||||
if (target_store):
|
||||
args.append('store')
|
||||
# Explicitly specifying the SDK version to build with to avoid accidentally
|
||||
# building with a new and untested SDK. This should stay in sync with the
|
||||
# packaged toolchain in build/vs_toolchain.py.
|
||||
args.append('10.0.20348.0')
|
||||
variables = _LoadEnvFromBat(args)
|
||||
return _ExtractImportantEnvironment(variables)
|
||||
|
||||
|
||||
def _FormatAsEnvironmentBlock(envvar_dict):
|
||||
|
@ -81,76 +201,114 @@ def _FormatAsEnvironmentBlock(envvar_dict):
|
|||
return block
|
||||
|
||||
|
||||
def _CopyTool(source_path):
|
||||
"""Copies the given tool to the current directory, including a warning not
|
||||
to edit it."""
|
||||
with open(source_path) as source_file:
|
||||
tool_source = source_file.readlines()
|
||||
def _LowercaseDict(d):
|
||||
"""Returns a copy of `d` with both key and values lowercased.
|
||||
|
||||
# Add header and write it out to the current directory (which should be the
|
||||
# root build dir).
|
||||
with open("gyp-win-tool", 'w') as tool_file:
|
||||
tool_file.write(''.join([tool_source[0],
|
||||
'# Generated by setup_toolchain.py do not edit.\n']
|
||||
+ tool_source[1:]))
|
||||
Args:
|
||||
d: dict to lowercase (e.g. {'A': 'BcD'}).
|
||||
|
||||
Returns:
|
||||
A dict with both keys and values lowercased (e.g.: {'a': 'bcd'}).
|
||||
"""
|
||||
return {k.lower(): d[k].lower() for k in d}
|
||||
|
||||
|
||||
def FindFileInEnvList(env, env_name, separator, file_name, optional=False):
|
||||
parts = env[env_name].split(separator)
|
||||
for path in parts:
|
||||
if os.path.exists(os.path.join(path, file_name)):
|
||||
return os.path.realpath(path)
|
||||
assert optional, "%s is not found in %s:\n%s\nCheck if it is installed." % (
|
||||
file_name, env_name, '\n'.join(parts))
|
||||
return ''
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 5:
|
||||
if len(sys.argv) != 7:
|
||||
print('Usage setup_toolchain.py '
|
||||
'<visual studio path> <win sdk path> '
|
||||
'<runtime dirs> <target_cpu>')
|
||||
'<runtime dirs> <target_os> <target_cpu> '
|
||||
'<environment block name|none>')
|
||||
sys.exit(2)
|
||||
# toolchain_root and win_sdk_path are only read if the hermetic Windows
|
||||
# toolchain is set, that is if DEPOT_TOOLS_WIN_TOOLCHAIN is not set to 0.
|
||||
# With the hermetic Windows toolchain, the visual studio path in argv[1]
|
||||
# is the root of the Windows toolchain directory.
|
||||
toolchain_root = sys.argv[1]
|
||||
win_sdk_path = sys.argv[2]
|
||||
runtime_dirs = sys.argv[3]
|
||||
target_cpu = sys.argv[4]
|
||||
|
||||
cpus = ('x86', 'x64', 'arm64')
|
||||
runtime_dirs = sys.argv[3]
|
||||
target_os = sys.argv[4]
|
||||
target_cpu = sys.argv[5]
|
||||
environment_block_name = sys.argv[6]
|
||||
if (environment_block_name == 'none'):
|
||||
environment_block_name = ''
|
||||
|
||||
if (target_os == 'winuwp'):
|
||||
target_store = True
|
||||
else:
|
||||
target_store = False
|
||||
|
||||
cpus = ('x86', 'x64', 'arm', 'arm64')
|
||||
assert target_cpu in cpus
|
||||
vc_bin_dir = ''
|
||||
include = ''
|
||||
lib = ''
|
||||
|
||||
# TODO(scottmg|goma): Do we need an equivalent of
|
||||
# ninja_use_custom_environment_files?
|
||||
|
||||
def relflag(s): # Make s relative to builddir when cwd and sdk on same drive.
|
||||
try:
|
||||
return os.path.relpath(s).replace('\\', '/')
|
||||
except ValueError:
|
||||
return s
|
||||
|
||||
def q(s): # Quote s if it contains spaces or other weird characters.
|
||||
return s if re.match(r'^[a-zA-Z0-9._/\\:-]*$', s) else '"' + s + '"'
|
||||
|
||||
for cpu in cpus:
|
||||
# Extract environment variables for subprocesses.
|
||||
args = _SetupScript(cpu, win_sdk_path)
|
||||
args.extend(('&&', 'set'))
|
||||
popen = subprocess.Popen(
|
||||
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
stdout_data, stderr_data = popen.communicate()
|
||||
if popen.returncode != 0:
|
||||
print('Error, got returncode:', popen.returncode)
|
||||
print('## stdout:')
|
||||
print(stdout_data)
|
||||
print('## stderr:')
|
||||
print(stderr_data)
|
||||
sys.exit(2)
|
||||
env = _ExtractImportantEnvironment(stdout_data)
|
||||
env['PATH'] = runtime_dirs + ';' + env['PATH']
|
||||
|
||||
if cpu == target_cpu:
|
||||
for path in env['PATH'].split(os.pathsep):
|
||||
if os.path.exists(os.path.join(path, 'cl.exe')):
|
||||
vc_bin_dir = os.path.realpath(path)
|
||||
break
|
||||
# Extract environment variables for subprocesses.
|
||||
env = _LoadToolchainEnv(cpu, toolchain_root, win_sdk_path, target_store)
|
||||
env['PATH'] = runtime_dirs + os.pathsep + env['PATH']
|
||||
|
||||
# The Windows SDK include directories must be first. They both have a sal.h,
|
||||
# and the SDK one is newer and the SDK uses some newer features from it not
|
||||
# present in the Visual Studio one.
|
||||
vc_bin_dir = FindFileInEnvList(env, 'PATH', os.pathsep, 'cl.exe')
|
||||
|
||||
if win_sdk_path:
|
||||
additional_includes = ('{sdk_dir}\\Include\\shared;' +
|
||||
'{sdk_dir}\\Include\\um;' +
|
||||
'{sdk_dir}\\Include\\winrt;').format(
|
||||
sdk_dir=win_sdk_path)
|
||||
env['INCLUDE'] = additional_includes + env['INCLUDE']
|
||||
env_block = _FormatAsEnvironmentBlock(env)
|
||||
with open('environment.' + cpu, 'wb') as f:
|
||||
f.write(env_block.encode())
|
||||
# The separator for INCLUDE here must match the one used in
|
||||
# _LoadToolchainEnv() above.
|
||||
include = [p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p]
|
||||
include = list(map(relflag, include))
|
||||
|
||||
assert vc_bin_dir
|
||||
print('vc_bin_dir = "%s"' % vc_bin_dir)
|
||||
lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p]
|
||||
lib = list(map(relflag, lib))
|
||||
|
||||
include_I = ' '.join([q('/I' + i) for i in include])
|
||||
include_imsvc = ' '.join([q('-imsvc' + i) for i in include])
|
||||
libpath_flags = ' '.join([q('-libpath:' + i) for i in lib])
|
||||
|
||||
if (environment_block_name != ''):
|
||||
env_block = _FormatAsEnvironmentBlock(env)
|
||||
with open(environment_block_name, 'w') as f:
|
||||
f.write(env_block)
|
||||
|
||||
print('vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir))
|
||||
assert include_I
|
||||
print('include_flags_I = ' + gn_helpers.ToGNString(include_I))
|
||||
assert include_imsvc
|
||||
if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and win_sdk_path:
|
||||
print('include_flags_imsvc = ' +
|
||||
gn_helpers.ToGNString(q('/winsysroot' + relflag(toolchain_root))))
|
||||
else:
|
||||
print('include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc))
|
||||
print('paths = ' + gn_helpers.ToGNString(env['PATH']))
|
||||
assert libpath_flags
|
||||
print('libpath_flags = ' + gn_helpers.ToGNString(libpath_flags))
|
||||
if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and win_sdk_path:
|
||||
print('libpath_lldlink_flags = ' +
|
||||
gn_helpers.ToGNString(q('/winsysroot:' + relflag(toolchain_root))))
|
||||
else:
|
||||
print('libpath_lldlink_flags = ' + gn_helpers.ToGNString(libpath_flags))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
|
||||
#
|
||||
# For Dart/Flutter developers:
|
||||
# This file keeps the MSVC toolchain up-to-date for Google developers.
|
||||
# It is copied from Chromium:
|
||||
# This file is copied from Chromium:
|
||||
# https://cs.chromium.org/chromium/src/build/vs_toolchain.py
|
||||
# with modifications that update paths, and remove dependencies on gyp.
|
||||
# To update to a new MSVC toolchain, copy the updated script from the Chromium
|
||||
# tree, and edit to make it work in the Dart tree by updating paths in the original script.
|
||||
|
||||
|
||||
# When updating replace reference to python on the first line with python3.
|
||||
#
|
||||
from __future__ import print_function
|
||||
|
||||
import collections
|
||||
import glob
|
||||
|
@ -26,21 +22,47 @@ import stat
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
from gn_helpers import ToGNString
|
||||
|
||||
# VS 2019 16.61 with 10.0.20348.0 SDK, 10.0.19041 version of Debuggers
|
||||
# with ARM64 libraries and UWP support.
|
||||
# See go/chromium-msvc-toolchain for instructions about how to update the
|
||||
# toolchain.
|
||||
#
|
||||
# When updating the toolchain, consider the following areas impacted by the
|
||||
# toolchain version:
|
||||
#
|
||||
# * //base/win/windows_version.cc NTDDI preprocessor check
|
||||
# Triggers a compiler error if the available SDK is older than the minimum.
|
||||
# * //build/config/win/BUILD.gn NTDDI_VERSION value
|
||||
# Affects the availability of APIs in the toolchain headers.
|
||||
# * //docs/windows_build_instructions.md mentions of VS or Windows SDK.
|
||||
# Keeps the document consistent with the toolchain version.
|
||||
TOOLCHAIN_HASH = '1023ce2e82'
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
json_data_file = os.path.join(script_dir, 'win_toolchain.json')
|
||||
|
||||
sys.path.insert(0, os.path.join(script_dir, '..', 'tools'))
|
||||
|
||||
# VS versions are listed in descending order of priority (highest first).
|
||||
# The first version is assumed by this script to be the one that is packaged,
|
||||
# which makes a difference for the arm64 runtime.
|
||||
MSVS_VERSIONS = collections.OrderedDict([
|
||||
('2017', '15.0'),
|
||||
('2019', '16.0'),
|
||||
('2022', '17.0'),
|
||||
('2019', '16.0'), # Default and packaged version of Visual Studio.
|
||||
('2022', '17.0'),
|
||||
('2017', '15.0'),
|
||||
])
|
||||
|
||||
# List of preferred VC toolset version based on MSVS
|
||||
# Order is not relevant for this dictionary.
|
||||
MSVC_TOOLSET_VERSION = {
|
||||
'2022': 'VC143',
|
||||
'2019': 'VC142',
|
||||
'2017': 'VC141',
|
||||
}
|
||||
|
||||
def _HostIsWindows():
|
||||
"""Returns True if running on a Windows host (including under cygwin)."""
|
||||
return sys.platform in ('win32', 'cygwin')
|
||||
|
||||
def SetEnvironmentAndGetRuntimeDllDirs():
|
||||
"""Sets up os.environ to use the depot_tools VS toolchain with gyp, and
|
||||
|
@ -56,7 +78,7 @@ def SetEnvironmentAndGetRuntimeDllDirs():
|
|||
bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
|
||||
# When running on a non-Windows host, only do this if the SDK has explicitly
|
||||
# been downloaded before (in which case json_data_file will exist).
|
||||
if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
|
||||
if ((_HostIsWindows() or os.path.exists(json_data_file))
|
||||
and depot_tools_win_toolchain):
|
||||
if ShouldUpdateToolchain():
|
||||
if len(sys.argv) > 1 and sys.argv[1] == 'update':
|
||||
|
@ -71,8 +93,6 @@ def SetEnvironmentAndGetRuntimeDllDirs():
|
|||
toolchain = toolchain_data['path']
|
||||
version = toolchain_data['version']
|
||||
win_sdk = toolchain_data.get('win_sdk')
|
||||
if not win_sdk:
|
||||
win_sdk = toolchain_data['win8sdk']
|
||||
wdk = toolchain_data['wdk']
|
||||
# TODO(scottmg): The order unfortunately matters in these. They should be
|
||||
# split into separate keys for x64/x86/arm64. (See CopyDlls call below).
|
||||
|
@ -85,7 +105,6 @@ def SetEnvironmentAndGetRuntimeDllDirs():
|
|||
vs_runtime_dll_dirs.append('Arm64Unused')
|
||||
|
||||
os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
|
||||
os.environ['GYP_MSVS_VERSION'] = version
|
||||
|
||||
os.environ['WINDOWSSDKDIR'] = win_sdk
|
||||
os.environ['WDK_DIR'] = wdk
|
||||
|
@ -95,8 +114,6 @@ def SetEnvironmentAndGetRuntimeDllDirs():
|
|||
elif sys.platform == 'win32' and not depot_tools_win_toolchain:
|
||||
if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
|
||||
os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
|
||||
if not 'GYP_MSVS_VERSION' in os.environ:
|
||||
os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
|
||||
|
||||
# When using an installed toolchain these files aren't needed in the output
|
||||
# directory in order to run binaries locally, but they are needed in order
|
||||
|
@ -126,12 +143,12 @@ def _RegistryGetValueUsingWinReg(key, value):
|
|||
contents of the registry key's value, or None on failure. Throws
|
||||
ImportError if _winreg is unavailable.
|
||||
"""
|
||||
import winreg
|
||||
import _winreg
|
||||
try:
|
||||
root, subkey = key.split('\\', 1)
|
||||
assert root == 'HKLM' # Only need HKLM for now.
|
||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
|
||||
return winreg.QueryValueEx(hkey, value)[0]
|
||||
with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
|
||||
return _winreg.QueryValueEx(hkey, value)[0]
|
||||
except WindowsError:
|
||||
return None
|
||||
|
||||
|
@ -146,31 +163,38 @@ def _RegistryGetValue(key, value):
|
|||
def GetVisualStudioVersion():
|
||||
"""Return best available version of Visual Studio.
|
||||
"""
|
||||
|
||||
env_version = os.environ.get('GYP_MSVS_VERSION')
|
||||
if env_version:
|
||||
return env_version
|
||||
|
||||
supported_versions = list(MSVS_VERSIONS.keys())
|
||||
|
||||
# VS installed in depot_tools for Googlers
|
||||
if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
|
||||
return list(supported_versions)[0]
|
||||
return supported_versions[0]
|
||||
|
||||
# VS installed in system for external developers
|
||||
supported_versions_str = ', '.join('{} ({})'.format(v,k)
|
||||
for k,v in list(MSVS_VERSIONS.items()))
|
||||
for k,v in MSVS_VERSIONS.items())
|
||||
available_versions = []
|
||||
for version in supported_versions:
|
||||
for path in (
|
||||
os.environ.get('vs%s_install' % version),
|
||||
os.path.expandvars('%ProgramFiles(x86)%' +
|
||||
'/Microsoft Visual Studio/%s' % version),
|
||||
os.path.expandvars('%ProgramFiles%' +
|
||||
'/Microsoft Visual Studio/%s' % version)):
|
||||
if path and os.path.exists(path):
|
||||
available_versions.append(version)
|
||||
break
|
||||
# Checking vs%s_install environment variables.
|
||||
# For example, vs2019_install could have the value
|
||||
# "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community".
|
||||
# Only vs2017_install, vs2019_install and vs2022_install are supported.
|
||||
path = os.environ.get('vs%s_install' % version)
|
||||
if path and os.path.exists(path):
|
||||
available_versions.append(version)
|
||||
break
|
||||
# Detecting VS under possible paths.
|
||||
if version >= '2022':
|
||||
program_files_path_variable = '%ProgramFiles%'
|
||||
else:
|
||||
program_files_path_variable = '%ProgramFiles(x86)%'
|
||||
path = os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s' % version)
|
||||
if path and any(
|
||||
os.path.exists(os.path.join(path, edition))
|
||||
for edition in ('Enterprise', 'Professional', 'Community', 'Preview',
|
||||
'BuildTools')):
|
||||
available_versions.append(version)
|
||||
break
|
||||
|
||||
if not available_versions:
|
||||
raise Exception('No supported Visual Studio can be found.'
|
||||
|
@ -179,7 +203,7 @@ def GetVisualStudioVersion():
|
|||
|
||||
|
||||
def DetectVisualStudioPath():
|
||||
"""Return path to the GYP_MSVS_VERSION of Visual Studio.
|
||||
"""Return path to the installed Visual Studio.
|
||||
"""
|
||||
|
||||
# Note that this code is used from
|
||||
|
@ -190,18 +214,30 @@ def DetectVisualStudioPath():
|
|||
# the registry. For details see:
|
||||
# https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
|
||||
# For now we use a hardcoded default with an environment variable override.
|
||||
possible_install_paths = (
|
||||
os.path.expandvars('%s/Microsoft Visual Studio/%s/%s' %
|
||||
(program_path_var, version_as_year, product))
|
||||
for program_path_var in ('%ProgramFiles%', '%ProgramFiles(x86)%')
|
||||
for product in ('Enterprise', 'Professional', 'Community', 'Preview'))
|
||||
for path in (
|
||||
os.environ.get('vs%s_install' % version_as_year), *possible_install_paths):
|
||||
if version_as_year >= '2022':
|
||||
program_files_path_variable = '%ProgramFiles%'
|
||||
else:
|
||||
program_files_path_variable = '%ProgramFiles(x86)%'
|
||||
for path in (os.environ.get('vs%s_install' % version_as_year),
|
||||
os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s/Enterprise' %
|
||||
version_as_year),
|
||||
os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s/Professional' %
|
||||
version_as_year),
|
||||
os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s/Community' %
|
||||
version_as_year),
|
||||
os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s/Preview' %
|
||||
version_as_year),
|
||||
os.path.expandvars(program_files_path_variable +
|
||||
'/Microsoft Visual Studio/%s/BuildTools' %
|
||||
version_as_year)):
|
||||
if path and os.path.exists(path):
|
||||
return path
|
||||
|
||||
raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)'
|
||||
' not found.' % version_as_year)
|
||||
raise Exception('Visual Studio Version %s not found.' % version_as_year)
|
||||
|
||||
|
||||
def _CopyRuntimeImpl(target, source, verbose=True):
|
||||
|
@ -243,21 +279,30 @@ def _SortByHighestVersionNumberFirst(list_of_str_versions):
|
|||
|
||||
list_of_str_versions.sort(key=to_number_sequence, reverse=True)
|
||||
|
||||
def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, suffix):
|
||||
|
||||
def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
|
||||
"""Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
|
||||
exist, but the target directory does exist."""
|
||||
if target_cpu == 'arm64':
|
||||
# Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/
|
||||
# {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC141.CRT/.
|
||||
# {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/.
|
||||
# Select VC toolset directory based on Visual Studio version
|
||||
vc_redist_root = FindVCRedistRoot()
|
||||
if suffix.startswith('.'):
|
||||
vc_toolset_dir = 'Microsoft.{}.CRT' \
|
||||
.format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
|
||||
source_dir = os.path.join(vc_redist_root,
|
||||
'arm64', 'Microsoft.VC141.CRT')
|
||||
'arm64', vc_toolset_dir)
|
||||
else:
|
||||
vc_toolset_dir = 'Microsoft.{}.DebugCRT' \
|
||||
.format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
|
||||
source_dir = os.path.join(vc_redist_root, 'debug_nonredist',
|
||||
'arm64', 'Microsoft.VC141.DebugCRT')
|
||||
for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
|
||||
dll = dll_pattern % file_part
|
||||
'arm64', vc_toolset_dir)
|
||||
file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140')
|
||||
if target_cpu == 'x64' and GetVisualStudioVersion() != '2017':
|
||||
file_parts = file_parts + ('vcruntime140_1', )
|
||||
for file_part in file_parts:
|
||||
dll = file_part + suffix
|
||||
target = os.path.join(target_dir, dll)
|
||||
source = os.path.join(source_dir, dll)
|
||||
_CopyRuntimeImpl(target, source)
|
||||
|
@ -317,14 +362,13 @@ def FindVCComponentRoot(component):
|
|||
assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
|
||||
vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
|
||||
'VC', component, 'MSVC')
|
||||
vc_component_msvc_contents = os.listdir(vc_component_msvc_root)
|
||||
vc_component_msvc_contents = glob.glob(
|
||||
os.path.join(vc_component_msvc_root, '14.*'))
|
||||
# Select the most recent toolchain if there are several.
|
||||
_SortByHighestVersionNumberFirst(vc_component_msvc_contents)
|
||||
for directory in vc_component_msvc_contents:
|
||||
if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)):
|
||||
continue
|
||||
if re.match(r'14\.\d+\.\d+', directory):
|
||||
return os.path.join(vc_component_msvc_root, directory)
|
||||
if os.path.isdir(directory):
|
||||
return directory
|
||||
raise Exception('Unable to find the VC %s directory.' % component)
|
||||
|
||||
|
||||
|
@ -342,8 +386,7 @@ def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
|
|||
directory does exist. Handles VS 2015, 2017 and 2019."""
|
||||
suffix = 'd.dll' if debug else '.dll'
|
||||
# VS 2015, 2017 and 2019 use the same CRT DLLs.
|
||||
_CopyUCRTRuntime(target_dir, source_dir, target_cpu, '%s140' + suffix,
|
||||
suffix)
|
||||
_CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix)
|
||||
|
||||
|
||||
def CopyDlls(target_dir, configuration, target_cpu):
|
||||
|
@ -394,17 +437,20 @@ def _CopyDebugger(target_dir, target_cpu):
|
|||
# List of debug files that should be copied, the first element of the tuple is
|
||||
# the name of the file and the second indicates if it's optional.
|
||||
debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
|
||||
# The UCRT is not a redistributable component on arm64.
|
||||
if target_cpu != 'arm64':
|
||||
debug_files.extend([('api-ms-win-downlevel-kernel32-l2-1-0.dll', False),
|
||||
('api-ms-win-eventing-provider-l1-1-0.dll', False)])
|
||||
for debug_file, is_optional in debug_files:
|
||||
full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
|
||||
if not os.path.exists(full_path):
|
||||
if is_optional:
|
||||
continue
|
||||
else:
|
||||
# TODO(crbug.com/773476): remove version requirement.
|
||||
raise Exception('%s not found in "%s"\r\nYou must install the '
|
||||
'"Debugging Tools for Windows" feature from the Windows'
|
||||
' 10 SDK.'
|
||||
% (debug_file, full_path))
|
||||
raise Exception('%s not found in "%s"\r\nYou must install '
|
||||
'Windows 10 SDK version 10.0.20348.0 including the '
|
||||
'"Debugging Tools for Windows" feature.' %
|
||||
(debug_file, full_path))
|
||||
target_path = os.path.join(target_dir, debug_file)
|
||||
_CopyRuntimeImpl(target_path, full_path)
|
||||
|
||||
|
@ -412,17 +458,10 @@ def _CopyDebugger(target_dir, target_cpu):
|
|||
def _GetDesiredVsToolchainHashes():
|
||||
"""Load a list of SHA1s corresponding to the toolchains that we want installed
|
||||
to build with."""
|
||||
env_version = GetVisualStudioVersion()
|
||||
if env_version == '2017':
|
||||
# VS 2017 Update 9 (15.9.12) with 10.0.18362 SDK, 10.0.17763 version of
|
||||
# Debuggers, and 10.0.17134 version of d3dcompiler_47.dll, with ARM64
|
||||
# libraries.
|
||||
toolchain_hash = '418b3076791776573a815eb298c8aa590307af63'
|
||||
# Third parties that do not have access to the canonical toolchain can map
|
||||
# canonical toolchain version to their own toolchain versions.
|
||||
toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % toolchain_hash
|
||||
return [os.environ.get(toolchain_hash_mapping_key, toolchain_hash)]
|
||||
raise Exception('Unsupported VS version %s' % env_version)
|
||||
# Third parties that do not have access to the canonical toolchain can map
|
||||
# canonical toolchain version to their own toolchain versions.
|
||||
toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % TOOLCHAIN_HASH
|
||||
return [os.environ.get(toolchain_hash_mapping_key, TOOLCHAIN_HASH)]
|
||||
|
||||
|
||||
def ShouldUpdateToolchain():
|
||||
|
@ -453,8 +492,7 @@ def Update(force=False, no_download=False):
|
|||
|
||||
depot_tools_win_toolchain = \
|
||||
bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
|
||||
if ((sys.platform in ('win32', 'cygwin') or force) and
|
||||
depot_tools_win_toolchain):
|
||||
if (_HostIsWindows() or force) and depot_tools_win_toolchain:
|
||||
import find_depot_tools
|
||||
depot_tools_path = find_depot_tools.add_depot_tools_to_path()
|
||||
|
||||
|
@ -482,9 +520,6 @@ def Update(force=False, no_download=False):
|
|||
subprocess.check_call([
|
||||
ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir])
|
||||
|
||||
# Necessary so that get_toolchain_if_necessary.py will put the VS toolkit
|
||||
# in the correct directory.
|
||||
os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
|
||||
get_toolchain_args = [
|
||||
sys.executable,
|
||||
os.path.join(depot_tools_path,
|
||||
|
|
Loading…
Reference in a new issue