xref: /aosp_15_r20/external/libchrome/build/vs_toolchain.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker#!/usr/bin/env python
2*635a8641SAndroid Build Coastguard Worker# Copyright 2014 The Chromium Authors. All rights reserved.
3*635a8641SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*635a8641SAndroid Build Coastguard Worker# found in the LICENSE file.
5*635a8641SAndroid Build Coastguard Worker
6*635a8641SAndroid Build Coastguard Workerimport glob
7*635a8641SAndroid Build Coastguard Workerimport json
8*635a8641SAndroid Build Coastguard Workerimport os
9*635a8641SAndroid Build Coastguard Workerimport pipes
10*635a8641SAndroid Build Coastguard Workerimport platform
11*635a8641SAndroid Build Coastguard Workerimport re
12*635a8641SAndroid Build Coastguard Workerimport shutil
13*635a8641SAndroid Build Coastguard Workerimport stat
14*635a8641SAndroid Build Coastguard Workerimport subprocess
15*635a8641SAndroid Build Coastguard Workerimport sys
16*635a8641SAndroid Build Coastguard Workerfrom gn_helpers import ToGNString
17*635a8641SAndroid Build Coastguard Worker
18*635a8641SAndroid Build Coastguard Worker
19*635a8641SAndroid Build Coastguard Workerscript_dir = os.path.dirname(os.path.realpath(__file__))
20*635a8641SAndroid Build Coastguard Workerchrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
21*635a8641SAndroid Build Coastguard WorkerSRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
22*635a8641SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
23*635a8641SAndroid Build Coastguard Workerjson_data_file = os.path.join(script_dir, 'win_toolchain.json')
24*635a8641SAndroid Build Coastguard Worker
25*635a8641SAndroid Build Coastguard Worker
26*635a8641SAndroid Build Coastguard Worker# Use MSVS2017 as the default toolchain.
27*635a8641SAndroid Build Coastguard WorkerCURRENT_DEFAULT_TOOLCHAIN_VERSION = '2017'
28*635a8641SAndroid Build Coastguard Worker
29*635a8641SAndroid Build Coastguard Worker
30*635a8641SAndroid Build Coastguard Workerdef SetEnvironmentAndGetRuntimeDllDirs():
31*635a8641SAndroid Build Coastguard Worker  """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
32*635a8641SAndroid Build Coastguard Worker  returns the location of the VS runtime DLLs so they can be copied into
33*635a8641SAndroid Build Coastguard Worker  the output directory after gyp generation.
34*635a8641SAndroid Build Coastguard Worker
35*635a8641SAndroid Build Coastguard Worker  Return value is [x64path, x86path] or None
36*635a8641SAndroid Build Coastguard Worker  """
37*635a8641SAndroid Build Coastguard Worker  vs_runtime_dll_dirs = None
38*635a8641SAndroid Build Coastguard Worker  depot_tools_win_toolchain = \
39*635a8641SAndroid Build Coastguard Worker      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
40*635a8641SAndroid Build Coastguard Worker  # When running on a non-Windows host, only do this if the SDK has explicitly
41*635a8641SAndroid Build Coastguard Worker  # been downloaded before (in which case json_data_file will exist).
42*635a8641SAndroid Build Coastguard Worker  if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
43*635a8641SAndroid Build Coastguard Worker      and depot_tools_win_toolchain):
44*635a8641SAndroid Build Coastguard Worker    if ShouldUpdateToolchain():
45*635a8641SAndroid Build Coastguard Worker      update_result = Update()
46*635a8641SAndroid Build Coastguard Worker      if update_result != 0:
47*635a8641SAndroid Build Coastguard Worker        raise Exception('Failed to update, error code %d.' % update_result)
48*635a8641SAndroid Build Coastguard Worker    with open(json_data_file, 'r') as tempf:
49*635a8641SAndroid Build Coastguard Worker      toolchain_data = json.load(tempf)
50*635a8641SAndroid Build Coastguard Worker
51*635a8641SAndroid Build Coastguard Worker    toolchain = toolchain_data['path']
52*635a8641SAndroid Build Coastguard Worker    version = toolchain_data['version']
53*635a8641SAndroid Build Coastguard Worker    win_sdk = toolchain_data.get('win_sdk')
54*635a8641SAndroid Build Coastguard Worker    if not win_sdk:
55*635a8641SAndroid Build Coastguard Worker      win_sdk = toolchain_data['win8sdk']
56*635a8641SAndroid Build Coastguard Worker    wdk = toolchain_data['wdk']
57*635a8641SAndroid Build Coastguard Worker    # TODO(scottmg): The order unfortunately matters in these. They should be
58*635a8641SAndroid Build Coastguard Worker    # split into separate keys for x86 and x64. (See CopyDlls call below).
59*635a8641SAndroid Build Coastguard Worker    # http://crbug.com/345992
60*635a8641SAndroid Build Coastguard Worker    vs_runtime_dll_dirs = toolchain_data['runtime_dirs']
61*635a8641SAndroid Build Coastguard Worker
62*635a8641SAndroid Build Coastguard Worker    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
63*635a8641SAndroid Build Coastguard Worker    os.environ['GYP_MSVS_VERSION'] = version
64*635a8641SAndroid Build Coastguard Worker
65*635a8641SAndroid Build Coastguard Worker    os.environ['WINDOWSSDKDIR'] = win_sdk
66*635a8641SAndroid Build Coastguard Worker    os.environ['WDK_DIR'] = wdk
67*635a8641SAndroid Build Coastguard Worker    # Include the VS runtime in the PATH in case it's not machine-installed.
68*635a8641SAndroid Build Coastguard Worker    runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
69*635a8641SAndroid Build Coastguard Worker    os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
70*635a8641SAndroid Build Coastguard Worker  elif sys.platform == 'win32' and not depot_tools_win_toolchain:
71*635a8641SAndroid Build Coastguard Worker    if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
72*635a8641SAndroid Build Coastguard Worker      os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
73*635a8641SAndroid Build Coastguard Worker    if not 'GYP_MSVS_VERSION' in os.environ:
74*635a8641SAndroid Build Coastguard Worker      os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
75*635a8641SAndroid Build Coastguard Worker
76*635a8641SAndroid Build Coastguard Worker    # When using an installed toolchain these files aren't needed in the output
77*635a8641SAndroid Build Coastguard Worker    # directory in order to run binaries locally, but they are needed in order
78*635a8641SAndroid Build Coastguard Worker    # to create isolates or the mini_installer. Copying them to the output
79*635a8641SAndroid Build Coastguard Worker    # directory ensures that they are available when needed.
80*635a8641SAndroid Build Coastguard Worker    bitness = platform.architecture()[0]
81*635a8641SAndroid Build Coastguard Worker    # When running 64-bit python the x64 DLLs will be in System32
82*635a8641SAndroid Build Coastguard Worker    x64_path = 'System32' if bitness == '64bit' else 'Sysnative'
83*635a8641SAndroid Build Coastguard Worker    x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path)
84*635a8641SAndroid Build Coastguard Worker    vs_runtime_dll_dirs = [x64_path, os.path.expandvars('%windir%/SysWOW64')]
85*635a8641SAndroid Build Coastguard Worker
86*635a8641SAndroid Build Coastguard Worker  return vs_runtime_dll_dirs
87*635a8641SAndroid Build Coastguard Worker
88*635a8641SAndroid Build Coastguard Worker
89*635a8641SAndroid Build Coastguard Workerdef _RegistryGetValueUsingWinReg(key, value):
90*635a8641SAndroid Build Coastguard Worker  """Use the _winreg module to obtain the value of a registry key.
91*635a8641SAndroid Build Coastguard Worker
92*635a8641SAndroid Build Coastguard Worker  Args:
93*635a8641SAndroid Build Coastguard Worker    key: The registry key.
94*635a8641SAndroid Build Coastguard Worker    value: The particular registry value to read.
95*635a8641SAndroid Build Coastguard Worker  Return:
96*635a8641SAndroid Build Coastguard Worker    contents of the registry key's value, or None on failure.  Throws
97*635a8641SAndroid Build Coastguard Worker    ImportError if _winreg is unavailable.
98*635a8641SAndroid Build Coastguard Worker  """
99*635a8641SAndroid Build Coastguard Worker  import _winreg
100*635a8641SAndroid Build Coastguard Worker  try:
101*635a8641SAndroid Build Coastguard Worker    root, subkey = key.split('\\', 1)
102*635a8641SAndroid Build Coastguard Worker    assert root == 'HKLM'  # Only need HKLM for now.
103*635a8641SAndroid Build Coastguard Worker    with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
104*635a8641SAndroid Build Coastguard Worker      return _winreg.QueryValueEx(hkey, value)[0]
105*635a8641SAndroid Build Coastguard Worker  except WindowsError:
106*635a8641SAndroid Build Coastguard Worker    return None
107*635a8641SAndroid Build Coastguard Worker
108*635a8641SAndroid Build Coastguard Worker
109*635a8641SAndroid Build Coastguard Workerdef _RegistryGetValue(key, value):
110*635a8641SAndroid Build Coastguard Worker  try:
111*635a8641SAndroid Build Coastguard Worker    return _RegistryGetValueUsingWinReg(key, value)
112*635a8641SAndroid Build Coastguard Worker  except ImportError:
113*635a8641SAndroid Build Coastguard Worker    raise Exception('The python library _winreg not found.')
114*635a8641SAndroid Build Coastguard Worker
115*635a8641SAndroid Build Coastguard Worker
116*635a8641SAndroid Build Coastguard Workerdef GetVisualStudioVersion():
117*635a8641SAndroid Build Coastguard Worker  """Return GYP_MSVS_VERSION of Visual Studio.
118*635a8641SAndroid Build Coastguard Worker  """
119*635a8641SAndroid Build Coastguard Worker  return os.environ.get('GYP_MSVS_VERSION', CURRENT_DEFAULT_TOOLCHAIN_VERSION)
120*635a8641SAndroid Build Coastguard Worker
121*635a8641SAndroid Build Coastguard Worker
122*635a8641SAndroid Build Coastguard Workerdef DetectVisualStudioPath():
123*635a8641SAndroid Build Coastguard Worker  """Return path to the GYP_MSVS_VERSION of Visual Studio.
124*635a8641SAndroid Build Coastguard Worker  """
125*635a8641SAndroid Build Coastguard Worker
126*635a8641SAndroid Build Coastguard Worker  # Note that this code is used from
127*635a8641SAndroid Build Coastguard Worker  # build/toolchain/win/setup_toolchain.py as well.
128*635a8641SAndroid Build Coastguard Worker  version_as_year = GetVisualStudioVersion()
129*635a8641SAndroid Build Coastguard Worker  year_to_version = {
130*635a8641SAndroid Build Coastguard Worker      '2017': '15.0',
131*635a8641SAndroid Build Coastguard Worker  }
132*635a8641SAndroid Build Coastguard Worker  if version_as_year not in year_to_version:
133*635a8641SAndroid Build Coastguard Worker    raise Exception(('Visual Studio version %s (from GYP_MSVS_VERSION)'
134*635a8641SAndroid Build Coastguard Worker                     ' not supported. Supported versions are: %s') % (
135*635a8641SAndroid Build Coastguard Worker                       version_as_year, ', '.join(year_to_version.keys())))
136*635a8641SAndroid Build Coastguard Worker  version = year_to_version[version_as_year]
137*635a8641SAndroid Build Coastguard Worker  if version_as_year == '2017':
138*635a8641SAndroid Build Coastguard Worker    # The VC++ 2017 install location needs to be located using COM instead of
139*635a8641SAndroid Build Coastguard Worker    # the registry. For details see:
140*635a8641SAndroid Build Coastguard Worker    # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
141*635a8641SAndroid Build Coastguard Worker    # For now we use a hardcoded default with an environment variable override.
142*635a8641SAndroid Build Coastguard Worker    for path in (
143*635a8641SAndroid Build Coastguard Worker        os.environ.get('vs2017_install'),
144*635a8641SAndroid Build Coastguard Worker        os.path.expandvars('%ProgramFiles(x86)%'
145*635a8641SAndroid Build Coastguard Worker                           '/Microsoft Visual Studio/2017/Enterprise'),
146*635a8641SAndroid Build Coastguard Worker        os.path.expandvars('%ProgramFiles(x86)%'
147*635a8641SAndroid Build Coastguard Worker                           '/Microsoft Visual Studio/2017/Professional'),
148*635a8641SAndroid Build Coastguard Worker        os.path.expandvars('%ProgramFiles(x86)%'
149*635a8641SAndroid Build Coastguard Worker                           '/Microsoft Visual Studio/2017/Community')):
150*635a8641SAndroid Build Coastguard Worker      if path and os.path.exists(path):
151*635a8641SAndroid Build Coastguard Worker        return path
152*635a8641SAndroid Build Coastguard Worker
153*635a8641SAndroid Build Coastguard Worker  raise Exception(('Visual Studio Version %s (from GYP_MSVS_VERSION)'
154*635a8641SAndroid Build Coastguard Worker                   ' not found.') % (version_as_year))
155*635a8641SAndroid Build Coastguard Worker
156*635a8641SAndroid Build Coastguard Worker
157*635a8641SAndroid Build Coastguard Workerdef _CopyRuntimeImpl(target, source, verbose=True):
158*635a8641SAndroid Build Coastguard Worker  """Copy |source| to |target| if it doesn't already exist or if it needs to be
159*635a8641SAndroid Build Coastguard Worker  updated (comparing last modified time as an approximate float match as for
160*635a8641SAndroid Build Coastguard Worker  some reason the values tend to differ by ~1e-07 despite being copies of the
161*635a8641SAndroid Build Coastguard Worker  same file... https://crbug.com/603603).
162*635a8641SAndroid Build Coastguard Worker  """
163*635a8641SAndroid Build Coastguard Worker  if (os.path.isdir(os.path.dirname(target)) and
164*635a8641SAndroid Build Coastguard Worker      (not os.path.isfile(target) or
165*635a8641SAndroid Build Coastguard Worker       abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)):
166*635a8641SAndroid Build Coastguard Worker    if verbose:
167*635a8641SAndroid Build Coastguard Worker      print('Copying %s to %s...' % (source, target))
168*635a8641SAndroid Build Coastguard Worker    if os.path.exists(target):
169*635a8641SAndroid Build Coastguard Worker      # Make the file writable so that we can delete it now, and keep it
170*635a8641SAndroid Build Coastguard Worker      # readable.
171*635a8641SAndroid Build Coastguard Worker      os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
172*635a8641SAndroid Build Coastguard Worker      os.unlink(target)
173*635a8641SAndroid Build Coastguard Worker    shutil.copy2(source, target)
174*635a8641SAndroid Build Coastguard Worker    # Make the file writable so that we can overwrite or delete it later,
175*635a8641SAndroid Build Coastguard Worker    # keep it readable.
176*635a8641SAndroid Build Coastguard Worker    os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
177*635a8641SAndroid Build Coastguard Worker
178*635a8641SAndroid Build Coastguard Worker
179*635a8641SAndroid Build Coastguard Workerdef _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, suffix):
180*635a8641SAndroid Build Coastguard Worker  """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
181*635a8641SAndroid Build Coastguard Worker  exist, but the target directory does exist."""
182*635a8641SAndroid Build Coastguard Worker  for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
183*635a8641SAndroid Build Coastguard Worker    dll = dll_pattern % file_part
184*635a8641SAndroid Build Coastguard Worker    target = os.path.join(target_dir, dll)
185*635a8641SAndroid Build Coastguard Worker    source = os.path.join(source_dir, dll)
186*635a8641SAndroid Build Coastguard Worker    _CopyRuntimeImpl(target, source)
187*635a8641SAndroid Build Coastguard Worker  # Copy the UCRT files from the Windows SDK. This location includes the
188*635a8641SAndroid Build Coastguard Worker  # api-ms-win-crt-*.dll files that are not found in the Windows directory.
189*635a8641SAndroid Build Coastguard Worker  # These files are needed for component builds. If WINDOWSSDKDIR is not set
190*635a8641SAndroid Build Coastguard Worker  # use the default SDK path. This will be the case when
191*635a8641SAndroid Build Coastguard Worker  # DEPOT_TOOLS_WIN_TOOLCHAIN=0 and vcvarsall.bat has not been run.
192*635a8641SAndroid Build Coastguard Worker  win_sdk_dir = os.path.normpath(
193*635a8641SAndroid Build Coastguard Worker      os.environ.get('WINDOWSSDKDIR',
194*635a8641SAndroid Build Coastguard Worker                     os.path.expandvars('%ProgramFiles(x86)%'
195*635a8641SAndroid Build Coastguard Worker                                        '\\Windows Kits\\10')))
196*635a8641SAndroid Build Coastguard Worker  ucrt_dll_dirs = os.path.join(win_sdk_dir, 'Redist', 'ucrt', 'DLLs',
197*635a8641SAndroid Build Coastguard Worker                               target_cpu)
198*635a8641SAndroid Build Coastguard Worker  ucrt_files = glob.glob(os.path.join(ucrt_dll_dirs, 'api-ms-win-*.dll'))
199*635a8641SAndroid Build Coastguard Worker  assert len(ucrt_files) > 0
200*635a8641SAndroid Build Coastguard Worker  for ucrt_src_file in ucrt_files:
201*635a8641SAndroid Build Coastguard Worker    file_part = os.path.basename(ucrt_src_file)
202*635a8641SAndroid Build Coastguard Worker    ucrt_dst_file = os.path.join(target_dir, file_part)
203*635a8641SAndroid Build Coastguard Worker    _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False)
204*635a8641SAndroid Build Coastguard Worker  _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
205*635a8641SAndroid Build Coastguard Worker                    os.path.join(source_dir, 'ucrtbase' + suffix))
206*635a8641SAndroid Build Coastguard Worker
207*635a8641SAndroid Build Coastguard Worker
208*635a8641SAndroid Build Coastguard Workerdef FindVCToolsRoot():
209*635a8641SAndroid Build Coastguard Worker  """In VS2017 the PGO runtime dependencies are located in
210*635a8641SAndroid Build Coastguard Worker  {toolchain_root}/VC/Tools/MSVC/{x.y.z}/bin/Host{target_cpu}/{target_cpu}/, the
211*635a8641SAndroid Build Coastguard Worker  {version_number} part is likely to change in case of a minor update of the
212*635a8641SAndroid Build Coastguard Worker  toolchain so we don't hardcode this value here (except for the major number).
213*635a8641SAndroid Build Coastguard Worker
214*635a8641SAndroid Build Coastguard Worker  This returns the '{toolchain_root}/VC/Tools/MSVC/{x.y.z}/bin/' path.
215*635a8641SAndroid Build Coastguard Worker
216*635a8641SAndroid Build Coastguard Worker  This function should only be called when using VS2017.
217*635a8641SAndroid Build Coastguard Worker  """
218*635a8641SAndroid Build Coastguard Worker  assert GetVisualStudioVersion() == '2017'
219*635a8641SAndroid Build Coastguard Worker  SetEnvironmentAndGetRuntimeDllDirs()
220*635a8641SAndroid Build Coastguard Worker  assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
221*635a8641SAndroid Build Coastguard Worker  vc_tools_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
222*635a8641SAndroid Build Coastguard Worker      'VC', 'Tools', 'MSVC')
223*635a8641SAndroid Build Coastguard Worker  for directory in os.listdir(vc_tools_msvc_root):
224*635a8641SAndroid Build Coastguard Worker    if not os.path.isdir(os.path.join(vc_tools_msvc_root, directory)):
225*635a8641SAndroid Build Coastguard Worker      continue
226*635a8641SAndroid Build Coastguard Worker    if re.match('14\.\d+\.\d+', directory):
227*635a8641SAndroid Build Coastguard Worker      return os.path.join(vc_tools_msvc_root, directory, 'bin')
228*635a8641SAndroid Build Coastguard Worker  raise Exception('Unable to find the VC tools directory.')
229*635a8641SAndroid Build Coastguard Worker
230*635a8641SAndroid Build Coastguard Worker
231*635a8641SAndroid Build Coastguard Workerdef _CopyPGORuntime(target_dir, target_cpu):
232*635a8641SAndroid Build Coastguard Worker  """Copy the runtime dependencies required during a PGO build.
233*635a8641SAndroid Build Coastguard Worker  """
234*635a8641SAndroid Build Coastguard Worker  env_version = GetVisualStudioVersion()
235*635a8641SAndroid Build Coastguard Worker  # These dependencies will be in a different location depending on the version
236*635a8641SAndroid Build Coastguard Worker  # of the toolchain.
237*635a8641SAndroid Build Coastguard Worker  if env_version == '2017':
238*635a8641SAndroid Build Coastguard Worker    pgo_runtime_root = FindVCToolsRoot()
239*635a8641SAndroid Build Coastguard Worker    assert pgo_runtime_root
240*635a8641SAndroid Build Coastguard Worker    # There's no version of pgosweep.exe in HostX64/x86, so we use the copy
241*635a8641SAndroid Build Coastguard Worker    # from HostX86/x86.
242*635a8641SAndroid Build Coastguard Worker    pgo_x86_runtime_dir = os.path.join(pgo_runtime_root, 'HostX86', 'x86')
243*635a8641SAndroid Build Coastguard Worker    pgo_x64_runtime_dir = os.path.join(pgo_runtime_root, 'HostX64', 'x64')
244*635a8641SAndroid Build Coastguard Worker  else:
245*635a8641SAndroid Build Coastguard Worker    raise Exception('Unexpected toolchain version: %s.' % env_version)
246*635a8641SAndroid Build Coastguard Worker
247*635a8641SAndroid Build Coastguard Worker  # We need to copy 2 runtime dependencies used during the profiling step:
248*635a8641SAndroid Build Coastguard Worker  #     - pgort140.dll: runtime library required to run the instrumented image.
249*635a8641SAndroid Build Coastguard Worker  #     - pgosweep.exe: executable used to collect the profiling data
250*635a8641SAndroid Build Coastguard Worker  pgo_runtimes = ['pgort140.dll', 'pgosweep.exe']
251*635a8641SAndroid Build Coastguard Worker  for runtime in pgo_runtimes:
252*635a8641SAndroid Build Coastguard Worker    if target_cpu == 'x86':
253*635a8641SAndroid Build Coastguard Worker      source = os.path.join(pgo_x86_runtime_dir, runtime)
254*635a8641SAndroid Build Coastguard Worker    elif target_cpu == 'x64':
255*635a8641SAndroid Build Coastguard Worker      source = os.path.join(pgo_x64_runtime_dir, runtime)
256*635a8641SAndroid Build Coastguard Worker    else:
257*635a8641SAndroid Build Coastguard Worker      raise NotImplementedError("Unexpected target_cpu value: " + target_cpu)
258*635a8641SAndroid Build Coastguard Worker    if not os.path.exists(source):
259*635a8641SAndroid Build Coastguard Worker      raise Exception('Unable to find %s.' % source)
260*635a8641SAndroid Build Coastguard Worker    _CopyRuntimeImpl(os.path.join(target_dir, runtime), source)
261*635a8641SAndroid Build Coastguard Worker
262*635a8641SAndroid Build Coastguard Worker
263*635a8641SAndroid Build Coastguard Workerdef _CopyRuntime(target_dir, source_dir, target_cpu, debug):
264*635a8641SAndroid Build Coastguard Worker  """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
265*635a8641SAndroid Build Coastguard Worker  directory does exist. Handles VS 2015 and VS 2017."""
266*635a8641SAndroid Build Coastguard Worker  suffix = "d.dll" if debug else ".dll"
267*635a8641SAndroid Build Coastguard Worker  # VS 2017 uses the same CRT DLLs as VS 2015.
268*635a8641SAndroid Build Coastguard Worker  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, '%s140' + suffix,
269*635a8641SAndroid Build Coastguard Worker                    suffix)
270*635a8641SAndroid Build Coastguard Worker
271*635a8641SAndroid Build Coastguard Worker
272*635a8641SAndroid Build Coastguard Workerdef CopyDlls(target_dir, configuration, target_cpu):
273*635a8641SAndroid Build Coastguard Worker  """Copy the VS runtime DLLs into the requested directory as needed.
274*635a8641SAndroid Build Coastguard Worker
275*635a8641SAndroid Build Coastguard Worker  configuration is one of 'Debug' or 'Release'.
276*635a8641SAndroid Build Coastguard Worker  target_cpu is one of 'x86' or 'x64'.
277*635a8641SAndroid Build Coastguard Worker
278*635a8641SAndroid Build Coastguard Worker  The debug configuration gets both the debug and release DLLs; the
279*635a8641SAndroid Build Coastguard Worker  release config only the latter.
280*635a8641SAndroid Build Coastguard Worker  """
281*635a8641SAndroid Build Coastguard Worker  vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
282*635a8641SAndroid Build Coastguard Worker  if not vs_runtime_dll_dirs:
283*635a8641SAndroid Build Coastguard Worker    return
284*635a8641SAndroid Build Coastguard Worker
285*635a8641SAndroid Build Coastguard Worker  x64_runtime, x86_runtime = vs_runtime_dll_dirs
286*635a8641SAndroid Build Coastguard Worker  runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
287*635a8641SAndroid Build Coastguard Worker  _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
288*635a8641SAndroid Build Coastguard Worker  if configuration == 'Debug':
289*635a8641SAndroid Build Coastguard Worker    _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
290*635a8641SAndroid Build Coastguard Worker  else:
291*635a8641SAndroid Build Coastguard Worker    _CopyPGORuntime(target_dir, target_cpu)
292*635a8641SAndroid Build Coastguard Worker
293*635a8641SAndroid Build Coastguard Worker  _CopyDebugger(target_dir, target_cpu)
294*635a8641SAndroid Build Coastguard Worker
295*635a8641SAndroid Build Coastguard Worker
296*635a8641SAndroid Build Coastguard Workerdef _CopyDebugger(target_dir, target_cpu):
297*635a8641SAndroid Build Coastguard Worker  """Copy dbghelp.dll and dbgcore.dll into the requested directory as needed.
298*635a8641SAndroid Build Coastguard Worker
299*635a8641SAndroid Build Coastguard Worker  target_cpu is one of 'x86' or 'x64'.
300*635a8641SAndroid Build Coastguard Worker
301*635a8641SAndroid Build Coastguard Worker  dbghelp.dll is used when Chrome needs to symbolize stacks. Copying this file
302*635a8641SAndroid Build Coastguard Worker  from the SDK directory avoids using the system copy of dbghelp.dll which then
303*635a8641SAndroid Build Coastguard Worker  ensures compatibility with recent debug information formats, such as VS
304*635a8641SAndroid Build Coastguard Worker  2017 /debug:fastlink PDBs.
305*635a8641SAndroid Build Coastguard Worker
306*635a8641SAndroid Build Coastguard Worker  dbgcore.dll is needed when using some functions from dbghelp.dll (like
307*635a8641SAndroid Build Coastguard Worker  MinidumpWriteDump).
308*635a8641SAndroid Build Coastguard Worker  """
309*635a8641SAndroid Build Coastguard Worker  win_sdk_dir = SetEnvironmentAndGetSDKDir()
310*635a8641SAndroid Build Coastguard Worker  if not win_sdk_dir:
311*635a8641SAndroid Build Coastguard Worker    return
312*635a8641SAndroid Build Coastguard Worker
313*635a8641SAndroid Build Coastguard Worker  # List of debug files that should be copied, the first element of the tuple is
314*635a8641SAndroid Build Coastguard Worker  # the name of the file and the second indicates if it's optional.
315*635a8641SAndroid Build Coastguard Worker  debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
316*635a8641SAndroid Build Coastguard Worker  for debug_file, is_optional in debug_files:
317*635a8641SAndroid Build Coastguard Worker    full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
318*635a8641SAndroid Build Coastguard Worker    if not os.path.exists(full_path):
319*635a8641SAndroid Build Coastguard Worker      if is_optional:
320*635a8641SAndroid Build Coastguard Worker        continue
321*635a8641SAndroid Build Coastguard Worker      else:
322*635a8641SAndroid Build Coastguard Worker        # TODO(crbug.com/773476): remove version requirement.
323*635a8641SAndroid Build Coastguard Worker        raise Exception('%s not found in "%s"\r\nYou must install the '
324*635a8641SAndroid Build Coastguard Worker                        '"Debugging Tools for Windows" feature from the Windows'
325*635a8641SAndroid Build Coastguard Worker                        ' 10 SDK. You must use v10.0.17134.0. of the SDK'
326*635a8641SAndroid Build Coastguard Worker                        % (debug_file, full_path))
327*635a8641SAndroid Build Coastguard Worker    target_path = os.path.join(target_dir, debug_file)
328*635a8641SAndroid Build Coastguard Worker    _CopyRuntimeImpl(target_path, full_path)
329*635a8641SAndroid Build Coastguard Worker
330*635a8641SAndroid Build Coastguard Worker
331*635a8641SAndroid Build Coastguard Workerdef _GetDesiredVsToolchainHashes():
332*635a8641SAndroid Build Coastguard Worker  """Load a list of SHA1s corresponding to the toolchains that we want installed
333*635a8641SAndroid Build Coastguard Worker  to build with."""
334*635a8641SAndroid Build Coastguard Worker  env_version = GetVisualStudioVersion()
335*635a8641SAndroid Build Coastguard Worker  if env_version == '2017':
336*635a8641SAndroid Build Coastguard Worker    # VS 2017 Update 7.1 (15.7.1) with 10.0.17134.12 SDK, rebuilt with
337*635a8641SAndroid Build Coastguard Worker    # dbghelp.dll fix.
338*635a8641SAndroid Build Coastguard Worker    toolchain_hash = '3bc0ec615cf20ee342f3bc29bc991b5ad66d8d2c'
339*635a8641SAndroid Build Coastguard Worker    # Third parties that do not have access to the canonical toolchain can map
340*635a8641SAndroid Build Coastguard Worker    # canonical toolchain version to their own toolchain versions.
341*635a8641SAndroid Build Coastguard Worker    toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % toolchain_hash
342*635a8641SAndroid Build Coastguard Worker    return [os.environ.get(toolchain_hash_mapping_key, toolchain_hash)]
343*635a8641SAndroid Build Coastguard Worker  raise Exception('Unsupported VS version %s' % env_version)
344*635a8641SAndroid Build Coastguard Worker
345*635a8641SAndroid Build Coastguard Worker
346*635a8641SAndroid Build Coastguard Workerdef ShouldUpdateToolchain():
347*635a8641SAndroid Build Coastguard Worker  """Check if the toolchain should be upgraded."""
348*635a8641SAndroid Build Coastguard Worker  if not os.path.exists(json_data_file):
349*635a8641SAndroid Build Coastguard Worker    return True
350*635a8641SAndroid Build Coastguard Worker  with open(json_data_file, 'r') as tempf:
351*635a8641SAndroid Build Coastguard Worker    toolchain_data = json.load(tempf)
352*635a8641SAndroid Build Coastguard Worker  version = toolchain_data['version']
353*635a8641SAndroid Build Coastguard Worker  env_version = GetVisualStudioVersion()
354*635a8641SAndroid Build Coastguard Worker  # If there's a mismatch between the version set in the environment and the one
355*635a8641SAndroid Build Coastguard Worker  # in the json file then the toolchain should be updated.
356*635a8641SAndroid Build Coastguard Worker  return version != env_version
357*635a8641SAndroid Build Coastguard Worker
358*635a8641SAndroid Build Coastguard Worker
359*635a8641SAndroid Build Coastguard Workerdef Update(force=False):
360*635a8641SAndroid Build Coastguard Worker  """Requests an update of the toolchain to the specific hashes we have at
361*635a8641SAndroid Build Coastguard Worker  this revision. The update outputs a .json of the various configuration
362*635a8641SAndroid Build Coastguard Worker  information required to pass to gyp which we use in |GetToolchainDir()|.
363*635a8641SAndroid Build Coastguard Worker  """
364*635a8641SAndroid Build Coastguard Worker  if force != False and force != '--force':
365*635a8641SAndroid Build Coastguard Worker    print('Unknown parameter "%s"' % force, file=sys.stderr)
366*635a8641SAndroid Build Coastguard Worker    return 1
367*635a8641SAndroid Build Coastguard Worker  if force == '--force' or os.path.exists(json_data_file):
368*635a8641SAndroid Build Coastguard Worker    force = True
369*635a8641SAndroid Build Coastguard Worker
370*635a8641SAndroid Build Coastguard Worker  depot_tools_win_toolchain = \
371*635a8641SAndroid Build Coastguard Worker      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
372*635a8641SAndroid Build Coastguard Worker  if ((sys.platform in ('win32', 'cygwin') or force) and
373*635a8641SAndroid Build Coastguard Worker        depot_tools_win_toolchain):
374*635a8641SAndroid Build Coastguard Worker    import find_depot_tools
375*635a8641SAndroid Build Coastguard Worker    depot_tools_path = find_depot_tools.add_depot_tools_to_path()
376*635a8641SAndroid Build Coastguard Worker
377*635a8641SAndroid Build Coastguard Worker    # On Linux, the file system is usually case-sensitive while the Windows
378*635a8641SAndroid Build Coastguard Worker    # SDK only works on case-insensitive file systems.  If it doesn't already
379*635a8641SAndroid Build Coastguard Worker    # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive
380*635a8641SAndroid Build Coastguard Worker    # part of the file system.
381*635a8641SAndroid Build Coastguard Worker    toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain', 'vs_files')
382*635a8641SAndroid Build Coastguard Worker    # For testing this block, unmount existing mounts with
383*635a8641SAndroid Build Coastguard Worker    # fusermount -u third_party/depot_tools/win_toolchain/vs_files
384*635a8641SAndroid Build Coastguard Worker    if sys.platform.startswith('linux') and not os.path.ismount(toolchain_dir):
385*635a8641SAndroid Build Coastguard Worker      import distutils.spawn
386*635a8641SAndroid Build Coastguard Worker      ciopfs = distutils.spawn.find_executable('ciopfs')
387*635a8641SAndroid Build Coastguard Worker      if not ciopfs:
388*635a8641SAndroid Build Coastguard Worker        # ciopfs not found in PATH; try the one downloaded from the DEPS hook.
389*635a8641SAndroid Build Coastguard Worker        ciopfs = os.path.join(script_dir, 'ciopfs')
390*635a8641SAndroid Build Coastguard Worker      if not os.path.isdir(toolchain_dir):
391*635a8641SAndroid Build Coastguard Worker        os.mkdir(toolchain_dir)
392*635a8641SAndroid Build Coastguard Worker      if not os.path.isdir(toolchain_dir + '.ciopfs'):
393*635a8641SAndroid Build Coastguard Worker        os.mkdir(toolchain_dir + '.ciopfs')
394*635a8641SAndroid Build Coastguard Worker      # Without use_ino, clang's #pragma once and Wnonportable-include-path
395*635a8641SAndroid Build Coastguard Worker      # both don't work right, see https://llvm.org/PR34931
396*635a8641SAndroid Build Coastguard Worker      # use_ino doesn't slow down builds, so it seems there's no drawback to
397*635a8641SAndroid Build Coastguard Worker      # just using it always.
398*635a8641SAndroid Build Coastguard Worker      subprocess.check_call([
399*635a8641SAndroid Build Coastguard Worker          ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir])
400*635a8641SAndroid Build Coastguard Worker
401*635a8641SAndroid Build Coastguard Worker    # Necessary so that get_toolchain_if_necessary.py will put the VS toolkit
402*635a8641SAndroid Build Coastguard Worker    # in the correct directory.
403*635a8641SAndroid Build Coastguard Worker    os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
404*635a8641SAndroid Build Coastguard Worker    get_toolchain_args = [
405*635a8641SAndroid Build Coastguard Worker        sys.executable,
406*635a8641SAndroid Build Coastguard Worker        os.path.join(depot_tools_path,
407*635a8641SAndroid Build Coastguard Worker                    'win_toolchain',
408*635a8641SAndroid Build Coastguard Worker                    'get_toolchain_if_necessary.py'),
409*635a8641SAndroid Build Coastguard Worker        '--output-json', json_data_file,
410*635a8641SAndroid Build Coastguard Worker      ] + _GetDesiredVsToolchainHashes()
411*635a8641SAndroid Build Coastguard Worker    if force:
412*635a8641SAndroid Build Coastguard Worker      get_toolchain_args.append('--force')
413*635a8641SAndroid Build Coastguard Worker    subprocess.check_call(get_toolchain_args)
414*635a8641SAndroid Build Coastguard Worker
415*635a8641SAndroid Build Coastguard Worker  return 0
416*635a8641SAndroid Build Coastguard Worker
417*635a8641SAndroid Build Coastguard Worker
418*635a8641SAndroid Build Coastguard Workerdef NormalizePath(path):
419*635a8641SAndroid Build Coastguard Worker  while path.endswith("\\"):
420*635a8641SAndroid Build Coastguard Worker    path = path[:-1]
421*635a8641SAndroid Build Coastguard Worker  return path
422*635a8641SAndroid Build Coastguard Worker
423*635a8641SAndroid Build Coastguard Worker
424*635a8641SAndroid Build Coastguard Workerdef SetEnvironmentAndGetSDKDir():
425*635a8641SAndroid Build Coastguard Worker  """Gets location information about the current sdk (must have been
426*635a8641SAndroid Build Coastguard Worker  previously updated by 'update'). This is used for the GN build."""
427*635a8641SAndroid Build Coastguard Worker  SetEnvironmentAndGetRuntimeDllDirs()
428*635a8641SAndroid Build Coastguard Worker
429*635a8641SAndroid Build Coastguard Worker  # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
430*635a8641SAndroid Build Coastguard Worker  if not 'WINDOWSSDKDIR' in os.environ:
431*635a8641SAndroid Build Coastguard Worker    default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%'
432*635a8641SAndroid Build Coastguard Worker                                          '\\Windows Kits\\10')
433*635a8641SAndroid Build Coastguard Worker    if os.path.isdir(default_sdk_path):
434*635a8641SAndroid Build Coastguard Worker      os.environ['WINDOWSSDKDIR'] = default_sdk_path
435*635a8641SAndroid Build Coastguard Worker
436*635a8641SAndroid Build Coastguard Worker  return NormalizePath(os.environ['WINDOWSSDKDIR'])
437*635a8641SAndroid Build Coastguard Worker
438*635a8641SAndroid Build Coastguard Worker
439*635a8641SAndroid Build Coastguard Workerdef GetToolchainDir():
440*635a8641SAndroid Build Coastguard Worker  """Gets location information about the current toolchain (must have been
441*635a8641SAndroid Build Coastguard Worker  previously updated by 'update'). This is used for the GN build."""
442*635a8641SAndroid Build Coastguard Worker  runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
443*635a8641SAndroid Build Coastguard Worker  win_sdk_dir = SetEnvironmentAndGetSDKDir()
444*635a8641SAndroid Build Coastguard Worker
445*635a8641SAndroid Build Coastguard Worker  print('''vs_path = %s
446*635a8641SAndroid Build Coastguard Workersdk_path = %s
447*635a8641SAndroid Build Coastguard Workervs_version = %s
448*635a8641SAndroid Build Coastguard Workerwdk_dir = %s
449*635a8641SAndroid Build Coastguard Workerruntime_dirs = %s
450*635a8641SAndroid Build Coastguard Worker''' % (
451*635a8641SAndroid Build Coastguard Worker      ToGNString(NormalizePath(os.environ['GYP_MSVS_OVERRIDE_PATH'])),
452*635a8641SAndroid Build Coastguard Worker      ToGNString(win_sdk_dir),
453*635a8641SAndroid Build Coastguard Worker      ToGNString(GetVisualStudioVersion()),
454*635a8641SAndroid Build Coastguard Worker      ToGNString(NormalizePath(os.environ.get('WDK_DIR', ''))),
455*635a8641SAndroid Build Coastguard Worker      ToGNString(os.path.pathsep.join(runtime_dll_dirs or ['None']))))
456*635a8641SAndroid Build Coastguard Worker
457*635a8641SAndroid Build Coastguard Worker
458*635a8641SAndroid Build Coastguard Workerdef main():
459*635a8641SAndroid Build Coastguard Worker  commands = {
460*635a8641SAndroid Build Coastguard Worker      'update': Update,
461*635a8641SAndroid Build Coastguard Worker      'get_toolchain_dir': GetToolchainDir,
462*635a8641SAndroid Build Coastguard Worker      'copy_dlls': CopyDlls,
463*635a8641SAndroid Build Coastguard Worker  }
464*635a8641SAndroid Build Coastguard Worker  if len(sys.argv) < 2 or sys.argv[1] not in commands:
465*635a8641SAndroid Build Coastguard Worker    print('Expected one of: %s' % ', '.join(commands), file=sys.stderr)
466*635a8641SAndroid Build Coastguard Worker    return 1
467*635a8641SAndroid Build Coastguard Worker  return commands[sys.argv[1]](*sys.argv[2:])
468*635a8641SAndroid Build Coastguard Worker
469*635a8641SAndroid Build Coastguard Worker
470*635a8641SAndroid Build Coastguard Workerif __name__ == '__main__':
471*635a8641SAndroid Build Coastguard Worker  sys.exit(main())
472