[M120 Migration] Implement ewk_view_is_video_playing api
[platform/framework/web/chromium-efl.git] / build / vs_toolchain.py
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
7 import collections
8 import glob
9 import json
10 import os
11 import platform
12 import re
13 import shutil
14 import stat
15 import subprocess
16 import sys
17
18 from gn_helpers import ToGNString
19
20 # VS 2022 17.4 with 10.0.22621.0 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
42 TOOLCHAIN_HASH = '27370823e7'
43 SDK_VERSION = '10.0.22621.0'
44
45 script_dir = os.path.dirname(os.path.realpath(__file__))
46 json_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.
51 MSVS_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.
59 MSVC_TOOLSET_VERSION = {
60     '2022': 'VC143',
61     '2019': 'VC142',
62     '2017': 'VC141',
63 }
64
65 def _HostIsWindows():
66   """Returns True if running on a Windows host (including under cygwin)."""
67   return sys.platform in ('win32', 'cygwin')
68
69 def 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
138 def _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
158 def _RegistryGetValue(key, value):
159   try:
160     return _RegistryGetValueUsingWinReg(key, value)
161   except ImportError:
162     raise Exception('The python library _winreg not found.')
163
164
165 def 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
207 def 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
245 def _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
266 def _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
285 def _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
337 def 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
358 def 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
367 def _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
375 def 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
412 def _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
456 def _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
465 def 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
478 def 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
536 def NormalizePath(path):
537   while path.endswith('\\'):
538     path = path[:-1]
539   return path
540
541
542 def 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
557 def 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
564 sdk_version = %s
565 sdk_path = %s
566 vs_version = %s
567 wdk_dir = %s
568 runtime_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
576 def 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
588 if __name__ == '__main__':
589   sys.exit(main())