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