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