1. With introduction of compressed formats, e.g. ETC1, the number of
[chromium-blink-merge.git] / build / vs_toolchain.py
blob25131659f795588f3156c48b0150890aad2f9c48
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 win_sdk = toolchain_data.get('win_sdk')
41 if not win_sdk:
42 win_sdk = toolchain_data['win8sdk']
43 wdk = toolchain_data['wdk']
44 # TODO(scottmg): The order unfortunately matters in these. They should be
45 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
46 # below). http://crbug.com/345992
47 vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
49 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
50 os.environ['GYP_MSVS_VERSION'] = version
51 # We need to make sure windows_sdk_path is set to the automated
52 # toolchain values in GYP_DEFINES, but don't want to override any
53 # otheroptions.express
54 # values there.
55 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
56 gyp_defines_dict['windows_sdk_path'] = win_sdk
57 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
58 for k, v in gyp_defines_dict.iteritems())
59 os.environ['WINDOWSSDKDIR'] = win_sdk
60 os.environ['WDK_DIR'] = wdk
61 # Include the VS runtime in the PATH in case it's not machine-installed.
62 runtime_path = ';'.join(vs2013_runtime_dll_dirs)
63 os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
64 return vs2013_runtime_dll_dirs
67 def _VersionNumber():
68 """Gets the standard version number ('120', '140', etc.) based on
69 GYP_MSVS_VERSION."""
70 if os.environ['GYP_MSVS_VERSION'] == '2013':
71 return '120'
72 elif os.environ['GYP_MSVS_VERSION'] == '2015':
73 return '140'
74 else:
75 raise ValueError('Unexpected GYP_MSVS_VERSION')
78 def _CopyRuntimeImpl(target, source):
79 """Copy |source| to |target| if it doesn't already exist or if it
80 needs to be updated.
81 """
82 if (os.path.isdir(os.path.dirname(target)) and
83 (not os.path.isfile(target) or
84 os.stat(target).st_mtime != os.stat(source).st_mtime)):
85 print 'Copying %s to %s...' % (source, target)
86 if os.path.exists(target):
87 os.unlink(target)
88 shutil.copy2(source, target)
91 def _CopyRuntime2013(target_dir, source_dir, dll_pattern):
92 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
93 exist, but the target directory does exist."""
94 for file_part in ('p', 'r'):
95 dll = dll_pattern % file_part
96 target = os.path.join(target_dir, dll)
97 source = os.path.join(source_dir, dll)
98 _CopyRuntimeImpl(target, source)
101 def _CopyRuntime2015(target_dir, source_dir, dll_pattern):
102 """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
103 exist, but the target directory does exist."""
104 for file_part in ('msvcp', 'vccorlib'):
105 dll = dll_pattern % file_part
106 target = os.path.join(target_dir, dll)
107 source = os.path.join(source_dir, dll)
108 _CopyRuntimeImpl(target, source)
111 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
112 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
113 directory so that even if not system-installed, built binaries are likely to
114 be able to run.
116 This needs to be run after gyp has been run so that the expected target
117 output directories are already created.
119 assert sys.platform.startswith(('win32', 'cygwin'))
121 x86, x64 = runtime_dirs
122 out_debug = os.path.join(output_dir, 'Debug')
123 out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
124 out_release = os.path.join(output_dir, 'Release')
125 out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
126 out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
127 out_release_x64 = os.path.join(output_dir, 'Release_x64')
129 if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
130 os.makedirs(out_debug_nacl64)
131 if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
132 os.makedirs(out_release_nacl64)
133 if os.environ.get('GYP_MSVS_VERSION') == '2015':
134 _CopyRuntime2015(out_debug, x86, '%s140d.dll')
135 _CopyRuntime2015(out_release, x86, '%s140.dll')
136 _CopyRuntime2015(out_debug_x64, x64, '%s140d.dll')
137 _CopyRuntime2015(out_release_x64, x64, '%s140.dll')
138 _CopyRuntime2015(out_debug_nacl64, x64, '%s140d.dll')
139 _CopyRuntime2015(out_release_nacl64, x64, '%s140.dll')
140 else:
141 # VS2013 is the default.
142 _CopyRuntime2013(out_debug, x86, 'msvc%s120d.dll')
143 _CopyRuntime2013(out_release, x86, 'msvc%s120.dll')
144 _CopyRuntime2013(out_debug_x64, x64, 'msvc%s120d.dll')
145 _CopyRuntime2013(out_release_x64, x64, 'msvc%s120.dll')
146 _CopyRuntime2013(out_debug_nacl64, x64, 'msvc%s120d.dll')
147 _CopyRuntime2013(out_release_nacl64, x64, 'msvc%s120.dll')
149 # Copy the PGO runtime library to the release directories.
150 if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
151 pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
152 'VC', 'bin')
153 pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
154 pgo_runtime_dll = 'pgort' + _VersionNumber() + '.dll'
155 source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
156 if os.path.exists(source_x86):
157 _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86)
158 source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
159 if os.path.exists(source_x64):
160 _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll),
161 source_x64)
164 def CopyDlls(target_dir, configuration, target_cpu):
165 """Copy the VS runtime DLLs into the requested directory as needed.
167 configuration is one of 'Debug' or 'Release'.
168 target_cpu is one of 'x86' or 'x64'.
170 The debug configuration gets both the debug and release DLLs; the
171 release config only the latter.
173 vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
174 if not vs2013_runtime_dll_dirs:
175 return
177 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
178 runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
179 _CopyRuntime2013(
180 target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + '.dll')
181 if configuration == 'Debug':
182 _CopyRuntime2013(
183 target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + 'd.dll')
186 def _GetDesiredVsToolchainHashes():
187 """Load a list of SHA1s corresponding to the toolchains that we want installed
188 to build with."""
189 if os.environ.get('GYP_MSVS_VERSION') == '2015':
190 return ['49ae4b60d898182fc3f521c2fcda82c453915011']
191 else:
192 # Default to VS2013.
193 return ['ee7d718ec60c2dc5d255bbe325909c2021a7efef']
196 def Update():
197 """Requests an update of the toolchain to the specific hashes we have at
198 this revision. The update outputs a .json of the various configuration
199 information required to pass to gyp which we use in |GetToolchainDir()|.
201 depot_tools_win_toolchain = \
202 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
203 if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
204 import find_depot_tools
205 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
206 get_toolchain_args = [
207 sys.executable,
208 os.path.join(depot_tools_path,
209 'win_toolchain',
210 'get_toolchain_if_necessary.py'),
211 '--output-json', json_data_file,
212 ] + _GetDesiredVsToolchainHashes()
213 subprocess.check_call(get_toolchain_args)
215 return 0
218 def GetToolchainDir():
219 """Gets location information about the current toolchain (must have been
220 previously updated by 'update'). This is used for the GN build."""
221 runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
223 # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
224 if not 'WINDOWSSDKDIR' in os.environ:
225 default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1'
226 if os.path.isdir(default_sdk_path):
227 os.environ['WINDOWSSDKDIR'] = default_sdk_path
229 print '''vs_path = "%s"
230 sdk_path = "%s"
231 vs_version = "%s"
232 wdk_dir = "%s"
233 runtime_dirs = "%s"
234 ''' % (
235 os.environ['GYP_MSVS_OVERRIDE_PATH'],
236 os.environ['WINDOWSSDKDIR'],
237 os.environ['GYP_MSVS_VERSION'],
238 os.environ.get('WDK_DIR', ''),
239 ';'.join(runtime_dll_dirs or ['None']))
242 def main():
243 if not sys.platform.startswith(('win32', 'cygwin')):
244 return 0
245 commands = {
246 'update': Update,
247 'get_toolchain_dir': GetToolchainDir,
248 'copy_dlls': CopyDlls,
250 if len(sys.argv) < 2 or sys.argv[1] not in commands:
251 print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
252 return 1
253 return commands[sys.argv[1]](*sys.argv[2:])
256 if __name__ == '__main__':
257 sys.exit(main())