12f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com# Copyright (c) 2012 Google Inc. All rights reserved.
2df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org# Use of this source code is governed by a BSD-style license that can be
3df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org# found in the LICENSE file.
4df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org
52c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.orgimport filecmp
6f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.orgimport gyp.common
7f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.orgimport gyp.xcodeproj_file
8d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.orgimport gyp.xcode_ninja
9a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.orgimport errno
10a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.orgimport os
11365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.orgimport sys
12ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.orgimport posixpath
131f3179fdaed9a35b6314adebc01675601cdcaf48mark@chromium.orgimport re
146105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.orgimport shutil
15d2b869f20e7b69f5de48a911d1984d9a3c3133eathomasvl@chromium.orgimport subprocess
162c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.orgimport tempfile
17a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
18a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
19581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# Project files generated by this module will use _intermediate_var as a
20581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# custom Xcode setting whose value is a DerivedSources-like directory that's
21581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# project-specific and configuration-specific.  The normal choice,
22581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# DERIVED_FILE_DIR, is target-specific, which is thought to be too restrictive
23581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# as it is likely that multiple targets within a single project file will want
24581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# to access the same set of generated files.  The other option,
25581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# PROJECT_DERIVED_FILE_DIR, is unsuitable because while it is project-specific,
26581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# it is not configuration-specific.  INTERMEDIATE_DIR is defined as
27581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org# $(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION).
28581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org_intermediate_var = 'INTERMEDIATE_DIR'
294ab0292c010f9aa9a35c57ca1413cf01cded7091mark@chromium.org
30debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org# SHARED_INTERMEDIATE_DIR is the same, except that it is shared among all
31debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org# targets that share the same BUILT_PRODUCTS_DIR.
32debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org_shared_intermediate_var = 'SHARED_INTERMEDIATE_DIR'
33debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org
346b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org_library_search_paths_var = 'LIBRARY_SEARCH_PATHS'
356b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org
361d4bf03af656e8f45c6ff3edd85af569acf710aamark@chromium.orggenerator_default_variables = {
376717b752a535daa9c401bc823c36397ca823ebd5mark@chromium.org  'EXECUTABLE_PREFIX': '',
386717b752a535daa9c401bc823c36397ca823ebd5mark@chromium.org  'EXECUTABLE_SUFFIX': '',
39d813fe1dcf7ab60f4ab729d1e32199598059b7bfbradnelson@google.com  'STATIC_LIB_PREFIX': 'lib',
40526e49cfa997c408b05d0155984b8a3e5def3bf4thomasvl@chromium.org  'SHARED_LIB_PREFIX': 'lib',
41aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com  'STATIC_LIB_SUFFIX': '.a',
42aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com  'SHARED_LIB_SUFFIX': '.dylib',
435ca0052c48077a11341c00a09bf37a0276416af9mark@chromium.org  # INTERMEDIATE_DIR is a place for targets to build up intermediate products.
445ca0052c48077a11341c00a09bf37a0276416af9mark@chromium.org  # It is specific to each build environment.  It is only guaranteed to exist
45581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  # and be constant within the context of a project, corresponding to a single
46581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  # input file.  Some build environments may allow their intermediate directory
47581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  # to be shared on a wider scale, but this is not guaranteed.
48581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  'INTERMEDIATE_DIR': '$(%s)' % _intermediate_var,
491d4bf03af656e8f45c6ff3edd85af569acf710aamark@chromium.org  'OS': 'mac',
506717b752a535daa9c401bc823c36397ca823ebd5mark@chromium.org  'PRODUCT_DIR': '$(BUILT_PRODUCTS_DIR)',
51d813fe1dcf7ab60f4ab729d1e32199598059b7bfbradnelson@google.com  'LIB_DIR': '$(BUILT_PRODUCTS_DIR)',
526105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  'RULE_INPUT_ROOT': '$(INPUT_FILE_BASE)',
536105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  'RULE_INPUT_EXT': '$(INPUT_FILE_SUFFIX)',
546105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  'RULE_INPUT_NAME': '$(INPUT_FILE_NAME)',
556105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  'RULE_INPUT_PATH': '$(INPUT_FILE_PATH)',
56cdaa7e6d2e12c7d592265333396c6d1307632e22scr@chromium.org  'RULE_INPUT_DIRNAME': '$(INPUT_FILE_DIRNAME)',
57debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org  'SHARED_INTERMEDIATE_DIR': '$(%s)' % _shared_intermediate_var,
582c9e8675a425664fcc6fa1038c20dae9f2138af3bradnelson@google.com  'CONFIGURATION_NAME': '$(CONFIGURATION)',
591d4bf03af656e8f45c6ff3edd85af569acf710aamark@chromium.org}
6081f836d22e87477721a659f6edb72ff6113594c9mark@chromium.org
61565b6377c2de175e7177cc9fd3dcf957b37601d9thomasvl@chromium.org# The Xcode-specific sections that hold paths.
62a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.orggenerator_additional_path_sections = [
63a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org  'mac_bundle_resources',
64c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org  'mac_framework_headers',
6529f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org  'mac_framework_private_headers',
66a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org  # 'mac_framework_dirs', input already handles _dirs endings.
67a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org]
68a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org
69565b6377c2de175e7177cc9fd3dcf957b37601d9thomasvl@chromium.org# The Xcode-specific keys that exist on targets and aren't moved down to
70a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org# configurations.
71a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.orggenerator_additional_non_configuration_keys = [
72f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org  'ios_app_extension',
73a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org  'mac_bundle',
74a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org  'mac_bundle_resources',
75c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org  'mac_framework_headers',
7629f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org  'mac_framework_private_headers',
77c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org  'mac_xctest_bundle',
78a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org  'xcode_create_dependents_test_runner',
79a887771018238c16ecbc9ef66bbc38becce0f0fethomasvl@chromium.org]
8081f836d22e87477721a659f6edb72ff6113594c9mark@chromium.org
816bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org# We want to let any rules apply to files that are resources also.
826bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.orggenerator_extra_sources_for_rules = [
836bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org  'mac_bundle_resources',
84c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org  'mac_framework_headers',
8529f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org  'mac_framework_private_headers',
866bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org]
876bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org
886b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org# Xcode's standard set of library directories, which don't need to be duplicated
896b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org# in LIBRARY_SEARCH_PATHS. This list is not exhaustive, but that's okay.
906b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.orgxcode_standard_library_dirs = frozenset([
916b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org  '$(SDKROOT)/usr/lib',
926b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org  '$(SDKROOT)/usr/local/lib',
936b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org])
942fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org
952fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.orgdef CreateXCConfigurationList(configuration_names):
962fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org  xccl = gyp.xcodeproj_file.XCConfigurationList({'buildConfigurations': []})
974fcdd886cef5cfc58446e51b17b0b48f46ffd8a5mark@chromium.org  if len(configuration_names) == 0:
984fcdd886cef5cfc58446e51b17b0b48f46ffd8a5mark@chromium.org    configuration_names = ['Default']
992fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org  for configuration_name in configuration_names:
1002fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    xcbc = gyp.xcodeproj_file.XCBuildConfiguration({
1012fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        'name': configuration_name})
1022fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    xccl.AppendProperty('buildConfigurations', xcbc)
1032fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org  xccl.SetProperty('defaultConfigurationName', configuration_names[0])
1042fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org  return xccl
1052fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org
1062fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org
107a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.orgclass XcodeProject(object):
10820db697424a38bd19aac48ae14dbf49e8fa6a4d3mark@chromium.org  def __init__(self, gyp_path, path, build_file_dict):
10959bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    self.gyp_path = gyp_path
110475089d4d1a7ea54e6f25312d665da1e982d96d0mark@chromium.org    self.path = path
111f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org    self.project = gyp.xcodeproj_file.PBXProject(path=path)
1129c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org    projectDirPath = gyp.common.RelativePath(
1139c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org                         os.path.dirname(os.path.abspath(self.gyp_path)),
1149c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org                         os.path.dirname(path) or '.')
1159c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org    self.project.SetProperty('projectDirPath', projectDirPath)
116f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org    self.project_file = \
117f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org        gyp.xcodeproj_file.XCProjectFile({'rootObject': self.project})
11820db697424a38bd19aac48ae14dbf49e8fa6a4d3mark@chromium.org    self.build_file_dict = build_file_dict
119a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
1206105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # TODO(mark): add destructor that cleans up self.path if created_dir is
121937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org    # True and things didn't complete successfully.  Or do something even
122937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org    # better with "try"?
1236105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    self.created_dir = False
1246105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    try:
1259c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org      os.makedirs(self.path)
1266105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      self.created_dir = True
1276105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    except OSError, e:
1286105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      if e.errno != errno.EEXIST:
1296105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        raise
1306105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
131366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org  def Finalize1(self, xcode_targets, serialize_all_tests):
13235d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # Collect a list of all of the build configuration names used by the
13335d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # various targets in the file.  It is very heavily advised to keep each
13435d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # target in an entire project (even across multiple project files) using
13535d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # the same set of configuration names.
13635d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    configurations = []
13735d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    for xct in self.project.GetProperty('targets'):
13835d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org      xccl = xct.GetProperty('buildConfigurationList')
13935d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org      xcbcs = xccl.GetProperty('buildConfigurations')
14035d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org      for xcbc in xcbcs:
14135d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org        name = xcbc.GetProperty('name')
14235d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org        if name not in configurations:
14335d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org          configurations.append(name)
14435d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org
14535d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # Replace the XCConfigurationList attached to the PBXProject object with
14635d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # a new one specifying all of the configuration names used by the various
14735d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # targets.
148e8db48c52a4ed5df520e472193d54674b681e9d4gspencer@google.com    try:
1492fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      xccl = CreateXCConfigurationList(configurations)
150e8db48c52a4ed5df520e472193d54674b681e9d4gspencer@google.com      self.project.SetProperty('buildConfigurationList', xccl)
151e8db48c52a4ed5df520e472193d54674b681e9d4gspencer@google.com    except:
152e8db48c52a4ed5df520e472193d54674b681e9d4gspencer@google.com      sys.stderr.write("Problem with gyp file %s\n" % self.gyp_path)
153e8db48c52a4ed5df520e472193d54674b681e9d4gspencer@google.com      raise
15435d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org
155581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # The need for this setting is explained above where _intermediate_var is
156581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # defined.  The comments below about wanting to avoid project-wide build
157581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # settings apply here too, but this needs to be set on a project-wide basis
158581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # so that files relative to the _intermediate_var setting can be displayed
159581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # properly in the Xcode UI.
160581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    #
161581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # Note that for configuration-relative files such as anything relative to
162581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # _intermediate_var, for the purposes of UI tree view display, Xcode will
163581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # only resolve the configuration name once, when the project file is
164581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # opened.  If the active build configuration is changed, the project file
165581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # must be closed and reopened if it is desired for the tree view to update.
166581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # This is filed as Apple radar 6588391.
167581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    xccl.SetBuildSetting(_intermediate_var,
1684ab0292c010f9aa9a35c57ca1413cf01cded7091mark@chromium.org                         '$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)')
169debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org    xccl.SetBuildSetting(_shared_intermediate_var,
170debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org                         '$(SYMROOT)/DerivedSources/$(CONFIGURATION)')
1714ab0292c010f9aa9a35c57ca1413cf01cded7091mark@chromium.org
172dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # Set user-specified project-wide build settings and config files.  This
173dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # is intended to be used very sparingly.  Really, almost everything should
174dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # go into target-specific build settings sections.  The project-wide
175dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # settings are only intended to be used in cases where Xcode attempts to
176dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # resolve variable references in a project context as opposed to a target
177dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # context, such as when resolving sourceTree references while building up
178dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # the tree tree view for UI display.
179dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # Any values set globally are applied to all configurations, then any
180dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    # per-configuration values are applied.
18120db697424a38bd19aac48ae14dbf49e8fa6a4d3mark@chromium.org    for xck, xcv in self.build_file_dict.get('xcode_settings', {}).iteritems():
18220db697424a38bd19aac48ae14dbf49e8fa6a4d3mark@chromium.org      xccl.SetBuildSetting(xck, xcv)
183dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    if 'xcode_config_file' in self.build_file_dict:
184dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org      config_ref = self.project.AddOrGetFileInRootGroup(
185dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org          self.build_file_dict['xcode_config_file'])
186dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org      xccl.SetBaseConfiguration(config_ref)
187dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    build_file_configurations = self.build_file_dict.get('configurations', {})
188dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org    if build_file_configurations:
189dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org      for config_name in configurations:
190dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org        build_file_configuration_named = \
191dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org            build_file_configurations.get(config_name, {})
192dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org        if build_file_configuration_named:
193dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org          xcc = xccl.ConfigurationNamed(config_name)
194dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org          for xck, xcv in build_file_configuration_named.get('xcode_settings',
195dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org                                                             {}).iteritems():
196dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org            xcc.SetBuildSetting(xck, xcv)
197dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org          if 'xcode_config_file' in build_file_configuration_named:
198dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org            config_ref = self.project.AddOrGetFileInRootGroup(
199dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org                build_file_configurations[config_name]['xcode_config_file'])
200dd7f027a8e695320332cbcb91cae83daf59a0f46thomasvl@chromium.org            xcc.SetBaseConfiguration(config_ref)
20120db697424a38bd19aac48ae14dbf49e8fa6a4d3mark@chromium.org
20259bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # Sort the targets based on how they appeared in the input.
20359bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # TODO(mark): Like a lot of other things here, this assumes internal
20459bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # knowledge of PBXProject - in this case, of its "targets" property.
2051cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org
206ca91bab057f10704438b15410a4fa084e1e63110thomasvl@chromium.org    # ordinary_targets are ordinary targets that are already in the project
207366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    # file. run_test_targets are the targets that run unittests and should be
2082fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # used for the Run All Tests target.  support_targets are the action/rule
2092fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # targets used by GYP file targets, just kept for the assert check.
210ca91bab057f10704438b15410a4fa084e1e63110thomasvl@chromium.org    ordinary_targets = []
211366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    run_test_targets = []
2122fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    support_targets = []
2131cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org
214ca91bab057f10704438b15410a4fa084e1e63110thomasvl@chromium.org    # targets is full list of targets in the project.
2151cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org    targets = []
2161cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org
21783891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # does the it define it's own "all"?
21883891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    has_custom_all = False
21983891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org
220ca91bab057f10704438b15410a4fa084e1e63110thomasvl@chromium.org    # targets_for_all is the list of ordinary_targets that should be listed
2211cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org    # in this project's "All" target.  It includes each non_runtest_target
2221cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org    # that does not have suppress_wildcard set.
2231cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org    targets_for_all = []
2241cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org
225c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org    for target in self.build_file_dict['targets']:
22659bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      target_name = target['target_name']
227821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com      toolset = target['toolset']
228821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com      qualified_target = gyp.common.QualifiedTarget(self.gyp_path, target_name,
229821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com                                                    toolset)
23059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      xcode_target = xcode_targets[qualified_target]
23159bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      # Make sure that the target being added to the sorted list is already in
23259bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      # the unsorted list.
23359bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      assert xcode_target in self.project._properties['targets']
2341cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org      targets.append(xcode_target)
235ca91bab057f10704438b15410a4fa084e1e63110thomasvl@chromium.org      ordinary_targets.append(xcode_target)
2362fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      if xcode_target.support_target:
2372fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        support_targets.append(xcode_target.support_target)
2382fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        targets.append(xcode_target.support_target)
2391cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org
2407136d45e1193828e00c026cb133289cc11cbcbf3ajwong@chromium.org      if not int(target.get('suppress_wildcard', False)):
2411cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org        targets_for_all.append(xcode_target)
2429cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
24383891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org      if target_name.lower() == 'all':
24483891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org        has_custom_all = True;
24583891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org
246731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org      # If this target has a 'run_as' attribute, add its target to the
247731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org      # targets, and add it to the test targets.
248731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org      if target.get('run_as'):
249aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # Make a target to run something.  It should have one
250aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # dependency, the parent xcode target.
2512fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        xccl = CreateXCConfigurationList(configurations)
2529cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org        run_target = gyp.xcodeproj_file.PBXAggregateTarget({
2532fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org              'name':                   'Run ' + target_name,
2542fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org              'productName':            xcode_target.GetProperty('productName'),
2552fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org              'buildConfigurationList': xccl,
2569cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org            },
2579cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org            parent=self.project)
2589cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org        run_target.AddDependency(xcode_target)
2599cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
260731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org        command = target['run_as']
261aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        script = ''
262aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        if command.get('working_directory'):
263366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          script = script + 'cd "%s"\n' % \
264aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com                   gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
265aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com                       command.get('working_directory'))
266aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com
267aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        if command.get('environment'):
268aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com          script = script + "\n".join(
269aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com            ['export %s="%s"' %
270aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com             (key, gyp.xcodeproj_file.ConvertVariablesToShellSyntax(val))
271aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com             for (key, val) in command.get('environment').iteritems()]) + "\n"
272aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com
273366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # Some test end up using sockets, files on disk, etc. and can get
274366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # confused if more then one test runs at a time.  The generator
275366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # flag 'xcode_serialize_all_test_runs' controls the forcing of all
276366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # tests serially.  It defaults to True.  To get serial runs this
277366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # little bit of python does the same as the linux flock utility to
278366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        # make sure only one runs at a time.
279366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        command_prefix = ''
280731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org        if serialize_all_tests:
281366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          command_prefix = \
282366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org"""python -c "import fcntl, subprocess, sys
283366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.orgfile = open('$TMPDIR/GYP_serialize_test_runs', 'a')
284366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.orgfcntl.flock(file.fileno(), fcntl.LOCK_EX)
285366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.orgsys.exit(subprocess.call(sys.argv[1:]))" """
286366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org
287aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # If we were unable to exec for some reason, we want to exit
288aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # with an error, and fixup variable references to be shell
289aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # syntax instead of xcode syntax.
290366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        script = script + 'exec ' + command_prefix + '%s\nexit 1\n' % \
291aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com                 gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
292aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com                     gyp.common.EncodePOSIXShellList(command.get('action')))
293aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com
2949cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org        ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
2959cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org              'shellScript':      script,
2969cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org              'showEnvVarsInLog': 0,
2979cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org            })
2989cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org        run_target.AppendProperty('buildPhases', ssbp)
2999cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
300aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        # Add the run target to the project file.
3019cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org        targets.append(run_target)
302731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org        run_test_targets.append(run_target)
303731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org        xcode_target.test_runner = run_target
304aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com
3059cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
30659bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # Make sure that the list of targets being replaced is the same length as
3079cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org    # the one replacing it, but allow for the added test runner targets.
3082fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    assert len(self.project._properties['targets']) == \
3092fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      len(ordinary_targets) + len(support_targets)
31059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
31159bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    self.project._properties['targets'] = targets
31259bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
313581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    # Get rid of unnecessary levels of depth in groups like the Source group.
3147ed07f90f08824e19760db1be06ad107ce6cf85bmark@chromium.org    self.project.RootGroupsTakeOverOnlyChildren(True)
3157ed07f90f08824e19760db1be06ad107ce6cf85bmark@chromium.org
3166eeb5ee0a871fd52817b2034e539cb2d4e529c0amark@chromium.org    # Sort the groups nicely.  Do this after sorting the targets, because the
3176eeb5ee0a871fd52817b2034e539cb2d4e529c0amark@chromium.org    # Products group is sorted based on the order of the targets.
3186eeb5ee0a871fd52817b2034e539cb2d4e529c0amark@chromium.org    self.project.SortGroups()
3196eeb5ee0a871fd52817b2034e539cb2d4e529c0amark@chromium.org
32059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # Create an "All" target if there's more than one target in this project
32183891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # file and the project didn't define its own "All" target.  Put a generated
32283891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # "All" target first so that people opening up the project for the first
32383891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # time will build everything by default.
32483891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    if len(targets_for_all) > 1 and not has_custom_all:
3252fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      xccl = CreateXCConfigurationList(configurations)
32637964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      all_target = gyp.xcodeproj_file.PBXAggregateTarget(
32737964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org          {
32837964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org            'buildConfigurationList': xccl,
32937964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org            'name':                   'All',
33037964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org          },
33137964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org          parent=self.project)
33237964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org
3331cce7e1a85d52ab149e5bf4357d5b8352fec423amark@chromium.org      for target in targets_for_all:
33437964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org        all_target.AddDependency(target)
33537964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org
33637964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      # TODO(mark): This is evil because it relies on internal knowledge of
33737964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      # PBXProject._properties.  It's important to get the "All" target first,
33837964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      # though.
33937964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      self.project._properties['targets'].insert(0, all_target)
34037964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org
341366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    # The same, but for run_test_targets.
342366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    if len(run_test_targets) > 1:
3432fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      xccl = CreateXCConfigurationList(configurations)
3449cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org      run_all_tests_target = gyp.xcodeproj_file.PBXAggregateTarget(
3459cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org          {
3469cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org            'buildConfigurationList': xccl,
3479cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org            'name':                   'Run All Tests',
3489cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org          },
3499cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org          parent=self.project)
350366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org      for run_test_target in run_test_targets:
351366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org        run_all_tests_target.AddDependency(run_test_target)
3529cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
3539cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org      # Insert after the "All" target, which must exist if there is more than
3549cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org      # one run_test_target.
3559cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org      self.project._properties['targets'].insert(1, run_all_tests_target)
3569cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org
357c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org  def Finalize2(self, xcode_targets, xcode_target_to_target_dict):
35859bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # Finalize2 needs to happen in a separate step because the process of
35959bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # updating references to other projects depends on the ordering of targets
36059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # within remote project files.  Finalize1 is responsible for sorting duty,
36159bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # and once all project files are sorted, Finalize2 can come in and update
36259bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    # these references.
36359bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
36483891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # To support making a "test runner" target that will run all the tests
36583891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # that are direct dependents of any given target, we look for
36683891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    # xcode_create_dependents_test_runner being set on an Aggregate target,
367366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    # and generate a second target that will run the tests runners found under
368366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    # the marked target.
36983891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org    for bf_tgt in self.build_file_dict['targets']:
370ca359a77c83539ba95896309f9aacc0ba6180868mark@chromium.org      if int(bf_tgt.get('xcode_create_dependents_test_runner', 0)):
37183891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org        tgt_name = bf_tgt['target_name']
372821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com        toolset = bf_tgt['toolset']
37383891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org        qualified_target = gyp.common.QualifiedTarget(self.gyp_path,
374821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com                                                      tgt_name, toolset)
37583891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org        xcode_target = xcode_targets[qualified_target]
37683891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org        if isinstance(xcode_target, gyp.xcodeproj_file.PBXAggregateTarget):
377366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          # Collect all the run test targets.
378366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          all_run_tests = []
37983891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org          pbxtds = xcode_target.GetProperty('dependencies')
38083891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org          for pbxtd in pbxtds:
38183891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            pbxcip = pbxtd.GetProperty('targetProxy')
38283891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            dependency_xct = pbxcip.GetProperty('remoteGlobalIDString')
383731b434223c7b3cf8e03d1e40bb4ad513f786871thomasvl@chromium.org            if hasattr(dependency_xct, 'test_runner'):
384366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org              all_run_tests.append(dependency_xct.test_runner)
38583891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org
386366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          # Directly depend on all the runners as they depend on the target
387366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          # that builds them.
388366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org          if len(all_run_tests) > 0:
38983891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            run_all_target = gyp.xcodeproj_file.PBXAggregateTarget({
39083891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org                  'name':        'Run %s Tests' % tgt_name,
39183891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org                  'productName': tgt_name,
39283891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org                },
39383891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org                parent=self.project)
394366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org            for run_test_target in all_run_tests:
395366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org              run_all_target.AddDependency(run_test_target)
39683891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org
39783891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            # Insert the test runner after the related target.
39883891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            idx = self.project._properties['targets'].index(xcode_target)
39983891a83bee6f9ab4a471d2ef74ce5c28fc81c64thomasvl@chromium.org            self.project._properties['targets'].insert(idx + 1, run_all_target)
4000652ae9b86c7a3ede25695d8cc77d07ac448b40emark@chromium.org
40137964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    # Update all references to other projects, to make sure that the lists of
40237964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    # remote products are complete.  Otherwise, Xcode will fill them in when
40337964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    # it opens the project file, which will result in unnecessary diffs.
40437964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    # TODO(mark): This is evil because it relies on internal knowledge of
40537964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    # PBXProject._other_pbxprojects.
40637964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org    for other_pbxproject in self.project._other_pbxprojects.keys():
40737964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org      self.project.AddOrGetProjectReference(other_pbxproject)
40837964e1e03b108dd29cba4d9b87e05684b8bc504mark@chromium.org
40959bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    self.project.SortRemoteProductReferences()
41059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
41135d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    # Give everything an ID.
412a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org    self.project_file.ComputeIDs()
413475089d4d1a7ea54e6f25312d665da1e982d96d0mark@chromium.org
4147d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org    # Make sure that no two objects in the project file have the same ID.  If
4157d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org    # multiple objects wind up with the same ID, upon loading the file, Xcode
4167d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org    # will only recognize one object (the last one in the file?) and the
4177d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org    # results are unpredictable.
4187d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org    self.project_file.EnsureNoIDCollisions()
4197d90ffc5c63c7af8181d8bb388fa684b257d3010mark@chromium.org
420475089d4d1a7ea54e6f25312d665da1e982d96d0mark@chromium.org  def Write(self):
4212c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # Write the project file to a temporary location first.  Xcode watches for
4222c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # changes to the project file and presents a UI sheet offering to reload
4232c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # the project when it does change.  However, in some cases, especially when
4242c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # multiple projects are open or when Xcode is busy, things don't work so
4252c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # seamlessly.  Sometimes, Xcode is able to detect that a project file has
4262c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # changed but can't unload it because something else is referencing it.
4272c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # To mitigate this problem, and to avoid even having Xcode present the UI
4282c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # sheet when an open project is rewritten for inconsequential changes, the
4292c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # project file is written to a temporary file in the xcodeproj directory
4302c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # first.  The new temporary file is then compared to the existing project
4312c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # file, if any.  If they differ, the new file replaces the old; otherwise,
4322c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # the new project file is simply deleted.  Xcode properly detects a file
4332c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # being renamed over an open project file as a change and so it remains
4342c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # able to present the "project file changed" sheet under this system.
4352c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # Writing to a temporary file first also avoids the possible problem of
4362c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    # Xcode rereading an incomplete project file.
4372c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    (output_fd, new_pbxproj_path) = \
4382c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        tempfile.mkstemp(suffix='.tmp', prefix='project.pbxproj.gyp.',
4392c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org                         dir=self.path)
4402c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4412c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    try:
442ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org      output_file = os.fdopen(output_fd, 'wb')
4432c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4442c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      self.project_file.Print(output_file)
4452c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      output_file.close()
4462c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4472c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      pbxproj_path = os.path.join(self.path, 'project.pbxproj')
4482c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4492c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      same = False
4502c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      try:
4512c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        same = filecmp.cmp(pbxproj_path, new_pbxproj_path, False)
4522c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      except OSError, e:
4532c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        if e.errno != errno.ENOENT:
4542c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org          raise
4552c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4562c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      if same:
4572c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # The new file is identical to the old one, just get rid of the new
4582c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # one.
4592c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        os.unlink(new_pbxproj_path)
4602c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      else:
4612c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # The new file is different from the old one, or there is no old one.
4622c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # Rename the new file to the permanent name.
4632c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        #
4642c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # tempfile.mkstemp uses an overly restrictive mode, resulting in a
4652c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # file that can only be read by the owner, regardless of the umask.
4662c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # There's no reason to not respect the umask here, which means that
4672c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # an extra hoop is required to fetch it and reset the new file's mode.
4682c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        #
4692c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # No way to get the umask without setting a new one?  Set a safe one
4702c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        # and then set it back to the old value.
4712c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        umask = os.umask(077)
4722c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        os.umask(umask)
4732c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4742c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        os.chmod(new_pbxproj_path, 0666 & ~umask)
4752c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org        os.rename(new_pbxproj_path, pbxproj_path)
4762c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org
4772c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org    except Exception:
4782c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      # Don't leave turds behind.  In fact, if this code was responsible for
4792c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      # creating the xcodeproj directory, get rid of that too.
4802c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      os.unlink(new_pbxproj_path)
4816105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      if self.created_dir:
4826105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        shutil.rmtree(self.path, True)
4832c57244bcc526da737b23c01bce81b3a432018a6mark@chromium.org      raise
484a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
4854dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
4862f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.comdef AddSourceToTarget(source, type, pbxp, xct):
4879114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # TODO(mark): Perhaps source_extensions and library_extensions can be made a
4889114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # little bit fancier.
4891cb8ccdc9dadd727394f1b81ddcaa98b8ec34c69mark@chromium.org  source_extensions = ['c', 'cc', 'cpp', 'cxx', 'm', 'mm', 's']
4909114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org
4919114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # .o is conceptually more of a "source" than a "library," but Xcode thinks
4929114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # of "sources" as things to compile and "libraries" (or "frameworks") as
4939114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # things to link with. Adding an object file to an Xcode target's frameworks
4949114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  # phase works properly.
4959114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org  library_extensions = ['a', 'dylib', 'framework', 'o']
4969114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org
497ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org  basename = posixpath.basename(source)
498ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org  (root, ext) = posixpath.splitext(basename)
499365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org  if ext:
50093cb07e8ee315e78a0524c9a1bcf6fb69d874a6dbradnelson@google.com    ext = ext[1:].lower()
501df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org
5022f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com  if ext in source_extensions and type != 'none':
503df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org    xct.SourcesPhase().AddFile(source)
5042f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com  elif ext in library_extensions and type != 'none':
5059114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org    xct.FrameworksPhase().AddFile(source)
506df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org  else:
5079114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org    # Files that aren't added to a sources or frameworks build phase can still
5089114a1c5625522ba4a26ba25a87d4c58a9fc8bb4mark@chromium.org    # go into the project file, just not as part of a build phase.
5096eb312d5514774feaeb85bce5217d46f65c99f4cmark@chromium.org    pbxp.AddOrGetFileInRootGroup(source)
510a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
5114dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
512df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.orgdef AddResourceToTarget(resource, pbxp, xct):
51341b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org  # TODO(mark): Combine with AddSourceToTarget above?  Or just inline this call
51441b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org  # where it's used.
515df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org  xct.ResourcesPhase().AddFile(resource)
516df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org
517df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org
51829f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.orgdef AddHeaderToTarget(header, pbxp, xct, is_public):
519c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org  # TODO(mark): Combine with AddSourceToTarget above?  Or just inline this call
520c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org  # where it's used.
52129f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org  settings = '{ATTRIBUTES = (%s, ); }' % ('Private', 'Public')[is_public]
52229f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org  xct.HeadersPhase().AddFile(header, settings)
523c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org
524c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org
5256105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org_xcode_variable_re = re.compile('(\$\((.*?)\))')
5266105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.orgdef ExpandXcodeVariables(string, expansions):
5276105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  """Expands Xcode-style $(VARIABLES) in string per the expansions dict.
5284dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
5296105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  In some rare cases, it is appropriate to expand Xcode variables when a
5306105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  project file is generated.  For any substring $(VAR) in string, if VAR is a
5316105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  key in the expansions dict, $(VAR) will be replaced with expansions[VAR].
5326105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  Any $(VAR) substring in string for which VAR is not a key in the expansions
5336105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  dict will remain in the returned string.
5346105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  """
535581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org
5366105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  matches = _xcode_variable_re.findall(string)
537581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  if matches == None:
538581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    return string
5394dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
540581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  matches.reverse()
541581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org  for match in matches:
542581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org    (to_replace, variable) = match
5436105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    if not variable in expansions:
544581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org      continue
5454dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
5466105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    replacement = expansions[variable]
5476105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    string = re.sub(re.escape(to_replace), replacement, string)
548581bb7d62b84353879c8aeb1950127644f669799mark@chromium.org
5496105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org  return string
5504dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
5514dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org
552c1acc4ec18f3c15510d36c85e2b9f737df375c32mark@chromium.org_xcode_define_re = re.compile(r'([\\\"\' ])')
553c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.orgdef EscapeXcodeDefine(s):
554c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.org  """We must escape the defines that we give to XCode so that it knows not to
555c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.org     split on spaces and to respect backslash and quote literals. However, we
556c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.org     must not quote the define, or Xcode will incorrectly intepret variables
557c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.org     especially $(inherited)."""
558c1acc4ec18f3c15510d36c85e2b9f737df375c32mark@chromium.org  return re.sub(_xcode_define_re, r'\\\1', s)
559e604b70501586dd3396720641f652da55e5930aftschmelcher@chromium.org
56008fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org
56108fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.orgdef PerformBuild(data, configurations, params):
56208fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org  options = params['options']
56308fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org
56408fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org  for build_file, build_file_dict in data.iteritems():
56508fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    (build_file_root, build_file_ext) = os.path.splitext(build_file)
56608fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    if build_file_ext != '.gyp':
56708fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org      continue
56808fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    xcodeproj_path = build_file_root + options.suffix + '.xcodeproj'
56908fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    if options.generator_output:
57008fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org      xcodeproj_path = os.path.join(options.generator_output, xcodeproj_path)
57108fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org
57208fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org  for config in configurations:
57308fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    arguments = ['xcodebuild', '-project', xcodeproj_path]
57408fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    arguments += ['-configuration', config]
57508fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    print "Building [%s]: %s" % (config, arguments)
57608fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org    subprocess.check_call(arguments)
57708fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org
57808fc464f96f4d8664ae6549d9b7767e8d7fbfa6bsbc@chromium.org
5795c712802f5d54ec659ba0a1a9551ff53db110f41agl@chromium.orgdef GenerateOutput(target_list, target_dicts, data, params):
580d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org  # Optionally configure each spec to use ninja as the external builder.
581d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org  ninja_wrapper = params.get('flavor') == 'ninja'
582d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org  if ninja_wrapper:
583d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org    (target_list, target_dicts, data) = \
584d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org        gyp.xcode_ninja.CreateWrapper(target_list, target_dicts, data, params)
585d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org
5865c712802f5d54ec659ba0a1a9551ff53db110f41agl@chromium.org  options = params['options']
58762cccaf64f861d4a96f48c759ecbf476e9289bf1sgk@chromium.org  generator_flags = params.get('generator_flags', {})
58891ad8f838fbffd7d169788b5f5fe611a5ed1a872thomasvl@chromium.org  parallel_builds = generator_flags.get('xcode_parallel_builds', True)
589366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org  serialize_all_tests = \
590366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org      generator_flags.get('xcode_serialize_all_test_runs', True)
5912728d47070a86a333e111bd8807a132bd96d0cebthomasvl@chromium.org  project_version = generator_flags.get('xcode_project_version', None)
592bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org  skip_excluded_files = \
593bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org      not generator_flags.get('xcode_list_excluded_files', True)
594a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org  xcode_projects = {}
595a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org  for build_file, build_file_dict in data.iteritems():
59640a44668e6a551919713f058d3c2002c3308efdcmark@chromium.org    (build_file_root, build_file_ext) = os.path.splitext(build_file)
59740a44668e6a551919713f058d3c2002c3308efdcmark@chromium.org    if build_file_ext != '.gyp':
598bf684ddd17bb8e6c0ad08d3d693875a8c070e20amark@chromium.org      continue
5999c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org    xcodeproj_path = build_file_root + options.suffix + '.xcodeproj'
6009c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org    if options.generator_output:
6019c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org      xcodeproj_path = os.path.join(options.generator_output, xcodeproj_path)
6029c88b549cd9f1773ed472daa27d6cbd8bc690b41sgk@chromium.org    xcp = XcodeProject(build_file, xcodeproj_path, build_file_dict)
6032bafdca3c85166060506e2b06d393f735eb35747mark@chromium.org    xcode_projects[build_file] = xcp
6042bafdca3c85166060506e2b06d393f735eb35747mark@chromium.org    pbxp = xcp.project
6052bafdca3c85166060506e2b06d393f735eb35747mark@chromium.org
60691ad8f838fbffd7d169788b5f5fe611a5ed1a872thomasvl@chromium.org    if parallel_builds:
60791ad8f838fbffd7d169788b5f5fe611a5ed1a872thomasvl@chromium.org      pbxp.SetProperty('attributes',
60891ad8f838fbffd7d169788b5f5fe611a5ed1a872thomasvl@chromium.org                       {'BuildIndependentTargetsInParallel': 'YES'})
6092728d47070a86a333e111bd8807a132bd96d0cebthomasvl@chromium.org    if project_version:
6102728d47070a86a333e111bd8807a132bd96d0cebthomasvl@chromium.org      xcp.project_file.SetXcodeVersion(project_version)
61196f9b1cc4ed20262839d012a6ae854347a8f597bmark@chromium.org
612365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org    # Add gyp/gypi files to project
613365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org    if not generator_flags.get('standalone'):
614365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org      main_group = pbxp.GetProperty('mainGroup')
615365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org      build_group = gyp.xcodeproj_file.PBXGroup({'name': 'Build'})
616365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org      main_group.AppendChild(build_group)
617365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org      for included_file in build_file_dict['included_files']:
618365cdc471e361b0bad9f27cbe6cde76abc56a343sbc@chromium.org        build_group.AddOrGetFileByPath(included_file, False)
619a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
620a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org  xcode_targets = {}
621c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org  xcode_target_to_target_dict = {}
6227868141eb6a6211aeada7ef186e2d14dfda05929mark@chromium.org  for qualified_target in target_list:
623821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com    [build_file, target_name, toolset] = \
624821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com        gyp.common.ParseQualifiedTarget(qualified_target)
625821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com
6267868141eb6a6211aeada7ef186e2d14dfda05929mark@chromium.org    spec = target_dicts[qualified_target]
627821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com    if spec['toolset'] != 'target':
628821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com      raise Exception(
629821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com          'Multiple toolsets not supported in xcode build (target %s)' %
630821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com          qualified_target)
631fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org    configuration_names = [spec['default_configuration']]
632fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org    for configuration_name in sorted(spec['configurations'].keys()):
633fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org      if configuration_name not in configuration_names:
634fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org        configuration_names.append(configuration_name)
6359cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org    xcp = xcode_projects[build_file]
6369cc2a902440738e239ff7a860ec4c4c226a9c056mark@chromium.org    pbxp = xcp.project
637d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
638d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    # Set up the configurations for the target according to the list of names
639d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    # supplied.
6402fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    xccl = CreateXCConfigurationList(configuration_names)
641d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
642b80ff0aec12d63b3386cfd551ae450465295d29amark@chromium.org    # Create an XCTarget subclass object for the target. The type with
643b80ff0aec12d63b3386cfd551ae450465295d29amark@chromium.org    # "+bundle" appended will be used if the target has "mac_bundle" set.
644b80ff0aec12d63b3386cfd551ae450465295d29amark@chromium.org    # loadable_modules not in a mac_bundle are mapped to
645b80ff0aec12d63b3386cfd551ae450465295d29amark@chromium.org    # com.googlecode.gyp.xcode.bundle, a pseudo-type that xcode.py interprets
646b80ff0aec12d63b3386cfd551ae450465295d29amark@chromium.org    # to create a single-file mh_bundle.
647d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    _types = {
648f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'executable':                  'com.apple.product-type.tool',
649f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'loadable_module':             'com.googlecode.gyp.xcode.bundle',
650f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'shared_library':              'com.apple.product-type.library.dynamic',
651f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'static_library':              'com.apple.product-type.library.static',
652f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'executable+bundle':           'com.apple.product-type.application',
653f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'loadable_module+bundle':      'com.apple.product-type.bundle',
654f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'loadable_module+xctest':      'com.apple.product-type.bundle.unit-test',
655f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'shared_library+bundle':       'com.apple.product-type.framework',
656f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      'executable+extension+bundle': 'com.apple.product-type.app-extension',
657d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    }
658d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
659d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    target_properties = {
660d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org      'buildConfigurationList': xccl,
661d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org      'name':                   target_name,
662d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    }
663d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
664d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    type = spec['type']
665c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org    is_xctest = int(spec.get('mac_xctest_bundle', 0))
666c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org    is_bundle = int(spec.get('mac_bundle', 0)) or is_xctest
667f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org    is_extension = int(spec.get('ios_app_extension', 0))
668d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    if type != 'none':
669ef6985cb69e329799d36b6629d4926fe5422ebeethomasvl@chromium.org      type_bundle_key = type
670c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org      if is_xctest:
671c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org        type_bundle_key += '+xctest'
672c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org        assert type == 'loadable_module', (
673c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org            'mac_xctest_bundle targets must have type loadable_module '
674c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org            '(target %s)' % target_name)
675f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org      elif is_extension:
676f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org        assert is_bundle, ('ios_app_extension flag requires mac_bundle '
677f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org            '(target %s)' % target_name)
678f536bafefff908e7001e5634a5a75d29b1e2cab8sdefresne@chromium.org        type_bundle_key += '+extension+bundle'
679c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org      elif is_bundle:
680ef6985cb69e329799d36b6629d4926fe5422ebeethomasvl@chromium.org        type_bundle_key += '+bundle'
681c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org
682d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org      xctarget_type = gyp.xcodeproj_file.PBXNativeTarget
68337a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com      try:
68437a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com        target_properties['productType'] = _types[type_bundle_key]
68537a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com      except KeyError, e:
68637a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com        gyp.common.ExceptionAppend(e, "-- unknown product type while "
68737a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com                                   "writing target %s" % target_name)
68837a6f64232dc1dda7b605ee8dd39c33e2a4d24efgspencer@google.com        raise
689d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    else:
690d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org      xctarget_type = gyp.xcodeproj_file.PBXAggregateTarget
69174091530de3012a744af0e64239155002e79cb0dthakis@chromium.org      assert not is_bundle, (
69274091530de3012a744af0e64239155002e79cb0dthakis@chromium.org          'mac_bundle targets cannot have type none (target "%s")' %
69374091530de3012a744af0e64239155002e79cb0dthakis@chromium.org          target_name)
694c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org      assert not is_xctest, (
695c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org          'mac_xctest_bundle targets cannot have type none (target "%s")' %
696c0b8cd7cd914fcda8d0ed8215f458886321bc848mark@chromium.org          target_name)
697d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
6984433ceec4876f3db8aa845a5fe08aa8e0bae2b1cbradnelson@google.com    target_product_name = spec.get('product_name')
6994433ceec4876f3db8aa845a5fe08aa8e0bae2b1cbradnelson@google.com    if target_product_name is not None:
7002fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      target_properties['productName'] = target_product_name
701d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
7024d6b2679749b27ad04944251692892c17e5aa5a7mark@chromium.org    xct = xctarget_type(target_properties, parent=pbxp,
7034433ceec4876f3db8aa845a5fe08aa8e0bae2b1cbradnelson@google.com                        force_outdir=spec.get('product_dir'),
7044433ceec4876f3db8aa845a5fe08aa8e0bae2b1cbradnelson@google.com                        force_prefix=spec.get('product_prefix'),
7054433ceec4876f3db8aa845a5fe08aa8e0bae2b1cbradnelson@google.com                        force_extension=spec.get('product_extension'))
706d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org    pbxp.AppendProperty('targets', xct)
70735d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org    xcode_targets[qualified_target] = xct
708c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org    xcode_target_to_target_dict[xct] = spec
709d7c127eea478f455f80c57a051cf43c0b9f1a605mark@chromium.org
7102fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    spec_actions = spec.get('actions', [])
7112fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    spec_rules = spec.get('rules', [])
7122fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org
7132fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # Xcode has some "issues" with checking dependencies for the "Compile
7142fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # sources" step with any source files/headers generated by actions/rules.
7152fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # To work around this, if a target is building anything directly (not
716e8934c9af66645f47884f59ac08eb19f40269a40thakis@chromium.org    # type "none"), then a second target is used to run the GYP actions/rules
7172fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # and is made a dependency of this target.  This way the work is done
7182fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # before the dependency checks for what should be recompiled.
7192fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    support_xct = None
720d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org    # The Xcode "issues" don't affect xcode-ninja builds, since the dependency
721d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org    # logic all happens in ninja.  Don't bother creating the extra targets in
722d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org    # that case.
723d49e6fec9631d1c51a1fe34ff9ee9dd60fa692b7justincohen@chromium.org    if type != 'none' and (spec_actions or spec_rules) and not ninja_wrapper:
7242fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      support_xccl = CreateXCConfigurationList(configuration_names);
72507e497bd1c8f08e3d64b7b89b0d0fa741b3f0756mark@chromium.org      support_target_suffix = generator_flags.get(
72607e497bd1c8f08e3d64b7b89b0d0fa741b3f0756mark@chromium.org          'support_target_suffix', ' Support')
7272fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      support_target_properties = {
7282fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        'buildConfigurationList': support_xccl,
72907e497bd1c8f08e3d64b7b89b0d0fa741b3f0756mark@chromium.org        'name':                   target_name + support_target_suffix,
7302fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      }
7312fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      if target_product_name:
7322fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        support_target_properties['productName'] = \
7332fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org            target_product_name + ' Support'
7342fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      support_xct = \
7352fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          gyp.xcodeproj_file.PBXAggregateTarget(support_target_properties,
7362fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org                                                parent=pbxp)
7372fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      pbxp.AppendProperty('targets', support_xct)
7382fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      xct.AddDependency(support_xct)
7392fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # Hang the support target off the main target so it can be tested/found
7402fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    # by the generator during Finalize.
7412fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    xct.support_target = support_xct
7422fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org
7434dc2b8955125d56199bdd40e5c59ea1e2df79884mark@chromium.org    prebuild_index = 0
74435d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org
7456105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Add custom shell script phases for "actions" sections.
7462fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    for action in spec_actions:
747cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org      # There's no need to write anything into the script to ensure that the
748cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org      # output directories already exist, because Xcode will look at the
749cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org      # declared outputs and automatically ensure that they exist for us.
750debfd1043df75767ec9c2b91f9c0685faf13a669mark@chromium.org
75114cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # Do we have a message to print when this action runs?
75214cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      message = action.get('message')
75314cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      if message:
75414cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        message = 'echo note: ' + gyp.common.EncodePOSIXShellArgument(message)
75514cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      else:
75614cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        message = ''
75714cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org
7580be4796de4a43311feb4cca55725f685f5182581mark@chromium.org      # Turn the list into a string that can be passed to a shell.
7590be4796de4a43311feb4cca55725f685f5182581mark@chromium.org      action_string = gyp.common.EncodePOSIXShellList(action['action'])
7600be4796de4a43311feb4cca55725f685f5182581mark@chromium.org
7610c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org      # Convert Xcode-type variable references to sh-compatible environment
7620be4796de4a43311feb4cca55725f685f5182581mark@chromium.org      # variable references.
763aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com      message_sh = gyp.xcodeproj_file.ConvertVariablesToShellSyntax(message)
764aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com      action_string_sh = gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
765aaf279794cd1028f98a2cb9a2be3bca794afcf35gspencer@google.com        action_string)
7660be4796de4a43311feb4cca55725f685f5182581mark@chromium.org
76714cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      script = ''
76814cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # Include the optional message
76914cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      if message_sh:
77014cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        script += message_sh + '\n'
7710be4796de4a43311feb4cca55725f685f5182581mark@chromium.org      # Be sure the script runs in exec, and that if exec fails, the script
7720be4796de4a43311feb4cca55725f685f5182581mark@chromium.org      # exits signalling an error.
77314cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      script += 'exec ' + action_string_sh + '\nexit 1\n'
7740c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org      ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
7750c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org            'inputPaths': action['inputs'],
7760c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org            'name': 'Action "' + action['action_name'] + '"',
7770c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org            'outputPaths': action['outputs'],
7780c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org            'shellScript': script,
7790c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org            'showEnvVarsInLog': 0,
7800c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org          })
7810c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org
7822fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      if support_xct:
7832fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        support_xct.AppendProperty('buildPhases', ssbp)
7842fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org      else:
7852fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        # TODO(mark): this assumes too much knowledge of the internals of
7862fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        # xcodeproj_file; some of these smarts should move into xcodeproj_file
7872fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        # itself.
7882fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        xct._properties['buildPhases'].insert(prebuild_index, ssbp)
7892fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        prebuild_index = prebuild_index + 1
7900c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org
7913a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org      # TODO(mark): Should verify that at most one of these is specified.
7927136d45e1193828e00c026cb133289cc11cbcbf3ajwong@chromium.org      if int(action.get('process_outputs_as_sources', False)):
7930c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org        for output in action['outputs']:
7942f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com          AddSourceToTarget(output, type, pbxp, xct)
7950c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org
7967136d45e1193828e00c026cb133289cc11cbcbf3ajwong@chromium.org      if int(action.get('process_outputs_as_mac_bundle_resources', False)):
7973a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org        for output in action['outputs']:
7983a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org          AddResourceToTarget(output, pbxp, xct)
7993a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org
8006bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org    # tgt_mac_bundle_resources holds the list of bundle resources so
8016bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org    # the rule processing can check against it.
802ca359a77c83539ba95896309f9aacc0ba6180868mark@chromium.org    if is_bundle:
8036bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org      tgt_mac_bundle_resources = spec.get('mac_bundle_resources', [])
8046bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org    else:
8056bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org      tgt_mac_bundle_resources = []
8066bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org
8076105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Add custom shell script phases driving "make" for "rules" sections.
8086105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #
8096105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Xcode's built-in rule support is almost powerful enough to use directly,
8106105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # but there are a few significant deficiencies that render them unusable.
8116105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # There are workarounds for some of its inadequacies, but in aggregate,
8126105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # the workarounds added complexity to the generator, and some workarounds
8136105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # actually require input files to be crafted more carefully than I'd like.
8146105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Consequently, until Xcode rules are made more capable, "rules" input
8156105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # sections will be handled in Xcode output by shell script build phases
8166105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # performed prior to the compilation phase.
8176105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #
8186105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # The following problems with Xcode rules were found.  The numbers are
8196105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Apple radar IDs.  I hope that these shortcomings are addressed, I really
8206105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # liked having the rules handled directly in Xcode during the period that
8216105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # I was prototyping this.
8226105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #
8236105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # 6588600 Xcode compiles custom script rule outputs too soon, compilation
8246105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         fails.  This occurs when rule outputs from distinct inputs are
8256105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         interdependent.  The only workaround is to put rules and their
8266105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         inputs in a separate target from the one that compiles the rule
8276105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         outputs.  This requires input file cooperation and it means that
8286105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         process_outputs_as_sources is unusable.
8296105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # 6584932 Need to declare that custom rule outputs should be excluded from
8306105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         compilation.  A possible workaround is to lie to Xcode about a
8316105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         rule's output, giving it a dummy file it doesn't know how to
8326105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         compile.  The rule action script would need to touch the dummy.
8336105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # 6584839 I need a way to declare additional inputs to a custom rule.
8346105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         A possible workaround is a shell script phase prior to
8356105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         compilation that touches a rule's primary input files if any
8366105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         would-be additional inputs are newer than the output.  Modifying
8376105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         the source tree - even just modification times - feels dirty.
8386105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # 6564240 Xcode "custom script" build rules always dump all environment
8396105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         variables.  This is a low-prioroty problem and is not a
8406105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    #         show-stopper.
8416105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    rules_by_ext = {}
8422fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org    for rule in spec_rules:
8436105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      rules_by_ext[rule['extension']] = rule
844937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org
845937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # First, some definitions:
846937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      #
847937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # A "rule source" is a file that was listed in a target's "sources"
848937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # list and will have a rule applied to it on the basis of matching the
849937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # rule's "extensions" attribute.  Rule sources are direct inputs to
850937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # rules.
851937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      #
852937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # Rule definitions may specify additional inputs in their "inputs"
853937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # attribute.  These additional inputs are used for dependency tracking
854937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # purposes.
855937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      #
856937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # A "concrete output" is a rule output with input-dependent variables
857937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # resolved.  For example, given a rule with:
858937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      #   'extension': 'ext', 'outputs': ['$(INPUT_FILE_BASE).cc'],
859937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # if the target's "sources" list contained "one.ext" and "two.ext",
860937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # the "concrete output" for rule input "two.ext" would be "two.cc".  If
861937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # a rule specifies multiple outputs, each input file that the rule is
862937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # applied to will have the same number of concrete outputs.
863937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      #
864937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # If any concrete outputs are outdated or missing relative to their
865937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # corresponding rule_source or to any specified additional input, the
866937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # rule action must be performed to generate the concrete outputs.
867937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org
868937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # concrete_outputs_by_rule_source will have an item at the same index
869937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # as the rule['rule_sources'] that it corresponds to.  Each item is a
870937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # list of all of the concrete outputs for the rule_source.
8716105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      concrete_outputs_by_rule_source = []
872937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org
873937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # concrete_outputs_all is a flat list of all concrete outputs that this
874937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # rule is able to produce, given the known set of input files
875937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # (rule_sources) that apply to it.
8766105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      concrete_outputs_all = []
877937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org
87814cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # messages & actions are keyed by the same indices as rule['rule_sources']
87914cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # and concrete_outputs_by_rule_source.  They contain the message and
88014cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # action to perform after resolving input-dependent variables.  The
88114cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      # message is optional, in which case None is stored for each rule source.
88214cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org      messages = []
8836105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      actions = []
8846105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
8856105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      for rule_source in rule.get('rule_sources', []):
886cdaa7e6d2e12c7d592265333396c6d1307632e22scr@chromium.org        rule_source_dirname, rule_source_basename = \
887cdaa7e6d2e12c7d592265333396c6d1307632e22scr@chromium.org            posixpath.split(rule_source)
8886105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        (rule_source_root, rule_source_ext) = \
889ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org            posixpath.splitext(rule_source_basename)
8906105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
891937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # These are the same variable names that Xcode uses for its own native
892937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # rule support.  Because Xcode's rule engine is not being used, they
893937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # need to be expanded as they are written to the makefile.
8946105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        rule_input_dict = {
8956105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          'INPUT_FILE_BASE':   rule_source_root,
8966105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          'INPUT_FILE_SUFFIX': rule_source_ext,
8976105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          'INPUT_FILE_NAME':   rule_source_basename,
8986105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          'INPUT_FILE_PATH':   rule_source,
899cdaa7e6d2e12c7d592265333396c6d1307632e22scr@chromium.org          'INPUT_FILE_DIRNAME': rule_source_dirname,
9006105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        }
9016105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
9026105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        concrete_outputs_for_this_rule_source = []
9036105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        for output in rule.get('outputs', []):
904937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # Fortunately, Xcode and make both use $(VAR) format for their
905937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # variables, so the expansion is the only transformation necessary.
906937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # Any remaning $(VAR)-type variables in the string can be given
907937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # directly to make, which will pick up the correct settings from
908937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # what Xcode puts into the environment.
9096105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          concrete_output = ExpandXcodeVariables(output, rule_input_dict)
9106105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          concrete_outputs_for_this_rule_source.append(concrete_output)
9116105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
912937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # Add all concrete outputs to the project.
9136105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          pbxp.AddOrGetFileInRootGroup(concrete_output)
9146105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
9156105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        concrete_outputs_by_rule_source.append( \
9166105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            concrete_outputs_for_this_rule_source)
9176105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        concrete_outputs_all.extend(concrete_outputs_for_this_rule_source)
9183a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org
9193a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org        # TODO(mark): Should verify that at most one of these is specified.
920ca359a77c83539ba95896309f9aacc0ba6180868mark@chromium.org        if int(rule.get('process_outputs_as_sources', False)):
9216105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          for output in concrete_outputs_for_this_rule_source:
9222f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com            AddSourceToTarget(output, type, pbxp, xct)
9236105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
9246bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        # If the file came from the mac_bundle_resources list or if the rule
9256bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        # is marked to process outputs as bundle resource, do so.
9266bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        was_mac_bundle_resource = rule_source in tgt_mac_bundle_resources
9276bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        if was_mac_bundle_resource or \
928ca359a77c83539ba95896309f9aacc0ba6180868mark@chromium.org            int(rule.get('process_outputs_as_mac_bundle_resources', False)):
9293a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org          for output in concrete_outputs_for_this_rule_source:
9303a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org            AddResourceToTarget(output, pbxp, xct)
9313a3f1240c04c2a5120052b87646514f562bd88ecmark@chromium.org
93214cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        # Do we have a message to print when this rule runs?
93314cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        message = rule.get('message')
93414cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        if message:
93514cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org          message = gyp.common.EncodePOSIXShellArgument(message)
936ec670df7ec53a964ae4a2b9f39265598c286c0d4thomasvl@chromium.org          message = ExpandXcodeVariables(message, rule_input_dict)
93714cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        messages.append(message)
93814cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org
9390be4796de4a43311feb4cca55725f685f5182581mark@chromium.org        # Turn the list into a string that can be passed to a shell.
9400be4796de4a43311feb4cca55725f685f5182581mark@chromium.org        action_string = gyp.common.EncodePOSIXShellList(rule['action'])
9410be4796de4a43311feb4cca55725f685f5182581mark@chromium.org
9420be4796de4a43311feb4cca55725f685f5182581mark@chromium.org        action = ExpandXcodeVariables(action_string, rule_input_dict)
9436105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        actions.append(action)
9446105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
9456105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      if len(concrete_outputs_all) > 0:
9466105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # TODO(mark): There's a possibilty for collision here.  Consider
9476105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # target "t" rule "A_r" and target "t_A" rule "r".
948e7771c0907843e73a75222a2bde44c0cdfe3f6a7thakis@chromium.org        makefile_name = '%s.make' % re.sub(
949e7771c0907843e73a75222a2bde44c0cdfe3f6a7thakis@chromium.org            '[^a-zA-Z0-9_]', '_' , '%s_%s' % (target_name, rule['rule_name']))
9506105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        makefile_path = os.path.join(xcode_projects[build_file].path,
9516105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org                                     makefile_name)
952937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # TODO(mark): try/close?  Write to a temporary file and swap it only
953937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # if it's got changes?
954ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org        makefile = open(makefile_path, 'wb')
9556105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
956937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # make will build the first target in the makefile by default.  By
957937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # convention, it's called "all".  List all (or at least one)
958937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # concrete output for each rule source as a prerequisite of the "all"
959937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org        # target.
9606105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        makefile.write('all: \\\n')
9616105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        for concrete_output_index in \
9626105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            xrange(0, len(concrete_outputs_by_rule_source)):
9636105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          # Only list the first (index [0]) concrete output of each input
9646105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          # in the "all" target.  Otherwise, a parallel make (-j > 1) would
9656105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          # attempt to process each input multiple times simultaneously.
9666105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          # Otherwise, "all" could just contain the entire list of
9676105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          # concrete_outputs_all.
9686105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          concrete_output = \
9696105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              concrete_outputs_by_rule_source[concrete_output_index][0]
9706105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          if concrete_output_index == len(concrete_outputs_by_rule_source) - 1:
9716105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            eol = ''
9726105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          else:
9736105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            eol = ' \\'
9746105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          makefile.write('    %s%s\n' % (concrete_output, eol))
9756105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
97614cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org        for (rule_source, concrete_outputs, message, action) in \
97714cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org            zip(rule['rule_sources'], concrete_outputs_by_rule_source,
97814cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org                messages, actions):
979937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          makefile.write('\n')
980937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org
981937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # Add a rule that declares it can build each concrete output of a
982cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          # rule source.  Collect the names of the directories that are
983cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          # required.
984cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          concrete_output_dirs = []
9856105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          for concrete_output_index in xrange(0, len(concrete_outputs)):
9866105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            concrete_output = concrete_outputs[concrete_output_index]
9876105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            if concrete_output_index == 0:
9886105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              bol = ''
9896105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            else:
9906105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              bol = '    '
9916105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            makefile.write('%s%s \\\n' % (bol, concrete_output))
9926105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
993ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org            concrete_output_dir = posixpath.dirname(concrete_output)
99445a90d82e8c974fb09788af03418fccf97e03f29sgk@chromium.org            if (concrete_output_dir and
99545a90d82e8c974fb09788af03418fccf97e03f29sgk@chromium.org                concrete_output_dir not in concrete_output_dirs):
996cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org              concrete_output_dirs.append(concrete_output_dir)
997cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org
9986105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          makefile.write('    : \\\n')
9996105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
1000937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # The prerequisites for this rule are the rule source itself and
1001937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # the set of additional rule inputs, if any.
1002937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          prerequisites = [rule_source]
1003937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          prerequisites.extend(rule.get('inputs', []))
1004937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          for prerequisite_index in xrange(0, len(prerequisites)):
1005937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org            prerequisite = prerequisites[prerequisite_index]
1006937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org            if prerequisite_index == len(prerequisites) - 1:
10076105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              eol = ''
10086105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            else:
10096105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              eol = ' \\'
1010937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org            makefile.write('    %s%s\n' % (prerequisite, eol))
10116105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
1012cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          # Make sure that output directories exist before executing the rule
1013cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          # action.
1014cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org          if len(concrete_output_dirs) > 0:
1015ec670df7ec53a964ae4a2b9f39265598c286c0d4thomasvl@chromium.org            makefile.write('\t@mkdir -p "%s"\n' %
1016ec670df7ec53a964ae4a2b9f39265598c286c0d4thomasvl@chromium.org                           '" "'.join(concrete_output_dirs))
1017cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org
101814cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org          # The rule message and action have already had the necessary variable
1019937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org          # substitutions performed.
102014cfb71e1e4c67b0779999a7cf4eb6be2e2c2ed1thomasvl@chromium.org          if message:
1021ec670df7ec53a964ae4a2b9f39265598c286c0d4thomasvl@chromium.org            # Mark it with note: so Xcode picks it up in build output.
1022ec670df7ec53a964ae4a2b9f39265598c286c0d4thomasvl@chromium.org            makefile.write('\t@echo note: %s\n' % message)
10236105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          makefile.write('\t%s\n' % action)
10246105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
10256105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        makefile.close()
10266105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
1027cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org        # It might be nice to ensure that needed output directories exist
1028cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org        # here rather than in each target in the Makefile, but that wouldn't
1029cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org        # work if there ever was a concrete output that had an input-dependent
1030cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org        # variable anywhere other than in the leaf position.
10310336cc25f6c699536b570c60dcf220a0d48321cdmark@chromium.org
10326105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # Don't declare any inputPaths or outputPaths.  If they're present,
10336105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # Xcode will provide a slight optimization by only running the script
10346105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # phase if any output is missing or outdated relative to any input.
10356105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # Unfortunately, it will also assume that all outputs are touched by
10366105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # the script, and if the outputs serve as files in a compilation
10376105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # phase, they will be unconditionally rebuilt.  Since make might not
10386105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # rebuild everything that could be declared here as an output, this
10396105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # extra compilation activity is unnecessary.  With inputPaths and
10406105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # outputPaths not supplied, make will always be called, but it knows
10416105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # enough to not do anything when everything is up-to-date.
10425270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org
10435270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # To help speed things up, pass -j COUNT to make so it does some work
10445270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # in parallel.  Don't use ncpus because Xcode will build ncpus targets
10455270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # in parallel and if each target happens to have a rules step, there
10465270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # would be ncpus^2 things going.  With a machine that has 2 quad-core
10475270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # Xeons, a build can quickly run out of processes based on
10485270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org        # scheduling/other tasks, and randomly failing builds are no good.
1049cb6f53f225b902a519e22f391e3907ea4acd8968mark@chromium.org        script = \
105028a7a94e52dc391488a6b9e79c4c39a60643f31cmark@chromium.org"""JOB_COUNT="$(/usr/sbin/sysctl -n hw.ncpu)"
10515270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.orgif [ "${JOB_COUNT}" -gt 4 ]; then
10525270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.org  JOB_COUNT=4
10535270fcdd9af421a59ad14994938d133334656a15thomasvl@chromium.orgfi
1054cb1c21bb0aa903c094b7c4fe273fb8bf88b7354fmark@chromium.orgexec xcrun make -f "${PROJECT_FILE_PATH}/%s" -j "${JOB_COUNT}"
10556105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.orgexit 1
10566105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org""" % makefile_name
10576105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
10586105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              'name': 'Rule "' + rule['rule_name'] + '"',
10596105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              'shellScript': script,
10606105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org              'showEnvVarsInLog': 0,
10616105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org            })
10626105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
10632fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        if support_xct:
10642fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          support_xct.AppendProperty('buildPhases', ssbp)
10652fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        else:
10662fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          # TODO(mark): this assumes too much knowledge of the internals of
10672fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          # xcodeproj_file; some of these smarts should move into xcodeproj_file
10682fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          # itself.
10692fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          xct._properties['buildPhases'].insert(prebuild_index, ssbp)
10702fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          prebuild_index = prebuild_index + 1
10716105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
1072937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # Extra rule inputs also go into the project file.  Concrete outputs were
1073937c5e2af9d90e88558b2cf4edbf2c4f8132e63bmark@chromium.org      # already added when they were computed.
1074bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org      groups = ['inputs', 'inputs_excluded']
1075bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org      if skip_excluded_files:
1076bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org        groups = [x for x in groups if not x.endswith('_excluded')]
1077bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org      for group in groups:
10786105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        for item in rule.get(group, []):
10796105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org          pbxp.AddOrGetFileInRootGroup(item)
10806105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org
10816105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org    # Add "sources".
10820c3d85d2650c028b4f8887615d1c1defbad9cec5mark@chromium.org    for source in spec.get('sources', []):
1083ff3d7702d6dba2fa383fb695f25fd8e4f151237ethomasvl@chromium.org      (source_root, source_extension) = posixpath.splitext(source)
10846bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org      if source_extension[1:] not in rules_by_ext:
10856105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # AddSourceToTarget will add the file to a root group if it's not
10866105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        # already there.
10872f50e033f2bbdde2b168177750995e54a98a7375bradnelson@google.com        AddSourceToTarget(source, type, pbxp, xct)
10886105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org      else:
10896105598b39c567277bd230431a3e7a1b83f4447bmark@chromium.org        pbxp.AddOrGetFileInRootGroup(source)
1090dbd5170901cfeb753bd7cee93b5b7c40dcc76337mark@chromium.org
10914cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org    # Add "mac_bundle_resources" and "mac_framework_private_headers" if
10924cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org    # it's a bundle of any type.
1093ca359a77c83539ba95896309f9aacc0ba6180868mark@chromium.org    if is_bundle:
10946bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org      for resource in tgt_mac_bundle_resources:
10956bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        (resource_root, resource_extension) = posixpath.splitext(resource)
10966bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        if resource_extension[1:] not in rules_by_ext:
10976bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org          AddResourceToTarget(resource, pbxp, xct)
10986bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org        else:
10996bfd4530db863baa22944fe9564d9f12586ab472thomasvl@chromium.org          pbxp.AddOrGetFileInRootGroup(resource)
1100df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org
110129f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org      for header in spec.get('mac_framework_private_headers', []):
110229f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org        AddHeaderToTarget(header, pbxp, xct, False)
1103c5ff18b9991ffd97d70e296ff3e042c25ed9879dmark@chromium.org
11044cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org    # Add "mac_framework_headers". These can be valid for both frameworks
11054cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org    # and static libraries.
11064cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org    if is_bundle or type == 'static_library':
11074cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org      for header in spec.get('mac_framework_headers', []):
11084cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org        AddHeaderToTarget(header, pbxp, xct, True)
11094cfea613522a978e2d34c4b7197fb460b6b4f8afthakis@chromium.org
1110660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org    # Add "copies".
11115f30bb815433b9feea320d7221182e79363848f1mark@chromium.org    pbxcp_dict = {}
1112660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org    for copy_group in spec.get('copies', []):
11134f6c7bda24f785ad4a7a3e084c5999329dc05660sgk@chromium.org      dest = copy_group['destination']
11144f6c7bda24f785ad4a7a3e084c5999329dc05660sgk@chromium.org      if dest[0] not in ('/', '$'):
11154f6c7bda24f785ad4a7a3e084c5999329dc05660sgk@chromium.org        # Relative paths are relative to $(SRCROOT).
11164f6c7bda24f785ad4a7a3e084c5999329dc05660sgk@chromium.org        dest = '$(SRCROOT)/' + dest
1117660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org
11185f30bb815433b9feea320d7221182e79363848f1mark@chromium.org      # Coalesce multiple "copies" sections in the same target with the same
11195f30bb815433b9feea320d7221182e79363848f1mark@chromium.org      # "destination" property into the same PBXCopyFilesBuildPhase, otherwise
11205f30bb815433b9feea320d7221182e79363848f1mark@chromium.org      # they'll wind up with ID collisions.
11215f30bb815433b9feea320d7221182e79363848f1mark@chromium.org      pbxcp = pbxcp_dict.get(dest, None)
11225f30bb815433b9feea320d7221182e79363848f1mark@chromium.org      if pbxcp is None:
11235f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        pbxcp = gyp.xcodeproj_file.PBXCopyFilesBuildPhase({
11245f30bb815433b9feea320d7221182e79363848f1mark@chromium.org              'name': 'Copy to ' + copy_group['destination']
11255f30bb815433b9feea320d7221182e79363848f1mark@chromium.org            },
11265f30bb815433b9feea320d7221182e79363848f1mark@chromium.org            parent=xct)
11275f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        pbxcp.SetDestination(dest)
11285f30bb815433b9feea320d7221182e79363848f1mark@chromium.org
11295f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        # TODO(mark): The usual comment about this knowing too much about
11305f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        # gyp.xcodeproj_file internals applies.
11315f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        xct._properties['buildPhases'].insert(prebuild_index, pbxcp)
11325f30bb815433b9feea320d7221182e79363848f1mark@chromium.org
11335f30bb815433b9feea320d7221182e79363848f1mark@chromium.org        pbxcp_dict[dest] = pbxcp
1134660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org
1135660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org      for file in copy_group['files']:
1136660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org        pbxcp.AddFile(file)
1137660f956666798a1d732cc612d2adbaeac4fe3b14mark@chromium.org
11386eb312d5514774feaeb85bce5217d46f65c99f4cmark@chromium.org    # Excluded files can also go into the project file.
1139bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org    if not skip_excluded_files:
114029f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org      for key in ['sources', 'mac_bundle_resources', 'mac_framework_headers',
114129f8e2ce4be5527f72745c7345e7b48b090630ccmark@chromium.org                  'mac_framework_private_headers']:
1142bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org        excluded_key = key + '_excluded'
1143bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org        for item in spec.get(excluded_key, []):
1144bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org          pbxp.AddOrGetFileInRootGroup(item)
11457bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org
1146925aac506abf3125b8e7d5024b40897196afbd0dmark@chromium.org    # So can "inputs" and "outputs" sections of "actions" groups.
1147bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org    groups = ['inputs', 'inputs_excluded', 'outputs', 'outputs_excluded']
1148bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org    if skip_excluded_files:
1149bc09226aa146f71557f6f22ed54fe21f9dd9ff6bthomasvl@chromium.org      groups = [x for x in groups if not x.endswith('_excluded')]
1150df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org    for action in spec.get('actions', []):
1151df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org      for group in groups:
1152df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org        for item in action.get(group, []):
1153df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org          # Exclude anything in BUILT_PRODUCTS_DIR.  They're products, not
1154df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org          # sources.
1155df568f04a7a521e316155e3bda790ad664b18cb1mark@chromium.org          if not item.startswith('$(BUILT_PRODUCTS_DIR)/'):
11566eb312d5514774feaeb85bce5217d46f65c99f4cmark@chromium.org            pbxp.AddOrGetFileInRootGroup(item)
1157925aac506abf3125b8e7d5024b40897196afbd0dmark@chromium.org
11586d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org    for postbuild in spec.get('postbuilds', []):
11596d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org      action_string_sh = gyp.common.EncodePOSIXShellList(postbuild['action'])
11606d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org      script = 'exec ' + action_string_sh + '\nexit 1\n'
116126d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org
116226d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org      # Make the postbuild step depend on the output of ld or ar from this
116326d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org      # target. Apparently putting the script step after the link step isn't
116426d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org      # sufficient to ensure proper ordering in all cases. With an input
116526d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org      # declared but no outputs, the script step should run every time, as
116626d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org      # desired.
11676d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org      ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
116826d2ce054dc62a3d78d520b4cbaa72272b0fabe1mark@chromium.org            'inputPaths': ['$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_PATH)'],
11696d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org            'name': 'Postbuild "' + postbuild['postbuild_name'] + '"',
11706d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org            'shellScript': script,
11716d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org            'showEnvVarsInLog': 0,
11726d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org          })
11736d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org      xct.AppendProperty('buildPhases', ssbp)
11746d246822bc38b8fb10f1b7b1e583e47f4babf765mark@chromium.org
11757bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # Add dependencies before libraries, because adding a dependency may imply
11767bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # adding a library.  It's preferable to keep dependencies listed first
11777bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # during a link phase so that they can override symbols that would
11787bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # otherwise be provided by libraries, which will usually include system
11797bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # libraries.  On some systems, ld is finicky and even requires the
11807bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # libraries to be ordered in such a way that unresolved symbols in
11817bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # earlier-listed libraries may only be resolved by later-listed libraries.
11827bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # The Mac linker doesn't work that way, but other platforms do, and so
11837bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # their linker invocations need to be constructed in this way.  There's
11847bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    # no compelling reason for Xcode's linker invocations to differ.
11857bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org
118642ca7d073c71f0f4b7e7f1255ae4e915eafecb8fmark@chromium.org    if 'dependencies' in spec:
118742ca7d073c71f0f4b7e7f1255ae4e915eafecb8fmark@chromium.org      for dependency in spec['dependencies']:
118835d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org        xct.AddDependency(xcode_targets[dependency])
11892fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        # The support project also gets the dependencies (in case they are
11902fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        # needed for the actions/rules to work).
11912fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org        if support_xct:
11922fa5568850ffcf19f5c8631cec4e4cd65236eaf3thomasvl@chromium.org          support_xct.AddDependency(xcode_targets[dependency])
1193a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org
11947bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org    if 'libraries' in spec:
11957bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org      for library in spec['libraries']:
119635d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org        xct.FrameworksPhase().AddFile(library)
1197ee5a85a1fe3f930d233487bdd3f2139fb6d0197fmark@chromium.org        # Add the library's directory to LIBRARY_SEARCH_PATHS if necessary.
1198ee5a85a1fe3f930d233487bdd3f2139fb6d0197fmark@chromium.org        # I wish Xcode handled this automatically.
11996b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org        library_dir = posixpath.dirname(library)
12006b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org        if library_dir not in xcode_standard_library_dirs and (
12016b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org            not xct.HasBuildSetting(_library_search_paths_var) or
12026b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org            library_dir not in xct.GetBuildSetting(_library_search_paths_var)):
12036b4f0fecc1ebaf7f785ae4f1efbc193b49356500mark@chromium.org          xct.AppendBuildSetting(_library_search_paths_var, library_dir)
120435d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org
1205fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org    for configuration_name in configuration_names:
1206fbafe02cf1d9050ef42e598af10eac66041ede3fmark@chromium.org      configuration = spec['configurations'][configuration_name]
120735d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org      xcbc = xct.ConfigurationNamed(configuration_name)
120841b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org      for include_dir in configuration.get('mac_framework_dirs', []):
120941b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org        xcbc.AppendBuildSetting('FRAMEWORK_SEARCH_PATHS', include_dir)
121041b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org      for include_dir in configuration.get('include_dirs', []):
121141b29907da6ab12d5f831de9e324ceac2302bd50mark@chromium.org        xcbc.AppendBuildSetting('HEADER_SEARCH_PATHS', include_dir)
1212f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com      for library_dir in configuration.get('library_dirs', []):
1213f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com        if library_dir not in xcode_standard_library_dirs and (
1214f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com            not xcbc.HasBuildSetting(_library_search_paths_var) or
1215f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com            library_dir not in xcbc.GetBuildSetting(_library_search_paths_var)):
1216f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com          xcbc.AppendBuildSetting(_library_search_paths_var, library_dir)
1217f37c83ad866169311ce41cb09b32116c00c82244dimator@google.com
121835d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org      if 'defines' in configuration:
121935d23def8cd397d452a491a0ac7aa8bc1720c7d9mark@chromium.org        for define in configuration['defines']:
1220c00a2e4dd4094100a45651d8ef48e68df1453a5cmark@chromium.org          set_define = EscapeXcodeDefine(define)
12217f5db5ccaadb3b469204da6e9c16c21e2d625cf3mark@chromium.org          xcbc.AppendBuildSetting('GCC_PREPROCESSOR_DEFINITIONS', set_define)
1222407804ed35f70aa71aaf2302c552a2acb537c1d8mark@chromium.org      if 'xcode_settings' in configuration:
1223407804ed35f70aa71aaf2302c552a2acb537c1d8mark@chromium.org        for xck, xcv in configuration['xcode_settings'].iteritems():
1224407804ed35f70aa71aaf2302c552a2acb537c1d8mark@chromium.org          xcbc.SetBuildSetting(xck, xcv)
1225d13b3844deb56e21c867495e3e7ec2e0e114184cstuartmorgan@chromium.org      if 'xcode_config_file' in configuration:
1226d13b3844deb56e21c867495e3e7ec2e0e114184cstuartmorgan@chromium.org        config_ref = pbxp.AddOrGetFileInRootGroup(
1227d13b3844deb56e21c867495e3e7ec2e0e114184cstuartmorgan@chromium.org            configuration['xcode_config_file'])
1228d13b3844deb56e21c867495e3e7ec2e0e114184cstuartmorgan@chromium.org        xcbc.SetBaseConfiguration(config_ref)
12297bf9d8be9e8ea36b38da8093730af981c33c6ca3mark@chromium.org
123059bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org  build_files = []
1231a85d7de5129e37e558dc6e9c70ded4d300a71c5emark@chromium.org  for build_file, build_file_dict in data.iteritems():
1232bf684ddd17bb8e6c0ad08d3d693875a8c070e20amark@chromium.org    if build_file.endswith('.gyp'):
123359bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org      build_files.append(build_file)
1234475089d4d1a7ea54e6f25312d665da1e982d96d0mark@chromium.org
123559bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org  for build_file in build_files:
1236366c66406ae02027430da1a30554810bcfc7f90ethomasvl@chromium.org    xcode_projects[build_file].Finalize1(xcode_targets, serialize_all_tests)
123759bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
123859bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org  for build_file in build_files:
1239c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org    xcode_projects[build_file].Finalize2(xcode_targets,
1240c4e3156b31433b6ab7f607975c22cf466fcf0051thomasvl@chromium.org                                         xcode_target_to_target_dict)
124159bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org
124259bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org  for build_file in build_files:
124359bcf62c6030f720b82251b2464fcc632c0ad8bamark@chromium.org    xcode_projects[build_file].Write()
1244