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