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