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