xref: /aosp_15_r20/external/cronet/build/vs_toolchain.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python3
2# Copyright 2014 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6
7import collections
8import glob
9import json
10import os
11import platform
12import re
13import shutil
14import stat
15import subprocess
16import sys
17
18from gn_helpers import ToGNString
19
20# VS 2022 17.9.2 with 10.0.22621.2428 SDK with ARM64 libraries and UWP support.
21# See go/chromium-msvc-toolchain for instructions about how to update the
22# toolchain.
23#
24# When updating the toolchain, consider the following areas impacted by the
25# toolchain version:
26#
27# * //base/win/windows_version.cc NTDDI preprocessor check
28#   Triggers a compiler error if the available SDK is older than the minimum.
29# * SDK_VERSION in this file
30#   Must match the packaged/required SDK version.
31# * SDK_VERSION in build/toolchain/win/setup_toolchain.py.
32# * //build/config/win/BUILD.gn NTDDI_VERSION value
33#   Affects the availability of APIs in the toolchain headers.
34# * //docs/windows_build_instructions.md mentions of VS or Windows SDK.
35#   Keeps the document consistent with the toolchain version.
36# * //tools/win/setenv.py
37#   Add/remove VS versions when upgrading to a new VS version.
38# * MSVC_TOOLSET_VERSION in this file
39#   Maps between Visual Studio version and MSVC toolset
40# * MSVS_VERSIONS in this file
41#   Records the packaged and default version of Visual Studio
42TOOLCHAIN_HASH = '7393122652'
43SDK_VERSION = '10.0.22621.0'
44
45script_dir = os.path.dirname(os.path.realpath(__file__))
46json_data_file = os.path.join(script_dir, 'win_toolchain.json')
47
48# VS versions are listed in descending order of priority (highest first).
49# The first version is assumed by this script to be the one that is packaged,
50# which makes a difference for the arm64 runtime.
51MSVS_VERSIONS = collections.OrderedDict([
52    ('2022', '17.0'),  # Default and packaged version of Visual Studio.
53    ('2019', '16.0'),
54    ('2017', '15.0'),
55])
56
57# List of preferred VC toolset version based on MSVS
58# Order is not relevant for this dictionary.
59MSVC_TOOLSET_VERSION = {
60    '2022': 'VC143',
61    '2019': 'VC142',
62    '2017': 'VC141',
63}
64
65def _HostIsWindows():
66  """Returns True if running on a Windows host (including under cygwin)."""
67  return sys.platform in ('win32', 'cygwin')
68
69def SetEnvironmentAndGetRuntimeDllDirs():
70  """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
71  returns the location of the VC runtime DLLs so they can be copied into
72  the output directory after gyp generation.
73
74  Return value is [x64path, x86path, 'Arm64Unused'] or None. arm64path is
75  generated separately because there are multiple folders for the arm64 VC
76  runtime.
77  """
78  vs_runtime_dll_dirs = None
79  depot_tools_win_toolchain = \
80      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
81  # When running on a non-Windows host, only do this if the SDK has explicitly
82  # been downloaded before (in which case json_data_file will exist).
83  if ((_HostIsWindows() or os.path.exists(json_data_file))
84      and depot_tools_win_toolchain):
85    if ShouldUpdateToolchain():
86      if len(sys.argv) > 1 and sys.argv[1] == 'update':
87        update_result = Update()
88      else:
89        update_result = Update(no_download=True)
90      if update_result != 0:
91        raise Exception('Failed to update, error code %d.' % update_result)
92    with open(json_data_file, 'r') as tempf:
93      toolchain_data = json.load(tempf)
94
95    toolchain = toolchain_data['path']
96    version = toolchain_data['version']
97    win_sdk = toolchain_data.get('win_sdk')
98    wdk = toolchain_data['wdk']
99    # TODO(scottmg): The order unfortunately matters in these. They should be
100    # split into separate keys for x64/x86/arm64. (See CopyDlls call below).
101    # http://crbug.com/345992
102    vs_runtime_dll_dirs = toolchain_data['runtime_dirs']
103    # The number of runtime_dirs in the toolchain_data was two (x64/x86) but
104    # changed to three (x64/x86/arm64) and this code needs to handle both
105    # possibilities, which can change independently from this code.
106    if len(vs_runtime_dll_dirs) == 2:
107      vs_runtime_dll_dirs.append('Arm64Unused')
108
109    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
110
111    os.environ['WINDOWSSDKDIR'] = win_sdk
112    os.environ['WDK_DIR'] = wdk
113    # Include the VS runtime in the PATH in case it's not machine-installed.
114    runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
115    os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
116  elif sys.platform == 'win32' and not depot_tools_win_toolchain:
117    if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
118      os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
119
120    # When using an installed toolchain these files aren't needed in the output
121    # directory in order to run binaries locally, but they are needed in order
122    # to create isolates or the mini_installer. Copying them to the output
123    # directory ensures that they are available when needed.
124    bitness = platform.architecture()[0]
125    # When running 64-bit python the x64 DLLs will be in System32
126    # ARM64 binaries will not be available in the system directories because we
127    # don't build on ARM64 machines.
128    x64_path = 'System32' if bitness == '64bit' else 'Sysnative'
129    x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path)
130    vs_runtime_dll_dirs = [x64_path,
131                           os.path.join(os.path.expandvars('%windir%'),
132                                        'SysWOW64'),
133                           'Arm64Unused']
134
135  return vs_runtime_dll_dirs
136
137
138def _RegistryGetValueUsingWinReg(key, value):
139  """Use the _winreg module to obtain the value of a registry key.
140
141  Args:
142    key: The registry key.
143    value: The particular registry value to read.
144  Return:
145    contents of the registry key's value, or None on failure.  Throws
146    ImportError if _winreg is unavailable.
147  """
148  import _winreg
149  try:
150    root, subkey = key.split('\\', 1)
151    assert root == 'HKLM'  # Only need HKLM for now.
152    with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
153      return _winreg.QueryValueEx(hkey, value)[0]
154  except WindowsError:
155    return None
156
157
158def _RegistryGetValue(key, value):
159  try:
160    return _RegistryGetValueUsingWinReg(key, value)
161  except ImportError:
162    raise Exception('The python library _winreg not found.')
163
164
165def GetVisualStudioVersion():
166  """Return best available version of Visual Studio.
167  """
168  supported_versions = list(MSVS_VERSIONS.keys())
169
170  # VS installed in depot_tools for Googlers
171  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
172    return supported_versions[0]
173
174  # VS installed in system for external developers
175  supported_versions_str = ', '.join('{} ({})'.format(v,k)
176      for k,v in MSVS_VERSIONS.items())
177  available_versions = []
178  for version in supported_versions:
179    # Checking vs%s_install environment variables.
180    # For example, vs2019_install could have the value
181    # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community".
182    # Only vs2017_install, vs2019_install and vs2022_install are supported.
183    path = os.environ.get('vs%s_install' % version)
184    if path and os.path.exists(path):
185      available_versions.append(version)
186      break
187    # Detecting VS under possible paths.
188    if version >= '2022':
189      program_files_path_variable = '%ProgramFiles%'
190    else:
191      program_files_path_variable = '%ProgramFiles(x86)%'
192    path = os.path.expandvars(program_files_path_variable +
193                              '/Microsoft Visual Studio/%s' % version)
194    if path and any(
195        os.path.exists(os.path.join(path, edition))
196        for edition in ('Enterprise', 'Professional', 'Community', 'Preview',
197                        'BuildTools')):
198      available_versions.append(version)
199      break
200
201  if not available_versions:
202    raise Exception('No supported Visual Studio can be found.'
203                    ' Supported versions are: %s.' % supported_versions_str)
204  return available_versions[0]
205
206
207def DetectVisualStudioPath():
208  """Return path to the installed Visual Studio.
209  """
210
211  # Note that this code is used from
212  # build/toolchain/win/setup_toolchain.py as well.
213  version_as_year = GetVisualStudioVersion()
214
215  # The VC++ >=2017 install location needs to be located using COM instead of
216  # the registry. For details see:
217  # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
218  # For now we use a hardcoded default with an environment variable override.
219  if version_as_year >= '2022':
220    program_files_path_variable = '%ProgramFiles%'
221  else:
222    program_files_path_variable = '%ProgramFiles(x86)%'
223  for path in (os.environ.get('vs%s_install' % version_as_year),
224               os.path.expandvars(program_files_path_variable +
225                                  '/Microsoft Visual Studio/%s/Enterprise' %
226                                  version_as_year),
227               os.path.expandvars(program_files_path_variable +
228                                  '/Microsoft Visual Studio/%s/Professional' %
229                                  version_as_year),
230               os.path.expandvars(program_files_path_variable +
231                                  '/Microsoft Visual Studio/%s/Community' %
232                                  version_as_year),
233               os.path.expandvars(program_files_path_variable +
234                                  '/Microsoft Visual Studio/%s/Preview' %
235                                  version_as_year),
236               os.path.expandvars(program_files_path_variable +
237                                  '/Microsoft Visual Studio/%s/BuildTools' %
238                                  version_as_year)):
239    if path and os.path.exists(path):
240      return path
241
242  raise Exception('Visual Studio Version %s not found.' % version_as_year)
243
244
245def _CopyRuntimeImpl(target, source, verbose=True):
246  """Copy |source| to |target| if it doesn't already exist or if it needs to be
247  updated (comparing last modified time as an approximate float match as for
248  some reason the values tend to differ by ~1e-07 despite being copies of the
249  same file... https://crbug.com/603603).
250  """
251  if (os.path.isdir(os.path.dirname(target)) and
252      (not os.path.isfile(target) or
253       abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)):
254    if verbose:
255      print('Copying %s to %s...' % (source, target))
256    if os.path.exists(target):
257      # Make the file writable so that we can delete it now, and keep it
258      # readable.
259      os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
260      os.unlink(target)
261    shutil.copy2(source, target)
262    # Make the file writable so that we can overwrite or delete it later,
263    # keep it readable.
264    os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
265
266def _SortByHighestVersionNumberFirst(list_of_str_versions):
267  """This sorts |list_of_str_versions| according to version number rules
268  so that version "1.12" is higher than version "1.9". Does not work
269  with non-numeric versions like 1.4.a8 which will be higher than
270  1.4.a12. It does handle the versions being embedded in file paths.
271  """
272  def to_int_if_int(x):
273    try:
274      return int(x)
275    except ValueError:
276      return x
277
278  def to_number_sequence(x):
279    part_sequence = re.split(r'[\\/\.]', x)
280    return [to_int_if_int(x) for x in part_sequence]
281
282  list_of_str_versions.sort(key=to_number_sequence, reverse=True)
283
284
285def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
286  """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
287  exist, but the target directory does exist."""
288  if target_cpu == 'arm64':
289    # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/
290    # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/.
291    # Select VC toolset directory based on Visual Studio version
292    vc_redist_root = FindVCRedistRoot()
293    if suffix.startswith('.'):
294      vc_toolset_dir = 'Microsoft.{}.CRT' \
295         .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
296      source_dir = os.path.join(vc_redist_root,
297                                'arm64', vc_toolset_dir)
298    else:
299      vc_toolset_dir = 'Microsoft.{}.DebugCRT' \
300         .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
301      source_dir = os.path.join(vc_redist_root, 'debug_nonredist',
302                                'arm64', vc_toolset_dir)
303  file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140')
304  if target_cpu == 'x64' and GetVisualStudioVersion() != '2017':
305    file_parts = file_parts + ('vcruntime140_1', )
306  for file_part in file_parts:
307    dll = file_part + suffix
308    target = os.path.join(target_dir, dll)
309    source = os.path.join(source_dir, dll)
310    _CopyRuntimeImpl(target, source)
311  # We must copy ucrtbased.dll for all CPU types. The rest of the Universal CRT
312  # is installed as part of the OS in Windows 10 and beyond.
313  if not suffix.startswith('.'):
314    win_sdk_dir = os.path.normpath(
315        os.environ.get(
316            'WINDOWSSDKDIR',
317            os.path.expandvars('%ProgramFiles(x86)%'
318                               '\\Windows Kits\\10')))
319    # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/
320    # ucrt/.
321    sdk_bin_root = os.path.join(win_sdk_dir, 'bin')
322    sdk_bin_sub_dirs = glob.glob(os.path.join(sdk_bin_root, '10.*'))
323    # Select the most recent SDK if there are multiple versions installed.
324    _SortByHighestVersionNumberFirst(sdk_bin_sub_dirs)
325    for directory in sdk_bin_sub_dirs:
326      sdk_redist_root_version = os.path.join(sdk_bin_root, directory)
327      if not os.path.isdir(sdk_redist_root_version):
328        continue
329      source_dir = os.path.join(sdk_redist_root_version, target_cpu, 'ucrt')
330      if not os.path.isdir(source_dir):
331        continue
332      break
333    _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
334                     os.path.join(source_dir, 'ucrtbase' + suffix))
335
336
337def FindVCComponentRoot(component):
338  """Find the most recent Tools or Redist or other directory in an MSVC install.
339  Typical results are {toolchain_root}/VC/{component}/MSVC/{x.y.z}. The {x.y.z}
340  version number part changes frequently so the highest version number found is
341  used.
342  """
343
344  SetEnvironmentAndGetRuntimeDllDirs()
345  assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
346  vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
347      'VC', component, 'MSVC')
348  vc_component_msvc_contents = glob.glob(
349      os.path.join(vc_component_msvc_root, '14.*'))
350  # Select the most recent toolchain if there are several.
351  _SortByHighestVersionNumberFirst(vc_component_msvc_contents)
352  for directory in vc_component_msvc_contents:
353    if os.path.isdir(directory):
354      return directory
355  raise Exception('Unable to find the VC %s directory.' % component)
356
357
358def FindVCRedistRoot():
359  """In >=VS2017, Redist binaries are located in
360  {toolchain_root}/VC/Redist/MSVC/{x.y.z}/{target_cpu}/.
361
362  This returns the '{toolchain_root}/VC/Redist/MSVC/{x.y.z}/' path.
363  """
364  return FindVCComponentRoot('Redist')
365
366
367def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
368  """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
369  directory does exist. Handles VS 2015, 2017 and 2019."""
370  suffix = 'd.dll' if debug else '.dll'
371  # VS 2015, 2017 and 2019 use the same CRT DLLs.
372  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix)
373
374
375def CopyDlls(target_dir, configuration, target_cpu):
376  """Copy the VS runtime DLLs into the requested directory as needed.
377
378  configuration is one of 'Debug' or 'Release'.
379  target_cpu is one of 'x86', 'x64' or 'arm64'.
380
381  The debug configuration gets both the debug and release DLLs; the
382  release config only the latter.
383  """
384  vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
385  if not vs_runtime_dll_dirs:
386    return
387
388  x64_runtime, x86_runtime, arm64_runtime = vs_runtime_dll_dirs
389  if target_cpu == 'x64':
390    runtime_dir = x64_runtime
391  elif target_cpu == 'x86':
392    runtime_dir = x86_runtime
393  elif target_cpu == 'arm64':
394    runtime_dir = arm64_runtime
395  else:
396    raise Exception('Unknown target_cpu: ' + target_cpu)
397  _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
398  if configuration == 'Debug':
399    _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
400  _CopyDebugger(target_dir, target_cpu)
401  if target_cpu == 'arm64':
402    target_dir = os.path.join(target_dir, 'win_clang_x64')
403    target_cpu = 'x64'
404    runtime_dir = x64_runtime
405    os.makedirs(target_dir, exist_ok=True)
406    _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
407    if configuration == 'Debug':
408      _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
409    _CopyDebugger(target_dir, target_cpu)
410
411
412def _CopyDebugger(target_dir, target_cpu):
413  """Copy dbghelp.dll, dbgcore.dll, and msdia140.dll into the requested
414  directory.
415
416  target_cpu is one of 'x86', 'x64' or 'arm64'.
417
418  dbghelp.dll is used when Chrome needs to symbolize stacks. Copying this file
419  from the SDK directory avoids using the system copy of dbghelp.dll which then
420  ensures compatibility with recent debug information formats, such as
421  large-page PDBs. Note that for these DLLs to be deployed to swarming bots they
422  also need to be listed in group("runtime_libs").
423
424  dbgcore.dll is needed when using some functions from dbghelp.dll (like
425  MinidumpWriteDump).
426
427  msdia140.dll is needed for tools like symupload.exe and dump_syms.exe.
428  """
429  win_sdk_dir = SetEnvironmentAndGetSDKDir()
430  if not win_sdk_dir:
431    return
432
433  # List of debug files that should be copied, the first element of the tuple is
434  # the name of the file and the second indicates if it's optional.
435  debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
436  for debug_file, is_optional in debug_files:
437    full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
438    if not os.path.exists(full_path):
439      if is_optional:
440        continue
441      else:
442        raise Exception('%s not found in "%s"\r\nYou must install '
443                        'Windows 10 SDK version %s including the '
444                        '"Debugging Tools for Windows" feature.' %
445                        (debug_file, full_path, SDK_VERSION))
446    target_path = os.path.join(target_dir, debug_file)
447    _CopyRuntimeImpl(target_path, full_path)
448
449  # The x64 version of msdia140.dll is always used because symupload and
450  # dump_syms are always built as x64 binaries.
451  dia_path = os.path.join(NormalizePath(os.environ['GYP_MSVS_OVERRIDE_PATH']),
452                          'DIA SDK', 'bin', 'amd64', 'msdia140.dll')
453  _CopyRuntimeImpl(os.path.join(target_dir, 'msdia140.dll'), dia_path)
454
455
456def _GetDesiredVsToolchainHashes():
457  """Load a list of SHA1s corresponding to the toolchains that we want installed
458  to build with."""
459  # Third parties that do not have access to the canonical toolchain can map
460  # canonical toolchain version to their own toolchain versions.
461  toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % TOOLCHAIN_HASH
462  return [os.environ.get(toolchain_hash_mapping_key, TOOLCHAIN_HASH)]
463
464
465def ShouldUpdateToolchain():
466  """Check if the toolchain should be upgraded."""
467  if not os.path.exists(json_data_file):
468    return True
469  with open(json_data_file, 'r') as tempf:
470    toolchain_data = json.load(tempf)
471  version = toolchain_data['version']
472  env_version = GetVisualStudioVersion()
473  # If there's a mismatch between the version set in the environment and the one
474  # in the json file then the toolchain should be updated.
475  return version != env_version
476
477
478def Update(force=False, no_download=False):
479  """Requests an update of the toolchain to the specific hashes we have at
480  this revision. The update outputs a .json of the various configuration
481  information required to pass to gyp which we use in |GetToolchainDir()|.
482  If no_download is true then the toolchain will be configured if present but
483  will not be downloaded.
484  """
485  if force != False and force != '--force':
486    print('Unknown parameter "%s"' % force, file=sys.stderr)
487    return 1
488  if force == '--force' or os.path.exists(json_data_file):
489    force = True
490
491  depot_tools_win_toolchain = \
492      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
493  if (_HostIsWindows() or force) and depot_tools_win_toolchain:
494    import find_depot_tools
495    depot_tools_path = find_depot_tools.add_depot_tools_to_path()
496
497    # On Linux, the file system is usually case-sensitive while the Windows
498    # SDK only works on case-insensitive file systems.  If it doesn't already
499    # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive
500    # part of the file system.
501    toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain', 'vs_files')
502    # For testing this block, unmount existing mounts with
503    # fusermount -u third_party/depot_tools/win_toolchain/vs_files
504    if sys.platform.startswith('linux') and not os.path.ismount(toolchain_dir):
505      ciopfs = shutil.which('ciopfs')
506      if not ciopfs:
507        # ciopfs not found in PATH; try the one downloaded from the DEPS hook.
508        ciopfs = os.path.join(script_dir, 'ciopfs')
509      if not os.path.isdir(toolchain_dir):
510        os.mkdir(toolchain_dir)
511      if not os.path.isdir(toolchain_dir + '.ciopfs'):
512        os.mkdir(toolchain_dir + '.ciopfs')
513      # Without use_ino, clang's #pragma once and Wnonportable-include-path
514      # both don't work right, see https://llvm.org/PR34931
515      # use_ino doesn't slow down builds, so it seems there's no drawback to
516      # just using it always.
517      subprocess.check_call([
518          ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir])
519
520    get_toolchain_args = [
521        sys.executable,
522        os.path.join(depot_tools_path,
523                    'win_toolchain',
524                    'get_toolchain_if_necessary.py'),
525        '--output-json', json_data_file,
526      ] + _GetDesiredVsToolchainHashes()
527    if force:
528      get_toolchain_args.append('--force')
529    if no_download:
530      get_toolchain_args.append('--no-download')
531    subprocess.check_call(get_toolchain_args)
532
533  return 0
534
535
536def NormalizePath(path):
537  while path.endswith('\\'):
538    path = path[:-1]
539  return path
540
541
542def SetEnvironmentAndGetSDKDir():
543  """Gets location information about the current sdk (must have been
544  previously updated by 'update'). This is used for the GN build."""
545  SetEnvironmentAndGetRuntimeDllDirs()
546
547  # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
548  if not 'WINDOWSSDKDIR' in os.environ:
549    default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%'
550                                          '\\Windows Kits\\10')
551    if os.path.isdir(default_sdk_path):
552      os.environ['WINDOWSSDKDIR'] = default_sdk_path
553
554  return NormalizePath(os.environ['WINDOWSSDKDIR'])
555
556
557def GetToolchainDir():
558  """Gets location information about the current toolchain (must have been
559  previously updated by 'update'). This is used for the GN build."""
560  runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
561  win_sdk_dir = SetEnvironmentAndGetSDKDir()
562
563  print('''vs_path = %s
564sdk_version = %s
565sdk_path = %s
566vs_version = %s
567wdk_dir = %s
568runtime_dirs = %s
569''' % (ToGNString(NormalizePath(
570      os.environ['GYP_MSVS_OVERRIDE_PATH'])), ToGNString(SDK_VERSION),
571       ToGNString(win_sdk_dir), ToGNString(GetVisualStudioVersion()),
572       ToGNString(NormalizePath(os.environ.get('WDK_DIR', ''))),
573       ToGNString(os.path.pathsep.join(runtime_dll_dirs or ['None']))))
574
575
576def main():
577  commands = {
578      'update': Update,
579      'get_toolchain_dir': GetToolchainDir,
580      'copy_dlls': CopyDlls,
581  }
582  if len(sys.argv) < 2 or sys.argv[1] not in commands:
583    print('Expected one of: %s' % ', '.join(commands), file=sys.stderr)
584    return 1
585  return commands[sys.argv[1]](*sys.argv[2:])
586
587
588if __name__ == '__main__':
589  sys.exit(main())
590