test: terminate gracefully in cluster-net-send
[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, build_dir):
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('ForceSymbolReferences', prefix='/INCLUDE:')
506     ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
507     ld('LinkTimeCodeGeneration',
508         map={'1': '', '2': ':PGINSTRUMENT', '3': ':PGOPTIMIZE',
509              '4': ':PGUPDATE'},
510         prefix='/LTCG')
511     ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
512     ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
513     ld('EntryPointSymbol', prefix='/ENTRY:')
514     ld('Profile', map={'true': '/PROFILE'})
515     ld('LargeAddressAware',
516         map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
517     # TODO(scottmg): This should sort of be somewhere else (not really a flag).
518     ld('AdditionalDependencies', prefix='')
519
520     # If the base address is not specifically controlled, DYNAMICBASE should
521     # be on by default.
522     base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
523                         ldflags)
524     if not base_flags:
525       ldflags.append('/DYNAMICBASE')
526
527     # If the NXCOMPAT flag has not been specified, default to on. Despite the
528     # documentation that says this only defaults to on when the subsystem is
529     # Vista or greater (which applies to the linker), the IDE defaults it on
530     # unless it's explicitly off.
531     if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
532       ldflags.append('/NXCOMPAT')
533
534     have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
535     manifest_flags, intermediate_manifest, manifest_files = \
536         self._GetLdManifestFlags(config, manifest_base_name, gyp_to_build_path,
537                                  is_executable and not have_def_file, build_dir)
538     ldflags.extend(manifest_flags)
539     return ldflags, intermediate_manifest, manifest_files
540
541   def _GetLdManifestFlags(self, config, name, gyp_to_build_path,
542                           allow_isolation, build_dir):
543     """Returns a 3-tuple:
544     - the set of flags that need to be added to the link to generate
545       a default manifest
546     - the intermediate manifest that the linker will generate that should be
547       used to assert it doesn't add anything to the merged one.
548     - the list of all the manifest files to be merged by the manifest tool and
549       included into the link."""
550     generate_manifest = self._Setting(('VCLinkerTool', 'GenerateManifest'),
551                                       config,
552                                       default='true')
553     if generate_manifest != 'true':
554       # This means not only that the linker should not generate the intermediate
555       # manifest but also that the manifest tool should do nothing even when
556       # additional manifests are specified.
557       return ['/MANIFEST:NO'], [], []
558
559     output_name = name + '.intermediate.manifest'
560     flags = [
561       '/MANIFEST',
562       '/ManifestFile:' + output_name,
563     ]
564
565     # Instead of using the MANIFESTUAC flags, we generate a .manifest to
566     # include into the list of manifests. This allows us to avoid the need to
567     # do two passes during linking. The /MANIFEST flag and /ManifestFile are
568     # still used, and the intermediate manifest is used to assert that the
569     # final manifest we get from merging all the additional manifest files
570     # (plus the one we generate here) isn't modified by merging the
571     # intermediate into it.
572
573     # Always NO, because we generate a manifest file that has what we want.
574     flags.append('/MANIFESTUAC:NO')
575
576     config = self._TargetConfig(config)
577     enable_uac = self._Setting(('VCLinkerTool', 'EnableUAC'), config,
578                                default='true')
579     manifest_files = []
580     generated_manifest_outer = \
581 "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" \
582 "<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>%s" \
583 "</assembly>"
584     if enable_uac == 'true':
585       execution_level = self._Setting(('VCLinkerTool', 'UACExecutionLevel'),
586                                       config, default='0')
587       execution_level_map = {
588         '0': 'asInvoker',
589         '1': 'highestAvailable',
590         '2': 'requireAdministrator'
591       }
592
593       ui_access = self._Setting(('VCLinkerTool', 'UACUIAccess'), config,
594                                 default='false')
595
596       inner = '''
597 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
598   <security>
599     <requestedPrivileges>
600       <requestedExecutionLevel level='%s' uiAccess='%s' />
601     </requestedPrivileges>
602   </security>
603 </trustInfo>''' % (execution_level_map[execution_level], ui_access)
604     else:
605       inner = ''
606
607     generated_manifest_contents = generated_manifest_outer % inner
608     generated_name = name + '.generated.manifest'
609     # Need to join with the build_dir here as we're writing it during
610     # generation time, but we return the un-joined version because the build
611     # will occur in that directory. We only write the file if the contents
612     # have changed so that simply regenerating the project files doesn't
613     # cause a relink.
614     build_dir_generated_name = os.path.join(build_dir, generated_name)
615     gyp.common.EnsureDirExists(build_dir_generated_name)
616     f = gyp.common.WriteOnDiff(build_dir_generated_name)
617     f.write(generated_manifest_contents)
618     f.close()
619     manifest_files = [generated_name]
620
621     if allow_isolation:
622       flags.append('/ALLOWISOLATION')
623
624     manifest_files += self._GetAdditionalManifestFiles(config,
625                                                        gyp_to_build_path)
626     return flags, output_name, manifest_files
627
628   def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
629     """Gets additional manifest files that are added to the default one
630     generated by the linker."""
631     files = self._Setting(('VCManifestTool', 'AdditionalManifestFiles'), config,
632                           default=[])
633     if isinstance(files, str):
634       files = files.split(';')
635     return [os.path.normpath(
636                 gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
637             for f in files]
638
639   def IsUseLibraryDependencyInputs(self, config):
640     """Returns whether the target should be linked via Use Library Dependency
641     Inputs (using component .objs of a given .lib)."""
642     config = self._TargetConfig(config)
643     uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
644     return uldi == 'true'
645
646   def IsEmbedManifest(self, config):
647     """Returns whether manifest should be linked into binary."""
648     config = self._TargetConfig(config)
649     embed = self._Setting(('VCManifestTool', 'EmbedManifest'), config,
650                           default='true')
651     return embed == 'true'
652
653   def IsLinkIncremental(self, config):
654     """Returns whether the target should be linked incrementally."""
655     config = self._TargetConfig(config)
656     link_inc = self._Setting(('VCLinkerTool', 'LinkIncremental'), config)
657     return link_inc != '1'
658
659   def GetRcflags(self, config, gyp_to_ninja_path):
660     """Returns the flags that need to be added to invocations of the resource
661     compiler."""
662     config = self._TargetConfig(config)
663     rcflags = []
664     rc = self._GetWrapper(self, self.msvs_settings[config],
665         'VCResourceCompilerTool', append=rcflags)
666     rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
667     rcflags.append('/I' + gyp_to_ninja_path('.'))
668     rc('PreprocessorDefinitions', prefix='/d')
669     # /l arg must be in hex without leading '0x'
670     rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
671     return rcflags
672
673   def BuildCygwinBashCommandLine(self, args, path_to_base):
674     """Build a command line that runs args via cygwin bash. We assume that all
675     incoming paths are in Windows normpath'd form, so they need to be
676     converted to posix style for the part of the command line that's passed to
677     bash. We also have to do some Visual Studio macro emulation here because
678     various rules use magic VS names for things. Also note that rules that
679     contain ninja variables cannot be fixed here (for example ${source}), so
680     the outer generator needs to make sure that the paths that are written out
681     are in posix style, if the command line will be used here."""
682     cygwin_dir = os.path.normpath(
683         os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
684     cd = ('cd %s' % path_to_base).replace('\\', '/')
685     args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
686     args = ["'%s'" % a.replace("'", "'\\''") for a in args]
687     bash_cmd = ' '.join(args)
688     cmd = (
689         'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
690         'bash -c "%s ; %s"' % (cd, bash_cmd))
691     return cmd
692
693   def IsRuleRunUnderCygwin(self, rule):
694     """Determine if an action should be run under cygwin. If the variable is
695     unset, or set to 1 we use cygwin."""
696     return int(rule.get('msvs_cygwin_shell',
697                         self.spec.get('msvs_cygwin_shell', 1))) != 0
698
699   def _HasExplicitRuleForExtension(self, spec, extension):
700     """Determine if there's an explicit rule for a particular extension."""
701     for rule in spec.get('rules', []):
702       if rule['extension'] == extension:
703         return True
704     return False
705
706   def HasExplicitIdlRules(self, spec):
707     """Determine if there's an explicit rule for idl files. When there isn't we
708     need to generate implicit rules to build MIDL .idl files."""
709     return self._HasExplicitRuleForExtension(spec, 'idl')
710
711   def HasExplicitAsmRules(self, spec):
712     """Determine if there's an explicit rule for asm files. When there isn't we
713     need to generate implicit rules to assemble .asm files."""
714     return self._HasExplicitRuleForExtension(spec, 'asm')
715
716   def GetIdlBuildData(self, source, config):
717     """Determine the implicit outputs for an idl file. Returns output
718     directory, outputs, and variables and flags that are required."""
719     config = self._TargetConfig(config)
720     midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
721     def midl(name, default=None):
722       return self.ConvertVSMacros(midl_get(name, default=default),
723                                   config=config)
724     tlb = midl('TypeLibraryName', default='${root}.tlb')
725     header = midl('HeaderFileName', default='${root}.h')
726     dlldata = midl('DLLDataFileName', default='dlldata.c')
727     iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
728     proxy = midl('ProxyFileName', default='${root}_p.c')
729     # Note that .tlb is not included in the outputs as it is not always
730     # generated depending on the content of the input idl file.
731     outdir = midl('OutputDirectory', default='')
732     output = [header, dlldata, iid, proxy]
733     variables = [('tlb', tlb),
734                  ('h', header),
735                  ('dlldata', dlldata),
736                  ('iid', iid),
737                  ('proxy', proxy)]
738     # TODO(scottmg): Are there configuration settings to set these flags?
739     target_platform = 'win32' if self.GetArch(config) == 'x86' else 'x64'
740     flags = ['/char', 'signed', '/env', target_platform, '/Oicf']
741     return outdir, output, variables, flags
742
743
744 def _LanguageMatchesForPch(source_ext, pch_source_ext):
745   c_exts = ('.c',)
746   cc_exts = ('.cc', '.cxx', '.cpp')
747   return ((source_ext in c_exts and pch_source_ext in c_exts) or
748           (source_ext in cc_exts and pch_source_ext in cc_exts))
749
750
751 class PrecompiledHeader(object):
752   """Helper to generate dependencies and build rules to handle generation of
753   precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
754   """
755   def __init__(
756       self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext):
757     self.settings = settings
758     self.config = config
759     pch_source = self.settings.msvs_precompiled_source[self.config]
760     self.pch_source = gyp_to_build_path(pch_source)
761     filename, _ = os.path.splitext(pch_source)
762     self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
763
764   def _PchHeader(self):
765     """Get the header that will appear in an #include line for all source
766     files."""
767     return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
768
769   def GetObjDependencies(self, sources, objs, arch):
770     """Given a list of sources files and the corresponding object files,
771     returns a list of the pch files that should be depended upon. The
772     additional wrapping in the return value is for interface compatability
773     with make.py on Mac, and xcode_emulation.py."""
774     assert arch is None
775     if not self._PchHeader():
776       return []
777     pch_ext = os.path.splitext(self.pch_source)[1]
778     for source in sources:
779       if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
780         return [(None, None, self.output_obj)]
781     return []
782
783   def GetPchBuildCommands(self, arch):
784     """Not used on Windows as there are no additional build steps required
785     (instead, existing steps are modified in GetFlagsModifications below)."""
786     return []
787
788   def GetFlagsModifications(self, input, output, implicit, command,
789                             cflags_c, cflags_cc, expand_special):
790     """Get the modified cflags and implicit dependencies that should be used
791     for the pch compilation step."""
792     if input == self.pch_source:
793       pch_output = ['/Yc' + self._PchHeader()]
794       if command == 'cxx':
795         return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
796                 self.output_obj, [])
797       elif command == 'cc':
798         return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
799                 self.output_obj, [])
800     return [], output, implicit
801
802
803 vs_version = None
804 def GetVSVersion(generator_flags):
805   global vs_version
806   if not vs_version:
807     vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
808         generator_flags.get('msvs_version', 'auto'))
809   return vs_version
810
811 def _GetVsvarsSetupArgs(generator_flags, arch):
812   vs = GetVSVersion(generator_flags)
813   return vs.SetupScript()
814
815 def ExpandMacros(string, expansions):
816   """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
817   for the canonical way to retrieve a suitable dict."""
818   if '$' in string:
819     for old, new in expansions.iteritems():
820       assert '$(' not in new, new
821       string = string.replace(old, new)
822   return string
823
824 def _ExtractImportantEnvironment(output_of_set):
825   """Extracts environment variables required for the toolchain to run from
826   a textual dump output by the cmd.exe 'set' command."""
827   envvars_to_save = (
828       'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
829       'include',
830       'lib',
831       'libpath',
832       'path',
833       'pathext',
834       'systemroot',
835       'temp',
836       'tmp',
837       )
838   env = {}
839   for line in output_of_set.splitlines():
840     for envvar in envvars_to_save:
841       if re.match(envvar + '=', line.lower()):
842         var, setting = line.split('=', 1)
843         if envvar == 'path':
844           # Our own rules (for running gyp-win-tool) and other actions in
845           # Chromium rely on python being in the path. Add the path to this
846           # python here so that if it's not in the path when ninja is run
847           # later, python will still be found.
848           setting = os.path.dirname(sys.executable) + os.pathsep + setting
849         env[var.upper()] = setting
850         break
851   for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
852     if required not in env:
853       raise Exception('Environment variable "%s" '
854                       'required to be set to valid path' % required)
855   return env
856
857 def _FormatAsEnvironmentBlock(envvar_dict):
858   """Format as an 'environment block' directly suitable for CreateProcess.
859   Briefly this is a list of key=value\0, terminated by an additional \0. See
860   CreateProcess documentation for more details."""
861   block = ''
862   nul = '\0'
863   for key, value in envvar_dict.iteritems():
864     block += key + '=' + value + nul
865   block += nul
866   return block
867
868 def _ExtractCLPath(output_of_where):
869   """Gets the path to cl.exe based on the output of calling the environment
870   setup batch file, followed by the equivalent of `where`."""
871   # Take the first line, as that's the first found in the PATH.
872   for line in output_of_where.strip().splitlines():
873     if line.startswith('LOC:'):
874       return line[len('LOC:'):].strip()
875
876 def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
877   """It's not sufficient to have the absolute path to the compiler, linker,
878   etc. on Windows, as those tools rely on .dlls being in the PATH. We also
879   need to support both x86 and x64 compilers within the same build (to support
880   msvs_target_platform hackery). Different architectures require a different
881   compiler binary, and different supporting environment variables (INCLUDE,
882   LIB, LIBPATH). So, we extract the environment here, wrap all invocations
883   of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
884   sets up the environment, and then we do not prefix the compiler with
885   an absolute path, instead preferring something like "cl.exe" in the rule
886   which will then run whichever the environment setup has put in the path.
887   When the following procedure to generate environment files does not
888   meet your requirement (e.g. for custom toolchains), you can pass
889   "-G ninja_use_custom_environment_files" to the gyp to suppress file
890   generation and use custom environment files prepared by yourself."""
891   archs = ('x86', 'x64')
892   if generator_flags.get('ninja_use_custom_environment_files', 0):
893     cl_paths = {}
894     for arch in archs:
895       cl_paths[arch] = 'cl.exe'
896     return cl_paths
897   vs = GetVSVersion(generator_flags)
898   cl_paths = {}
899   for arch in archs:
900     # Extract environment variables for subprocesses.
901     args = vs.SetupScript(arch)
902     args.extend(('&&', 'set'))
903     popen = subprocess.Popen(
904         args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
905     variables, _ = popen.communicate()
906     env = _ExtractImportantEnvironment(variables)
907     env_block = _FormatAsEnvironmentBlock(env)
908     f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
909     f.write(env_block)
910     f.close()
911
912     # Find cl.exe location for this architecture.
913     args = vs.SetupScript(arch)
914     args.extend(('&&',
915       'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
916     popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
917     output, _ = popen.communicate()
918     cl_paths[arch] = _ExtractCLPath(output)
919   return cl_paths
920
921 def VerifyMissingSources(sources, build_dir, generator_flags, gyp_to_ninja):
922   """Emulate behavior of msvs_error_on_missing_sources present in the msvs
923   generator: Check that all regular source files, i.e. not created at run time,
924   exist on disk. Missing files cause needless recompilation when building via
925   VS, and we want this check to match for people/bots that build using ninja,
926   so they're not surprised when the VS build fails."""
927   if int(generator_flags.get('msvs_error_on_missing_sources', 0)):
928     no_specials = filter(lambda x: '$' not in x, sources)
929     relative = [os.path.join(build_dir, gyp_to_ninja(s)) for s in no_specials]
930     missing = filter(lambda x: not os.path.exists(x), relative)
931     if missing:
932       # They'll look like out\Release\..\..\stuff\things.cc, so normalize the
933       # path for a slightly less crazy looking output.
934       cleaned_up = [os.path.normpath(x) for x in missing]
935       raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up))
936
937 # Sets some values in default_variables, which are required for many
938 # generators, run on Windows.
939 def CalculateCommonVariables(default_variables, params):
940   generator_flags = params.get('generator_flags', {})
941
942   # Set a variable so conditions can be based on msvs_version.
943   msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
944   default_variables['MSVS_VERSION'] = msvs_version.ShortName()
945
946   # To determine processor word size on Windows, in addition to checking
947   # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
948   # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
949   # contains the actual word size of the system when running thru WOW64).
950   if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
951       '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
952     default_variables['MSVS_OS_BITS'] = 64
953   else:
954     default_variables['MSVS_OS_BITS'] = 32