MD Downloads: tweak hazardous downloads
[chromium-blink-merge.git] / build / vs_toolchain.py
blobcea0d6d0e32f518f73388f000b1e1cf6c6c4b233
1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 import json
7 import os
8 import pipes
9 import shutil
10 import subprocess
11 import sys
14 script_dir = os.path.dirname(os.path.realpath(__file__))
15 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
16 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
18 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
19 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
22 import gyp
25 def SetEnvironmentAndGetRuntimeDllDirs():
26 """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
27 returns the location of the VS runtime DLLs so they can be copied into
28 the output directory after gyp generation.
29 """
30 vs2013_runtime_dll_dirs = None
31 depot_tools_win_toolchain = \
32 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
33 # When running on a non-Windows host, only do this if the SDK has explicitly
34 # been downloaded before (in which case json_data_file will exist).
35 if ((sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain) or
36 os.path.exists(json_data_file)):
37 if not os.path.exists(json_data_file):
38 Update()
39 with open(json_data_file, 'r') as tempf:
40 toolchain_data = json.load(tempf)
42 toolchain = toolchain_data['path']
43 version = toolchain_data['version']
44 win_sdk = toolchain_data.get('win_sdk')
45 if not win_sdk:
46 win_sdk = toolchain_data['win8sdk']
47 wdk = toolchain_data['wdk']
48 # TODO(scottmg): The order unfortunately matters in these. They should be
49 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
50 # below). http://crbug.com/345992
51 vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
53 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
54 os.environ['GYP_MSVS_VERSION'] = version
55 # We need to make sure windows_sdk_path is set to the automated
56 # toolchain values in GYP_DEFINES, but don't want to override any
57 # otheroptions.express
58 # values there.
59 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
60 gyp_defines_dict['windows_sdk_path'] = win_sdk
61 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
62 for k, v in gyp_defines_dict.iteritems())
63 os.environ['WINDOWSSDKDIR'] = win_sdk
64 os.environ['WDK_DIR'] = wdk
65 # Include the VS runtime in the PATH in case it's not machine-installed.
66 runtime_path = ';'.join(vs2013_runtime_dll_dirs)
67 os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
68 return vs2013_runtime_dll_dirs
71 def _VersionNumber():
72 """Gets the standard version number ('120', '140', etc.) based on
73 GYP_MSVS_VERSION."""
74 if os.environ['GYP_MSVS_VERSION'] == '2013':
75 return '120'
76 elif os.environ['GYP_MSVS_VERSION'] == '2015':
77 return '140'
78 else:
79 raise ValueError('Unexpected GYP_MSVS_VERSION')
82 def _CopyRuntimeImpl(target, source):
83 """Copy |source| to |target| if it doesn't already exist or if it
84 needs to be updated.
85 """
86 if (os.path.isdir(os.path.dirname(target)) and
87 (not os.path.isfile(target) or
88 os.stat(target).st_mtime != os.stat(source).st_mtime)):
89 print 'Copying %s to %s...' % (source, target)
90 if os.path.exists(target):
91 os.unlink(target)
92 shutil.copy2(source, target)
95 def _CopyRuntime2013(target_dir, source_dir, dll_pattern):
96 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
97 exist, but the target directory does exist."""
98 for file_part in ('p', 'r'):
99 dll = dll_pattern % file_part
100 target = os.path.join(target_dir, dll)
101 source = os.path.join(source_dir, dll)
102 _CopyRuntimeImpl(target, source)
105 def _CopyRuntime2015(target_dir, source_dir, dll_pattern):
106 """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
107 exist, but the target directory does exist."""
108 for file_part in ('msvcp', 'vccorlib'):
109 dll = dll_pattern % file_part
110 target = os.path.join(target_dir, dll)
111 source = os.path.join(source_dir, dll)
112 _CopyRuntimeImpl(target, source)
115 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
116 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
117 directory so that even if not system-installed, built binaries are likely to
118 be able to run.
120 This needs to be run after gyp has been run so that the expected target
121 output directories are already created.
123 x86, x64 = runtime_dirs
124 out_debug = os.path.join(output_dir, 'Debug')
125 out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
126 out_release = os.path.join(output_dir, 'Release')
127 out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
128 out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
129 out_release_x64 = os.path.join(output_dir, 'Release_x64')
131 if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
132 os.makedirs(out_debug_nacl64)
133 if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
134 os.makedirs(out_release_nacl64)
135 if os.environ.get('GYP_MSVS_VERSION') == '2015':
136 _CopyRuntime2015(out_debug, x86, '%s140d.dll')
137 _CopyRuntime2015(out_release, x86, '%s140.dll')
138 _CopyRuntime2015(out_debug_x64, x64, '%s140d.dll')
139 _CopyRuntime2015(out_release_x64, x64, '%s140.dll')
140 _CopyRuntime2015(out_debug_nacl64, x64, '%s140d.dll')
141 _CopyRuntime2015(out_release_nacl64, x64, '%s140.dll')
142 else:
143 # VS2013 is the default.
144 _CopyRuntime2013(out_debug, x86, 'msvc%s120d.dll')
145 _CopyRuntime2013(out_release, x86, 'msvc%s120.dll')
146 _CopyRuntime2013(out_debug_x64, x64, 'msvc%s120d.dll')
147 _CopyRuntime2013(out_release_x64, x64, 'msvc%s120.dll')
148 _CopyRuntime2013(out_debug_nacl64, x64, 'msvc%s120d.dll')
149 _CopyRuntime2013(out_release_nacl64, x64, 'msvc%s120.dll')
151 # Copy the PGO runtime library to the release directories.
152 if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
153 pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
154 'VC', 'bin')
155 pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
156 pgo_runtime_dll = 'pgort' + _VersionNumber() + '.dll'
157 source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
158 if os.path.exists(source_x86):
159 _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86)
160 source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
161 if os.path.exists(source_x64):
162 _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll),
163 source_x64)
166 def CopyDlls(target_dir, configuration, target_cpu):
167 """Copy the VS runtime DLLs into the requested directory as needed.
169 configuration is one of 'Debug' or 'Release'.
170 target_cpu is one of 'x86' or 'x64'.
172 The debug configuration gets both the debug and release DLLs; the
173 release config only the latter.
175 vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
176 if not vs2013_runtime_dll_dirs:
177 return
179 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
180 runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
181 _CopyRuntime2013(
182 target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + '.dll')
183 if configuration == 'Debug':
184 _CopyRuntime2013(
185 target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + 'd.dll')
188 def _GetDesiredVsToolchainHashes():
189 """Load a list of SHA1s corresponding to the toolchains that we want installed
190 to build with."""
191 if os.environ.get('GYP_MSVS_VERSION') == '2015':
192 return ['49ae4b60d898182fc3f521c2fcda82c453915011']
193 else:
194 # Default to VS2013.
195 return ['ee7d718ec60c2dc5d255bbe325909c2021a7efef']
198 def Update(force=False):
199 """Requests an update of the toolchain to the specific hashes we have at
200 this revision. The update outputs a .json of the various configuration
201 information required to pass to gyp which we use in |GetToolchainDir()|.
203 if force != False and force != '--force':
204 print >>sys.stderr, 'Unknown parameter "%s"' % force
205 return 1
206 if force == '--force' or os.path.exists(json_data_file):
207 force = True
209 depot_tools_win_toolchain = \
210 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
211 if ((sys.platform in ('win32', 'cygwin') or force) and
212 depot_tools_win_toolchain):
213 import find_depot_tools
214 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
215 get_toolchain_args = [
216 sys.executable,
217 os.path.join(depot_tools_path,
218 'win_toolchain',
219 'get_toolchain_if_necessary.py'),
220 '--output-json', json_data_file,
221 ] + _GetDesiredVsToolchainHashes()
222 if force:
223 get_toolchain_args.append('--force')
224 subprocess.check_call(get_toolchain_args)
226 return 0
229 def GetToolchainDir():
230 """Gets location information about the current toolchain (must have been
231 previously updated by 'update'). This is used for the GN build."""
232 runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
234 # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
235 if not 'WINDOWSSDKDIR' in os.environ:
236 default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1'
237 if os.path.isdir(default_sdk_path):
238 os.environ['WINDOWSSDKDIR'] = default_sdk_path
240 print '''vs_path = "%s"
241 sdk_path = "%s"
242 vs_version = "%s"
243 wdk_dir = "%s"
244 runtime_dirs = "%s"
245 ''' % (
246 os.environ['GYP_MSVS_OVERRIDE_PATH'],
247 os.environ['WINDOWSSDKDIR'],
248 os.environ['GYP_MSVS_VERSION'],
249 os.environ.get('WDK_DIR', ''),
250 ';'.join(runtime_dll_dirs or ['None']))
253 def main():
254 commands = {
255 'update': Update,
256 'get_toolchain_dir': GetToolchainDir,
257 'copy_dlls': CopyDlls,
259 if len(sys.argv) < 2 or sys.argv[1] not in commands:
260 print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
261 return 1
262 return commands[sys.argv[1]](*sys.argv[2:])
265 if __name__ == '__main__':
266 sys.exit(main())