Initial commit
[yaffs-website] / node_modules / node-gyp / gyp / pylib / gyp / msvs_emulation.py
1 # 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 """
6 This module helps emulate Visual Studio 2008 behavior on top of other
7 build systems, primarily ninja.
8 """
9
10 import os
11 import re
12 import subprocess
13 import sys
14
15 from gyp.common import OrderedSet
16 import gyp.MSVSUtil
17 import gyp.MSVSVersion
18
19
20 windows_quoter_regex = re.compile(r'(\\*)"')
21
22
23 def 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
53 def 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
69 def _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
79 def _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
90 def _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
103 def _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
116 def _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
138 def 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
158 def 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
186 class 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 = os.path.split(self.msvs_precompiled_header[config])[1]
489         return ['/Yu' + pch, '/FI' + pch, '/Fp${pchprefix}.' + pch + '.pch']
490     return  []
491
492   def GetCflagsC(self, config):
493     """Returns the flags that need to be added to .c compilations."""
494     config = self._TargetConfig(config)
495     return self._GetPchFlags(config, '.c')
496
497   def GetCflagsCC(self, config):
498     """Returns the flags that need to be added to .cc compilations."""
499     config = self._TargetConfig(config)
500     return ['/TP'] + self._GetPchFlags(config, '.cc')
501
502   def _GetAdditionalLibraryDirectories(self, root, config, gyp_to_build_path):
503     """Get and normalize the list of paths in AdditionalLibraryDirectories
504     setting."""
505     config = self._TargetConfig(config)
506     libpaths = self._Setting((root, 'AdditionalLibraryDirectories'),
507                              config, default=[])
508     libpaths = [os.path.normpath(
509                     gyp_to_build_path(self.ConvertVSMacros(p, config=config)))
510                 for p in libpaths]
511     return ['/LIBPATH:"' + p + '"' for p in libpaths]
512
513   def GetLibFlags(self, config, gyp_to_build_path):
514     """Returns the flags that need to be added to lib commands."""
515     config = self._TargetConfig(config)
516     libflags = []
517     lib = self._GetWrapper(self, self.msvs_settings[config],
518                           'VCLibrarianTool', append=libflags)
519     libflags.extend(self._GetAdditionalLibraryDirectories(
520         'VCLibrarianTool', config, gyp_to_build_path))
521     lib('LinkTimeCodeGeneration', map={'true': '/LTCG'})
522     lib('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
523         prefix='/MACHINE:')
524     lib('AdditionalOptions')
525     return libflags
526
527   def GetDefFile(self, gyp_to_build_path):
528     """Returns the .def file from sources, if any.  Otherwise returns None."""
529     spec = self.spec
530     if spec['type'] in ('shared_library', 'loadable_module', 'executable'):
531       def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
532       if len(def_files) == 1:
533         return gyp_to_build_path(def_files[0])
534       elif len(def_files) > 1:
535         raise Exception("Multiple .def files")
536     return None
537
538   def _GetDefFileAsLdflags(self, ldflags, gyp_to_build_path):
539     """.def files get implicitly converted to a ModuleDefinitionFile for the
540     linker in the VS generator. Emulate that behaviour here."""
541     def_file = self.GetDefFile(gyp_to_build_path)
542     if def_file:
543       ldflags.append('/DEF:"%s"' % def_file)
544
545   def GetPGDName(self, config, expand_special):
546     """Gets the explicitly overridden pgd name for a target or returns None
547     if it's not overridden."""
548     config = self._TargetConfig(config)
549     output_file = self._Setting(
550         ('VCLinkerTool', 'ProfileGuidedDatabase'), config)
551     if output_file:
552       output_file = expand_special(self.ConvertVSMacros(
553           output_file, config=config))
554     return output_file
555
556   def GetLdflags(self, config, gyp_to_build_path, expand_special,
557                  manifest_base_name, output_name, is_executable, build_dir):
558     """Returns the flags that need to be added to link commands, and the
559     manifest files."""
560     config = self._TargetConfig(config)
561     ldflags = []
562     ld = self._GetWrapper(self, self.msvs_settings[config],
563                           'VCLinkerTool', append=ldflags)
564     self._GetDefFileAsLdflags(ldflags, gyp_to_build_path)
565     ld('GenerateDebugInformation', map={'true': '/DEBUG'})
566     ld('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
567        prefix='/MACHINE:')
568     ldflags.extend(self._GetAdditionalLibraryDirectories(
569         'VCLinkerTool', config, gyp_to_build_path))
570     ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
571     ld('TreatLinkerWarningAsErrors', prefix='/WX',
572        map={'true': '', 'false': ':NO'})
573     out = self.GetOutputName(config, expand_special)
574     if out:
575       ldflags.append('/OUT:' + out)
576     pdb = self.GetPDBName(config, expand_special, output_name + '.pdb')
577     if pdb:
578       ldflags.append('/PDB:' + pdb)
579     pgd = self.GetPGDName(config, expand_special)
580     if pgd:
581       ldflags.append('/PGD:' + pgd)
582     map_file = self.GetMapFileName(config, expand_special)
583     ld('GenerateMapFile', map={'true': '/MAP:' + map_file if map_file
584         else '/MAP'})
585     ld('MapExports', map={'true': '/MAPINFO:EXPORTS'})
586     ld('AdditionalOptions', prefix='')
587
588     minimum_required_version = self._Setting(
589         ('VCLinkerTool', 'MinimumRequiredVersion'), config, default='')
590     if minimum_required_version:
591       minimum_required_version = ',' + minimum_required_version
592     ld('SubSystem',
593        map={'1': 'CONSOLE%s' % minimum_required_version,
594             '2': 'WINDOWS%s' % minimum_required_version},
595        prefix='/SUBSYSTEM:')
596
597     stack_reserve_size = self._Setting(
598         ('VCLinkerTool', 'StackReserveSize'), config, default='')
599     if stack_reserve_size:
600       stack_commit_size = self._Setting(
601           ('VCLinkerTool', 'StackCommitSize'), config, default='')
602       if stack_commit_size:
603         stack_commit_size = ',' + stack_commit_size
604       ldflags.append('/STACK:%s%s' % (stack_reserve_size, stack_commit_size))
605
606     ld('TerminalServerAware', map={'1': ':NO', '2': ''}, prefix='/TSAWARE')
607     ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
608     ld('BaseAddress', prefix='/BASE:')
609     ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
610     ld('RandomizedBaseAddress',
611         map={'1': ':NO', '2': ''}, prefix='/DYNAMICBASE')
612     ld('DataExecutionPrevention',
613         map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT')
614     ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:')
615     ld('ForceSymbolReferences', prefix='/INCLUDE:')
616     ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
617     ld('LinkTimeCodeGeneration',
618         map={'1': '', '2': ':PGINSTRUMENT', '3': ':PGOPTIMIZE',
619              '4': ':PGUPDATE'},
620         prefix='/LTCG')
621     ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
622     ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
623     ld('EntryPointSymbol', prefix='/ENTRY:')
624     ld('Profile', map={'true': '/PROFILE'})
625     ld('LargeAddressAware',
626         map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
627     # TODO(scottmg): This should sort of be somewhere else (not really a flag).
628     ld('AdditionalDependencies', prefix='')
629
630     if self.GetArch(config) == 'x86':
631       safeseh_default = 'true'
632     else:
633       safeseh_default = None
634     ld('ImageHasSafeExceptionHandlers',
635         map={'false': ':NO', 'true': ''}, prefix='/SAFESEH',
636         default=safeseh_default)
637
638     # If the base address is not specifically controlled, DYNAMICBASE should
639     # be on by default.
640     base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
641                         ldflags)
642     if not base_flags:
643       ldflags.append('/DYNAMICBASE')
644
645     # If the NXCOMPAT flag has not been specified, default to on. Despite the
646     # documentation that says this only defaults to on when the subsystem is
647     # Vista or greater (which applies to the linker), the IDE defaults it on
648     # unless it's explicitly off.
649     if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
650       ldflags.append('/NXCOMPAT')
651
652     have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
653     manifest_flags, intermediate_manifest, manifest_files = \
654         self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path,
655                                  is_executable and not have_def_file, build_dir)
656     ldflags.extend(manifest_flags)
657     return ldflags, intermediate_manifest, manifest_files
658
659   def _GetLdManifestFlags(self, config, name, gyp_to_build_path,
660                           allow_isolation, build_dir):
661     """Returns a 3-tuple:
662     - the set of flags that need to be added to the link to generate
663       a default manifest
664     - the intermediate manifest that the linker will generate that should be
665       used to assert it doesn't add anything to the merged one.
666     - the list of all the manifest files to be merged by the manifest tool and
667       included into the link."""
668     generate_manifest = self._Setting(('VCLinkerTool', 'GenerateManifest'),
669                                       config,
670                                       default='true')
671     if generate_manifest != 'true':
672       # This means not only that the linker should not generate the intermediate
673       # manifest but also that the manifest tool should do nothing even when
674       # additional manifests are specified.
675       return ['/MANIFEST:NO'], [], []
676
677     output_name = name + '.intermediate.manifest'
678     flags = [
679       '/MANIFEST',
680       '/ManifestFile:' + output_name,
681     ]
682
683     # Instead of using the MANIFESTUAC flags, we generate a .manifest to
684     # include into the list of manifests. This allows us to avoid the need to
685     # do two passes during linking. The /MANIFEST flag and /ManifestFile are
686     # still used, and the intermediate manifest is used to assert that the
687     # final manifest we get from merging all the additional manifest files
688     # (plus the one we generate here) isn't modified by merging the
689     # intermediate into it.
690
691     # Always NO, because we generate a manifest file that has what we want.
692     flags.append('/MANIFESTUAC:NO')
693
694     config = self._TargetConfig(config)
695     enable_uac = self._Setting(('VCLinkerTool', 'EnableUAC'), config,
696                                default='true')
697     manifest_files = []
698     generated_manifest_outer = \
699 "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" \
700 "<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>%s" \
701 "</assembly>"
702     if enable_uac == 'true':
703       execution_level = self._Setting(('VCLinkerTool', 'UACExecutionLevel'),
704                                       config, default='0')
705       execution_level_map = {
706         '0': 'asInvoker',
707         '1': 'highestAvailable',
708         '2': 'requireAdministrator'
709       }
710
711       ui_access = self._Setting(('VCLinkerTool', 'UACUIAccess'), config,
712                                 default='false')
713
714       inner = '''
715 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
716   <security>
717     <requestedPrivileges>
718       <requestedExecutionLevel level='%s' uiAccess='%s' />
719     </requestedPrivileges>
720   </security>
721 </trustInfo>''' % (execution_level_map[execution_level], ui_access)
722     else:
723       inner = ''
724
725     generated_manifest_contents = generated_manifest_outer % inner
726     generated_name = name + '.generated.manifest'
727     # Need to join with the build_dir here as we're writing it during
728     # generation time, but we return the un-joined version because the build
729     # will occur in that directory. We only write the file if the contents
730     # have changed so that simply regenerating the project files doesn't
731     # cause a relink.
732     build_dir_generated_name = os.path.join(build_dir, generated_name)
733     gyp.common.EnsureDirExists(build_dir_generated_name)
734     f = gyp.common.WriteOnDiff(build_dir_generated_name)
735     f.write(generated_manifest_contents)
736     f.close()
737     manifest_files = [generated_name]
738
739     if allow_isolation:
740       flags.append('/ALLOWISOLATION')
741
742     manifest_files += self._GetAdditionalManifestFiles(config,
743                                                        gyp_to_build_path)
744     return flags, output_name, manifest_files
745
746   def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
747     """Gets additional manifest files that are added to the default one
748     generated by the linker."""
749     files = self._Setting(('VCManifestTool', 'AdditionalManifestFiles'), config,
750                           default=[])
751     if isinstance(files, str):
752       files = files.split(';')
753     return [os.path.normpath(
754                 gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
755             for f in files]
756
757   def IsUseLibraryDependencyInputs(self, config):
758     """Returns whether the target should be linked via Use Library Dependency
759     Inputs (using component .objs of a given .lib)."""
760     config = self._TargetConfig(config)
761     uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
762     return uldi == 'true'
763
764   def IsEmbedManifest(self, config):
765     """Returns whether manifest should be linked into binary."""
766     config = self._TargetConfig(config)
767     embed = self._Setting(('VCManifestTool', 'EmbedManifest'), config,
768                           default='true')
769     return embed == 'true'
770
771   def IsLinkIncremental(self, config):
772     """Returns whether the target should be linked incrementally."""
773     config = self._TargetConfig(config)
774     link_inc = self._Setting(('VCLinkerTool', 'LinkIncremental'), config)
775     return link_inc != '1'
776
777   def GetRcflags(self, config, gyp_to_ninja_path):
778     """Returns the flags that need to be added to invocations of the resource
779     compiler."""
780     config = self._TargetConfig(config)
781     rcflags = []
782     rc = self._GetWrapper(self, self.msvs_settings[config],
783         'VCResourceCompilerTool', append=rcflags)
784     rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
785     rcflags.append('/I' + gyp_to_ninja_path('.'))
786     rc('PreprocessorDefinitions', prefix='/d')
787     # /l arg must be in hex without leading '0x'
788     rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
789     return rcflags
790
791   def BuildCygwinBashCommandLine(self, args, path_to_base):
792     """Build a command line that runs args via cygwin bash. We assume that all
793     incoming paths are in Windows normpath'd form, so they need to be
794     converted to posix style for the part of the command line that's passed to
795     bash. We also have to do some Visual Studio macro emulation here because
796     various rules use magic VS names for things. Also note that rules that
797     contain ninja variables cannot be fixed here (for example ${source}), so
798     the outer generator needs to make sure that the paths that are written out
799     are in posix style, if the command line will be used here."""
800     cygwin_dir = os.path.normpath(
801         os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
802     cd = ('cd %s' % path_to_base).replace('\\', '/')
803     args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
804     args = ["'%s'" % a.replace("'", "'\\''") for a in args]
805     bash_cmd = ' '.join(args)
806     cmd = (
807         'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
808         'bash -c "%s ; %s"' % (cd, bash_cmd))
809     return cmd
810
811   def IsRuleRunUnderCygwin(self, rule):
812     """Determine if an action should be run under cygwin. If the variable is
813     unset, or set to 1 we use cygwin."""
814     return int(rule.get('msvs_cygwin_shell',
815                         self.spec.get('msvs_cygwin_shell', 1))) != 0
816
817   def _HasExplicitRuleForExtension(self, spec, extension):
818     """Determine if there's an explicit rule for a particular extension."""
819     for rule in spec.get('rules', []):
820       if rule['extension'] == extension:
821         return True
822     return False
823
824   def _HasExplicitIdlActions(self, spec):
825     """Determine if an action should not run midl for .idl files."""
826     return any([action.get('explicit_idl_action', 0)
827                 for action in spec.get('actions', [])])
828
829   def HasExplicitIdlRulesOrActions(self, spec):
830     """Determine if there's an explicit rule or action for idl files. When
831     there isn't we need to generate implicit rules to build MIDL .idl files."""
832     return (self._HasExplicitRuleForExtension(spec, 'idl') or
833             self._HasExplicitIdlActions(spec))
834
835   def HasExplicitAsmRules(self, spec):
836     """Determine if there's an explicit rule for asm files. When there isn't we
837     need to generate implicit rules to assemble .asm files."""
838     return self._HasExplicitRuleForExtension(spec, 'asm')
839
840   def GetIdlBuildData(self, source, config):
841     """Determine the implicit outputs for an idl file. Returns output
842     directory, outputs, and variables and flags that are required."""
843     config = self._TargetConfig(config)
844     midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
845     def midl(name, default=None):
846       return self.ConvertVSMacros(midl_get(name, default=default),
847                                   config=config)
848     tlb = midl('TypeLibraryName', default='${root}.tlb')
849     header = midl('HeaderFileName', default='${root}.h')
850     dlldata = midl('DLLDataFileName', default='dlldata.c')
851     iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
852     proxy = midl('ProxyFileName', default='${root}_p.c')
853     # Note that .tlb is not included in the outputs as it is not always
854     # generated depending on the content of the input idl file.
855     outdir = midl('OutputDirectory', default='')
856     output = [header, dlldata, iid, proxy]
857     variables = [('tlb', tlb),
858                  ('h', header),
859                  ('dlldata', dlldata),
860                  ('iid', iid),
861                  ('proxy', proxy)]
862     # TODO(scottmg): Are there configuration settings to set these flags?
863     target_platform = 'win32' if self.GetArch(config) == 'x86' else 'x64'
864     flags = ['/char', 'signed', '/env', target_platform, '/Oicf']
865     return outdir, output, variables, flags
866
867
868 def _LanguageMatchesForPch(source_ext, pch_source_ext):
869   c_exts = ('.c',)
870   cc_exts = ('.cc', '.cxx', '.cpp')
871   return ((source_ext in c_exts and pch_source_ext in c_exts) or
872           (source_ext in cc_exts and pch_source_ext in cc_exts))
873
874
875 class PrecompiledHeader(object):
876   """Helper to generate dependencies and build rules to handle generation of
877   precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
878   """
879   def __init__(
880       self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext):
881     self.settings = settings
882     self.config = config
883     pch_source = self.settings.msvs_precompiled_source[self.config]
884     self.pch_source = gyp_to_build_path(pch_source)
885     filename, _ = os.path.splitext(pch_source)
886     self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
887
888   def _PchHeader(self):
889     """Get the header that will appear in an #include line for all source
890     files."""
891     return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
892
893   def GetObjDependencies(self, sources, objs, arch):
894     """Given a list of sources files and the corresponding object files,
895     returns a list of the pch files that should be depended upon. The
896     additional wrapping in the return value is for interface compatibility
897     with make.py on Mac, and xcode_emulation.py."""
898     assert arch is None
899     if not self._PchHeader():
900       return []
901     pch_ext = os.path.splitext(self.pch_source)[1]
902     for source in sources:
903       if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
904         return [(None, None, self.output_obj)]
905     return []
906
907   def GetPchBuildCommands(self, arch):
908     """Not used on Windows as there are no additional build steps required
909     (instead, existing steps are modified in GetFlagsModifications below)."""
910     return []
911
912   def GetFlagsModifications(self, input, output, implicit, command,
913                             cflags_c, cflags_cc, expand_special):
914     """Get the modified cflags and implicit dependencies that should be used
915     for the pch compilation step."""
916     if input == self.pch_source:
917       pch_output = ['/Yc' + self._PchHeader()]
918       if command == 'cxx':
919         return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
920                 self.output_obj, [])
921       elif command == 'cc':
922         return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
923                 self.output_obj, [])
924     return [], output, implicit
925
926
927 vs_version = None
928 def GetVSVersion(generator_flags):
929   global vs_version
930   if not vs_version:
931     vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
932         generator_flags.get('msvs_version', 'auto'),
933         allow_fallback=False)
934   return vs_version
935
936 def _GetVsvarsSetupArgs(generator_flags, arch):
937   vs = GetVSVersion(generator_flags)
938   return vs.SetupScript()
939
940 def ExpandMacros(string, expansions):
941   """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
942   for the canonical way to retrieve a suitable dict."""
943   if '$' in string:
944     for old, new in expansions.iteritems():
945       assert '$(' not in new, new
946       string = string.replace(old, new)
947   return string
948
949 def _ExtractImportantEnvironment(output_of_set):
950   """Extracts environment variables required for the toolchain to run from
951   a textual dump output by the cmd.exe 'set' command."""
952   envvars_to_save = (
953       'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
954       'include',
955       'lib',
956       'libpath',
957       'path',
958       'pathext',
959       'systemroot',
960       'temp',
961       'tmp',
962       )
963   env = {}
964   for line in output_of_set.splitlines():
965     for envvar in envvars_to_save:
966       if re.match(envvar + '=', line.lower()):
967         var, setting = line.split('=', 1)
968         if envvar == 'path':
969           # Our own rules (for running gyp-win-tool) and other actions in
970           # Chromium rely on python being in the path. Add the path to this
971           # python here so that if it's not in the path when ninja is run
972           # later, python will still be found.
973           setting = os.path.dirname(sys.executable) + os.pathsep + setting
974         env[var.upper()] = setting
975         break
976   for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
977     if required not in env:
978       raise Exception('Environment variable "%s" '
979                       'required to be set to valid path' % required)
980   return env
981
982 def _FormatAsEnvironmentBlock(envvar_dict):
983   """Format as an 'environment block' directly suitable for CreateProcess.
984   Briefly this is a list of key=value\0, terminated by an additional \0. See
985   CreateProcess documentation for more details."""
986   block = ''
987   nul = '\0'
988   for key, value in envvar_dict.iteritems():
989     block += key + '=' + value + nul
990   block += nul
991   return block
992
993 def _ExtractCLPath(output_of_where):
994   """Gets the path to cl.exe based on the output of calling the environment
995   setup batch file, followed by the equivalent of `where`."""
996   # Take the first line, as that's the first found in the PATH.
997   for line in output_of_where.strip().splitlines():
998     if line.startswith('LOC:'):
999       return line[len('LOC:'):].strip()
1000
1001 def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags,
1002                              system_includes, open_out):
1003   """It's not sufficient to have the absolute path to the compiler, linker,
1004   etc. on Windows, as those tools rely on .dlls being in the PATH. We also
1005   need to support both x86 and x64 compilers within the same build (to support
1006   msvs_target_platform hackery). Different architectures require a different
1007   compiler binary, and different supporting environment variables (INCLUDE,
1008   LIB, LIBPATH). So, we extract the environment here, wrap all invocations
1009   of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
1010   sets up the environment, and then we do not prefix the compiler with
1011   an absolute path, instead preferring something like "cl.exe" in the rule
1012   which will then run whichever the environment setup has put in the path.
1013   When the following procedure to generate environment files does not
1014   meet your requirement (e.g. for custom toolchains), you can pass
1015   "-G ninja_use_custom_environment_files" to the gyp to suppress file
1016   generation and use custom environment files prepared by yourself."""
1017   archs = ('x86', 'x64')
1018   if generator_flags.get('ninja_use_custom_environment_files', 0):
1019     cl_paths = {}
1020     for arch in archs:
1021       cl_paths[arch] = 'cl.exe'
1022     return cl_paths
1023   vs = GetVSVersion(generator_flags)
1024   cl_paths = {}
1025   for arch in archs:
1026     # Extract environment variables for subprocesses.
1027     args = vs.SetupScript(arch)
1028     args.extend(('&&', 'set'))
1029     popen = subprocess.Popen(
1030         args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1031     variables, _ = popen.communicate()
1032     env = _ExtractImportantEnvironment(variables)
1033
1034     # Inject system includes from gyp files into INCLUDE.
1035     if system_includes:
1036       system_includes = system_includes | OrderedSet(
1037                                               env.get('INCLUDE', '').split(';'))
1038       env['INCLUDE'] = ';'.join(system_includes)
1039
1040     env_block = _FormatAsEnvironmentBlock(env)
1041     f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
1042     f.write(env_block)
1043     f.close()
1044
1045     # Find cl.exe location for this architecture.
1046     args = vs.SetupScript(arch)
1047     args.extend(('&&',
1048       'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
1049     popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
1050     output, _ = popen.communicate()
1051     cl_paths[arch] = _ExtractCLPath(output)
1052   return cl_paths
1053
1054 def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja):
1055   """Emulate behavior of msvs_error_on_missing_sources present in the msvs
1056   generator: Check that all regular source files, i.e. not created at run time,
1057   exist on disk. Missing files cause needless recompilation when building via
1058   VS, and we want this check to match for people/bots that build using ninja,
1059   so they're not surprised when the VS build fails."""
1060   if int(generator_flags.get('msvs_error_on_missing_sources', 0)):
1061     no_specials = filter(lambda x: '$' not in x, sources)
1062     relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials]
1063     missing = filter(lambda x: not os.path.exists(x), relative)
1064     if missing:
1065       # They'll look like out\Release\..\..\stuff\things.cc, so normalize the
1066       # path for a slightly less crazy looking output.
1067       cleaned_up = [os.path.normpath(x) for x in missing]
1068       raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up))
1069
1070 # Sets some values in default_variables, which are required for many
1071 # generators, run on Windows.
1072 def CalculateCommonVariables(default_variables, params):
1073   generator_flags = params.get('generator_flags', {})
1074
1075   # Set a variable so conditions can be based on msvs_version.
1076   msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
1077   default_variables['MSVS_VERSION'] = msvs_version.ShortName()
1078
1079   # To determine processor word size on Windows, in addition to checking
1080   # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
1081   # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
1082   # contains the actual word size of the system when running thru WOW64).
1083   if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
1084       '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
1085     default_variables['MSVS_OS_BITS'] = 64
1086   else:
1087     default_variables['MSVS_OS_BITS'] = 32