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