Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: build/vs_toolchain.py

Issue 1897523002: Use visual studio toolchain from depot_tools for PDFium compilation (Closed) Base URL: https://pdfium.googlesource.com/pdfium.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« build/find_depot_tools.py ('K') | « build/gyp_pdfium ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2016 PDFium Authors. All rights reserved.
3 # Copyright 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 import glob
8 import json
9 import os
10 import pipes
11 import shutil
12 import subprocess
13 import sys
14
15
16 script_dir = os.path.dirname(os.path.realpath(__file__))
17 src_root = os.path.abspath(os.path.join(script_dir, os.pardir))
18 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
19 # Add gyp path which is needed to import gyp.
20 sys.path.insert(1, os.path.join(src_root, 'tools', 'gyp', 'pylib'))
21 sys.path.insert(0, os.path.join(src_root, 'build', 'gyp', 'pylib'))
Lei Zhang 2016/04/18 20:58:03 On my machine, sys.path starts out as: ['', '/foo
Wei Li 2016/04/18 22:22:00 yes, paths here are absolute paths same as build/g
22 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
23
24
25 import gyp
26
27
28 # Use MSVS2015 as the default toolchain.
29 CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2015'
30
31
32 def SetEnvironmentAndGetRuntimeDllDirs():
33 """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
34 returns the location of the VS runtime DLLs so they can be copied into
35 the output directory after gyp generation.
36 """
37 vs_runtime_dll_dirs = None
38 depot_tools_win_toolchain = \
39 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
40 # When running on a non-Windows host, only do this if the SDK has explicitly
41 # been downloaded before (in which case json_data_file will exist).
42 if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
43 and depot_tools_win_toolchain):
44 if ShouldUpdateToolchain():
45 Update()
46 with open(json_data_file, 'r') as tempf:
47 toolchain_data = json.load(tempf)
48
49 toolchain = toolchain_data['path']
50 version = toolchain_data['version']
51 win_sdk = toolchain_data.get('win_sdk')
52 if not win_sdk:
53 win_sdk = toolchain_data['win8sdk']
54 wdk = toolchain_data['wdk']
55 # TODO(scottmg): The order unfortunately matters in these. They should be
56 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
57 # below). http://crbug.com/345992
58 vs_runtime_dll_dirs = toolchain_data['runtime_dirs']
59
60 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
61 os.environ['GYP_MSVS_VERSION'] = version
62 # We need to make sure windows_sdk_path is set to the automated
63 # toolchain values in GYP_DEFINES, but don't want to override any
64 # otheroptions.express
65 # values there.
66 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
67 gyp_defines_dict['windows_sdk_path'] = win_sdk
68 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
69 for k, v in gyp_defines_dict.iteritems())
70 os.environ['WINDOWSSDKDIR'] = win_sdk
71 os.environ['WDK_DIR'] = wdk
72 # Include the VS runtime in the PATH in case it's not machine-installed.
73 runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
74 os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
75 elif sys.platform == 'win32' and not depot_tools_win_toolchain:
76 if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
77 os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
78 if not 'GYP_MSVS_VERSION' in os.environ:
79 os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
80
81 return vs_runtime_dll_dirs
82
83
84 def _RegistryGetValueUsingWinReg(key, value):
85 """Use the _winreg module to obtain the value of a registry key.
86
87 Args:
88 key: The registry key.
89 value: The particular registry value to read.
90 Return:
91 contents of the registry key's value, or None on failure. Throws
92 ImportError if _winreg is unavailable.
93 """
94 import _winreg
95 try:
96 root, subkey = key.split('\\', 1)
97 assert root == 'HKLM' # Only need HKLM for now.
98 with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
99 return _winreg.QueryValueEx(hkey, value)[0]
100 except WindowsError:
101 return None
102
103
104 def _RegistryGetValue(key, value):
105 try:
106 return _RegistryGetValueUsingWinReg(key, value)
107 except ImportError:
108 raise Exception('The python library _winreg not found.')
109
110
111 def GetVisualStudioVersion():
112 """Return GYP_MSVS_VERSION of Visual Studio.
113 """
114 return os.environ.get('GYP_MSVS_VERSION', CURRENT_DEFAULT_TOOLCHAIN_VERSION)
115
116
117 def DetectVisualStudioPath():
118 """Return path to the GYP_MSVS_VERSION of Visual Studio.
119 """
120
121 # Note that this code is used from
122 # build/toolchain/win/setup_toolchain.py as well.
123 version_as_year = GetVisualStudioVersion()
124 year_to_version = {
125 '2013': '12.0',
126 '2015': '14.0',
127 }
128 if version_as_year not in year_to_version:
129 raise Exception(('Visual Studio version %s (from GYP_MSVS_VERSION)'
130 ' not supported. Supported versions are: %s') % (
131 version_as_year, ', '.join(year_to_version.keys())))
132 version = year_to_version[version_as_year]
133 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
134 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version]
135 for key in keys:
136 path = _RegistryGetValue(key, 'InstallDir')
137 if not path:
138 continue
139 path = os.path.normpath(os.path.join(path, '..', '..'))
140 return path
141
142 raise Exception(('Visual Studio Version %s (from GYP_MSVS_VERSION)'
143 ' not found.') % (version_as_year))
144
145
146 def _VersionNumber():
147 """Gets the standard version number ('120', '140', etc.) based on
148 GYP_MSVS_VERSION."""
149 vs_version = GetVisualStudioVersion()
150 if vs_version == '2013':
151 return '120'
152 elif vs_version == '2015':
153 return '140'
154 else:
155 raise ValueError('Unexpected GYP_MSVS_VERSION')
156
157
158 def _CopyRuntimeImpl(target, source, verbose=True):
159 """Copy |source| to |target| if it doesn't already exist or if it
160 needs to be updated.
161 """
162 if (os.path.isdir(os.path.dirname(target)) and
163 (not os.path.isfile(target) or
164 os.stat(target).st_mtime != os.stat(source).st_mtime)):
165 if verbose:
166 print 'Copying %s to %s...' % (source, target)
167 if os.path.exists(target):
168 os.unlink(target)
169 shutil.copy2(source, target)
170
171
172 def _CopyRuntime2013(target_dir, source_dir, dll_pattern):
173 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
174 exist, but the target directory does exist."""
175 for file_part in ('p', 'r'):
176 dll = dll_pattern % file_part
177 target = os.path.join(target_dir, dll)
178 source = os.path.join(source_dir, dll)
179 _CopyRuntimeImpl(target, source)
180
181
182 def _CopyRuntime2015(target_dir, source_dir, dll_pattern, suffix):
183 """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
184 exist, but the target directory does exist."""
185 for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
186 dll = dll_pattern % file_part
187 target = os.path.join(target_dir, dll)
188 source = os.path.join(source_dir, dll)
189 _CopyRuntimeImpl(target, source)
190 ucrt_src_dir = os.path.join(source_dir, 'api-ms-win-*.dll')
191 print 'Copying %s to %s...' % (ucrt_src_dir, target_dir)
192 for ucrt_src_file in glob.glob(ucrt_src_dir):
193 file_part = os.path.basename(ucrt_src_file)
194 ucrt_dst_file = os.path.join(target_dir, file_part)
195 _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False)
196 _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
197 os.path.join(source_dir, 'ucrtbase' + suffix))
198
199
200 def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
201 """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
202 directory does exist. Handles VS 2013 and VS 2015."""
203 suffix = "d.dll" if debug else ".dll"
204 if GetVisualStudioVersion() == '2015':
205 _CopyRuntime2015(target_dir, source_dir, '%s140' + suffix, suffix)
206 else:
207 _CopyRuntime2013(target_dir, source_dir, 'msvc%s120' + suffix)
208
209 # Copy the PGO runtime library to the release directories.
210 if not debug and os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
211 pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
212 'VC', 'bin')
213 pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
214 pgo_runtime_dll = 'pgort' + _VersionNumber() + '.dll'
215 if target_cpu == "x86":
216 source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
217 if os.path.exists(source_x86):
218 _CopyRuntimeImpl(os.path.join(target_dir, pgo_runtime_dll), source_x86)
219 elif target_cpu == "x64":
220 source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
221 if os.path.exists(source_x64):
222 _CopyRuntimeImpl(os.path.join(target_dir, pgo_runtime_dll),
223 source_x64)
224 else:
225 raise NotImplementedError("Unexpected target_cpu value:" + target_cpu)
226
227
228 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
229 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
230 directory so that even if not system-installed, built binaries are likely to
231 be able to run.
232
233 This needs to be run after gyp has been run so that the expected target
234 output directories are already created.
235
236 This is used for the GYP build and gclient runhooks.
237 """
238 x86, x64 = runtime_dirs
239 out_debug = os.path.join(output_dir, 'Debug')
240 out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
241 out_release = os.path.join(output_dir, 'Release')
242 out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
243 out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
244 out_release_x64 = os.path.join(output_dir, 'Release_x64')
245
246 if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
247 os.makedirs(out_debug_nacl64)
248 if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
249 os.makedirs(out_release_nacl64)
250 _CopyRuntime(out_debug, x86, "x86", debug=True)
251 _CopyRuntime(out_release, x86, "x86", debug=False)
252 _CopyRuntime(out_debug_x64, x64, "x64", debug=True)
253 _CopyRuntime(out_release_x64, x64, "x64", debug=False)
254 _CopyRuntime(out_debug_nacl64, x64, "x64", debug=True)
255 _CopyRuntime(out_release_nacl64, x64, "x64", debug=False)
256
257
258 def CopyDlls(target_dir, configuration, target_cpu):
259 """Copy the VS runtime DLLs into the requested directory as needed.
260
261 configuration is one of 'Debug' or 'Release'.
262 target_cpu is one of 'x86' or 'x64'.
263
264 The debug configuration gets both the debug and release DLLs; the
265 release config only the latter.
266
267 This is used for the GN build.
268 """
269 vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
270 if not vs_runtime_dll_dirs:
271 return
272
273 x64_runtime, x86_runtime = vs_runtime_dll_dirs
274 runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
275 _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
276 if configuration == 'Debug':
277 _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
278
279
280 def _GetDesiredVsToolchainHashes():
281 """Load a list of SHA1s corresponding to the toolchains that we want installed
282 to build with."""
283 if GetVisualStudioVersion() == '2015':
284 # Update 2.
285 return ['95ddda401ec5678f15eeed01d2bee08fcbc5ee97']
286 else:
287 return ['4087e065abebdca6dbd0caca2910c6718d2ec67f']
288
289
290 def ShouldUpdateToolchain():
291 """Check if the toolchain should be upgraded."""
292 if not os.path.exists(json_data_file):
293 return True
294 with open(json_data_file, 'r') as tempf:
295 toolchain_data = json.load(tempf)
296 version = toolchain_data['version']
297 env_version = GetVisualStudioVersion()
298 # If there's a mismatch between the version set in the environment and the one
299 # in the json file then the toolchain should be updated.
300 return version != env_version
301
302
303 def Update(force=False):
304 """Requests an update of the toolchain to the specific hashes we have at
305 this revision. The update outputs a .json of the various configuration
306 information required to pass to gyp which we use in |GetToolchainDir()|.
307 """
308 if force != False and force != '--force':
309 print >>sys.stderr, 'Unknown parameter "%s"' % force
310 return 1
311 if force == '--force' or os.path.exists(json_data_file):
312 force = True
313
314 depot_tools_win_toolchain = \
315 bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
316 if ((sys.platform in ('win32', 'cygwin') or force) and
317 depot_tools_win_toolchain):
318 import find_depot_tools
319 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
320 # Necessary so that get_toolchain_if_necessary.py will put the VS toolkit
321 # in the correct directory.
322 os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
323 get_toolchain_args = [
324 sys.executable,
325 os.path.join(depot_tools_path,
326 'win_toolchain',
327 'get_toolchain_if_necessary.py'),
328 '--output-json', json_data_file,
329 ] + _GetDesiredVsToolchainHashes()
330 if force:
331 get_toolchain_args.append('--force')
332 subprocess.check_call(get_toolchain_args)
333
334 return 0
335
336
337 def NormalizePath(path):
338 while path.endswith("\\"):
339 path = path[:-1]
340 return path
341
342
343 def GetToolchainDir():
344 """Gets location information about the current toolchain (must have been
345 previously updated by 'update'). This is used for the GN build."""
346 runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
347
348 # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
349 if not 'WINDOWSSDKDIR' in os.environ:
350 default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\10'
351 if os.path.isdir(default_sdk_path):
352 os.environ['WINDOWSSDKDIR'] = default_sdk_path
353
354 print '''vs_path = "%s"
355 sdk_path = "%s"
356 vs_version = "%s"
357 wdk_dir = "%s"
358 runtime_dirs = "%s"
359 ''' % (
360 NormalizePath(os.environ['GYP_MSVS_OVERRIDE_PATH']),
361 NormalizePath(os.environ['WINDOWSSDKDIR']),
362 GetVisualStudioVersion(),
363 NormalizePath(os.environ.get('WDK_DIR', '')),
364 os.path.pathsep.join(runtime_dll_dirs or ['None']))
365
366
367 def main():
368 commands = {
369 'update': Update,
370 'get_toolchain_dir': GetToolchainDir,
371 'copy_dlls': CopyDlls,
372 }
373 if len(sys.argv) < 2 or sys.argv[1] not in commands:
374 print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
375 return 1
376 return commands[sys.argv[1]](*sys.argv[2:])
377
378
379 if __name__ == '__main__':
380 sys.exit(main())
OLDNEW
« build/find_depot_tools.py ('K') | « build/gyp_pdfium ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698