OLD | NEW |
| (Empty) |
1 # Copyright (c) 2013 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. | |
4 | |
5 # This file copies the logic from GYP to find the MSVC configuration. It's not | |
6 # currently used because it is too slow. We will probably build this | |
7 # functionality into the C++ code in the future. | |
8 | |
9 """Handle version information related to Visual Stuio.""" | |
10 | |
11 import errno | |
12 import os | |
13 import re | |
14 import subprocess | |
15 import sys | |
16 | |
17 class VisualStudioVersion(object): | |
18 """Information regarding a version of Visual Studio.""" | |
19 | |
20 def __init__(self, short_name, description, | |
21 solution_version, project_version, flat_sln, uses_vcxproj, | |
22 path, sdk_based, default_toolset=None): | |
23 self.short_name = short_name | |
24 self.description = description | |
25 self.solution_version = solution_version | |
26 self.project_version = project_version | |
27 self.flat_sln = flat_sln | |
28 self.uses_vcxproj = uses_vcxproj | |
29 self.path = path | |
30 self.sdk_based = sdk_based | |
31 self.default_toolset = default_toolset | |
32 | |
33 def ShortName(self): | |
34 return self.short_name | |
35 | |
36 def Description(self): | |
37 """Get the full description of the version.""" | |
38 return self.description | |
39 | |
40 def SolutionVersion(self): | |
41 """Get the version number of the sln files.""" | |
42 return self.solution_version | |
43 | |
44 def ProjectVersion(self): | |
45 """Get the version number of the vcproj or vcxproj files.""" | |
46 return self.project_version | |
47 | |
48 def FlatSolution(self): | |
49 return self.flat_sln | |
50 | |
51 def UsesVcxproj(self): | |
52 """Returns true if this version uses a vcxproj file.""" | |
53 return self.uses_vcxproj | |
54 | |
55 def ProjectExtension(self): | |
56 """Returns the file extension for the project.""" | |
57 return self.uses_vcxproj and '.vcxproj' or '.vcproj' | |
58 | |
59 def Path(self): | |
60 """Returns the path to Visual Studio installation.""" | |
61 return self.path | |
62 | |
63 def ToolPath(self, tool): | |
64 """Returns the path to a given compiler tool. """ | |
65 return os.path.normpath(os.path.join(self.path, "VC/bin", tool)) | |
66 | |
67 def DefaultToolset(self): | |
68 """Returns the msbuild toolset version that will be used in the absence | |
69 of a user override.""" | |
70 return self.default_toolset | |
71 | |
72 def SetupScript(self, target_arch): | |
73 """Returns a command (with arguments) to be used to set up the | |
74 environment.""" | |
75 # Check if we are running in the SDK command line environment and use | |
76 # the setup script from the SDK if so. |target_arch| should be either | |
77 # 'x86' or 'x64'. | |
78 assert target_arch in ('x86', 'x64') | |
79 sdk_dir = os.environ.get('WindowsSDKDir') | |
80 if self.sdk_based and sdk_dir: | |
81 return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')), | |
82 '/' + target_arch] | |
83 else: | |
84 # We don't use VC/vcvarsall.bat for x86 because vcvarsall calls | |
85 # vcvars32, which it can only find if VS??COMNTOOLS is set, which it | |
86 # isn't always. | |
87 if target_arch == 'x86': | |
88 return [os.path.normpath( | |
89 os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))] | |
90 else: | |
91 assert target_arch == 'x64' | |
92 arg = 'x86_amd64' | |
93 if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or | |
94 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'): | |
95 # Use the 64-on-64 compiler if we can. | |
96 arg = 'amd64' | |
97 return [os.path.normpath( | |
98 os.path.join(self.path, 'VC/vcvarsall.bat')), arg] | |
99 | |
100 | |
101 def _RegistryQueryBase(sysdir, key, value): | |
102 """Use reg.exe to read a particular key. | |
103 | |
104 While ideally we might use the win32 module, we would like gyp to be | |
105 python neutral, so for instance cygwin python lacks this module. | |
106 | |
107 Arguments: | |
108 sysdir: The system subdirectory to attempt to launch reg.exe from. | |
109 key: The registry key to read from. | |
110 value: The particular value to read. | |
111 Return: | |
112 stdout from reg.exe, or None for failure. | |
113 """ | |
114 # Skip if not on Windows or Python Win32 setup issue | |
115 if sys.platform not in ('win32', 'cygwin'): | |
116 return None | |
117 # Setup params to pass to and attempt to launch reg.exe | |
118 cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'), | |
119 'query', key] | |
120 if value: | |
121 cmd.extend(['/v', value]) | |
122 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
123 # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid | |
124 # Note that the error text may be in [1] in some cases | |
125 text = p.communicate()[0] | |
126 # Check return code from reg.exe; officially 0==success and 1==error | |
127 if p.returncode: | |
128 return None | |
129 return text | |
130 | |
131 | |
132 def _RegistryQuery(key, value=None): | |
133 """Use reg.exe to read a particular key through _RegistryQueryBase. | |
134 | |
135 First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If | |
136 that fails, it falls back to System32. Sysnative is available on Vista and | |
137 up and available on Windows Server 2003 and XP through KB patch 942589. Note | |
138 that Sysnative will always fail if using 64-bit python due to it being a | |
139 virtual directory and System32 will work correctly in the first place. | |
140 | |
141 KB 942589 - http://support.microsoft.com/kb/942589/en-us. | |
142 | |
143 Arguments: | |
144 key: The registry key. | |
145 value: The particular registry value to read (optional). | |
146 Return: | |
147 stdout from reg.exe, or None for failure. | |
148 """ | |
149 text = None | |
150 try: | |
151 text = _RegistryQueryBase('Sysnative', key, value) | |
152 except OSError, e: | |
153 if e.errno == errno.ENOENT: | |
154 text = _RegistryQueryBase('System32', key, value) | |
155 else: | |
156 raise | |
157 return text | |
158 | |
159 | |
160 def _RegistryGetValue(key, value): | |
161 """Use reg.exe to obtain the value of a registry key. | |
162 | |
163 Args: | |
164 key: The registry key. | |
165 value: The particular registry value to read. | |
166 Return: | |
167 contents of the registry key's value, or None on failure. | |
168 """ | |
169 text = _RegistryQuery(key, value) | |
170 if not text: | |
171 return None | |
172 # Extract value. | |
173 match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text) | |
174 if not match: | |
175 return None | |
176 return match.group(1) | |
177 | |
178 | |
179 def _RegistryKeyExists(key): | |
180 """Use reg.exe to see if a key exists. | |
181 | |
182 Args: | |
183 key: The registry key to check. | |
184 Return: | |
185 True if the key exists | |
186 """ | |
187 if not _RegistryQuery(key): | |
188 return False | |
189 return True | |
190 | |
191 | |
192 def _CreateVersion(name, path, sdk_based=False): | |
193 """Sets up MSVS project generation. | |
194 | |
195 Setup is based off the GYP_MSVS_VERSION environment variable or whatever is | |
196 autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is | |
197 passed in that doesn't match a value in versions python will throw a error. | |
198 """ | |
199 if path: | |
200 path = os.path.normpath(path) | |
201 versions = { | |
202 '2013': VisualStudioVersion('2013', | |
203 'Visual Studio 2013', | |
204 solution_version='13.00', | |
205 project_version='4.0', | |
206 flat_sln=False, | |
207 uses_vcxproj=True, | |
208 path=path, | |
209 sdk_based=sdk_based, | |
210 default_toolset='v110'), | |
211 '2013e': VisualStudioVersion('2013e', | |
212 'Visual Studio 2013', | |
213 solution_version='13.00', | |
214 project_version='4.0', | |
215 flat_sln=True, | |
216 uses_vcxproj=True, | |
217 path=path, | |
218 sdk_based=sdk_based, | |
219 default_toolset='v110'), | |
220 '2012': VisualStudioVersion('2012', | |
221 'Visual Studio 2012', | |
222 solution_version='12.00', | |
223 project_version='4.0', | |
224 flat_sln=False, | |
225 uses_vcxproj=True, | |
226 path=path, | |
227 sdk_based=sdk_based, | |
228 default_toolset='v110'), | |
229 '2012e': VisualStudioVersion('2012e', | |
230 'Visual Studio 2012', | |
231 solution_version='12.00', | |
232 project_version='4.0', | |
233 flat_sln=True, | |
234 uses_vcxproj=True, | |
235 path=path, | |
236 sdk_based=sdk_based, | |
237 default_toolset='v110'), | |
238 '2010': VisualStudioVersion('2010', | |
239 'Visual Studio 2010', | |
240 solution_version='11.00', | |
241 project_version='4.0', | |
242 flat_sln=False, | |
243 uses_vcxproj=True, | |
244 path=path, | |
245 sdk_based=sdk_based), | |
246 '2010e': VisualStudioVersion('2010e', | |
247 'Visual Studio 2010', | |
248 solution_version='11.00', | |
249 project_version='4.0', | |
250 flat_sln=True, | |
251 uses_vcxproj=True, | |
252 path=path, | |
253 sdk_based=sdk_based), | |
254 '2008': VisualStudioVersion('2008', | |
255 'Visual Studio 2008', | |
256 solution_version='10.00', | |
257 project_version='9.00', | |
258 flat_sln=False, | |
259 uses_vcxproj=False, | |
260 path=path, | |
261 sdk_based=sdk_based), | |
262 '2008e': VisualStudioVersion('2008e', | |
263 'Visual Studio 2008', | |
264 solution_version='10.00', | |
265 project_version='9.00', | |
266 flat_sln=True, | |
267 uses_vcxproj=False, | |
268 path=path, | |
269 sdk_based=sdk_based), | |
270 '2005': VisualStudioVersion('2005', | |
271 'Visual Studio 2005', | |
272 solution_version='9.00', | |
273 project_version='8.00', | |
274 flat_sln=False, | |
275 uses_vcxproj=False, | |
276 path=path, | |
277 sdk_based=sdk_based), | |
278 '2005e': VisualStudioVersion('2005e', | |
279 'Visual Studio 2005', | |
280 solution_version='9.00', | |
281 project_version='8.00', | |
282 flat_sln=True, | |
283 uses_vcxproj=False, | |
284 path=path, | |
285 sdk_based=sdk_based), | |
286 } | |
287 return versions[str(name)] | |
288 | |
289 | |
290 def _ConvertToCygpath(path): | |
291 """Convert to cygwin path if we are using cygwin.""" | |
292 if sys.platform == 'cygwin': | |
293 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE) | |
294 path = p.communicate()[0].strip() | |
295 return path | |
296 | |
297 | |
298 def _DetectVisualStudioVersions(versions_to_check, force_express): | |
299 """Collect the list of installed visual studio versions. | |
300 | |
301 Returns: | |
302 A list of visual studio versions installed in descending order of | |
303 usage preference. | |
304 Base this on the registry and a quick check if devenv.exe exists. | |
305 Only versions 8-10 are considered. | |
306 Possibilities are: | |
307 2005(e) - Visual Studio 2005 (8) | |
308 2008(e) - Visual Studio 2008 (9) | |
309 2010(e) - Visual Studio 2010 (10) | |
310 2012(e) - Visual Studio 2012 (11) | |
311 2013(e) - Visual Studio 2013 (11) | |
312 Where (e) is e for express editions of MSVS and blank otherwise. | |
313 """ | |
314 version_to_year = { | |
315 '8.0': '2005', | |
316 '9.0': '2008', | |
317 '10.0': '2010', | |
318 '11.0': '2012', | |
319 '12.0': '2013', | |
320 } | |
321 versions = [] | |
322 for version in versions_to_check: | |
323 # Old method of searching for which VS version is installed | |
324 # We don't use the 2010-encouraged-way because we also want to get the | |
325 # path to the binaries, which it doesn't offer. | |
326 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version, | |
327 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version, | |
328 r'HKLM\Software\Microsoft\VCExpress\%s' % version, | |
329 r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version] | |
330 for index in range(len(keys)): | |
331 path = _RegistryGetValue(keys[index], 'InstallDir') | |
332 if not path: | |
333 continue | |
334 path = _ConvertToCygpath(path) | |
335 # Check for full. | |
336 full_path = os.path.join(path, 'devenv.exe') | |
337 express_path = os.path.join(path, 'vcexpress.exe') | |
338 if not force_express and os.path.exists(full_path): | |
339 # Add this one. | |
340 versions.append(_CreateVersion(version_to_year[version], | |
341 os.path.join(path, '..', '..'))) | |
342 # Check for express. | |
343 elif os.path.exists(express_path): | |
344 # Add this one. | |
345 versions.append(_CreateVersion(version_to_year[version] + 'e', | |
346 os.path.join(path, '..', '..'))) | |
347 | |
348 # The old method above does not work when only SDK is installed. | |
349 keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7', | |
350 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7'] | |
351 for index in range(len(keys)): | |
352 path = _RegistryGetValue(keys[index], version) | |
353 if not path: | |
354 continue | |
355 path = _ConvertToCygpath(path) | |
356 versions.append(_CreateVersion(version_to_year[version] + 'e', | |
357 os.path.join(path, '..'), sdk_based=True)) | |
358 | |
359 return versions | |
360 | |
361 | |
362 def SelectVisualStudioVersion(version='auto'): | |
363 """Select which version of Visual Studio projects to generate. | |
364 | |
365 Arguments: | |
366 version: Hook to allow caller to force a particular version (vs auto). | |
367 Returns: | |
368 An object representing a visual studio project format version. | |
369 """ | |
370 # In auto mode, check environment variable for override. | |
371 if version == 'auto': | |
372 version = os.environ.get('GYP_MSVS_VERSION', 'auto') | |
373 version_map = { | |
374 'auto': ('10.0', '9.0', '8.0', '11.0'), | |
375 '2005': ('8.0',), | |
376 '2005e': ('8.0',), | |
377 '2008': ('9.0',), | |
378 '2008e': ('9.0',), | |
379 '2010': ('10.0',), | |
380 '2010e': ('10.0',), | |
381 '2012': ('11.0',), | |
382 '2012e': ('11.0',), | |
383 '2013': ('12.0',), | |
384 '2013e': ('12.0',), | |
385 } | |
386 override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH') | |
387 if override_path: | |
388 msvs_version = os.environ.get('GYP_MSVS_VERSION') | |
389 if not msvs_version or 'e' not in msvs_version: | |
390 raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be ' | |
391 'set to an "e" version (e.g. 2010e)') | |
392 return _CreateVersion(msvs_version, override_path, sdk_based=True) | |
393 version = str(version) | |
394 versions = _DetectVisualStudioVersions(version_map[version], 'e' in version) | |
395 if not versions: | |
396 if version == 'auto': | |
397 # Default to 2005 if we couldn't find anything | |
398 return _CreateVersion('2005', None) | |
399 else: | |
400 return _CreateVersion(version, None) | |
401 return versions[0] | |
402 | |
403 def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out): | |
404 """It's not sufficient to have the absolute path to the compiler, linker, | |
405 etc. on Windows, as those tools rely on .dlls being in the PATH. We also | |
406 need to support both x86 and x64 compilers within the same build (to support | |
407 msvs_target_platform hackery). Different architectures require a different | |
408 compiler binary, and different supporting environment variables (INCLUDE, | |
409 LIB, LIBPATH). So, we extract the environment here, wrap all invocations | |
410 of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which | |
411 sets up the environment, and then we do not prefix the compiler with | |
412 an absolute path, instead preferring something like "cl.exe" in the rule | |
413 which will then run whichever the environment setup has put in the path. | |
414 When the following procedure to generate environment files does not | |
415 meet your requirement (e.g. for custom toolchains), you can pass | |
416 "-G ninja_use_custom_environment_files" to the gyp to suppress file | |
417 generation and use custom environment files prepared by yourself.""" | |
418 archs = ('x86', 'x64') | |
419 if generator_flags.get('ninja_use_custom_environment_files', 0): | |
420 cl_paths = {} | |
421 for arch in archs: | |
422 cl_paths[arch] = 'cl.exe' | |
423 return cl_paths | |
424 vs = GetVSVersion(generator_flags) | |
425 cl_paths = {} | |
426 for arch in archs: | |
427 # Extract environment variables for subprocesses. | |
428 args = vs.SetupScript(arch) | |
429 args.extend(('&&', 'set')) | |
430 popen = subprocess.Popen( | |
431 args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
432 variables, _ = popen.communicate() | |
433 env = _ExtractImportantEnvironment(variables) | |
434 env_block = _FormatAsEnvironmentBlock(env) | |
435 f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb') | |
436 f.write(env_block) | |
437 f.close() | |
438 | |
439 # Find cl.exe location for this architecture. | |
440 args = vs.SetupScript(arch) | |
441 args.extend(('&&', | |
442 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i')) | |
443 popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE) | |
444 output, _ = popen.communicate() | |
445 cl_paths[arch] = _ExtractCLPath(output) | |
446 return cl_paths | |
447 | |
448 def OpenOutput(path, mode='w'): | |
449 """Open |path| for writing, creating directories if necessary.""" | |
450 try: | |
451 os.makedirs(os.path.dirname(path)) | |
452 except OSError: | |
453 pass | |
454 return open(path, mode) | |
455 | |
456 vs_version = None | |
457 def GetVSVersion(generator_flags): | |
458 global vs_version | |
459 if not vs_version: | |
460 vs_version = SelectVisualStudioVersion( | |
461 generator_flags.get('msvs_version', 'auto')) | |
462 return vs_version | |
463 | |
464 def _ExtractImportantEnvironment(output_of_set): | |
465 """Extracts environment variables required for the toolchain to run from | |
466 a textual dump output by the cmd.exe 'set' command.""" | |
467 envvars_to_save = ( | |
468 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. | |
469 'include', | |
470 'lib', | |
471 'libpath', | |
472 'path', | |
473 'pathext', | |
474 'systemroot', | |
475 'temp', | |
476 'tmp', | |
477 ) | |
478 env = {} | |
479 for line in output_of_set.splitlines(): | |
480 for envvar in envvars_to_save: | |
481 if re.match(envvar + '=', line.lower()): | |
482 var, setting = line.split('=', 1) | |
483 if envvar == 'path': | |
484 # Our own rules (for running gyp-win-tool) and other actions in | |
485 # Chromium rely on python being in the path. Add the path to this | |
486 # python here so that if it's not in the path when ninja is run | |
487 # later, python will still be found. | |
488 setting = os.path.dirname(sys.executable) + os.pathsep + setting | |
489 env[var.upper()] = setting | |
490 break | |
491 for required in ('SYSTEMROOT', 'TEMP', 'TMP'): | |
492 if required not in env: | |
493 raise Exception('Environment variable "%s" ' | |
494 'required to be set to valid path' % required) | |
495 return env | |
496 | |
497 def _FormatAsEnvironmentBlock(envvar_dict): | |
498 """Format as an 'environment block' directly suitable for CreateProcess. | |
499 Briefly this is a list of key=value\0, terminated by an additional \0. See | |
500 CreateProcess documentation for more details.""" | |
501 block = '' | |
502 nul = '\0' | |
503 for key, value in envvar_dict.iteritems(): | |
504 block += key + '=' + value + nul | |
505 block += nul | |
506 return block | |
507 | |
508 | |
509 def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags): | |
510 """It's not sufficient to have the absolute path to the compiler, linker, | |
511 etc. on Windows, as those tools rely on .dlls being in the PATH. We also | |
512 need to support both x86 and x64 compilers within the same build (to support | |
513 msvs_target_platform hackery). Different architectures require a different | |
514 compiler binary, and different supporting environment variables (INCLUDE, | |
515 LIB, LIBPATH). So, we extract the environment here, wrap all invocations | |
516 of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which | |
517 sets up the environment, and then we do not prefix the compiler with | |
518 an absolute path, instead preferring something like "cl.exe" in the rule | |
519 which will then run whichever the environment setup has put in the path. | |
520 When the following procedure to generate environment files does not | |
521 meet your requirement (e.g. for custom toolchains), you can pass | |
522 "-G ninja_use_custom_environment_files" to the gyp to suppress file | |
523 generation and use custom environment files prepared by yourself.""" | |
524 archs = ('x86', 'x64') | |
525 if generator_flags.get('ninja_use_custom_environment_files', 0): | |
526 cl_paths = {} | |
527 for arch in archs: | |
528 cl_paths[arch] = 'cl.exe' | |
529 return cl_paths | |
530 vs = GetVSVersion(generator_flags) | |
531 cl_paths = {} | |
532 for arch in archs: | |
533 # Extract environment variables for subprocesses. | |
534 args = vs.SetupScript(arch) | |
535 args.extend(('&&', 'set')) | |
536 popen = subprocess.Popen( | |
537 args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
538 variables, _ = popen.communicate() | |
539 env = _ExtractImportantEnvironment(variables) | |
540 env_block = _FormatAsEnvironmentBlock(env) | |
541 f = OpenOutput(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb'
) | |
542 f.write(env_block) | |
543 f.close() | |
544 | |
545 # Find cl.exe location for this architecture. | |
546 args = vs.SetupScript(arch) | |
547 args.extend(('&&', | |
548 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i')) | |
549 popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE) | |
550 output, _ = popen.communicate() | |
551 cl_paths[arch] = _ExtractCLPath(output) | |
552 return cl_paths | |
553 | |
554 def _ExtractCLPath(output_of_where): | |
555 """Gets the path to cl.exe based on the output of calling the environment | |
556 setup batch file, followed by the equivalent of `where`.""" | |
557 # Take the first line, as that's the first found in the PATH. | |
558 for line in output_of_where.strip().splitlines(): | |
559 if line.startswith('LOC:'): | |
560 return line[len('LOC:'):].strip() | |
561 | |
562 #print SelectVisualStudioVersion().DefaultToolset() | |
563 #GenerateEnvironmentFiles("D:\\src\\src1\\src\\out\\gn\\eraseme", {}) | |
564 #print '"', GetVSVersion({}).Path(), '"' | |
565 print '"', GetVSVersion({}).sdk_based, '"' | |
566 | |
567 #------------------------------------------------------------------------------- | |
568 | |
569 version_info = { | |
570 '2010': { | |
571 'includes': [ | |
572 'VC\\atlmfc\\include', | |
573 ], | |
574 }, | |
575 } | |
OLD | NEW |