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