"Fossies" - the Fresh Open Source Software Archive

Member "node-v12.18.4-win-x64/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py" (22 Jul 2020, 49334 Bytes) of package /windows/www/node-v12.18.4-win-x64.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    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 PY3 = bytes != str
   20 
   21 windows_quoter_regex = re.compile(r'(\\*)"')
   22 
   23 
   24 def 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 
   54 def 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 
   70 def _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 
   80 def _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 
   91 def _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 
  104 def _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 
  117 def _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 
  142 def 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 
  162 def 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 
  190 class 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 
  881 def _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 
  888 class 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 
  940 vs_version = None
  941 def 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 
  949 def _GetVsvarsSetupArgs(generator_flags, arch):
  950   vs = GetVSVersion(generator_flags)
  951   return vs.SetupScript()
  952 
  953 def 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 
  962 def _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 
  995 def _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 
 1006 def _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 
 1014 def 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 
 1071 def 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.
 1089 def 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