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