Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / build / vs_toolchain.py
blob5b175eb13169af7dc7f9a51619052ce92fabf88c
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 win8sdk = toolchain_data['win8sdk']
41 wdk = toolchain_data['wdk']
42 # TODO(scottmg): The order unfortunately matters in these. They should be
43 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
44 # below). http://crbug.com/345992
45 vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
47 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
48 os.environ['GYP_MSVS_VERSION'] = version
49 # We need to make sure windows_sdk_path is set to the automated
50 # toolchain values in GYP_DEFINES, but don't want to override any
51 # otheroptions.express
52 # values there.
53 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
54 gyp_defines_dict['windows_sdk_path'] = win8sdk
55 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
56 for k, v in gyp_defines_dict.iteritems())
57 os.environ['WINDOWSSDKDIR'] = win8sdk
58 os.environ['WDK_DIR'] = wdk
59 # Include the VS runtime in the PATH in case it's not machine-installed.
60 runtime_path = ';'.join(vs2013_runtime_dll_dirs)
61 os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
62 return vs2013_runtime_dll_dirs
65 def _CopyRuntimeImpl(target, source):
66 """Copy |source| to |target| if it doesn't already exist or if it
67 needs to be updated.
68 """
69 if (os.path.isdir(os.path.dirname(target)) and
70 (not os.path.isfile(target) or
71 os.stat(target).st_mtime != os.stat(source).st_mtime)):
72 print 'Copying %s to %s...' % (source, target)
73 if os.path.exists(target):
74 os.unlink(target)
75 shutil.copy2(source, target)
78 def _CopyRuntime(target_dir, source_dir, dll_pattern):
79 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
80 exist, but the target directory does exist."""
81 for which in ('p', 'r'):
82 dll = dll_pattern % which
83 target = os.path.join(target_dir, dll)
84 source = os.path.join(source_dir, dll)
85 _CopyRuntimeImpl(target, source)
88 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
89 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
90 directory so that even if not system-installed, built binaries are likely to
91 be able to run.
93 This needs to be run after gyp has been run so that the expected target
94 output directories are already created.
95 """
96 assert sys.platform.startswith(('win32', 'cygwin'))
98 x86, x64 = runtime_dirs
99 out_debug = os.path.join(output_dir, 'Debug')
100 out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
101 out_release = os.path.join(output_dir, 'Release')
102 out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
103 out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
104 out_release_x64 = os.path.join(output_dir, 'Release_x64')
106 if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
107 os.makedirs(out_debug_nacl64)
108 if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
109 os.makedirs(out_release_nacl64)
110 _CopyRuntime(out_debug, x86, 'msvc%s120d.dll')
111 _CopyRuntime(out_release, x86, 'msvc%s120.dll')
112 _CopyRuntime(out_debug_x64, x64, 'msvc%s120d.dll')
113 _CopyRuntime(out_release_x64, x64, 'msvc%s120.dll')
114 _CopyRuntime(out_debug_nacl64, x64, 'msvc%s120d.dll')
115 _CopyRuntime(out_release_nacl64, x64, 'msvc%s120.dll')
117 # Copy the PGO runtime library to the release directories.
118 if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
119 pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
120 'VC', 'bin')
121 pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
122 pgo_runtime_dll = 'pgort120.dll'
123 source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
124 if os.path.exists(source_x86):
125 _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86)
126 source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
127 if os.path.exists(source_x64):
128 _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll),
129 source_x64)
132 def CopyDlls(target_dir, configuration, target_cpu):
133 """Copy the VS runtime DLLs into the requested directory as needed.
135 configuration is one of 'Debug' or 'Release'.
136 target_cpu is one of 'x86' or 'x64'.
138 The debug configuration gets both the debug and release DLLs; the
139 release config only the latter.
141 vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
142 if not vs2013_runtime_dll_dirs:
143 return
145 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
146 runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
147 _CopyRuntime(target_dir, runtime_dir, 'msvc%s120.dll')
148 if configuration == 'Debug':
149 _CopyRuntime(target_dir, runtime_dir, 'msvc%s120d.dll')
152 def _GetDesiredVsToolchainHashes():
153 """Load a list of SHA1s corresponding to the toolchains that we want installed
154 to build with."""
155 sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash')
156 with open(sha1path, 'rb') as f:
157 return f.read().strip().splitlines()
160 def Update():
161 """Requests an update of the toolchain to the specific hashes we have at
162 this revision. The update outputs a .json of the various configuration
163 information required to pass to gyp which we use in |GetToolchainDir()|.
165 depot_tools_win_toolchain = \
166 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
167 if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
168 import find_depot_tools
169 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
170 get_toolchain_args = [
171 sys.executable,
172 os.path.join(depot_tools_path,
173 'win_toolchain',
174 'get_toolchain_if_necessary.py'),
175 '--output-json', json_data_file,
176 ] + _GetDesiredVsToolchainHashes()
177 subprocess.check_call(get_toolchain_args)
179 return 0
182 def GetToolchainDir():
183 """Gets location information about the current toolchain (must have been
184 previously updated by 'update'). This is used for the GN build."""
185 runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
187 # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
188 if not 'WINDOWSSDKDIR' in os.environ:
189 default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1'
190 if os.path.isdir(default_sdk_path):
191 os.environ['WINDOWSSDKDIR'] = default_sdk_path
193 print '''vs_path = "%s"
194 sdk_path = "%s"
195 vs_version = "%s"
196 wdk_dir = "%s"
197 runtime_dirs = "%s"
198 ''' % (
199 os.environ['GYP_MSVS_OVERRIDE_PATH'],
200 os.environ['WINDOWSSDKDIR'],
201 os.environ['GYP_MSVS_VERSION'],
202 os.environ.get('WDK_DIR', ''),
203 ';'.join(runtime_dll_dirs or ['None']))
206 def main():
207 if not sys.platform.startswith(('win32', 'cygwin')):
208 return 0
209 commands = {
210 'update': Update,
211 'get_toolchain_dir': GetToolchainDir,
212 'copy_dlls': CopyDlls,
214 if len(sys.argv) < 2 or sys.argv[1] not in commands:
215 print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
216 return 1
217 return commands[sys.argv[1]](*sys.argv[2:])
220 if __name__ == '__main__':
221 sys.exit(main())