Roll src/third_party/WebKit 44e0d7b:d79e3c1 (svn 190013:190016)
[chromium-blink-merge.git] / build / vs_toolchain.py
blob5373f03e578a718ef49cf3d84d3975b687327114
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 import json
6 import os
7 import pipes
8 import shutil
9 import subprocess
10 import sys
13 script_dir = os.path.dirname(os.path.realpath(__file__))
14 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
15 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
17 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
18 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
21 import gyp
24 def SetEnvironmentAndGetRuntimeDllDirs():
25 """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
26 returns the location of the VS runtime DLLs so they can be copied into
27 the output directory after gyp generation.
28 """
29 vs2013_runtime_dll_dirs = None
30 depot_tools_win_toolchain = \
31 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
32 if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
33 if not os.path.exists(json_data_file):
34 Update()
35 with open(json_data_file, 'r') as tempf:
36 toolchain_data = json.load(tempf)
38 toolchain = toolchain_data['path']
39 version = toolchain_data['version']
40 version_is_pro = version[-1] != 'e'
41 win8sdk = toolchain_data['win8sdk']
42 wdk = toolchain_data['wdk']
43 # TODO(scottmg): The order unfortunately matters in these. They should be
44 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
45 # below). http://crbug.com/345992
46 vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
48 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
49 os.environ['GYP_MSVS_VERSION'] = version
50 # We need to make sure windows_sdk_path is set to the automated
51 # toolchain values in GYP_DEFINES, but don't want to override any
52 # otheroptions.express
53 # values there.
54 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
55 gyp_defines_dict['windows_sdk_path'] = win8sdk
56 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
57 for k, v in gyp_defines_dict.iteritems())
58 os.environ['WINDOWSSDKDIR'] = win8sdk
59 os.environ['WDK_DIR'] = wdk
60 # Include the VS runtime in the PATH in case it's not machine-installed.
61 runtime_path = ';'.join(vs2013_runtime_dll_dirs)
62 os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
63 return vs2013_runtime_dll_dirs
66 def _CopyRuntimeImpl(target, source):
67 """Copy |source| to |target| if it doesn't already exist or if it
68 needs to be updated.
69 """
70 if (os.path.isdir(os.path.dirname(target)) and
71 (not os.path.isfile(target) or
72 os.stat(target).st_mtime != os.stat(source).st_mtime)):
73 print 'Copying %s to %s...' % (source, target)
74 if os.path.exists(target):
75 os.unlink(target)
76 shutil.copy2(source, target)
79 def _CopyRuntime(target_dir, source_dir, dll_pattern):
80 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
81 exist, but the target directory does exist."""
82 for which in ('p', 'r'):
83 dll = dll_pattern % which
84 target = os.path.join(target_dir, dll)
85 source = os.path.join(source_dir, dll)
86 _CopyRuntimeImpl(target, source)
89 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
90 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
91 directory so that even if not system-installed, built binaries are likely to
92 be able to run.
94 This needs to be run after gyp has been run so that the expected target
95 output directories are already created.
96 """
97 assert sys.platform.startswith(('win32', 'cygwin'))
99 x86, x64 = runtime_dirs
100 out_debug = os.path.join(output_dir, 'Debug')
101 out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
102 out_release = os.path.join(output_dir, 'Release')
103 out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
104 out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
105 out_release_x64 = os.path.join(output_dir, 'Release_x64')
107 if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
108 os.makedirs(out_debug_nacl64)
109 if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
110 os.makedirs(out_release_nacl64)
111 _CopyRuntime(out_debug, x86, 'msvc%s120d.dll')
112 _CopyRuntime(out_release, x86, 'msvc%s120.dll')
113 _CopyRuntime(out_debug_x64, x64, 'msvc%s120d.dll')
114 _CopyRuntime(out_release_x64, x64, 'msvc%s120.dll')
115 _CopyRuntime(out_debug_nacl64, x64, 'msvc%s120d.dll')
116 _CopyRuntime(out_release_nacl64, x64, 'msvc%s120.dll')
118 # Copy the PGO runtime library to the release directories.
119 if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
120 pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
121 'VC', 'bin')
122 pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
123 pgo_runtime_dll = 'pgort120.dll'
124 source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
125 if os.path.exists(source_x86):
126 _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86)
127 source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
128 if os.path.exists(source_x64):
129 _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll),
130 source_x64)
133 def CopyDlls(target_dir, configuration, cpu_arch):
134 """Copy the VS runtime DLLs into the requested directory as needed.
136 configuration is one of 'Debug' or 'Release'.
137 cpu_arch is one of 'x86' or 'x64'.
139 The debug configuration gets both the debug and release DLLs; the
140 release config only the latter.
142 vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
143 if not vs2013_runtime_dll_dirs:
144 return
146 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
147 runtime_dir = x64_runtime if cpu_arch == 'x64' else x86_runtime
148 _CopyRuntime(target_dir, runtime_dir, 'msvc%s120.dll')
149 if configuration == 'Debug':
150 _CopyRuntime(target_dir, runtime_dir, 'msvc%s120d.dll')
153 def _GetDesiredVsToolchainHashes():
154 """Load a list of SHA1s corresponding to the toolchains that we want installed
155 to build with."""
156 sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash')
157 with open(sha1path, 'rb') as f:
158 return f.read().strip().splitlines()
161 def Update():
162 """Requests an update of the toolchain to the specific hashes we have at
163 this revision. The update outputs a .json of the various configuration
164 information required to pass to gyp which we use in |GetToolchainDir()|.
166 depot_tools_win_toolchain = \
167 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
168 if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
169 import find_depot_tools
170 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
171 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
172 get_toolchain_args = [
173 sys.executable,
174 os.path.join(depot_tools_path,
175 'win_toolchain',
176 'get_toolchain_if_necessary.py'),
177 '--output-json', json_data_file,
178 ] + _GetDesiredVsToolchainHashes()
179 subprocess.check_call(get_toolchain_args)
181 return 0
184 def GetToolchainDir():
185 """Gets location information about the current toolchain (must have been
186 previously updated by 'update'). This is used for the GN build."""
187 runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
189 # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
190 if not 'WINDOWSSDKDIR' in os.environ:
191 default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1'
192 if os.path.isdir(default_sdk_path):
193 os.environ['WINDOWSSDKDIR'] = default_sdk_path
195 print '''vs_path = "%s"
196 sdk_path = "%s"
197 vs_version = "%s"
198 wdk_dir = "%s"
199 runtime_dirs = "%s"
200 ''' % (
201 os.environ['GYP_MSVS_OVERRIDE_PATH'],
202 os.environ['WINDOWSSDKDIR'],
203 os.environ['GYP_MSVS_VERSION'],
204 os.environ.get('WDK_DIR', ''),
205 ';'.join(runtime_dll_dirs or ['None']))
208 def main():
209 if not sys.platform.startswith(('win32', 'cygwin')):
210 return 0
211 commands = {
212 'update': Update,
213 'get_toolchain_dir': GetToolchainDir,
214 'copy_dlls': CopyDlls,
216 if len(sys.argv) < 2 or sys.argv[1] not in commands:
217 print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
218 return 1
219 return commands[sys.argv[1]](*sys.argv[2:])
222 if __name__ == '__main__':
223 sys.exit(main())