blob: e4a85a96e6c53643bfa994d57da5dc4c4d46df54 [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001# Copyright (c) 2012 Google Inc. 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"""
6This module helps emulate Visual Studio 2008 behavior on top of other
7build systems, primarily ninja.
8"""
9
10import os
11import re
12import subprocess
13import sys
14
15from gyp.common import OrderedSet
16import gyp.MSVSUtil
17import gyp.MSVSVersion
18
19
20windows_quoter_regex = re.compile(r'(\\*)"')
21
22
23def QuoteForRspFile(arg):
24 """Quote a command line argument so that it appears as one argument when
25 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
26 Windows programs)."""
27 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment
28 # threads. This is actually the quoting rules for CommandLineToArgvW, not
29 # for the shell, because the shell doesn't do anything in Windows. This
30 # works more or less because most programs (including the compiler, etc.)
31 # use that function to handle command line arguments.
32
33 # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
34 # preceding it, and results in n backslashes + the quote. So we substitute
35 # in 2* what we match, +1 more, plus the quote.
36 arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg)
37
38 # %'s also need to be doubled otherwise they're interpreted as batch
39 # positional arguments. Also make sure to escape the % so that they're
40 # passed literally through escaping so they can be singled to just the
41 # original %. Otherwise, trying to pass the literal representation that
42 # looks like an environment variable to the shell (e.g. %PATH%) would fail.
43 arg = arg.replace('%', '%%')
44
45 # These commands are used in rsp files, so no escaping for the shell (via ^)
46 # is necessary.
47
48 # Finally, wrap the whole thing in quotes so that the above quote rule
49 # applies and whitespace isn't a word break.
50 return '"' + arg + '"'
51
52
53def EncodeRspFileList(args):
54 """Process a list of arguments using QuoteCmdExeArgument."""
55 # Note that the first argument is assumed to be the command. Don't add
56 # quotes around it because then built-ins like 'echo', etc. won't work.
57 # Take care to normpath only the path in the case of 'call ../x.bat' because
58 # otherwise the whole thing is incorrectly interpreted as a path and not
59 # normalized correctly.
60 if not args: return ''
61 if args[0].startswith('call '):
62 call, program = args[0].split(' ', 1)
63 program = call + ' ' + os.path.normpath(program)
64 else:
65 program = os.path.normpath(args[0])
66 return program + ' ' + ' '.join(QuoteForRspFile(arg) for arg in args[1:])
67
68
69def _GenericRetrieve(root, default, path):
70 """Given a list of dictionary keys |path| and a tree of dicts |root|, find
71 value at path, or return |default| if any of the path doesn't exist."""
72 if not root:
73 return default
74 if not path:
75 return root
76 return _GenericRetrieve(root.get(path[0]), default, path[1:])
77
78
79def _AddPrefix(element, prefix):
80 """Add |prefix| to |element| or each subelement if element is iterable."""
81 if element is None:
82 return element
83 # Note, not Iterable because we don't want to handle strings like that.
84 if isinstance(element, list) or isinstance(element, tuple):
85 return [prefix + e for e in element]
86 else:
87 return prefix + element
88
89
90def _DoRemapping(element, map):
91 """If |element| then remap it through |map|. If |element| is iterable then
92 each item will be remapped. Any elements not found will be removed."""
93 if map is not None and element is not None:
94 if not callable(map):
95 map = map.get # Assume it's a dict, otherwise a callable to do the remap.
96 if isinstance(element, list) or isinstance(element, tuple):
97 element = filter(None, [map(elem) for elem in element])
98 else:
99 element = map(element)
100 return element
101
102
103def _AppendOrReturn(append, element):
104 """If |append| is None, simply return |element|. If |append| is not None,
105 then add |element| to it, adding each item in |element| if it's a list or
106 tuple."""
107 if append is not None and element is not None:
108 if isinstance(element, list) or isinstance(element, tuple):
109 append.extend(element)
110 else:
111 append.append(element)
112 else:
113 return element
114
115
116def _FindDirectXInstallation():
117 """Try to find an installation location for the DirectX SDK. Check for the
118 standard environment variable, and if that doesn't exist, try to find
119 via the registry. May return None if not found in either location."""
120 # Return previously calculated value, if there is one
121 if hasattr(_FindDirectXInstallation, 'dxsdk_dir'):
122 return _FindDirectXInstallation.dxsdk_dir
123
124 dxsdk_dir = os.environ.get('DXSDK_DIR')
125 if not dxsdk_dir:
126 # Setup params to pass to and attempt to launch reg.exe.
127 cmd = ['reg.exe', 'query', r'HKLM\Software\Microsoft\DirectX', '/s']
128 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
129 for line in p.communicate()[0].splitlines():
130 if 'InstallPath' in line:
131 dxsdk_dir = line.split(' ')[3] + "\\"
132
133 # Cache return value
134 _FindDirectXInstallation.dxsdk_dir = dxsdk_dir
135 return dxsdk_dir
136
137
138def GetGlobalVSMacroEnv(vs_version):
139 """Get a dict of variables mapping internal VS macro names to their gyp
140 equivalents. Returns all variables that are independent of the target."""
141 env = {}
142 # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
143 # Visual Studio is actually installed.
144 if vs_version.Path():
145 env['$(VSInstallDir)'] = vs_version.Path()
146 env['$(VCInstallDir)'] = os.path.join(vs_version.Path(), 'VC') + '\\'
147 # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
148 # set. This happens when the SDK is sync'd via src-internal, rather than
149 # by typical end-user installation of the SDK. If it's not set, we don't
150 # want to leave the unexpanded variable in the path, so simply strip it.
151 dxsdk_dir = _FindDirectXInstallation()
152 env['$(DXSDK_DIR)'] = dxsdk_dir if dxsdk_dir else ''
153 # Try to find an installation location for the Windows DDK by checking
154 # the WDK_DIR environment variable, may be None.
155 env['$(WDK_DIR)'] = os.environ.get('WDK_DIR', '')
156 return env
157
158def ExtractSharedMSVSSystemIncludes(configs, generator_flags):
159 """Finds msvs_system_include_dirs that are common to all targets, removes
160 them from all targets, and returns an OrderedSet containing them."""
161 all_system_includes = OrderedSet(
162 configs[0].get('msvs_system_include_dirs', []))
163 for config in configs[1:]:
164 system_includes = config.get('msvs_system_include_dirs', [])
165 all_system_includes = all_system_includes & OrderedSet(system_includes)
166 if not all_system_includes:
167 return None
168 # Expand macros in all_system_includes.
169 env = GetGlobalVSMacroEnv(GetVSVersion(generator_flags))
170 expanded_system_includes = OrderedSet([ExpandMacros(include, env)
171 for include in all_system_includes])
172 if any(['$' in include for include in expanded_system_includes]):
173 # Some path relies on target-specific variables, bail.
174 return None
175
176 # Remove system includes shared by all targets from the targets.
177 for config in configs:
178 includes = config.get('msvs_system_include_dirs', [])
179 if includes: # Don't insert a msvs_system_include_dirs key if not needed.
180 # This must check the unexpanded includes list:
181 new_includes = [i for i in includes if i not in all_system_includes]
182 config['msvs_system_include_dirs'] = new_includes
183 return expanded_system_includes
184
185
186class MsvsSettings(object):
187 """A class that understands the gyp 'msvs_...' values (especially the
188 msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
189 class helps map those settings to command line options."""
190
191 def __init__(self, spec, generator_flags):
192 self.spec = spec
193 self.vs_version = GetVSVersion(generator_flags)
194
195 supported_fields = [
196 ('msvs_configuration_attributes', dict),
197 ('msvs_settings', dict),
198 ('msvs_system_include_dirs', list),
199 ('msvs_disabled_warnings', list),
200 ('msvs_precompiled_header', str),
201 ('msvs_precompiled_source', str),
202 ('msvs_configuration_platform', str),
203 ('msvs_target_platform', str),
204 ]
205 configs = spec['configurations']
206 for field, default in supported_fields:
207 setattr(self, field, {})
208 for configname, config in configs.iteritems():
209 getattr(self, field)[configname] = config.get(field, default())
210
211 self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])
212
213 unsupported_fields = [
214 'msvs_prebuild',
215 'msvs_postbuild',
216 ]
217 unsupported = []
218 for field in unsupported_fields:
219 for config in configs.values():
220 if field in config:
221 unsupported += ["%s not supported (target %s)." %
222 (field, spec['target_name'])]
223 if unsupported:
224 raise Exception('\n'.join(unsupported))
225
226 def GetExtension(self):
227 """Returns the extension for the target, with no leading dot.
228
229 Uses 'product_extension' if specified, otherwise uses MSVS defaults based on
230 the target type.
231 """
232 ext = self.spec.get('product_extension', None)
233 if ext:
234 return ext
235 return gyp.MSVSUtil.TARGET_TYPE_EXT.get(self.spec['type'], '')
236
237 def GetVSMacroEnv(self, base_to_build=None, config=None):
238 """Get a dict of variables mapping internal VS macro names to their gyp
239 equivalents."""
240 target_platform = 'Win32' if self.GetArch(config) == 'x86' else 'x64'
241 target_name = self.spec.get('product_prefix', '') + \
242 self.spec.get('product_name', self.spec['target_name'])
243 target_dir = base_to_build + '\\' if base_to_build else ''
244 target_ext = '.' + self.GetExtension()
245 target_file_name = target_name + target_ext
246
247 replacements = {
248 '$(InputName)': '${root}',
249 '$(InputPath)': '${source}',
250 '$(IntDir)': '$!INTERMEDIATE_DIR',
251 '$(OutDir)\\': target_dir,
252 '$(PlatformName)': target_platform,
253 '$(ProjectDir)\\': '',
254 '$(ProjectName)': self.spec['target_name'],
255 '$(TargetDir)\\': target_dir,
256 '$(TargetExt)': target_ext,
257 '$(TargetFileName)': target_file_name,
258 '$(TargetName)': target_name,
259 '$(TargetPath)': os.path.join(target_dir, target_file_name),
260 }
261 replacements.update(GetGlobalVSMacroEnv(self.vs_version))
262 return replacements
263
264 def ConvertVSMacros(self, s, base_to_build=None, config=None):
265 """Convert from VS macro names to something equivalent."""
266 env = self.GetVSMacroEnv(base_to_build, config=config)
267 return ExpandMacros(s, env)
268
269 def AdjustLibraries(self, libraries):
270 """Strip -l from library if it's specified with that."""
271 libs = [lib[2:] if lib.startswith('-l') else lib for lib in libraries]
272 return [lib + '.lib' if not lib.endswith('.lib') else lib for lib in libs]
273
274 def _GetAndMunge(self, field, path, default, prefix, append, map):
275 """Retrieve a value from |field| at |path| or return |default|. If
276 |append| is specified, and the item is found, it will be appended to that
277 object instead of returned. If |map| is specified, results will be
278 remapped through |map| before being returned or appended."""
279 result = _GenericRetrieve(field, default, path)
280 result = _DoRemapping(result, map)
281 result = _AddPrefix(result, prefix)
282 return _AppendOrReturn(append, result)
283
284 class _GetWrapper(object):
285 def __init__(self, parent, field, base_path, append=None):
286 self.parent = parent
287 self.field = field
288 self.base_path = [base_path]
289 self.append = append
290 def __call__(self, name, map=None, prefix='', default=None):
291 return self.parent._GetAndMunge(self.field, self.base_path + [name],
292 default=default, prefix=prefix, append=self.append, map=map)
293
294 def GetArch(self, config):
295 """Get architecture based on msvs_configuration_platform and
296 msvs_target_platform. Returns either 'x86' or 'x64'."""
297 configuration_platform = self.msvs_configuration_platform.get(config, '')
298 platform = self.msvs_target_platform.get(config, '')
299 if not platform: # If no specific override, use the configuration's.
300 platform = configuration_platform
301 # Map from platform to architecture.
302 return {'Win32': 'x86', 'x64': 'x64'}.get(platform, 'x86')
303
304 def _TargetConfig(self, config):
305 """Returns the target-specific configuration."""
306 # There's two levels of architecture/platform specification in VS. The
307 # first level is globally for the configuration (this is what we consider
308 # "the" config at the gyp level, which will be something like 'Debug' or
309 # 'Release_x64'), and a second target-specific configuration, which is an
310 # override for the global one. |config| is remapped here to take into
311 # account the local target-specific overrides to the global configuration.
312 arch = self.GetArch(config)
313 if arch == 'x64' and not config.endswith('_x64'):
314 config += '_x64'
315 if arch == 'x86' and config.endswith('_x64'):
316 config = config.rsplit('_', 1)[0]
317 return config
318
319 def _Setting(self, path, config,
320 default=None, prefix='', append=None, map=None):
321 """_GetAndMunge for msvs_settings."""
322 return self._GetAndMunge(
323 self.msvs_settings[config], path, default, prefix, append, map)
324
325 def _ConfigAttrib(self, path, config,
326 default=None, prefix='', append=None, map=None):
327 """_GetAndMunge for msvs_configuration_attributes."""
328 return self._GetAndMunge(
329 self.msvs_configuration_attributes[config],
330 path, default, prefix, append, map)
331
332 def AdjustIncludeDirs(self, include_dirs, config):
333 """Updates include_dirs to expand VS specific paths, and adds the system
334 include dirs used for platform SDK and similar."""
335 config = self._TargetConfig(config)
336 includes = include_dirs + self.msvs_system_include_dirs[config]
337 includes.extend(self._Setting(
338 ('VCCLCompilerTool', 'AdditionalIncludeDirectories'), config, default=[]))
339 return [self.ConvertVSMacros(p, config=config) for p in includes]
340
341 def AdjustMidlIncludeDirs(self, midl_include_dirs, config):
342 """Updates midl_include_dirs to expand VS specific paths, and adds the
343 system include dirs used for platform SDK and similar."""
344 config = self._TargetConfig(config)
345 includes = midl_include_dirs + self.msvs_system_include_dirs[config]
346 includes.extend(self._Setting(
347 ('VCMIDLTool', 'AdditionalIncludeDirectories'), config, default=[]))
348 return [self.ConvertVSMacros(p, config=config) for p in includes]
349
350 def GetComputedDefines(self, config):
351 """Returns the set of defines that are injected to the defines list based
352 on other VS settings."""
353 config = self._TargetConfig(config)
354 defines = []
355 if self._ConfigAttrib(['CharacterSet'], config) == '1':
356 defines.extend(('_UNICODE', 'UNICODE'))
357 if self._ConfigAttrib(['CharacterSet'], config) == '2':
358 defines.append('_MBCS')
359 defines.extend(self._Setting(
360 ('VCCLCompilerTool', 'PreprocessorDefinitions'), config, default=[]))
361 return defines
362
363 def GetCompilerPdbName(self, config, expand_special):
364 """Get the pdb file name that should be used for compiler invocations, or
365 None if there's no explicit name specified."""
366 config = self._TargetConfig(config)
367 pdbname = self._Setting(
368 ('VCCLCompilerTool', 'ProgramDataBaseFileName'), config)
369 if pdbname:
370 pdbname = expand_special(self.ConvertVSMacros(pdbname))
371 return pdbname
372
373 def GetMapFileName(self, config, expand_special):
374 """Gets the explicitly overriden map file name for a target or returns None
375 if it's not set."""
376 config = self._TargetConfig(config)
377 map_file = self._Setting(('VCLinkerTool', 'MapFileName'), config)
378 if map_file:
379 map_file = expand_special(self.ConvertVSMacros(map_file, config=config))
380 return map_file
381
382 def GetOutputName(self, config, expand_special):
383 """Gets the explicitly overridden output name for a target or returns None
384 if it's not overridden."""
385 config = self._TargetConfig(config)
386 type = self.spec['type']
387 root = 'VCLibrarianTool' if type == 'static_library' else 'VCLinkerTool'
388 # TODO(scottmg): Handle OutputDirectory without OutputFile.
389 output_file = self._Setting((root, 'OutputFile'), config)
390 if output_file:
391 output_file = expand_special(self.ConvertVSMacros(
392 output_file, config=config))
393 return output_file
394
395 def GetPDBName(self, config, expand_special, default):
396 """Gets the explicitly overridden pdb name for a target or returns
397 default if it's not overridden, or if no pdb will be generated."""
398 config = self._TargetConfig(config)
399 output_file = self._Setting(('VCLinkerTool', 'ProgramDatabaseFile'), config)
400 generate_debug_info = self._Setting(
401 ('VCLinkerTool', 'GenerateDebugInformation'), config)
402 if generate_debug_info == 'true':
403 if output_file:
404 return expand_special(self.ConvertVSMacros(output_file, config=config))
405 else:
406 return default
407 else:
408 return None
409
410 def GetNoImportLibrary(self, config):
411 """If NoImportLibrary: true, ninja will not expect the output to include
412 an import library."""
413 config = self._TargetConfig(config)
414 noimplib = self._Setting(('NoImportLibrary',), config)
415 return noimplib == 'true'
416
417 def GetAsmflags(self, config):
418 """Returns the flags that need to be added to ml invocations."""
419 config = self._TargetConfig(config)
420 asmflags = []
421 safeseh = self._Setting(('MASM', 'UseSafeExceptionHandlers'), config)
422 if safeseh == 'true':
423 asmflags.append('/safeseh')
424 return asmflags
425
426 def GetCflags(self, config):
427 """Returns the flags that need to be added to .c and .cc compilations."""
428 config = self._TargetConfig(config)
429 cflags = []
430 cflags.extend(['/wd' + w for w in self.msvs_disabled_warnings[config]])
431 cl = self._GetWrapper(self, self.msvs_settings[config],
432 'VCCLCompilerTool', append=cflags)
433 cl('Optimization',
434 map={'0': 'd', '1': '1', '2': '2', '3': 'x'}, prefix='/O', default='2')
435 cl('InlineFunctionExpansion', prefix='/Ob')
436 cl('DisableSpecificWarnings', prefix='/wd')
437 cl('StringPooling', map={'true': '/GF'})
438 cl('EnableFiberSafeOptimizations', map={'true': '/GT'})
439 cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
440 cl('EnableIntrinsicFunctions', map={'false': '-', 'true': ''}, prefix='/Oi')
441 cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
442 cl('FloatingPointModel',
443 map={'0': 'precise', '1': 'strict', '2': 'fast'}, prefix='/fp:',
444 default='0')
445 cl('CompileAsManaged', map={'false': '', 'true': '/clr'})
446 cl('WholeProgramOptimization', map={'true': '/GL'})
447 cl('WarningLevel', prefix='/W')
448 cl('WarnAsError', map={'true': '/WX'})
449 cl('CallingConvention',
450 map={'0': 'd', '1': 'r', '2': 'z', '3': 'v'}, prefix='/G')
451 cl('DebugInformationFormat',
452 map={'1': '7', '3': 'i', '4': 'I'}, prefix='/Z')
453 cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'})
454 cl('EnableFunctionLevelLinking', map={'true': '/Gy', 'false': '/Gy-'})
455 cl('MinimalRebuild', map={'true': '/Gm'})
456 cl('BufferSecurityCheck', map={'true': '/GS', 'false': '/GS-'})
457 cl('BasicRuntimeChecks', map={'1': 's', '2': 'u', '3': '1'}, prefix='/RTC')
458 cl('RuntimeLibrary',
459 map={'0': 'T', '1': 'Td', '2': 'D', '3': 'Dd'}, prefix='/M')
460 cl('ExceptionHandling', map={'1': 'sc','2': 'a'}, prefix='/EH')
461 cl('DefaultCharIsUnsigned', map={'true': '/J'})
462 cl('TreatWChar_tAsBuiltInType',
463 map={'false': '-', 'true': ''}, prefix='/Zc:wchar_t')
464 cl('EnablePREfast', map={'true': '/analyze'})
465 cl('AdditionalOptions', prefix='')
466 cl('EnableEnhancedInstructionSet',
467 map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32', '5': 'AVX2'},
468 prefix='/arch:')
469 cflags.extend(['/FI' + f for f in self._Setting(
470 ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])])
471 if self.vs_version.short_name in ('2013', '2013e', '2015'):
472 # New flag required in 2013 to maintain previous PDB behavior.
473 cflags.append('/FS')
474 # ninja handles parallelism by itself, don't have the compiler do it too.
475 cflags = filter(lambda x: not x.startswith('/MP'), cflags)
476 return cflags
477
478 def _GetPchFlags(self, config, extension):
479 """Get the flags to be added to the cflags for precompiled header support.
480 """
481 config = self._TargetConfig(config)
482 # The PCH is only built once by a particular source file. Usage of PCH must
483 # only be for the same language (i.e. C vs. C++), so only include the pch
484 # flags when the language matches.
485 if self.msvs_precompiled_header[config]:
486 source_ext = os.path.splitext(self.msvs_precompiled_source[config])[1]
487 if _LanguageMatchesForPch(source_ext, extension):
488 pch = self.msvs_precompiled_header[config]
489 pchbase = os.path.split(pch)[1]
490 return ['/Yu' + pch, '/FI' + pch, '/Fp${pchprefix}.' + pchbase + '.pch']
491 return []
492
493 def GetCflagsC(self, config):
494 """Returns the flags that need to be added to .c compilations."""
495 config = self._TargetConfig(config)
496 return self._GetPchFlags(config, '.c')
497
498 def GetCflagsCC(self, config):
499 """Returns the flags that need to be added to .cc compilations."""
500 config = self._TargetConfig(config)
501 return ['/TP'] + self._GetPchFlags(config, '.cc')
502
503 def _GetAdditionalLibraryDirectories(self, root, config, gyp_to_build_path):
504 """Get and normalize the list of paths in AdditionalLibraryDirectories
505 setting."""
506 config = self._TargetConfig(config)
507 libpaths = self._Setting((root, 'AdditionalLibraryDirectories'),
508 config, default=[])
509 libpaths = [os.path.normpath(
510 gyp_to_build_path(self.ConvertVSMacros(p, config=config)))
511 for p in libpaths]
512 return ['/LIBPATH:"' + p + '"' for p in libpaths]
513
514 def GetLibFlags(self, config, gyp_to_build_path):
515 """Returns the flags that need to be added to lib commands."""
516 config = self._TargetConfig(config)
517 libflags = []
518 lib = self._GetWrapper(self, self.msvs_settings[config],
519 'VCLibrarianTool', append=libflags)
520 libflags.extend(self._GetAdditionalLibraryDirectories(
521 'VCLibrarianTool', config, gyp_to_build_path))
522 lib('LinkTimeCodeGeneration', map={'true': '/LTCG'})
523 lib('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
524 prefix='/MACHINE:')
525 lib('AdditionalOptions')
526 return libflags
527
528 def GetDefFile(self, gyp_to_build_path):
529 """Returns the .def file from sources, if any. Otherwise returns None."""
530 spec = self.spec
531 if spec['type'] in ('shared_library', 'loadable_module', 'executable'):
532 def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
533 if len(def_files) == 1:
534 return gyp_to_build_path(def_files[0])
535 elif len(def_files) > 1:
536 raise Exception("Multiple .def files")
537 return None
538
539 def _GetDefFileAsLdflags(self, ldflags, gyp_to_build_path):
540 """.def files get implicitly converted to a ModuleDefinitionFile for the
541 linker in the VS generator. Emulate that behaviour here."""
542 def_file = self.GetDefFile(gyp_to_build_path)
543 if def_file:
544 ldflags.append('/DEF:"%s"' % def_file)
545
546 def GetPGDName(self, config, expand_special):
547 """Gets the explicitly overridden pgd name for a target or returns None
548 if it's not overridden."""
549 config = self._TargetConfig(config)
550 output_file = self._Setting(
551 ('VCLinkerTool', 'ProfileGuidedDatabase'), config)
552 if output_file:
553 output_file = expand_special(self.ConvertVSMacros(
554 output_file, config=config))
555 return output_file
556
557 def GetLdflags(self, config, gyp_to_build_path, expand_special,
558 manifest_base_name, output_name, is_executable, build_dir):
559 """Returns the flags that need to be added to link commands, and the
560 manifest files."""
561 config = self._TargetConfig(config)
562 ldflags = []
563 ld = self._GetWrapper(self, self.msvs_settings[config],
564 'VCLinkerTool', append=ldflags)
565 self._GetDefFileAsLdflags(ldflags, gyp_to_build_path)
566 ld('GenerateDebugInformation', map={'true': '/DEBUG'})
567 ld('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
568 prefix='/MACHINE:')
569 ldflags.extend(self._GetAdditionalLibraryDirectories(
570 'VCLinkerTool', config, gyp_to_build_path))
571 ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
572 ld('TreatLinkerWarningAsErrors', prefix='/WX',
573 map={'true': '', 'false': ':NO'})
574 out = self.GetOutputName(config, expand_special)
575 if out:
576 ldflags.append('/OUT:' + out)
577 pdb = self.GetPDBName(config, expand_special, output_name + '.pdb')
578 if pdb:
579 ldflags.append('/PDB:' + pdb)
580 pgd = self.GetPGDName(config, expand_special)
581 if pgd:
582 ldflags.append('/PGD:' + pgd)
583 map_file = self.GetMapFileName(config, expand_special)
584 ld('GenerateMapFile', map={'true': '/MAP:' + map_file if map_file
585 else '/MAP'})
586 ld('MapExports', map={'true': '/MAPINFO:EXPORTS'})
587 ld('AdditionalOptions', prefix='')
588
589 minimum_required_version = self._Setting(
590 ('VCLinkerTool', 'MinimumRequiredVersion'), config, default='')
591 if minimum_required_version:
592 minimum_required_version = ',' + minimum_required_version
593 ld('SubSystem',
594 map={'1': 'CONSOLE%s' % minimum_required_version,
595 '2': 'WINDOWS%s' % minimum_required_version},
596 prefix='/SUBSYSTEM:')
597
598 stack_reserve_size = self._Setting(
599 ('VCLinkerTool', 'StackReserveSize'), config, default='')
600 if stack_reserve_size:
601 stack_commit_size = self._Setting(
602 ('VCLinkerTool', 'StackCommitSize'), config, default='')
603 if stack_commit_size:
604 stack_commit_size = ',' + stack_commit_size
605 ldflags.append('/STACK:%s%s' % (stack_reserve_size, stack_commit_size))
606
607 ld('TerminalServerAware', map={'1': ':NO', '2': ''}, prefix='/TSAWARE')
608 ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
609 ld('BaseAddress', prefix='/BASE:')
610 ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
611 ld('RandomizedBaseAddress',
612 map={'1': ':NO', '2': ''}, prefix='/DYNAMICBASE')
613 ld('DataExecutionPrevention',
614 map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT')
615 ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:')
616 ld('ForceSymbolReferences', prefix='/INCLUDE:')
617 ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
618 ld('LinkTimeCodeGeneration',
619 map={'1': '', '2': ':PGINSTRUMENT', '3': ':PGOPTIMIZE',
620 '4': ':PGUPDATE'},
621 prefix='/LTCG')
622 ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
623 ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
624 ld('EntryPointSymbol', prefix='/ENTRY:')
625 ld('Profile', map={'true': '/PROFILE'})
626 ld('LargeAddressAware',
627 map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
628 # TODO(scottmg): This should sort of be somewhere else (not really a flag).
629 ld('AdditionalDependencies', prefix='')
630
631 if self.GetArch(config) == 'x86':
632 safeseh_default = 'true'
633 else:
634 safeseh_default = None
635 ld('ImageHasSafeExceptionHandlers',
636 map={'false': ':NO', 'true': ''}, prefix='/SAFESEH',
637 default=safeseh_default)
638
639 # If the base address is not specifically controlled, DYNAMICBASE should
640 # be on by default.
641 base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
642 ldflags)
643 if not base_flags:
644 ldflags.append('/DYNAMICBASE')
645
646 # If the NXCOMPAT flag has not been specified, default to on. Despite the
647 # documentation that says this only defaults to on when the subsystem is
648 # Vista or greater (which applies to the linker), the IDE defaults it on
649 # unless it's explicitly off.
650 if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
651 ldflags.append('/NXCOMPAT')
652
653 have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
654 manifest_flags, intermediate_manifest, manifest_files = \
655 self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path,
656 is_executable and not have_def_file, build_dir)
657 ldflags.extend(manifest_flags)
658 return ldflags, intermediate_manifest, manifest_files
659
660 def _GetLdManifestFlags(self, config, name, gyp_to_build_path,
661 allow_isolation, build_dir):
662 """Returns a 3-tuple:
663 - the set of flags that need to be added to the link to generate
664 a default manifest
665 - the intermediate manifest that the linker will generate that should be
666 used to assert it doesn't add anything to the merged one.
667 - the list of all the manifest files to be merged by the manifest tool and
668 included into the link."""
669 generate_manifest = self._Setting(('VCLinkerTool', 'GenerateManifest'),
670 config,
671 default='true')
672 if generate_manifest != 'true':
673 # This means not only that the linker should not generate the intermediate
674 # manifest but also that the manifest tool should do nothing even when
675 # additional manifests are specified.
676 return ['/MANIFEST:NO'], [], []
677
678 output_name = name + '.intermediate.manifest'
679 flags = [
680 '/MANIFEST',
681 '/ManifestFile:' + output_name,
682 ]
683
684 # Instead of using the MANIFESTUAC flags, we generate a .manifest to
685 # include into the list of manifests. This allows us to avoid the need to
686 # do two passes during linking. The /MANIFEST flag and /ManifestFile are
687 # still used, and the intermediate manifest is used to assert that the
688 # final manifest we get from merging all the additional manifest files
689 # (plus the one we generate here) isn't modified by merging the
690 # intermediate into it.
691
692 # Always NO, because we generate a manifest file that has what we want.
693 flags.append('/MANIFESTUAC:NO')
694
695 config = self._TargetConfig(config)
696 enable_uac = self._Setting(('VCLinkerTool', 'EnableUAC'), config,
697 default='true')
698 manifest_files = []
699 generated_manifest_outer = \
700"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" \
701"<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>%s" \
702"</assembly>"
703 if enable_uac == 'true':
704 execution_level = self._Setting(('VCLinkerTool', 'UACExecutionLevel'),
705 config, default='0')
706 execution_level_map = {
707 '0': 'asInvoker',
708 '1': 'highestAvailable',
709 '2': 'requireAdministrator'
710 }
711
712 ui_access = self._Setting(('VCLinkerTool', 'UACUIAccess'), config,
713 default='false')
714
715 inner = '''
716<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
717 <security>
718 <requestedPrivileges>
719 <requestedExecutionLevel level='%s' uiAccess='%s' />
720 </requestedPrivileges>
721 </security>
722</trustInfo>''' % (execution_level_map[execution_level], ui_access)
723 else:
724 inner = ''
725
726 generated_manifest_contents = generated_manifest_outer % inner
727 generated_name = name + '.generated.manifest'
728 # Need to join with the build_dir here as we're writing it during
729 # generation time, but we return the un-joined version because the build
730 # will occur in that directory. We only write the file if the contents
731 # have changed so that simply regenerating the project files doesn't
732 # cause a relink.
733 build_dir_generated_name = os.path.join(build_dir, generated_name)
734 gyp.common.EnsureDirExists(build_dir_generated_name)
735 f = gyp.common.WriteOnDiff(build_dir_generated_name)
736 f.write(generated_manifest_contents)
737 f.close()
738 manifest_files = [generated_name]
739
740 if allow_isolation:
741 flags.append('/ALLOWISOLATION')
742
743 manifest_files += self._GetAdditionalManifestFiles(config,
744 gyp_to_build_path)
745 return flags, output_name, manifest_files
746
747 def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
748 """Gets additional manifest files that are added to the default one
749 generated by the linker."""
750 files = self._Setting(('VCManifestTool', 'AdditionalManifestFiles'), config,
751 default=[])
752 if isinstance(files, str):
753 files = files.split(';')
754 return [os.path.normpath(
755 gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
756 for f in files]
757
758 def IsUseLibraryDependencyInputs(self, config):
759 """Returns whether the target should be linked via Use Library Dependency
760 Inputs (using component .objs of a given .lib)."""
761 config = self._TargetConfig(config)
762 uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
763 return uldi == 'true'
764
765 def IsEmbedManifest(self, config):
766 """Returns whether manifest should be linked into binary."""
767 config = self._TargetConfig(config)
768 embed = self._Setting(('VCManifestTool', 'EmbedManifest'), config,
769 default='true')
770 return embed == 'true'
771
772 def IsLinkIncremental(self, config):
773 """Returns whether the target should be linked incrementally."""
774 config = self._TargetConfig(config)
775 link_inc = self._Setting(('VCLinkerTool', 'LinkIncremental'), config)
776 return link_inc != '1'
777
778 def GetRcflags(self, config, gyp_to_ninja_path):
779 """Returns the flags that need to be added to invocations of the resource
780 compiler."""
781 config = self._TargetConfig(config)
782 rcflags = []
783 rc = self._GetWrapper(self, self.msvs_settings[config],
784 'VCResourceCompilerTool', append=rcflags)
785 rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
786 rcflags.append('/I' + gyp_to_ninja_path('.'))
787 rc('PreprocessorDefinitions', prefix='/d')
788 # /l arg must be in hex without leading '0x'
789 rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
790 return rcflags
791
792 def BuildCygwinBashCommandLine(self, args, path_to_base):
793 """Build a command line that runs args via cygwin bash. We assume that all
794 incoming paths are in Windows normpath'd form, so they need to be
795 converted to posix style for the part of the command line that's passed to
796 bash. We also have to do some Visual Studio macro emulation here because
797 various rules use magic VS names for things. Also note that rules that
798 contain ninja variables cannot be fixed here (for example ${source}), so
799 the outer generator needs to make sure that the paths that are written out
800 are in posix style, if the command line will be used here."""
801 cygwin_dir = os.path.normpath(
802 os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
803 cd = ('cd %s' % path_to_base).replace('\\', '/')
804 args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
805 args = ["'%s'" % a.replace("'", "'\\''") for a in args]
806 bash_cmd = ' '.join(args)
807 cmd = (
808 'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
809 'bash -c "%s ; %s"' % (cd, bash_cmd))
810 return cmd
811
812 def IsRuleRunUnderCygwin(self, rule):
813 """Determine if an action should be run under cygwin. If the variable is
814 unset, or set to 1 we use cygwin."""
815 return int(rule.get('msvs_cygwin_shell',
816 self.spec.get('msvs_cygwin_shell', 1))) != 0
817
818 def _HasExplicitRuleForExtension(self, spec, extension):
819 """Determine if there's an explicit rule for a particular extension."""
820 for rule in spec.get('rules', []):
821 if rule['extension'] == extension:
822 return True
823 return False
824
825 def _HasExplicitIdlActions(self, spec):
826 """Determine if an action should not run midl for .idl files."""
827 return any([action.get('explicit_idl_action', 0)
828 for action in spec.get('actions', [])])
829
830 def HasExplicitIdlRulesOrActions(self, spec):
831 """Determine if there's an explicit rule or action for idl files. When
832 there isn't we need to generate implicit rules to build MIDL .idl files."""
833 return (self._HasExplicitRuleForExtension(spec, 'idl') or
834 self._HasExplicitIdlActions(spec))
835
836 def HasExplicitAsmRules(self, spec):
837 """Determine if there's an explicit rule for asm files. When there isn't we
838 need to generate implicit rules to assemble .asm files."""
839 return self._HasExplicitRuleForExtension(spec, 'asm')
840
841 def GetIdlBuildData(self, source, config):
842 """Determine the implicit outputs for an idl file. Returns output
843 directory, outputs, and variables and flags that are required."""
844 config = self._TargetConfig(config)
845 midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
846 def midl(name, default=None):
847 return self.ConvertVSMacros(midl_get(name, default=default),
848 config=config)
849 tlb = midl('TypeLibraryName', default='${root}.tlb')
850 header = midl('HeaderFileName', default='${root}.h')
851 dlldata = midl('DLLDataFileName', default='dlldata.c')
852 iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
853 proxy = midl('ProxyFileName', default='${root}_p.c')
854 # Note that .tlb is not included in the outputs as it is not always
855 # generated depending on the content of the input idl file.
856 outdir = midl('OutputDirectory', default='')
857 output = [header, dlldata, iid, proxy]
858 variables = [('tlb', tlb),
859 ('h', header),
860 ('dlldata', dlldata),
861 ('iid', iid),
862 ('proxy', proxy)]
863 # TODO(scottmg): Are there configuration settings to set these flags?
864 target_platform = 'win32' if self.GetArch(config) == 'x86' else 'x64'
865 flags = ['/char', 'signed', '/env', target_platform, '/Oicf']
866 return outdir, output, variables, flags
867
868
869def _LanguageMatchesForPch(source_ext, pch_source_ext):
870 c_exts = ('.c',)
871 cc_exts = ('.cc', '.cxx', '.cpp')
872 return ((source_ext in c_exts and pch_source_ext in c_exts) or
873 (source_ext in cc_exts and pch_source_ext in cc_exts))
874
875
876class PrecompiledHeader(object):
877 """Helper to generate dependencies and build rules to handle generation of
878 precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
879 """
880 def __init__(
881 self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext):
882 self.settings = settings
883 self.config = config
884 pch_source = self.settings.msvs_precompiled_source[self.config]
885 self.pch_source = gyp_to_build_path(pch_source)
886 filename, _ = os.path.splitext(pch_source)
887 self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
888
889 def _PchHeader(self):
890 """Get the header that will appear in an #include line for all source
891 files."""
892 return self.settings.msvs_precompiled_header[self.config]
893
894 def GetObjDependencies(self, sources, objs, arch):
895 """Given a list of sources files and the corresponding object files,
896 returns a list of the pch files that should be depended upon. The
897 additional wrapping in the return value is for interface compatibility
898 with make.py on Mac, and xcode_emulation.py."""
899 assert arch is None
900 if not self._PchHeader():
901 return []
902 pch_ext = os.path.splitext(self.pch_source)[1]
903 for source in sources:
904 if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
905 return [(None, None, self.output_obj)]
906 return []
907
908 def GetPchBuildCommands(self, arch):
909 """Not used on Windows as there are no additional build steps required
910 (instead, existing steps are modified in GetFlagsModifications below)."""
911 return []
912
913 def GetFlagsModifications(self, input, output, implicit, command,
914 cflags_c, cflags_cc, expand_special):
915 """Get the modified cflags and implicit dependencies that should be used
916 for the pch compilation step."""
917 if input == self.pch_source:
918 pch_output = ['/Yc' + self._PchHeader()]
919 if command == 'cxx':
920 return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
921 self.output_obj, [])
922 elif command == 'cc':
923 return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
924 self.output_obj, [])
925 return [], output, implicit
926
927
928vs_version = None
929def GetVSVersion(generator_flags):
930 global vs_version
931 if not vs_version:
932 vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
933 generator_flags.get('msvs_version', 'auto'),
934 allow_fallback=False)
935 return vs_version
936
937def _GetVsvarsSetupArgs(generator_flags, arch):
938 vs = GetVSVersion(generator_flags)
939 return vs.SetupScript()
940
941def ExpandMacros(string, expansions):
942 """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
943 for the canonical way to retrieve a suitable dict."""
944 if '$' in string:
945 for old, new in expansions.iteritems():
946 assert '$(' not in new, new
947 string = string.replace(old, new)
948 return string
949
950def _ExtractImportantEnvironment(output_of_set):
951 """Extracts environment variables required for the toolchain to run from
952 a textual dump output by the cmd.exe 'set' command."""
953 envvars_to_save = (
954 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
955 'include',
956 'lib',
957 'libpath',
958 'path',
959 'pathext',
960 'systemroot',
961 'temp',
962 'tmp',
963 )
964 env = {}
965 # This occasionally happens and leads to misleading SYSTEMROOT error messages
966 # if not caught here.
967 if output_of_set.count('=') == 0:
968 raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set)
969 for line in output_of_set.splitlines():
970 for envvar in envvars_to_save:
971 if re.match(envvar + '=', line.lower()):
972 var, setting = line.split('=', 1)
973 if envvar == 'path':
974 # Our own rules (for running gyp-win-tool) and other actions in
975 # Chromium rely on python being in the path. Add the path to this
976 # python here so that if it's not in the path when ninja is run
977 # later, python will still be found.
978 setting = os.path.dirname(sys.executable) + os.pathsep + setting
979 env[var.upper()] = setting
980 break
981 for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
982 if required not in env:
983 raise Exception('Environment variable "%s" '
984 'required to be set to valid path' % required)
985 return env
986
987def _FormatAsEnvironmentBlock(envvar_dict):
988 """Format as an 'environment block' directly suitable for CreateProcess.
989 Briefly this is a list of key=value\0, terminated by an additional \0. See
990 CreateProcess documentation for more details."""
991 block = ''
992 nul = '\0'
993 for key, value in envvar_dict.iteritems():
994 block += key + '=' + value + nul
995 block += nul
996 return block
997
998def _ExtractCLPath(output_of_where):
999 """Gets the path to cl.exe based on the output of calling the environment
1000 setup batch file, followed by the equivalent of `where`."""
1001 # Take the first line, as that's the first found in the PATH.
1002 for line in output_of_where.strip().splitlines():
1003 if line.startswith('LOC:'):
1004 return line[len('LOC:'):].strip()
1005
1006def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags,
1007 system_includes, open_out):
1008 """It's not sufficient to have the absolute path to the compiler, linker,
1009 etc. on Windows, as those tools rely on .dlls being in the PATH. We also
1010 need to support both x86 and x64 compilers within the same build (to support
1011 msvs_target_platform hackery). Different architectures require a different
1012 compiler binary, and different supporting environment variables (INCLUDE,
1013 LIB, LIBPATH). So, we extract the environment here, wrap all invocations
1014 of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
1015 sets up the environment, and then we do not prefix the compiler with
1016 an absolute path, instead preferring something like "cl.exe" in the rule
1017 which will then run whichever the environment setup has put in the path.
1018 When the following procedure to generate environment files does not
1019 meet your requirement (e.g. for custom toolchains), you can pass
1020 "-G ninja_use_custom_environment_files" to the gyp to suppress file
1021 generation and use custom environment files prepared by yourself."""
1022 archs = ('x86', 'x64')
1023 if generator_flags.get('ninja_use_custom_environment_files', 0):
1024 cl_paths = {}
1025 for arch in archs:
1026 cl_paths[arch] = 'cl.exe'
1027 return cl_paths
1028 vs = GetVSVersion(generator_flags)
1029 cl_paths = {}
1030 for arch in archs:
1031 # Extract environment variables for subprocesses.
1032 args = vs.SetupScript(arch)
1033 args.extend(('&&', 'set'))
1034 popen = subprocess.Popen(
1035 args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1036 variables, _ = popen.communicate()
1037 if popen.returncode != 0:
1038 raise Exception('"%s" failed with error %d' % (args, popen.returncode))
1039 env = _ExtractImportantEnvironment(variables)
1040
1041 # Inject system includes from gyp files into INCLUDE.
1042 if system_includes:
1043 system_includes = system_includes | OrderedSet(
1044 env.get('INCLUDE', '').split(';'))
1045 env['INCLUDE'] = ';'.join(system_includes)
1046
1047 env_block = _FormatAsEnvironmentBlock(env)
1048 f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
1049 f.write(env_block)
1050 f.close()
1051
1052 # Find cl.exe location for this architecture.
1053 args = vs.SetupScript(arch)
1054 args.extend(('&&',
1055 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
1056 popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
1057 output, _ = popen.communicate()
1058 cl_paths[arch] = _ExtractCLPath(output)
1059 return cl_paths
1060
1061def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja):
1062 """Emulate behavior of msvs_error_on_missing_sources present in the msvs
1063 generator: Check that all regular source files, i.e. not created at run time,
1064 exist on disk. Missing files cause needless recompilation when building via
1065 VS, and we want this check to match for people/bots that build using ninja,
1066 so they're not surprised when the VS build fails."""
1067 if int(generator_flags.get('msvs_error_on_missing_sources', 0)):
1068 no_specials = filter(lambda x: '$' not in x, sources)
1069 relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials]
1070 missing = filter(lambda x: not os.path.exists(x), relative)
1071 if missing:
1072 # They'll look like out\Release\..\..\stuff\things.cc, so normalize the
1073 # path for a slightly less crazy looking output.
1074 cleaned_up = [os.path.normpath(x) for x in missing]
1075 raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up))
1076
1077# Sets some values in default_variables, which are required for many
1078# generators, run on Windows.
1079def CalculateCommonVariables(default_variables, params):
1080 generator_flags = params.get('generator_flags', {})
1081
1082 # Set a variable so conditions can be based on msvs_version.
1083 msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
1084 default_variables['MSVS_VERSION'] = msvs_version.ShortName()
1085
1086 # To determine processor word size on Windows, in addition to checking
1087 # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
1088 # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
1089 # contains the actual word size of the system when running thru WOW64).
1090 if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
1091 '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
1092 default_variables['MSVS_OS_BITS'] = 64
1093 else:
1094 default_variables['MSVS_OS_BITS'] = 32