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

Side by Side Diff: tools/gn/secondary/build/config/win/get_msvc_config_real.py

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « tools/gn/secondary/build/config/win/get_msvc_config.py ('k') | tools/gn/secondary/ipc/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « tools/gn/secondary/build/config/win/get_msvc_config.py ('k') | tools/gn/secondary/ipc/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698