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 contains classes that help to emulate xcodebuild behavior on top of 7other build systems, such as make and ninja. 8""" 9 10import copy 11import gyp.common 12import os 13import os.path 14import re 15import shlex 16import subprocess 17import sys 18import tempfile 19from gyp.common import GypError 20 21class XcodeSettings(object): 22 """A class that understands the gyp 'xcode_settings' object.""" 23 24 # Populated lazily by _SdkPath(). Shared by all XcodeSettings, so cached 25 # at class-level for efficiency. 26 _sdk_path_cache = {} 27 _sdk_root_cache = {} 28 29 # Populated lazily by GetExtraPlistItems(). Shared by all XcodeSettings, so 30 # cached at class-level for efficiency. 31 _plist_cache = {} 32 33 # Populated lazily by GetIOSPostbuilds. Shared by all XcodeSettings, so 34 # cached at class-level for efficiency. 35 _codesigning_key_cache = {} 36 37 # Populated lazily by _XcodeVersion. Shared by all XcodeSettings, so cached 38 # at class-level for efficiency. 39 _xcode_version_cache = () 40 41 def __init__(self, spec): 42 self.spec = spec 43 44 self.isIOS = False 45 46 # Per-target 'xcode_settings' are pushed down into configs earlier by gyp. 47 # This means self.xcode_settings[config] always contains all settings 48 # for that config -- the per-target settings as well. Settings that are 49 # the same for all configs are implicitly per-target settings. 50 self.xcode_settings = {} 51 configs = spec['configurations'] 52 for configname, config in configs.iteritems(): 53 self.xcode_settings[configname] = config.get('xcode_settings', {}) 54 self._ConvertConditionalKeys(configname) 55 if self.xcode_settings[configname].get('IPHONEOS_DEPLOYMENT_TARGET', 56 None): 57 self.isIOS = True 58 59 # This is only non-None temporarily during the execution of some methods. 60 self.configname = None 61 62 # Used by _AdjustLibrary to match .a and .dylib entries in libraries. 63 self.library_re = re.compile(r'^lib([^/]+)\.(a|dylib)$') 64 65 def _ConvertConditionalKeys(self, configname): 66 """Converts or warns on conditional keys. Xcode supports conditional keys, 67 such as CODE_SIGN_IDENTITY[sdk=iphoneos*]. This is a partial implementation 68 with some keys converted while the rest force a warning.""" 69 settings = self.xcode_settings[configname] 70 conditional_keys = [key for key in settings if key.endswith(']')] 71 for key in conditional_keys: 72 # If you need more, speak up at http://crbug.com/122592 73 if key.endswith("[sdk=iphoneos*]"): 74 if configname.endswith("iphoneos"): 75 new_key = key.split("[")[0] 76 settings[new_key] = settings[key] 77 else: 78 print 'Warning: Conditional keys not implemented, ignoring:', \ 79 ' '.join(conditional_keys) 80 del settings[key] 81 82 def _Settings(self): 83 assert self.configname 84 return self.xcode_settings[self.configname] 85 86 def _Test(self, test_key, cond_key, default): 87 return self._Settings().get(test_key, default) == cond_key 88 89 def _Appendf(self, lst, test_key, format_str, default=None): 90 if test_key in self._Settings(): 91 lst.append(format_str % str(self._Settings()[test_key])) 92 elif default: 93 lst.append(format_str % str(default)) 94 95 def _WarnUnimplemented(self, test_key): 96 if test_key in self._Settings(): 97 print 'Warning: Ignoring not yet implemented key "%s".' % test_key 98 99 def _IsBundle(self): 100 return int(self.spec.get('mac_bundle', 0)) != 0 101 102 def GetFrameworkVersion(self): 103 """Returns the framework version of the current target. Only valid for 104 bundles.""" 105 assert self._IsBundle() 106 return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A') 107 108 def GetWrapperExtension(self): 109 """Returns the bundle extension (.app, .framework, .plugin, etc). Only 110 valid for bundles.""" 111 assert self._IsBundle() 112 if self.spec['type'] in ('loadable_module', 'shared_library'): 113 default_wrapper_extension = { 114 'loadable_module': 'bundle', 115 'shared_library': 'framework', 116 }[self.spec['type']] 117 wrapper_extension = self.GetPerTargetSetting( 118 'WRAPPER_EXTENSION', default=default_wrapper_extension) 119 return '.' + self.spec.get('product_extension', wrapper_extension) 120 elif self.spec['type'] == 'executable': 121 return '.' + self.spec.get('product_extension', 'app') 122 else: 123 assert False, "Don't know extension for '%s', target '%s'" % ( 124 self.spec['type'], self.spec['target_name']) 125 126 def GetProductName(self): 127 """Returns PRODUCT_NAME.""" 128 return self.spec.get('product_name', self.spec['target_name']) 129 130 def GetFullProductName(self): 131 """Returns FULL_PRODUCT_NAME.""" 132 if self._IsBundle(): 133 return self.GetWrapperName() 134 else: 135 return self._GetStandaloneBinaryPath() 136 137 def GetWrapperName(self): 138 """Returns the directory name of the bundle represented by this target. 139 Only valid for bundles.""" 140 assert self._IsBundle() 141 return self.GetProductName() + self.GetWrapperExtension() 142 143 def GetBundleContentsFolderPath(self): 144 """Returns the qualified path to the bundle's contents folder. E.g. 145 Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles.""" 146 if self.isIOS: 147 return self.GetWrapperName() 148 assert self._IsBundle() 149 if self.spec['type'] == 'shared_library': 150 return os.path.join( 151 self.GetWrapperName(), 'Versions', self.GetFrameworkVersion()) 152 else: 153 # loadable_modules have a 'Contents' folder like executables. 154 return os.path.join(self.GetWrapperName(), 'Contents') 155 156 def GetBundleResourceFolder(self): 157 """Returns the qualified path to the bundle's resource folder. E.g. 158 Chromium.app/Contents/Resources. Only valid for bundles.""" 159 assert self._IsBundle() 160 if self.isIOS: 161 return self.GetBundleContentsFolderPath() 162 return os.path.join(self.GetBundleContentsFolderPath(), 'Resources') 163 164 def GetBundlePlistPath(self): 165 """Returns the qualified path to the bundle's plist file. E.g. 166 Chromium.app/Contents/Info.plist. Only valid for bundles.""" 167 assert self._IsBundle() 168 if self.spec['type'] in ('executable', 'loadable_module'): 169 return os.path.join(self.GetBundleContentsFolderPath(), 'Info.plist') 170 else: 171 return os.path.join(self.GetBundleContentsFolderPath(), 172 'Resources', 'Info.plist') 173 174 def GetProductType(self): 175 """Returns the PRODUCT_TYPE of this target.""" 176 if self._IsBundle(): 177 return { 178 'executable': 'com.apple.product-type.application', 179 'loadable_module': 'com.apple.product-type.bundle', 180 'shared_library': 'com.apple.product-type.framework', 181 }[self.spec['type']] 182 else: 183 return { 184 'executable': 'com.apple.product-type.tool', 185 'loadable_module': 'com.apple.product-type.library.dynamic', 186 'shared_library': 'com.apple.product-type.library.dynamic', 187 'static_library': 'com.apple.product-type.library.static', 188 }[self.spec['type']] 189 190 def GetMachOType(self): 191 """Returns the MACH_O_TYPE of this target.""" 192 # Weird, but matches Xcode. 193 if not self._IsBundle() and self.spec['type'] == 'executable': 194 return '' 195 return { 196 'executable': 'mh_execute', 197 'static_library': 'staticlib', 198 'shared_library': 'mh_dylib', 199 'loadable_module': 'mh_bundle', 200 }[self.spec['type']] 201 202 def _GetBundleBinaryPath(self): 203 """Returns the name of the bundle binary of by this target. 204 E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles.""" 205 assert self._IsBundle() 206 if self.spec['type'] in ('shared_library') or self.isIOS: 207 path = self.GetBundleContentsFolderPath() 208 elif self.spec['type'] in ('executable', 'loadable_module'): 209 path = os.path.join(self.GetBundleContentsFolderPath(), 'MacOS') 210 return os.path.join(path, self.GetExecutableName()) 211 212 def _GetStandaloneExecutableSuffix(self): 213 if 'product_extension' in self.spec: 214 return '.' + self.spec['product_extension'] 215 return { 216 'executable': '', 217 'static_library': '.a', 218 'shared_library': '.dylib', 219 'loadable_module': '.so', 220 }[self.spec['type']] 221 222 def _GetStandaloneExecutablePrefix(self): 223 return self.spec.get('product_prefix', { 224 'executable': '', 225 'static_library': 'lib', 226 'shared_library': 'lib', 227 # Non-bundled loadable_modules are called foo.so for some reason 228 # (that is, .so and no prefix) with the xcode build -- match that. 229 'loadable_module': '', 230 }[self.spec['type']]) 231 232 def _GetStandaloneBinaryPath(self): 233 """Returns the name of the non-bundle binary represented by this target. 234 E.g. hello_world. Only valid for non-bundles.""" 235 assert not self._IsBundle() 236 assert self.spec['type'] in ( 237 'executable', 'shared_library', 'static_library', 'loadable_module'), ( 238 'Unexpected type %s' % self.spec['type']) 239 target = self.spec['target_name'] 240 if self.spec['type'] == 'static_library': 241 if target[:3] == 'lib': 242 target = target[3:] 243 elif self.spec['type'] in ('loadable_module', 'shared_library'): 244 if target[:3] == 'lib': 245 target = target[3:] 246 247 target_prefix = self._GetStandaloneExecutablePrefix() 248 target = self.spec.get('product_name', target) 249 target_ext = self._GetStandaloneExecutableSuffix() 250 return target_prefix + target + target_ext 251 252 def GetExecutableName(self): 253 """Returns the executable name of the bundle represented by this target. 254 E.g. Chromium.""" 255 if self._IsBundle(): 256 return self.spec.get('product_name', self.spec['target_name']) 257 else: 258 return self._GetStandaloneBinaryPath() 259 260 def GetExecutablePath(self): 261 """Returns the directory name of the bundle represented by this target. E.g. 262 Chromium.app/Contents/MacOS/Chromium.""" 263 if self._IsBundle(): 264 return self._GetBundleBinaryPath() 265 else: 266 return self._GetStandaloneBinaryPath() 267 268 def GetActiveArchs(self, configname): 269 """Returns the architectures this target should be built for.""" 270 # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set 271 # CURRENT_ARCH / NATIVE_ARCH env vars? 272 return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()]) 273 274 def _GetStdout(self, cmdlist): 275 job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE) 276 out = job.communicate()[0] 277 if job.returncode != 0: 278 sys.stderr.write(out + '\n') 279 raise GypError('Error %d running %s' % (job.returncode, cmdlist[0])) 280 return out.rstrip('\n') 281 282 def _GetSdkVersionInfoItem(self, sdk, infoitem): 283 return self._GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem]) 284 285 def _SdkRoot(self, configname): 286 if configname is None: 287 configname = self.configname 288 return self.GetPerConfigSetting('SDKROOT', configname, default='') 289 290 def _SdkPath(self, configname=None): 291 sdk_root = self._SdkRoot(configname) 292 if sdk_root.startswith('/'): 293 return sdk_root 294 return self._XcodeSdkPath(sdk_root) 295 296 def _XcodeSdkPath(self, sdk_root): 297 if sdk_root not in XcodeSettings._sdk_path_cache: 298 sdk_path = self._GetSdkVersionInfoItem(sdk_root, 'Path') 299 XcodeSettings._sdk_path_cache[sdk_root] = sdk_path 300 if sdk_root: 301 XcodeSettings._sdk_root_cache[sdk_path] = sdk_root 302 return XcodeSettings._sdk_path_cache[sdk_root] 303 304 def _AppendPlatformVersionMinFlags(self, lst): 305 self._Appendf(lst, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s') 306 if 'IPHONEOS_DEPLOYMENT_TARGET' in self._Settings(): 307 # TODO: Implement this better? 308 sdk_path_basename = os.path.basename(self._SdkPath()) 309 if sdk_path_basename.lower().startswith('iphonesimulator'): 310 self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET', 311 '-mios-simulator-version-min=%s') 312 else: 313 self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET', 314 '-miphoneos-version-min=%s') 315 316 def GetCflags(self, configname, arch=None): 317 """Returns flags that need to be added to .c, .cc, .m, and .mm 318 compilations.""" 319 # This functions (and the similar ones below) do not offer complete 320 # emulation of all xcode_settings keys. They're implemented on demand. 321 322 self.configname = configname 323 cflags = [] 324 325 sdk_root = self._SdkPath() 326 if 'SDKROOT' in self._Settings(): 327 cflags.append('-isysroot %s' % sdk_root) 328 329 if self._Test('CLANG_WARN_CONSTANT_CONVERSION', 'YES', default='NO'): 330 cflags.append('-Wconstant-conversion') 331 332 if self._Test('GCC_CHAR_IS_UNSIGNED_CHAR', 'YES', default='NO'): 333 cflags.append('-funsigned-char') 334 335 if self._Test('GCC_CW_ASM_SYNTAX', 'YES', default='YES'): 336 cflags.append('-fasm-blocks') 337 338 if 'GCC_DYNAMIC_NO_PIC' in self._Settings(): 339 if self._Settings()['GCC_DYNAMIC_NO_PIC'] == 'YES': 340 cflags.append('-mdynamic-no-pic') 341 else: 342 pass 343 # TODO: In this case, it depends on the target. xcode passes 344 # mdynamic-no-pic by default for executable and possibly static lib 345 # according to mento 346 347 if self._Test('GCC_ENABLE_PASCAL_STRINGS', 'YES', default='YES'): 348 cflags.append('-mpascal-strings') 349 350 self._Appendf(cflags, 'GCC_OPTIMIZATION_LEVEL', '-O%s', default='s') 351 352 if self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES'): 353 dbg_format = self._Settings().get('DEBUG_INFORMATION_FORMAT', 'dwarf') 354 if dbg_format == 'dwarf': 355 cflags.append('-gdwarf-2') 356 elif dbg_format == 'stabs': 357 raise NotImplementedError('stabs debug format is not supported yet.') 358 elif dbg_format == 'dwarf-with-dsym': 359 cflags.append('-gdwarf-2') 360 else: 361 raise NotImplementedError('Unknown debug format %s' % dbg_format) 362 363 if self._Settings().get('GCC_STRICT_ALIASING') == 'YES': 364 cflags.append('-fstrict-aliasing') 365 elif self._Settings().get('GCC_STRICT_ALIASING') == 'NO': 366 cflags.append('-fno-strict-aliasing') 367 368 if self._Test('GCC_SYMBOLS_PRIVATE_EXTERN', 'YES', default='NO'): 369 cflags.append('-fvisibility=hidden') 370 371 if self._Test('GCC_TREAT_WARNINGS_AS_ERRORS', 'YES', default='NO'): 372 cflags.append('-Werror') 373 374 if self._Test('GCC_WARN_ABOUT_MISSING_NEWLINE', 'YES', default='NO'): 375 cflags.append('-Wnewline-eof') 376 377 self._AppendPlatformVersionMinFlags(cflags) 378 379 # TODO: 380 if self._Test('COPY_PHASE_STRIP', 'YES', default='NO'): 381 self._WarnUnimplemented('COPY_PHASE_STRIP') 382 self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS') 383 self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS') 384 385 # TODO: This is exported correctly, but assigning to it is not supported. 386 self._WarnUnimplemented('MACH_O_TYPE') 387 self._WarnUnimplemented('PRODUCT_TYPE') 388 389 if arch is not None: 390 archs = [arch] 391 else: 392 archs = self._Settings().get('ARCHS', [self._DefaultArch()]) 393 if len(archs) != 1: 394 # TODO: Supporting fat binaries will be annoying. 395 self._WarnUnimplemented('ARCHS') 396 archs = ['i386'] 397 cflags.append('-arch ' + archs[0]) 398 399 if archs[0] in ('i386', 'x86_64'): 400 if self._Test('GCC_ENABLE_SSE3_EXTENSIONS', 'YES', default='NO'): 401 cflags.append('-msse3') 402 if self._Test('GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS', 'YES', 403 default='NO'): 404 cflags.append('-mssse3') # Note 3rd 's'. 405 if self._Test('GCC_ENABLE_SSE41_EXTENSIONS', 'YES', default='NO'): 406 cflags.append('-msse4.1') 407 if self._Test('GCC_ENABLE_SSE42_EXTENSIONS', 'YES', default='NO'): 408 cflags.append('-msse4.2') 409 410 cflags += self._Settings().get('WARNING_CFLAGS', []) 411 412 config = self.spec['configurations'][self.configname] 413 framework_dirs = config.get('mac_framework_dirs', []) 414 for directory in framework_dirs: 415 cflags.append('-F' + directory.replace('$(SDKROOT)', sdk_root)) 416 417 self.configname = None 418 return cflags 419 420 def GetCflagsC(self, configname): 421 """Returns flags that need to be added to .c, and .m compilations.""" 422 self.configname = configname 423 cflags_c = [] 424 if self._Settings().get('GCC_C_LANGUAGE_STANDARD', '') == 'ansi': 425 cflags_c.append('-ansi') 426 else: 427 self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s') 428 cflags_c += self._Settings().get('OTHER_CFLAGS', []) 429 self.configname = None 430 return cflags_c 431 432 def GetCflagsCC(self, configname): 433 """Returns flags that need to be added to .cc, and .mm compilations.""" 434 self.configname = configname 435 cflags_cc = [] 436 437 clang_cxx_language_standard = self._Settings().get( 438 'CLANG_CXX_LANGUAGE_STANDARD') 439 # Note: Don't make c++0x to c++11 so that c++0x can be used with older 440 # clangs that don't understand c++11 yet (like Xcode 4.2's). 441 if clang_cxx_language_standard: 442 cflags_cc.append('-std=%s' % clang_cxx_language_standard) 443 444 self._Appendf(cflags_cc, 'CLANG_CXX_LIBRARY', '-stdlib=%s') 445 446 if self._Test('GCC_ENABLE_CPP_RTTI', 'NO', default='YES'): 447 cflags_cc.append('-fno-rtti') 448 if self._Test('GCC_ENABLE_CPP_EXCEPTIONS', 'NO', default='YES'): 449 cflags_cc.append('-fno-exceptions') 450 if self._Test('GCC_INLINES_ARE_PRIVATE_EXTERN', 'YES', default='NO'): 451 cflags_cc.append('-fvisibility-inlines-hidden') 452 if self._Test('GCC_THREADSAFE_STATICS', 'NO', default='YES'): 453 cflags_cc.append('-fno-threadsafe-statics') 454 # Note: This flag is a no-op for clang, it only has an effect for gcc. 455 if self._Test('GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO', 'NO', default='YES'): 456 cflags_cc.append('-Wno-invalid-offsetof') 457 458 other_ccflags = [] 459 460 for flag in self._Settings().get('OTHER_CPLUSPLUSFLAGS', ['$(inherited)']): 461 # TODO: More general variable expansion. Missing in many other places too. 462 if flag in ('$inherited', '$(inherited)', '${inherited}'): 463 flag = '$OTHER_CFLAGS' 464 if flag in ('$OTHER_CFLAGS', '$(OTHER_CFLAGS)', '${OTHER_CFLAGS}'): 465 other_ccflags += self._Settings().get('OTHER_CFLAGS', []) 466 else: 467 other_ccflags.append(flag) 468 cflags_cc += other_ccflags 469 470 self.configname = None 471 return cflags_cc 472 473 def _AddObjectiveCGarbageCollectionFlags(self, flags): 474 gc_policy = self._Settings().get('GCC_ENABLE_OBJC_GC', 'unsupported') 475 if gc_policy == 'supported': 476 flags.append('-fobjc-gc') 477 elif gc_policy == 'required': 478 flags.append('-fobjc-gc-only') 479 480 def _AddObjectiveCARCFlags(self, flags): 481 if self._Test('CLANG_ENABLE_OBJC_ARC', 'YES', default='NO'): 482 flags.append('-fobjc-arc') 483 484 def _AddObjectiveCMissingPropertySynthesisFlags(self, flags): 485 if self._Test('CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS', 486 'YES', default='NO'): 487 flags.append('-Wobjc-missing-property-synthesis') 488 489 def GetCflagsObjC(self, configname): 490 """Returns flags that need to be added to .m compilations.""" 491 self.configname = configname 492 cflags_objc = [] 493 self._AddObjectiveCGarbageCollectionFlags(cflags_objc) 494 self._AddObjectiveCARCFlags(cflags_objc) 495 self._AddObjectiveCMissingPropertySynthesisFlags(cflags_objc) 496 self.configname = None 497 return cflags_objc 498 499 def GetCflagsObjCC(self, configname): 500 """Returns flags that need to be added to .mm compilations.""" 501 self.configname = configname 502 cflags_objcc = [] 503 self._AddObjectiveCGarbageCollectionFlags(cflags_objcc) 504 self._AddObjectiveCARCFlags(cflags_objcc) 505 self._AddObjectiveCMissingPropertySynthesisFlags(cflags_objcc) 506 if self._Test('GCC_OBJC_CALL_CXX_CDTORS', 'YES', default='NO'): 507 cflags_objcc.append('-fobjc-call-cxx-cdtors') 508 self.configname = None 509 return cflags_objcc 510 511 def GetInstallNameBase(self): 512 """Return DYLIB_INSTALL_NAME_BASE for this target.""" 513 # Xcode sets this for shared_libraries, and for nonbundled loadable_modules. 514 if (self.spec['type'] != 'shared_library' and 515 (self.spec['type'] != 'loadable_module' or self._IsBundle())): 516 return None 517 install_base = self.GetPerTargetSetting( 518 'DYLIB_INSTALL_NAME_BASE', 519 default='/Library/Frameworks' if self._IsBundle() else '/usr/local/lib') 520 return install_base 521 522 def _StandardizePath(self, path): 523 """Do :standardizepath processing for path.""" 524 # I'm not quite sure what :standardizepath does. Just call normpath(), 525 # but don't let @executable_path/../foo collapse to foo. 526 if '/' in path: 527 prefix, rest = '', path 528 if path.startswith('@'): 529 prefix, rest = path.split('/', 1) 530 rest = os.path.normpath(rest) # :standardizepath 531 path = os.path.join(prefix, rest) 532 return path 533 534 def GetInstallName(self): 535 """Return LD_DYLIB_INSTALL_NAME for this target.""" 536 # Xcode sets this for shared_libraries, and for nonbundled loadable_modules. 537 if (self.spec['type'] != 'shared_library' and 538 (self.spec['type'] != 'loadable_module' or self._IsBundle())): 539 return None 540 541 default_install_name = \ 542 '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)' 543 install_name = self.GetPerTargetSetting( 544 'LD_DYLIB_INSTALL_NAME', default=default_install_name) 545 546 # Hardcode support for the variables used in chromium for now, to 547 # unblock people using the make build. 548 if '$' in install_name: 549 assert install_name in ('$(DYLIB_INSTALL_NAME_BASE:standardizepath)/' 550 '$(WRAPPER_NAME)/$(PRODUCT_NAME)', default_install_name), ( 551 'Variables in LD_DYLIB_INSTALL_NAME are not generally supported ' 552 'yet in target \'%s\' (got \'%s\')' % 553 (self.spec['target_name'], install_name)) 554 555 install_name = install_name.replace( 556 '$(DYLIB_INSTALL_NAME_BASE:standardizepath)', 557 self._StandardizePath(self.GetInstallNameBase())) 558 if self._IsBundle(): 559 # These are only valid for bundles, hence the |if|. 560 install_name = install_name.replace( 561 '$(WRAPPER_NAME)', self.GetWrapperName()) 562 install_name = install_name.replace( 563 '$(PRODUCT_NAME)', self.GetProductName()) 564 else: 565 assert '$(WRAPPER_NAME)' not in install_name 566 assert '$(PRODUCT_NAME)' not in install_name 567 568 install_name = install_name.replace( 569 '$(EXECUTABLE_PATH)', self.GetExecutablePath()) 570 return install_name 571 572 def _MapLinkerFlagFilename(self, ldflag, gyp_to_build_path): 573 """Checks if ldflag contains a filename and if so remaps it from 574 gyp-directory-relative to build-directory-relative.""" 575 # This list is expanded on demand. 576 # They get matched as: 577 # -exported_symbols_list file 578 # -Wl,exported_symbols_list file 579 # -Wl,exported_symbols_list,file 580 LINKER_FILE = '(\S+)' 581 WORD = '\S+' 582 linker_flags = [ 583 ['-exported_symbols_list', LINKER_FILE], # Needed for NaCl. 584 ['-unexported_symbols_list', LINKER_FILE], 585 ['-reexported_symbols_list', LINKER_FILE], 586 ['-sectcreate', WORD, WORD, LINKER_FILE], # Needed for remoting. 587 ] 588 for flag_pattern in linker_flags: 589 regex = re.compile('(?:-Wl,)?' + '[ ,]'.join(flag_pattern)) 590 m = regex.match(ldflag) 591 if m: 592 ldflag = ldflag[:m.start(1)] + gyp_to_build_path(m.group(1)) + \ 593 ldflag[m.end(1):] 594 # Required for ffmpeg (no idea why they don't use LIBRARY_SEARCH_PATHS, 595 # TODO(thakis): Update ffmpeg.gyp): 596 if ldflag.startswith('-L'): 597 ldflag = '-L' + gyp_to_build_path(ldflag[len('-L'):]) 598 return ldflag 599 600 def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None): 601 """Returns flags that need to be passed to the linker. 602 603 Args: 604 configname: The name of the configuration to get ld flags for. 605 product_dir: The directory where products such static and dynamic 606 libraries are placed. This is added to the library search path. 607 gyp_to_build_path: A function that converts paths relative to the 608 current gyp file to paths relative to the build direcotry. 609 """ 610 self.configname = configname 611 ldflags = [] 612 613 # The xcode build is relative to a gyp file's directory, and OTHER_LDFLAGS 614 # can contain entries that depend on this. Explicitly absolutify these. 615 for ldflag in self._Settings().get('OTHER_LDFLAGS', []): 616 ldflags.append(self._MapLinkerFlagFilename(ldflag, gyp_to_build_path)) 617 618 if self._Test('DEAD_CODE_STRIPPING', 'YES', default='NO'): 619 ldflags.append('-Wl,-dead_strip') 620 621 if self._Test('PREBINDING', 'YES', default='NO'): 622 ldflags.append('-Wl,-prebind') 623 624 self._Appendf( 625 ldflags, 'DYLIB_COMPATIBILITY_VERSION', '-compatibility_version %s') 626 self._Appendf( 627 ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s') 628 629 self._AppendPlatformVersionMinFlags(ldflags) 630 631 if 'SDKROOT' in self._Settings(): 632 ldflags.append('-isysroot ' + self._SdkPath()) 633 634 for library_path in self._Settings().get('LIBRARY_SEARCH_PATHS', []): 635 ldflags.append('-L' + gyp_to_build_path(library_path)) 636 637 if 'ORDER_FILE' in self._Settings(): 638 ldflags.append('-Wl,-order_file ' + 639 '-Wl,' + gyp_to_build_path( 640 self._Settings()['ORDER_FILE'])) 641 642 if arch is not None: 643 archs = [arch] 644 else: 645 archs = self._Settings().get('ARCHS', [self._DefaultArch()]) 646 if len(archs) != 1: 647 # TODO: Supporting fat binaries will be annoying. 648 self._WarnUnimplemented('ARCHS') 649 archs = ['i386'] 650 ldflags.append('-arch ' + archs[0]) 651 652 # Xcode adds the product directory by default. 653 ldflags.append('-L' + product_dir) 654 655 install_name = self.GetInstallName() 656 if install_name and self.spec['type'] != 'loadable_module': 657 ldflags.append('-install_name ' + install_name.replace(' ', r'\ ')) 658 659 for rpath in self._Settings().get('LD_RUNPATH_SEARCH_PATHS', []): 660 ldflags.append('-Wl,-rpath,' + rpath) 661 662 config = self.spec['configurations'][self.configname] 663 framework_dirs = config.get('mac_framework_dirs', []) 664 for directory in framework_dirs: 665 ldflags.append('-F' + directory.replace('$(SDKROOT)', self._SdkPath())) 666 667 self.configname = None 668 return ldflags 669 670 def GetLibtoolflags(self, configname): 671 """Returns flags that need to be passed to the static linker. 672 673 Args: 674 configname: The name of the configuration to get ld flags for. 675 """ 676 self.configname = configname 677 libtoolflags = [] 678 679 for libtoolflag in self._Settings().get('OTHER_LDFLAGS', []): 680 libtoolflags.append(libtoolflag) 681 # TODO(thakis): ARCHS? 682 683 self.configname = None 684 return libtoolflags 685 686 def GetPerTargetSettings(self): 687 """Gets a list of all the per-target settings. This will only fetch keys 688 whose values are the same across all configurations.""" 689 first_pass = True 690 result = {} 691 for configname in sorted(self.xcode_settings.keys()): 692 if first_pass: 693 result = dict(self.xcode_settings[configname]) 694 first_pass = False 695 else: 696 for key, value in self.xcode_settings[configname].iteritems(): 697 if key not in result: 698 continue 699 elif result[key] != value: 700 del result[key] 701 return result 702 703 def GetPerConfigSetting(self, setting, configname, default=None): 704 if configname in self.xcode_settings: 705 return self.xcode_settings[configname].get(setting, default) 706 else: 707 return self.GetPerTargetSetting(setting, default) 708 709 def GetPerTargetSetting(self, setting, default=None): 710 """Tries to get xcode_settings.setting from spec. Assumes that the setting 711 has the same value in all configurations and throws otherwise.""" 712 is_first_pass = True 713 result = None 714 for configname in sorted(self.xcode_settings.keys()): 715 if is_first_pass: 716 result = self.xcode_settings[configname].get(setting, None) 717 is_first_pass = False 718 else: 719 assert result == self.xcode_settings[configname].get(setting, None), ( 720 "Expected per-target setting for '%s', got per-config setting " 721 "(target %s)" % (setting, self.spec['target_name'])) 722 if result is None: 723 return default 724 return result 725 726 def _GetStripPostbuilds(self, configname, output_binary, quiet): 727 """Returns a list of shell commands that contain the shell commands 728 neccessary to strip this target's binary. These should be run as postbuilds 729 before the actual postbuilds run.""" 730 self.configname = configname 731 732 result = [] 733 if (self._Test('DEPLOYMENT_POSTPROCESSING', 'YES', default='NO') and 734 self._Test('STRIP_INSTALLED_PRODUCT', 'YES', default='NO')): 735 736 default_strip_style = 'debugging' 737 if self.spec['type'] == 'loadable_module' and self._IsBundle(): 738 default_strip_style = 'non-global' 739 elif self.spec['type'] == 'executable': 740 default_strip_style = 'all' 741 742 strip_style = self._Settings().get('STRIP_STYLE', default_strip_style) 743 strip_flags = { 744 'all': '', 745 'non-global': '-x', 746 'debugging': '-S', 747 }[strip_style] 748 749 explicit_strip_flags = self._Settings().get('STRIPFLAGS', '') 750 if explicit_strip_flags: 751 strip_flags += ' ' + _NormalizeEnvVarReferences(explicit_strip_flags) 752 753 if not quiet: 754 result.append('echo STRIP\\(%s\\)' % self.spec['target_name']) 755 result.append('strip %s %s' % (strip_flags, output_binary)) 756 757 self.configname = None 758 return result 759 760 def _GetDebugInfoPostbuilds(self, configname, output, output_binary, quiet): 761 """Returns a list of shell commands that contain the shell commands 762 neccessary to massage this target's debug information. These should be run 763 as postbuilds before the actual postbuilds run.""" 764 self.configname = configname 765 766 # For static libraries, no dSYMs are created. 767 result = [] 768 if (self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES') and 769 self._Test( 770 'DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym', default='dwarf') and 771 self.spec['type'] != 'static_library'): 772 if not quiet: 773 result.append('echo DSYMUTIL\\(%s\\)' % self.spec['target_name']) 774 result.append('dsymutil %s -o %s' % (output_binary, output + '.dSYM')) 775 776 self.configname = None 777 return result 778 779 def _GetTargetPostbuilds(self, configname, output, output_binary, 780 quiet=False): 781 """Returns a list of shell commands that contain the shell commands 782 to run as postbuilds for this target, before the actual postbuilds.""" 783 # dSYMs need to build before stripping happens. 784 return ( 785 self._GetDebugInfoPostbuilds(configname, output, output_binary, quiet) + 786 self._GetStripPostbuilds(configname, output_binary, quiet)) 787 788 def _GetIOSPostbuilds(self, configname, output_binary): 789 """Return a shell command to codesign the iOS output binary so it can 790 be deployed to a device. This should be run as the very last step of the 791 build.""" 792 if not (self.isIOS and self.spec['type'] == "executable"): 793 return [] 794 795 settings = self.xcode_settings[configname] 796 key = self._GetIOSCodeSignIdentityKey(settings) 797 if not key: 798 return [] 799 800 # Warn for any unimplemented signing xcode keys. 801 unimpl = ['OTHER_CODE_SIGN_FLAGS'] 802 unimpl = set(unimpl) & set(self.xcode_settings[configname].keys()) 803 if unimpl: 804 print 'Warning: Some codesign keys not implemented, ignoring: %s' % ( 805 ', '.join(sorted(unimpl))) 806 807 return ['%s code-sign-bundle "%s" "%s" "%s" "%s"' % ( 808 os.path.join('${TARGET_BUILD_DIR}', 'gyp-mac-tool'), key, 809 settings.get('CODE_SIGN_RESOURCE_RULES_PATH', ''), 810 settings.get('CODE_SIGN_ENTITLEMENTS', ''), 811 settings.get('PROVISIONING_PROFILE', '')) 812 ] 813 814 def _GetIOSCodeSignIdentityKey(self, settings): 815 identity = settings.get('CODE_SIGN_IDENTITY') 816 if not identity: 817 return None 818 if identity not in XcodeSettings._codesigning_key_cache: 819 output = subprocess.check_output( 820 ['security', 'find-identity', '-p', 'codesigning', '-v']) 821 for line in output.splitlines(): 822 if identity in line: 823 assert identity not in XcodeSettings._codesigning_key_cache, ( 824 "Multiple codesigning identities for identity: %s" % identity) 825 XcodeSettings._codesigning_key_cache[identity] = line.split()[1] 826 return XcodeSettings._codesigning_key_cache.get(identity, '') 827 828 def AddImplicitPostbuilds(self, configname, output, output_binary, 829 postbuilds=[], quiet=False): 830 """Returns a list of shell commands that should run before and after 831 |postbuilds|.""" 832 assert output_binary is not None 833 pre = self._GetTargetPostbuilds(configname, output, output_binary, quiet) 834 post = self._GetIOSPostbuilds(configname, output_binary) 835 return pre + postbuilds + post 836 837 def _AdjustLibrary(self, library, config_name=None): 838 if library.endswith('.framework'): 839 l = '-framework ' + os.path.splitext(os.path.basename(library))[0] 840 else: 841 m = self.library_re.match(library) 842 if m: 843 l = '-l' + m.group(1) 844 else: 845 l = library 846 return l.replace('$(SDKROOT)', self._SdkPath(config_name)) 847 848 def AdjustLibraries(self, libraries, config_name=None): 849 """Transforms entries like 'Cocoa.framework' in libraries into entries like 850 '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc. 851 """ 852 libraries = [self._AdjustLibrary(library, config_name) 853 for library in libraries] 854 return libraries 855 856 def _BuildMachineOSBuild(self): 857 return self._GetStdout(['sw_vers', '-buildVersion']) 858 859 def _XcodeVersion(self): 860 # `xcodebuild -version` output looks like 861 # Xcode 4.6.3 862 # Build version 4H1503 863 # or like 864 # Xcode 3.2.6 865 # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0 866 # BuildVersion: 10M2518 867 # Convert that to '0463', '4H1503'. 868 if len(XcodeSettings._xcode_version_cache) == 0: 869 version_list = self._GetStdout(['xcodebuild', '-version']).splitlines() 870 version = version_list[0] 871 build = version_list[-1] 872 # Be careful to convert "4.2" to "0420": 873 version = version.split()[-1].replace('.', '') 874 version = (version + '0' * (3 - len(version))).zfill(4) 875 build = build.split()[-1] 876 XcodeSettings._xcode_version_cache = (version, build) 877 return XcodeSettings._xcode_version_cache 878 879 def _XcodeIOSDeviceFamily(self, configname): 880 family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1') 881 return [int(x) for x in family.split(',')] 882 883 def GetExtraPlistItems(self, configname=None): 884 """Returns a dictionary with extra items to insert into Info.plist.""" 885 if configname not in XcodeSettings._plist_cache: 886 cache = {} 887 cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild() 888 889 xcode, xcode_build = self._XcodeVersion() 890 cache['DTXcode'] = xcode 891 cache['DTXcodeBuild'] = xcode_build 892 893 sdk_root = self._SdkRoot(configname) 894 if not sdk_root: 895 sdk_root = self._DefaultSdkRoot() 896 cache['DTSDKName'] = sdk_root 897 if xcode >= '0430': 898 cache['DTSDKBuild'] = self._GetSdkVersionInfoItem( 899 sdk_root, 'ProductBuildVersion') 900 else: 901 cache['DTSDKBuild'] = cache['BuildMachineOSBuild'] 902 903 if self.isIOS: 904 cache['DTPlatformName'] = cache['DTSDKName'] 905 if configname.endswith("iphoneos"): 906 cache['DTPlatformVersion'] = self._GetSdkVersionInfoItem( 907 sdk_root, 'ProductVersion') 908 cache['CFBundleSupportedPlatforms'] = ['iPhoneOS'] 909 else: 910 cache['CFBundleSupportedPlatforms'] = ['iPhoneSimulator'] 911 XcodeSettings._plist_cache[configname] = cache 912 913 # Include extra plist items that are per-target, not per global 914 # XcodeSettings. 915 items = dict(XcodeSettings._plist_cache[configname]) 916 if self.isIOS: 917 items['UIDeviceFamily'] = self._XcodeIOSDeviceFamily(configname) 918 return items 919 920 def _DefaultSdkRoot(self): 921 """Returns the default SDKROOT to use. 922 923 Prior to version 5.0.0, if SDKROOT was not explicitly set in the Xcode 924 project, then the environment variable was empty. Starting with this 925 version, Xcode uses the name of the newest SDK installed. 926 """ 927 if self._XcodeVersion() < '0500': 928 return '' 929 default_sdk_path = self._XcodeSdkPath('') 930 default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path) 931 if default_sdk_root: 932 return default_sdk_root 933 all_sdks = self._GetStdout(['xcodebuild', '-showsdks']) 934 for line in all_sdks.splitlines(): 935 items = line.split() 936 if len(items) >= 3 and items[-2] == '-sdk': 937 sdk_root = items[-1] 938 sdk_path = self._XcodeSdkPath(sdk_root) 939 if sdk_path == default_sdk_path: 940 return sdk_root 941 return '' 942 943 def _DefaultArch(self): 944 # For Mac projects, Xcode changed the default value used when ARCHS is not 945 # set from "i386" to "x86_64". 946 # 947 # For iOS projects, if ARCHS is unset, it defaults to "armv7 armv7s" when 948 # building for a device, and the simulator binaries are always build for 949 # "i386". 950 # 951 # For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT), 952 # which correspond to "armv7 armv7s arm64", and when building the simulator 953 # the architecture is either "i386" or "x86_64" depending on the simulated 954 # device (respectively 32-bit or 64-bit device). 955 # 956 # Since the value returned by this function is only used when ARCHS is not 957 # set, then on iOS we return "i386", as the default xcode project generator 958 # does not set ARCHS if it is not set in the .gyp file. 959 if self.isIOS: 960 return 'i386' 961 version, build = self._XcodeVersion() 962 if version >= '0500': 963 return 'x86_64' 964 return 'i386' 965 966class MacPrefixHeader(object): 967 """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. 968 969 This feature consists of several pieces: 970 * If GCC_PREFIX_HEADER is present, all compilations in that project get an 971 additional |-include path_to_prefix_header| cflag. 972 * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is 973 instead compiled, and all other compilations in the project get an 974 additional |-include path_to_compiled_header| instead. 975 + Compiled prefix headers have the extension gch. There is one gch file for 976 every language used in the project (c, cc, m, mm), since gch files for 977 different languages aren't compatible. 978 + gch files themselves are built with the target's normal cflags, but they 979 obviously don't get the |-include| flag. Instead, they need a -x flag that 980 describes their language. 981 + All o files in the target need to depend on the gch file, to make sure 982 it's built before any o file is built. 983 984 This class helps with some of these tasks, but it needs help from the build 985 system for writing dependencies to the gch files, for writing build commands 986 for the gch files, and for figuring out the location of the gch files. 987 """ 988 def __init__(self, xcode_settings, 989 gyp_path_to_build_path, gyp_path_to_build_output): 990 """If xcode_settings is None, all methods on this class are no-ops. 991 992 Args: 993 gyp_path_to_build_path: A function that takes a gyp-relative path, 994 and returns a path relative to the build directory. 995 gyp_path_to_build_output: A function that takes a gyp-relative path and 996 a language code ('c', 'cc', 'm', or 'mm'), and that returns a path 997 to where the output of precompiling that path for that language 998 should be placed (without the trailing '.gch'). 999 """ 1000 # This doesn't support per-configuration prefix headers. Good enough 1001 # for now. 1002 self.header = None 1003 self.compile_headers = False 1004 if xcode_settings: 1005 self.header = xcode_settings.GetPerTargetSetting('GCC_PREFIX_HEADER') 1006 self.compile_headers = xcode_settings.GetPerTargetSetting( 1007 'GCC_PRECOMPILE_PREFIX_HEADER', default='NO') != 'NO' 1008 self.compiled_headers = {} 1009 if self.header: 1010 if self.compile_headers: 1011 for lang in ['c', 'cc', 'm', 'mm']: 1012 self.compiled_headers[lang] = gyp_path_to_build_output( 1013 self.header, lang) 1014 self.header = gyp_path_to_build_path(self.header) 1015 1016 def _CompiledHeader(self, lang, arch): 1017 assert self.compile_headers 1018 h = self.compiled_headers[lang] 1019 if arch: 1020 h += '.' + arch 1021 return h 1022 1023 def GetInclude(self, lang, arch=None): 1024 """Gets the cflags to include the prefix header for language |lang|.""" 1025 if self.compile_headers and lang in self.compiled_headers: 1026 return '-include %s' % self._CompiledHeader(lang, arch) 1027 elif self.header: 1028 return '-include %s' % self.header 1029 else: 1030 return '' 1031 1032 def _Gch(self, lang, arch): 1033 """Returns the actual file name of the prefix header for language |lang|.""" 1034 assert self.compile_headers 1035 return self._CompiledHeader(lang, arch) + '.gch' 1036 1037 def GetObjDependencies(self, sources, objs, arch=None): 1038 """Given a list of source files and the corresponding object files, returns 1039 a list of (source, object, gch) tuples, where |gch| is the build-directory 1040 relative path to the gch file each object file depends on. |compilable[i]| 1041 has to be the source file belonging to |objs[i]|.""" 1042 if not self.header or not self.compile_headers: 1043 return [] 1044 1045 result = [] 1046 for source, obj in zip(sources, objs): 1047 ext = os.path.splitext(source)[1] 1048 lang = { 1049 '.c': 'c', 1050 '.cpp': 'cc', '.cc': 'cc', '.cxx': 'cc', 1051 '.m': 'm', 1052 '.mm': 'mm', 1053 }.get(ext, None) 1054 if lang: 1055 result.append((source, obj, self._Gch(lang, arch))) 1056 return result 1057 1058 def GetPchBuildCommands(self, arch=None): 1059 """Returns [(path_to_gch, language_flag, language, header)]. 1060 |path_to_gch| and |header| are relative to the build directory. 1061 """ 1062 if not self.header or not self.compile_headers: 1063 return [] 1064 return [ 1065 (self._Gch('c', arch), '-x c-header', 'c', self.header), 1066 (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), 1067 (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), 1068 (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), 1069 ] 1070 1071 1072def MergeGlobalXcodeSettingsToSpec(global_dict, spec): 1073 """Merges the global xcode_settings dictionary into each configuration of the 1074 target represented by spec. For keys that are both in the global and the local 1075 xcode_settings dict, the local key gets precendence. 1076 """ 1077 # The xcode generator special-cases global xcode_settings and does something 1078 # that amounts to merging in the global xcode_settings into each local 1079 # xcode_settings dict. 1080 global_xcode_settings = global_dict.get('xcode_settings', {}) 1081 for config in spec['configurations'].values(): 1082 if 'xcode_settings' in config: 1083 new_settings = global_xcode_settings.copy() 1084 new_settings.update(config['xcode_settings']) 1085 config['xcode_settings'] = new_settings 1086 1087 1088def IsMacBundle(flavor, spec): 1089 """Returns if |spec| should be treated as a bundle. 1090 1091 Bundles are directories with a certain subdirectory structure, instead of 1092 just a single file. Bundle rules do not produce a binary but also package 1093 resources into that directory.""" 1094 is_mac_bundle = (int(spec.get('mac_bundle', 0)) != 0 and flavor == 'mac') 1095 if is_mac_bundle: 1096 assert spec['type'] != 'none', ( 1097 'mac_bundle targets cannot have type none (target "%s")' % 1098 spec['target_name']) 1099 return is_mac_bundle 1100 1101 1102def GetMacBundleResources(product_dir, xcode_settings, resources): 1103 """Yields (output, resource) pairs for every resource in |resources|. 1104 Only call this for mac bundle targets. 1105 1106 Args: 1107 product_dir: Path to the directory containing the output bundle, 1108 relative to the build directory. 1109 xcode_settings: The XcodeSettings of the current target. 1110 resources: A list of bundle resources, relative to the build directory. 1111 """ 1112 dest = os.path.join(product_dir, 1113 xcode_settings.GetBundleResourceFolder()) 1114 for res in resources: 1115 output = dest 1116 1117 # The make generator doesn't support it, so forbid it everywhere 1118 # to keep the generators more interchangable. 1119 assert ' ' not in res, ( 1120 "Spaces in resource filenames not supported (%s)" % res) 1121 1122 # Split into (path,file). 1123 res_parts = os.path.split(res) 1124 1125 # Now split the path into (prefix,maybe.lproj). 1126 lproj_parts = os.path.split(res_parts[0]) 1127 # If the resource lives in a .lproj bundle, add that to the destination. 1128 if lproj_parts[1].endswith('.lproj'): 1129 output = os.path.join(output, lproj_parts[1]) 1130 1131 output = os.path.join(output, res_parts[1]) 1132 # Compiled XIB files are referred to by .nib. 1133 if output.endswith('.xib'): 1134 output = os.path.splitext(output)[0] + '.nib' 1135 # Compiled storyboard files are referred to by .storyboardc. 1136 if output.endswith('.storyboard'): 1137 output = os.path.splitext(output)[0] + '.storyboardc' 1138 1139 yield output, res 1140 1141 1142def GetMacInfoPlist(product_dir, xcode_settings, gyp_path_to_build_path): 1143 """Returns (info_plist, dest_plist, defines, extra_env), where: 1144 * |info_plist| is the source plist path, relative to the 1145 build directory, 1146 * |dest_plist| is the destination plist path, relative to the 1147 build directory, 1148 * |defines| is a list of preprocessor defines (empty if the plist 1149 shouldn't be preprocessed, 1150 * |extra_env| is a dict of env variables that should be exported when 1151 invoking |mac_tool copy-info-plist|. 1152 1153 Only call this for mac bundle targets. 1154 1155 Args: 1156 product_dir: Path to the directory containing the output bundle, 1157 relative to the build directory. 1158 xcode_settings: The XcodeSettings of the current target. 1159 gyp_to_build_path: A function that converts paths relative to the 1160 current gyp file to paths relative to the build direcotry. 1161 """ 1162 info_plist = xcode_settings.GetPerTargetSetting('INFOPLIST_FILE') 1163 if not info_plist: 1164 return None, None, [], {} 1165 1166 # The make generator doesn't support it, so forbid it everywhere 1167 # to keep the generators more interchangable. 1168 assert ' ' not in info_plist, ( 1169 "Spaces in Info.plist filenames not supported (%s)" % info_plist) 1170 1171 info_plist = gyp_path_to_build_path(info_plist) 1172 1173 # If explicitly set to preprocess the plist, invoke the C preprocessor and 1174 # specify any defines as -D flags. 1175 if xcode_settings.GetPerTargetSetting( 1176 'INFOPLIST_PREPROCESS', default='NO') == 'YES': 1177 # Create an intermediate file based on the path. 1178 defines = shlex.split(xcode_settings.GetPerTargetSetting( 1179 'INFOPLIST_PREPROCESSOR_DEFINITIONS', default='')) 1180 else: 1181 defines = [] 1182 1183 dest_plist = os.path.join(product_dir, xcode_settings.GetBundlePlistPath()) 1184 extra_env = xcode_settings.GetPerTargetSettings() 1185 1186 return info_plist, dest_plist, defines, extra_env 1187 1188 1189def _GetXcodeEnv(xcode_settings, built_products_dir, srcroot, configuration, 1190 additional_settings=None): 1191 """Return the environment variables that Xcode would set. See 1192 http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153 1193 for a full list. 1194 1195 Args: 1196 xcode_settings: An XcodeSettings object. If this is None, this function 1197 returns an empty dict. 1198 built_products_dir: Absolute path to the built products dir. 1199 srcroot: Absolute path to the source root. 1200 configuration: The build configuration name. 1201 additional_settings: An optional dict with more values to add to the 1202 result. 1203 """ 1204 if not xcode_settings: return {} 1205 1206 # This function is considered a friend of XcodeSettings, so let it reach into 1207 # its implementation details. 1208 spec = xcode_settings.spec 1209 1210 # These are filled in on a as-needed basis. 1211 env = { 1212 'BUILT_PRODUCTS_DIR' : built_products_dir, 1213 'CONFIGURATION' : configuration, 1214 'PRODUCT_NAME' : xcode_settings.GetProductName(), 1215 # See /Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX\ Product\ Types.xcspec for FULL_PRODUCT_NAME 1216 'SRCROOT' : srcroot, 1217 'SOURCE_ROOT': '${SRCROOT}', 1218 # This is not true for static libraries, but currently the env is only 1219 # written for bundles: 1220 'TARGET_BUILD_DIR' : built_products_dir, 1221 'TEMP_DIR' : '${TMPDIR}', 1222 } 1223 if xcode_settings.GetPerConfigSetting('SDKROOT', configuration): 1224 env['SDKROOT'] = xcode_settings._SdkPath(configuration) 1225 else: 1226 env['SDKROOT'] = '' 1227 1228 if spec['type'] in ( 1229 'executable', 'static_library', 'shared_library', 'loadable_module'): 1230 env['EXECUTABLE_NAME'] = xcode_settings.GetExecutableName() 1231 env['EXECUTABLE_PATH'] = xcode_settings.GetExecutablePath() 1232 env['FULL_PRODUCT_NAME'] = xcode_settings.GetFullProductName() 1233 mach_o_type = xcode_settings.GetMachOType() 1234 if mach_o_type: 1235 env['MACH_O_TYPE'] = mach_o_type 1236 env['PRODUCT_TYPE'] = xcode_settings.GetProductType() 1237 if xcode_settings._IsBundle(): 1238 env['CONTENTS_FOLDER_PATH'] = \ 1239 xcode_settings.GetBundleContentsFolderPath() 1240 env['UNLOCALIZED_RESOURCES_FOLDER_PATH'] = \ 1241 xcode_settings.GetBundleResourceFolder() 1242 env['INFOPLIST_PATH'] = xcode_settings.GetBundlePlistPath() 1243 env['WRAPPER_NAME'] = xcode_settings.GetWrapperName() 1244 1245 install_name = xcode_settings.GetInstallName() 1246 if install_name: 1247 env['LD_DYLIB_INSTALL_NAME'] = install_name 1248 install_name_base = xcode_settings.GetInstallNameBase() 1249 if install_name_base: 1250 env['DYLIB_INSTALL_NAME_BASE'] = install_name_base 1251 1252 if not additional_settings: 1253 additional_settings = {} 1254 else: 1255 # Flatten lists to strings. 1256 for k in additional_settings: 1257 if not isinstance(additional_settings[k], str): 1258 additional_settings[k] = ' '.join(additional_settings[k]) 1259 additional_settings.update(env) 1260 1261 for k in additional_settings: 1262 additional_settings[k] = _NormalizeEnvVarReferences(additional_settings[k]) 1263 1264 return additional_settings 1265 1266 1267def _NormalizeEnvVarReferences(str): 1268 """Takes a string containing variable references in the form ${FOO}, $(FOO), 1269 or $FOO, and returns a string with all variable references in the form ${FOO}. 1270 """ 1271 # $FOO -> ${FOO} 1272 str = re.sub(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', r'${\1}', str) 1273 1274 # $(FOO) -> ${FOO} 1275 matches = re.findall(r'(\$\(([a-zA-Z0-9\-_]+)\))', str) 1276 for match in matches: 1277 to_replace, variable = match 1278 assert '$(' not in match, '$($(FOO)) variables not supported: ' + match 1279 str = str.replace(to_replace, '${' + variable + '}') 1280 1281 return str 1282 1283 1284def ExpandEnvVars(string, expansions): 1285 """Expands ${VARIABLES}, $(VARIABLES), and $VARIABLES in string per the 1286 expansions list. If the variable expands to something that references 1287 another variable, this variable is expanded as well if it's in env -- 1288 until no variables present in env are left.""" 1289 for k, v in reversed(expansions): 1290 string = string.replace('${' + k + '}', v) 1291 string = string.replace('$(' + k + ')', v) 1292 string = string.replace('$' + k, v) 1293 return string 1294 1295 1296def _TopologicallySortedEnvVarKeys(env): 1297 """Takes a dict |env| whose values are strings that can refer to other keys, 1298 for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of 1299 env such that key2 is after key1 in L if env[key2] refers to env[key1]. 1300 1301 Throws an Exception in case of dependency cycles. 1302 """ 1303 # Since environment variables can refer to other variables, the evaluation 1304 # order is important. Below is the logic to compute the dependency graph 1305 # and sort it. 1306 regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}') 1307 def GetEdges(node): 1308 # Use a definition of edges such that user_of_variable -> used_varible. 1309 # This happens to be easier in this case, since a variable's 1310 # definition contains all variables it references in a single string. 1311 # We can then reverse the result of the topological sort at the end. 1312 # Since: reverse(topsort(DAG)) = topsort(reverse_edges(DAG)) 1313 matches = set([v for v in regex.findall(env[node]) if v in env]) 1314 for dependee in matches: 1315 assert '${' not in dependee, 'Nested variables not supported: ' + dependee 1316 return matches 1317 1318 try: 1319 # Topologically sort, and then reverse, because we used an edge definition 1320 # that's inverted from the expected result of this function (see comment 1321 # above). 1322 order = gyp.common.TopologicallySorted(env.keys(), GetEdges) 1323 order.reverse() 1324 return order 1325 except gyp.common.CycleError, e: 1326 raise GypError( 1327 'Xcode environment variables are cyclically dependent: ' + str(e.nodes)) 1328 1329 1330def GetSortedXcodeEnv(xcode_settings, built_products_dir, srcroot, 1331 configuration, additional_settings=None): 1332 env = _GetXcodeEnv(xcode_settings, built_products_dir, srcroot, configuration, 1333 additional_settings) 1334 return [(key, env[key]) for key in _TopologicallySortedEnvVarKeys(env)] 1335 1336 1337def GetSpecPostbuildCommands(spec, quiet=False): 1338 """Returns the list of postbuilds explicitly defined on |spec|, in a form 1339 executable by a shell.""" 1340 postbuilds = [] 1341 for postbuild in spec.get('postbuilds', []): 1342 if not quiet: 1343 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( 1344 spec['target_name'], postbuild['postbuild_name'])) 1345 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) 1346 return postbuilds 1347 1348 1349def _HasIOSTarget(targets): 1350 """Returns true if any target contains the iOS specific key 1351 IPHONEOS_DEPLOYMENT_TARGET.""" 1352 for target_dict in targets.values(): 1353 for config in target_dict['configurations'].values(): 1354 if config.get('xcode_settings', {}).get('IPHONEOS_DEPLOYMENT_TARGET'): 1355 return True 1356 return False 1357 1358 1359def _AddIOSDeviceConfigurations(targets): 1360 """Clone all targets and append -iphoneos to the name. Configure these targets 1361 to build for iOS devices.""" 1362 for target_dict in targets.values(): 1363 for config_name in target_dict['configurations'].keys(): 1364 config = target_dict['configurations'][config_name] 1365 new_config_name = config_name + '-iphoneos' 1366 new_config_dict = copy.deepcopy(config) 1367 if target_dict['toolset'] == 'target': 1368 new_config_dict['xcode_settings']['ARCHS'] = ['armv7'] 1369 new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos' 1370 target_dict['configurations'][new_config_name] = new_config_dict 1371 return targets 1372 1373def CloneConfigurationForDeviceAndEmulator(target_dicts): 1374 """If |target_dicts| contains any iOS targets, automatically create -iphoneos 1375 targets for iOS device builds.""" 1376 if _HasIOSTarget(target_dicts): 1377 return _AddIOSDeviceConfigurations(target_dicts) 1378 return target_dicts 1379