"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py" (8 Mar 2017, 65086 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

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