~ubuntu-branches/ubuntu/oneiric/mozc/oneiric

« back to all changes in this revision

Viewing changes to third_party/gyp/pylib/gyp/generator/xcode.py

  • Committer: Bazaar Package Importer
  • Author(s): Nobuhiro Iwamatsu
  • Date: 2010-07-14 03:26:47 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100714032647-13qjisj6m8cm8jdx
Tags: 0.12.410.102-1
* New upstream release (Closes: #588971).
  - Add mozc-server, mozc-utils-gui and scim-mozc packages.
* Update debian/rules.
  Add --gypdir option to build_mozc.py.
* Update debian/control.
  - Bumped standards-version to 3.9.0.
  - Update description.
* Add mozc icon (Closes: #588972).
* Add patch which revises issue 18.
  ibus_mozc_issue18.patch
* kFreeBSD build support.
  support_kfreebsd.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# Copyright (c) 2010 Google Inc. All rights reserved.
 
4
# Use of this source code is governed by a BSD-style license that can be
 
5
# found in the LICENSE file.
 
6
 
 
7
import filecmp
 
8
import gyp.common
 
9
import gyp.xcodeproj_file
 
10
import errno
 
11
import os
 
12
import posixpath
 
13
import re
 
14
import shutil
 
15
import subprocess
 
16
import tempfile
 
17
 
 
18
 
 
19
# Project files generated by this module will use _intermediate_var as a
 
20
# custom Xcode setting whose value is a DerivedSources-like directory that's
 
21
# project-specific and configuration-specific.  The normal choice,
 
22
# DERIVED_FILE_DIR, is target-specific, which is thought to be too restrictive
 
23
# as it is likely that multiple targets within a single project file will want
 
24
# to access the same set of generated files.  The other option,
 
25
# PROJECT_DERIVED_FILE_DIR, is unsuitable because while it is project-specific,
 
26
# it is not configuration-specific.  INTERMEDIATE_DIR is defined as
 
27
# $(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION).
 
28
_intermediate_var = 'INTERMEDIATE_DIR'
 
29
 
 
30
# SHARED_INTERMEDIATE_DIR is the same, except that it is shared among all
 
31
# targets that share the same BUILT_PRODUCTS_DIR.
 
32
_shared_intermediate_var = 'SHARED_INTERMEDIATE_DIR'
 
33
 
 
34
generator_default_variables = {
 
35
  'EXECUTABLE_PREFIX': '',
 
36
  'EXECUTABLE_SUFFIX': '',
 
37
  'STATIC_LIB_PREFIX': 'lib',
 
38
  'SHARED_LIB_PREFIX': 'lib',
 
39
  'STATIC_LIB_SUFFIX': '.a',
 
40
  'SHARED_LIB_SUFFIX': '.dylib',
 
41
  # INTERMEDIATE_DIR is a place for targets to build up intermediate products.
 
42
  # It is specific to each build environment.  It is only guaranteed to exist
 
43
  # and be constant within the context of a project, corresponding to a single
 
44
  # input file.  Some build environments may allow their intermediate directory
 
45
  # to be shared on a wider scale, but this is not guaranteed.
 
46
  'INTERMEDIATE_DIR': '$(%s)' % _intermediate_var,
 
47
  'OS': 'mac',
 
48
  'PRODUCT_DIR': '$(BUILT_PRODUCTS_DIR)',
 
49
  'LIB_DIR': '$(BUILT_PRODUCTS_DIR)',
 
50
  'RULE_INPUT_ROOT': '$(INPUT_FILE_BASE)',
 
51
  'RULE_INPUT_EXT': '$(INPUT_FILE_SUFFIX)',
 
52
  'RULE_INPUT_NAME': '$(INPUT_FILE_NAME)',
 
53
  'RULE_INPUT_PATH': '$(INPUT_FILE_PATH)',
 
54
  'SHARED_INTERMEDIATE_DIR': '$(%s)' % _shared_intermediate_var,
 
55
  'CONFIGURATION_NAME': '$(CONFIGURATION)',
 
56
}
 
57
 
 
58
# The Xcode-specific sections that hold paths.
 
59
generator_additional_path_sections = [
 
60
  'mac_bundle_resources',
 
61
  # 'mac_framework_dirs', input already handles _dirs endings.
 
62
]
 
63
 
 
64
# The Xcode-specific keys that exist on targets and aren't moved down to
 
65
# configurations.
 
66
generator_additional_non_configuration_keys = [
 
67
  'mac_bundle',
 
68
  'mac_bundle_resources',
 
69
  'xcode_create_dependents_test_runner',
 
70
]
 
71
 
 
72
# We want to let any rules apply to files that are resources also.
 
73
generator_extra_sources_for_rules = [
 
74
  'mac_bundle_resources',
 
75
]
 
76
 
 
77
 
 
78
def CreateXCConfigurationList(configuration_names):
 
79
  xccl = gyp.xcodeproj_file.XCConfigurationList({'buildConfigurations': []})
 
80
  for configuration_name in configuration_names:
 
81
    xcbc = gyp.xcodeproj_file.XCBuildConfiguration({
 
82
        'name': configuration_name})
 
83
    xccl.AppendProperty('buildConfigurations', xcbc)
 
84
  xccl.SetProperty('defaultConfigurationName', configuration_names[0])
 
85
  return xccl
 
86
 
 
87
 
 
88
class XcodeProject(object):
 
89
  def __init__(self, gyp_path, path, build_file_dict):
 
90
    self.gyp_path = gyp_path
 
91
    self.path = path
 
92
    self.project = gyp.xcodeproj_file.PBXProject(path=path)
 
93
    projectDirPath = gyp.common.RelativePath(
 
94
                         os.path.dirname(os.path.abspath(self.gyp_path)),
 
95
                         os.path.dirname(path) or '.')
 
96
    self.project.SetProperty('projectDirPath', projectDirPath)
 
97
    self.project_file = \
 
98
        gyp.xcodeproj_file.XCProjectFile({'rootObject': self.project})
 
99
    self.build_file_dict = build_file_dict
 
100
 
 
101
    # TODO(mark): add destructor that cleans up self.path if created_dir is
 
102
    # True and things didn't complete successfully.  Or do something even
 
103
    # better with "try"?
 
104
    self.created_dir = False
 
105
    try:
 
106
      os.makedirs(self.path)
 
107
      self.created_dir = True
 
108
    except OSError, e:
 
109
      if e.errno != errno.EEXIST:
 
110
        raise
 
111
 
 
112
  def Finalize1(self, xcode_targets, serialize_all_tests):
 
113
    # Collect a list of all of the build configuration names used by the
 
114
    # various targets in the file.  It is very heavily advised to keep each
 
115
    # target in an entire project (even across multiple project files) using
 
116
    # the same set of configuration names.
 
117
    configurations = []
 
118
    for xct in self.project.GetProperty('targets'):
 
119
      xccl = xct.GetProperty('buildConfigurationList')
 
120
      xcbcs = xccl.GetProperty('buildConfigurations')
 
121
      for xcbc in xcbcs:
 
122
        name = xcbc.GetProperty('name')
 
123
        if name not in configurations:
 
124
          configurations.append(name)
 
125
 
 
126
    # Replace the XCConfigurationList attached to the PBXProject object with
 
127
    # a new one specifying all of the configuration names used by the various
 
128
    # targets.
 
129
    try:
 
130
      xccl = CreateXCConfigurationList(configurations)
 
131
      self.project.SetProperty('buildConfigurationList', xccl)
 
132
    except:
 
133
      import sys
 
134
      sys.stderr.write("Problem with gyp file %s\n" % self.gyp_path)
 
135
      raise
 
136
 
 
137
    # The need for this setting is explained above where _intermediate_var is
 
138
    # defined.  The comments below about wanting to avoid project-wide build
 
139
    # settings apply here too, but this needs to be set on a project-wide basis
 
140
    # so that files relative to the _intermediate_var setting can be displayed
 
141
    # properly in the Xcode UI.
 
142
    #
 
143
    # Note that for configuration-relative files such as anything relative to
 
144
    # _intermediate_var, for the purposes of UI tree view display, Xcode will
 
145
    # only resolve the configuration name once, when the project file is
 
146
    # opened.  If the active build configuration is changed, the project file
 
147
    # must be closed and reopened if it is desired for the tree view to update.
 
148
    # This is filed as Apple radar 6588391.
 
149
    xccl.SetBuildSetting(_intermediate_var,
 
150
                         '$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)')
 
151
    xccl.SetBuildSetting(_shared_intermediate_var,
 
152
                         '$(SYMROOT)/DerivedSources/$(CONFIGURATION)')
 
153
 
 
154
    # Set user-specified project-wide build settings.  This is intended to be
 
155
    # used very sparingly.  Really, almost everything should go into
 
156
    # target-specific build settings sections.  The project-wide settings are
 
157
    # only intended to be used in cases where Xcode attempts to resolve
 
158
    # variable references in a project context as opposed to a target context,
 
159
    # such as when resolving sourceTree references while building up the tree
 
160
    # tree view for UI display.
 
161
    for xck, xcv in self.build_file_dict.get('xcode_settings', {}).iteritems():
 
162
      xccl.SetBuildSetting(xck, xcv)
 
163
 
 
164
    # Sort the targets based on how they appeared in the input.
 
165
    # TODO(mark): Like a lot of other things here, this assumes internal
 
166
    # knowledge of PBXProject - in this case, of its "targets" property.
 
167
 
 
168
    # ordinary_targets are ordinary targets that are already in the project
 
169
    # file. run_test_targets are the targets that run unittests and should be
 
170
    # used for the Run All Tests target.  support_targets are the action/rule
 
171
    # targets used by GYP file targets, just kept for the assert check.
 
172
    ordinary_targets = []
 
173
    run_test_targets = []
 
174
    support_targets = []
 
175
 
 
176
    # targets is full list of targets in the project.
 
177
    targets = []
 
178
 
 
179
    # does the it define it's own "all"?
 
180
    has_custom_all = False
 
181
 
 
182
    # targets_for_all is the list of ordinary_targets that should be listed
 
183
    # in this project's "All" target.  It includes each non_runtest_target
 
184
    # that does not have suppress_wildcard set.
 
185
    targets_for_all = []
 
186
 
 
187
    for target in self.build_file_dict['targets']:
 
188
      target_name = target['target_name']
 
189
      toolset = target['toolset']
 
190
      qualified_target = gyp.common.QualifiedTarget(self.gyp_path, target_name,
 
191
                                                    toolset)
 
192
      xcode_target = xcode_targets[qualified_target]
 
193
      # Make sure that the target being added to the sorted list is already in
 
194
      # the unsorted list.
 
195
      assert xcode_target in self.project._properties['targets']
 
196
      targets.append(xcode_target)
 
197
      ordinary_targets.append(xcode_target)
 
198
      if xcode_target.support_target:
 
199
        support_targets.append(xcode_target.support_target)
 
200
        targets.append(xcode_target.support_target)
 
201
 
 
202
      if not int(target.get('suppress_wildcard', False)):
 
203
        targets_for_all.append(xcode_target)
 
204
 
 
205
      if target_name.lower() == 'all':
 
206
        has_custom_all = True;
 
207
 
 
208
      # If this target has a 'run_as' attribute, or is a test, add its
 
209
      # target to the targets, and (if it's a test) add it the to the
 
210
      # test targets.
 
211
      is_test = int(target.get('test', 0))
 
212
      if target.get('run_as') or is_test:
 
213
        # Make a target to run something.  It should have one
 
214
        # dependency, the parent xcode target.
 
215
        xccl = CreateXCConfigurationList(configurations)
 
216
        run_target = gyp.xcodeproj_file.PBXAggregateTarget({
 
217
              'name':                   'Run ' + target_name,
 
218
              'productName':            xcode_target.GetProperty('productName'),
 
219
              'buildConfigurationList': xccl,
 
220
            },
 
221
            parent=self.project)
 
222
        run_target.AddDependency(xcode_target)
 
223
 
 
224
        # The test runner target has a build phase that executes the
 
225
        # test, if this has the 'test' attribute.  If the 'run_as' tag
 
226
        # doesn't exist (meaning that this must be a test), then we
 
227
        # define a default test command line.
 
228
        command = target.get('run_as', {
 
229
          'action': ['${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}']
 
230
          })
 
231
 
 
232
        script = ''
 
233
        if command.get('working_directory'):
 
234
          script = script + 'cd "%s"\n' % \
 
235
                   gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
 
236
                       command.get('working_directory'))
 
237
 
 
238
        if command.get('environment'):
 
239
          script = script + "\n".join(
 
240
            ['export %s="%s"' %
 
241
             (key, gyp.xcodeproj_file.ConvertVariablesToShellSyntax(val))
 
242
             for (key, val) in command.get('environment').iteritems()]) + "\n"
 
243
 
 
244
        # Some test end up using sockets, files on disk, etc. and can get
 
245
        # confused if more then one test runs at a time.  The generator
 
246
        # flag 'xcode_serialize_all_test_runs' controls the forcing of all
 
247
        # tests serially.  It defaults to True.  To get serial runs this
 
248
        # little bit of python does the same as the linux flock utility to
 
249
        # make sure only one runs at a time.
 
250
        command_prefix = ''
 
251
        if is_test and serialize_all_tests:
 
252
          command_prefix = \
 
253
"""python -c "import fcntl, subprocess, sys
 
254
file = open('$TMPDIR/GYP_serialize_test_runs', 'a')
 
255
fcntl.flock(file.fileno(), fcntl.LOCK_EX)
 
256
sys.exit(subprocess.call(sys.argv[1:]))" """
 
257
 
 
258
        # If we were unable to exec for some reason, we want to exit
 
259
        # with an error, and fixup variable references to be shell
 
260
        # syntax instead of xcode syntax.
 
261
        script = script + 'exec ' + command_prefix + '%s\nexit 1\n' % \
 
262
                 gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
 
263
                     gyp.common.EncodePOSIXShellList(command.get('action')))
 
264
 
 
265
        ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
 
266
              'shellScript':      script,
 
267
              'showEnvVarsInLog': 0,
 
268
            })
 
269
        run_target.AppendProperty('buildPhases', ssbp)
 
270
 
 
271
        # Add the run target to the project file.
 
272
        targets.append(run_target)
 
273
        if is_test:
 
274
          run_test_targets.append(run_target)
 
275
          xcode_target.test_runner = run_target
 
276
 
 
277
 
 
278
    # Make sure that the list of targets being replaced is the same length as
 
279
    # the one replacing it, but allow for the added test runner targets.
 
280
    assert len(self.project._properties['targets']) == \
 
281
      len(ordinary_targets) + len(support_targets)
 
282
 
 
283
    self.project._properties['targets'] = targets
 
284
 
 
285
    # Get rid of unnecessary levels of depth in groups like the Source group.
 
286
    self.project.RootGroupsTakeOverOnlyChildren(True)
 
287
 
 
288
    # Sort the groups nicely.  Do this after sorting the targets, because the
 
289
    # Products group is sorted based on the order of the targets.
 
290
    self.project.SortGroups()
 
291
 
 
292
    # Create an "All" target if there's more than one target in this project
 
293
    # file and the project didn't define its own "All" target.  Put a generated
 
294
    # "All" target first so that people opening up the project for the first
 
295
    # time will build everything by default.
 
296
    if len(targets_for_all) > 1 and not has_custom_all:
 
297
      xccl = CreateXCConfigurationList(configurations)
 
298
      all_target = gyp.xcodeproj_file.PBXAggregateTarget(
 
299
          {
 
300
            'buildConfigurationList': xccl,
 
301
            'name':                   'All',
 
302
          },
 
303
          parent=self.project)
 
304
 
 
305
      for target in targets_for_all:
 
306
        all_target.AddDependency(target)
 
307
 
 
308
      # TODO(mark): This is evil because it relies on internal knowledge of
 
309
      # PBXProject._properties.  It's important to get the "All" target first,
 
310
      # though.
 
311
      self.project._properties['targets'].insert(0, all_target)
 
312
 
 
313
    # The same, but for run_test_targets.
 
314
    if len(run_test_targets) > 1:
 
315
      xccl = CreateXCConfigurationList(configurations)
 
316
      run_all_tests_target = gyp.xcodeproj_file.PBXAggregateTarget(
 
317
          {
 
318
            'buildConfigurationList': xccl,
 
319
            'name':                   'Run All Tests',
 
320
          },
 
321
          parent=self.project)
 
322
      for run_test_target in run_test_targets:
 
323
        run_all_tests_target.AddDependency(run_test_target)
 
324
 
 
325
      # Insert after the "All" target, which must exist if there is more than
 
326
      # one run_test_target.
 
327
      self.project._properties['targets'].insert(1, run_all_tests_target)
 
328
 
 
329
  def Finalize2(self, xcode_targets, xcode_target_to_target_dict):
 
330
    # Finalize2 needs to happen in a separate step because the process of
 
331
    # updating references to other projects depends on the ordering of targets
 
332
    # within remote project files.  Finalize1 is responsible for sorting duty,
 
333
    # and once all project files are sorted, Finalize2 can come in and update
 
334
    # these references.
 
335
 
 
336
    # To support making a "test runner" target that will run all the tests
 
337
    # that are direct dependents of any given target, we look for
 
338
    # xcode_create_dependents_test_runner being set on an Aggregate target,
 
339
    # and generate a second target that will run the tests runners found under
 
340
    # the marked target.
 
341
    for bf_tgt in self.build_file_dict['targets']:
 
342
      if int(bf_tgt.get('xcode_create_dependents_test_runner', 0)):
 
343
        tgt_name = bf_tgt['target_name']
 
344
        toolset = bf_tgt['toolset']
 
345
        qualified_target = gyp.common.QualifiedTarget(self.gyp_path,
 
346
                                                      tgt_name, toolset)
 
347
        xcode_target = xcode_targets[qualified_target]
 
348
        if isinstance(xcode_target, gyp.xcodeproj_file.PBXAggregateTarget):
 
349
          # Collect all the run test targets.
 
350
          all_run_tests = []
 
351
          pbxtds = xcode_target.GetProperty('dependencies')
 
352
          for pbxtd in pbxtds:
 
353
            pbxcip = pbxtd.GetProperty('targetProxy')
 
354
            dependency_xct = pbxcip.GetProperty('remoteGlobalIDString')
 
355
            target_dict = xcode_target_to_target_dict[dependency_xct]
 
356
            if target_dict and int(target_dict.get('test', 0)):
 
357
              assert dependency_xct.test_runner
 
358
              all_run_tests.append(dependency_xct.test_runner)
 
359
 
 
360
          # Directly depend on all the runners as they depend on the target
 
361
          # that builds them.
 
362
          if len(all_run_tests) > 0:
 
363
            run_all_target = gyp.xcodeproj_file.PBXAggregateTarget({
 
364
                  'name':        'Run %s Tests' % tgt_name,
 
365
                  'productName': tgt_name,
 
366
                },
 
367
                parent=self.project)
 
368
            for run_test_target in all_run_tests:
 
369
              run_all_target.AddDependency(run_test_target)
 
370
 
 
371
            # Insert the test runner after the related target.
 
372
            idx = self.project._properties['targets'].index(xcode_target)
 
373
            self.project._properties['targets'].insert(idx + 1, run_all_target)
 
374
 
 
375
    # Update all references to other projects, to make sure that the lists of
 
376
    # remote products are complete.  Otherwise, Xcode will fill them in when
 
377
    # it opens the project file, which will result in unnecessary diffs.
 
378
    # TODO(mark): This is evil because it relies on internal knowledge of
 
379
    # PBXProject._other_pbxprojects.
 
380
    for other_pbxproject in self.project._other_pbxprojects.keys():
 
381
      self.project.AddOrGetProjectReference(other_pbxproject)
 
382
 
 
383
    self.project.SortRemoteProductReferences()
 
384
 
 
385
    # Give everything an ID.
 
386
    self.project_file.ComputeIDs()
 
387
 
 
388
    # Make sure that no two objects in the project file have the same ID.  If
 
389
    # multiple objects wind up with the same ID, upon loading the file, Xcode
 
390
    # will only recognize one object (the last one in the file?) and the
 
391
    # results are unpredictable.
 
392
    self.project_file.EnsureNoIDCollisions()
 
393
 
 
394
  def Write(self):
 
395
    # Write the project file to a temporary location first.  Xcode watches for
 
396
    # changes to the project file and presents a UI sheet offering to reload
 
397
    # the project when it does change.  However, in some cases, especially when
 
398
    # multiple projects are open or when Xcode is busy, things don't work so
 
399
    # seamlessly.  Sometimes, Xcode is able to detect that a project file has
 
400
    # changed but can't unload it because something else is referencing it.
 
401
    # To mitigate this problem, and to avoid even having Xcode present the UI
 
402
    # sheet when an open project is rewritten for inconsequential changes, the
 
403
    # project file is written to a temporary file in the xcodeproj directory
 
404
    # first.  The new temporary file is then compared to the existing project
 
405
    # file, if any.  If they differ, the new file replaces the old; otherwise,
 
406
    # the new project file is simply deleted.  Xcode properly detects a file
 
407
    # being renamed over an open project file as a change and so it remains
 
408
    # able to present the "project file changed" sheet under this system.
 
409
    # Writing to a temporary file first also avoids the possible problem of
 
410
    # Xcode rereading an incomplete project file.
 
411
    (output_fd, new_pbxproj_path) = \
 
412
        tempfile.mkstemp(suffix='.tmp', prefix='project.pbxproj.gyp.',
 
413
                         dir=self.path)
 
414
 
 
415
    try:
 
416
      output_file = os.fdopen(output_fd, 'wb')
 
417
 
 
418
      self.project_file.Print(output_file)
 
419
      output_file.close()
 
420
 
 
421
      pbxproj_path = os.path.join(self.path, 'project.pbxproj')
 
422
 
 
423
      same = False
 
424
      try:
 
425
        same = filecmp.cmp(pbxproj_path, new_pbxproj_path, False)
 
426
      except OSError, e:
 
427
        if e.errno != errno.ENOENT:
 
428
          raise
 
429
 
 
430
      if same:
 
431
        # The new file is identical to the old one, just get rid of the new
 
432
        # one.
 
433
        os.unlink(new_pbxproj_path)
 
434
      else:
 
435
        # The new file is different from the old one, or there is no old one.
 
436
        # Rename the new file to the permanent name.
 
437
        #
 
438
        # tempfile.mkstemp uses an overly restrictive mode, resulting in a
 
439
        # file that can only be read by the owner, regardless of the umask.
 
440
        # There's no reason to not respect the umask here, which means that
 
441
        # an extra hoop is required to fetch it and reset the new file's mode.
 
442
        #
 
443
        # No way to get the umask without setting a new one?  Set a safe one
 
444
        # and then set it back to the old value.
 
445
        umask = os.umask(077)
 
446
        os.umask(umask)
 
447
 
 
448
        os.chmod(new_pbxproj_path, 0666 & ~umask)
 
449
        os.rename(new_pbxproj_path, pbxproj_path)
 
450
 
 
451
    except Exception:
 
452
      # Don't leave turds behind.  In fact, if this code was responsible for
 
453
      # creating the xcodeproj directory, get rid of that too.
 
454
      os.unlink(new_pbxproj_path)
 
455
      if self.created_dir:
 
456
        shutil.rmtree(self.path, True)
 
457
      raise
 
458
 
 
459
 
 
460
cached_xcode_version = None
 
461
def InstalledXcodeVersion():
 
462
  """Fetches the installed version of Xcode, returns empty string if it is
 
463
  unable to figure it out."""
 
464
 
 
465
  global cached_xcode_version
 
466
  if not cached_xcode_version is None:
 
467
    return cached_xcode_version
 
468
 
 
469
  # Default to an empty string
 
470
  cached_xcode_version = ''
 
471
 
 
472
  # Collect the xcodebuild's version information.
 
473
  try:
 
474
    import subprocess
 
475
    cmd = ['/usr/bin/xcodebuild', '-version']
 
476
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 
477
    xcodebuild_version_info = proc.communicate()[0]
 
478
    # Any error, return empty string
 
479
    if proc.returncode:
 
480
      xcodebuild_version_info = ''
 
481
  except OSError:
 
482
    # We failed to launch the tool
 
483
    xcodebuild_version_info = ''
 
484
 
 
485
  # Pull out the Xcode version itself.
 
486
  match_line = re.search('^Xcode (.*)$', xcodebuild_version_info, re.MULTILINE)
 
487
  if match_line:
 
488
    cached_xcode_version = match_line.group(1)
 
489
  # Done!
 
490
  return cached_xcode_version
 
491
 
 
492
 
 
493
def AddSourceToTarget(source, pbxp, xct):
 
494
  # TODO(mark): Perhaps this can be made a little bit fancier.
 
495
  source_extensions = ['c', 'cc', 'cpp', 'cxx', 'm', 'mm', 's']
 
496
  basename = posixpath.basename(source)
 
497
  (root, ext) = posixpath.splitext(basename)
 
498
  if ext != '':
 
499
    ext = ext[1:].lower()
 
500
 
 
501
  if ext in source_extensions:
 
502
    xct.SourcesPhase().AddFile(source)
 
503
  else:
 
504
    # Files that aren't added to a sources build phase can still go into
 
505
    # the project file, just not as part of a build phase.
 
506
    pbxp.AddOrGetFileInRootGroup(source)
 
507
 
 
508
 
 
509
def AddResourceToTarget(resource, pbxp, xct):
 
510
  # TODO(mark): Combine with AddSourceToTarget above?  Or just inline this call
 
511
  # where it's used.
 
512
  xct.ResourcesPhase().AddFile(resource)
 
513
 
 
514
 
 
515
_xcode_variable_re = re.compile('(\$\((.*?)\))')
 
516
def ExpandXcodeVariables(string, expansions):
 
517
  """Expands Xcode-style $(VARIABLES) in string per the expansions dict.
 
518
 
 
519
  In some rare cases, it is appropriate to expand Xcode variables when a
 
520
  project file is generated.  For any substring $(VAR) in string, if VAR is a
 
521
  key in the expansions dict, $(VAR) will be replaced with expansions[VAR].
 
522
  Any $(VAR) substring in string for which VAR is not a key in the expansions
 
523
  dict will remain in the returned string.
 
524
  """
 
525
 
 
526
  matches = _xcode_variable_re.findall(string)
 
527
  if matches == None:
 
528
    return string
 
529
 
 
530
  matches.reverse()
 
531
  for match in matches:
 
532
    (to_replace, variable) = match
 
533
    if not variable in expansions:
 
534
      continue
 
535
 
 
536
    replacement = expansions[variable]
 
537
    string = re.sub(re.escape(to_replace), replacement, string)
 
538
 
 
539
  return string
 
540
 
 
541
 
 
542
def EscapeXCodeArgument(s):
 
543
  """We must escape the arguments that we give to XCode so that it knows not to
 
544
     split on spaces and to respect backslash and quote literals."""
 
545
  s = s.replace('\\', '\\\\')
 
546
  s = s.replace('"', '\\"')
 
547
  return '"' + s + '"'
 
548
 
 
549
 
 
550
def GenerateOutput(target_list, target_dicts, data, params):
 
551
  options = params['options']
 
552
  generator_flags = params.get('generator_flags', {})
 
553
  parallel_builds = generator_flags.get('xcode_parallel_builds', True)
 
554
  serialize_all_tests = \
 
555
      generator_flags.get('xcode_serialize_all_test_runs', True)
 
556
  xcode_projects = {}
 
557
  for build_file, build_file_dict in data.iteritems():
 
558
    (build_file_root, build_file_ext) = os.path.splitext(build_file)
 
559
    if build_file_ext != '.gyp':
 
560
      continue
 
561
    xcodeproj_path = build_file_root + options.suffix + '.xcodeproj'
 
562
    if options.generator_output:
 
563
      xcodeproj_path = os.path.join(options.generator_output, xcodeproj_path)
 
564
    xcp = XcodeProject(build_file, xcodeproj_path, build_file_dict)
 
565
    xcode_projects[build_file] = xcp
 
566
    pbxp = xcp.project
 
567
 
 
568
    if parallel_builds:
 
569
      pbxp.SetProperty('attributes',
 
570
                       {'BuildIndependentTargetsInParallel': 'YES'})
 
571
 
 
572
    main_group = pbxp.GetProperty('mainGroup')
 
573
    build_group = gyp.xcodeproj_file.PBXGroup({'name': 'Build'})
 
574
    main_group.AppendChild(build_group)
 
575
    for included_file in build_file_dict['included_files']:
 
576
      build_group.AddOrGetFileByPath(included_file, False)
 
577
 
 
578
  xcode_targets = {}
 
579
  xcode_target_to_target_dict = {}
 
580
  for qualified_target in target_list:
 
581
    [build_file, target_name, toolset] = \
 
582
        gyp.common.ParseQualifiedTarget(qualified_target)
 
583
 
 
584
    spec = target_dicts[qualified_target]
 
585
    if spec['toolset'] != 'target':
 
586
      raise Exception(
 
587
          'Multiple toolsets not supported in xcode build (target %s)' %
 
588
          qualified_target)
 
589
    configuration_names = [spec['default_configuration']]
 
590
    for configuration_name in sorted(spec['configurations'].keys()):
 
591
      if configuration_name not in configuration_names:
 
592
        configuration_names.append(configuration_name)
 
593
    xcp = xcode_projects[build_file]
 
594
    pbxp = xcp.project
 
595
 
 
596
    # Set up the configurations for the target according to the list of names
 
597
    # supplied.
 
598
    xccl = CreateXCConfigurationList(configuration_names)
 
599
 
 
600
    # Create an XCTarget subclass object for the target.  We use the type
 
601
    # with "+bundle" appended if the target has "mac_bundle" set.
 
602
    _types = {
 
603
      'executable':             'com.apple.product-type.tool',
 
604
      'loadable_module':        'com.apple.product-type.library.dynamic',
 
605
      'shared_library':         'com.apple.product-type.library.dynamic',
 
606
      'static_library':         'com.apple.product-type.library.static',
 
607
      'executable+bundle':      'com.apple.product-type.application',
 
608
      'loadable_module+bundle': 'com.apple.product-type.bundle',
 
609
      'shared_library+bundle':  'com.apple.product-type.framework',
 
610
    }
 
611
 
 
612
    target_properties = {
 
613
      'buildConfigurationList': xccl,
 
614
      'name':                   target_name,
 
615
    }
 
616
 
 
617
    type = spec['type']
 
618
    is_bundle = int(spec.get('mac_bundle', 0))
 
619
    if type != 'none':
 
620
      type_bundle_key = type
 
621
      if is_bundle:
 
622
        type_bundle_key += '+bundle'
 
623
      xctarget_type = gyp.xcodeproj_file.PBXNativeTarget
 
624
      try:
 
625
        target_properties['productType'] = _types[type_bundle_key]
 
626
      except KeyError, e:
 
627
        gyp.common.ExceptionAppend(e, "-- unknown product type while "
 
628
                                   "writing target %s" % target_name)
 
629
        raise
 
630
    else:
 
631
      xctarget_type = gyp.xcodeproj_file.PBXAggregateTarget
 
632
 
 
633
    target_product_name = spec.get('product_name')
 
634
    if target_product_name is not None:
 
635
      target_properties['productName'] = target_product_name
 
636
 
 
637
    xct = xctarget_type(target_properties, parent=pbxp,
 
638
                        force_outdir=spec.get('product_dir'),
 
639
                        force_prefix=spec.get('product_prefix'),
 
640
                        force_extension=spec.get('product_extension'))
 
641
    pbxp.AppendProperty('targets', xct)
 
642
    xcode_targets[qualified_target] = xct
 
643
    xcode_target_to_target_dict[xct] = spec
 
644
 
 
645
    # Xcode does not have a distinct type for loadable_modules that are pure
 
646
    # BSD targets (ie-unbundled). It uses the same setup as a shared_library
 
647
    # but the mach-o type is explictly set in the settings.  So before we do
 
648
    # anything else, for this one case, we stuff in that one setting.  This
 
649
    # would allow the other data in the spec to change it if need be.
 
650
    if type == 'loadable_module' and not is_bundle:
 
651
      xccl.SetBuildSetting('MACH_O_TYPE', 'mh_bundle')
 
652
 
 
653
    spec_actions = spec.get('actions', [])
 
654
    spec_rules = spec.get('rules', [])
 
655
 
 
656
    # Xcode has some "issues" with checking dependencies for the "Compile
 
657
    # sources" step with any source files/headers generated by actions/rules.
 
658
    # To work around this, if a target is building anything directly (not
 
659
    # type "none"), then a second target as used to run the GYP actions/rules
 
660
    # and is made a dependency of this target.  This way the work is done
 
661
    # before the dependency checks for what should be recompiled.
 
662
    support_xct = None
 
663
    if type != 'none' and (spec_actions or spec_rules):
 
664
      support_xccl = CreateXCConfigurationList(configuration_names);
 
665
      support_target_properties = {
 
666
        'buildConfigurationList': support_xccl,
 
667
        'name':                   target_name + ' Support',
 
668
      }
 
669
      if target_product_name:
 
670
        support_target_properties['productName'] = \
 
671
            target_product_name + ' Support'
 
672
      support_xct = \
 
673
          gyp.xcodeproj_file.PBXAggregateTarget(support_target_properties,
 
674
                                                parent=pbxp)
 
675
      pbxp.AppendProperty('targets', support_xct)
 
676
      xct.AddDependency(support_xct)
 
677
    # Hang the support target off the main target so it can be tested/found
 
678
    # by the generator during Finalize.
 
679
    xct.support_target = support_xct
 
680
 
 
681
    prebuild_index = 0
 
682
 
 
683
    # Add custom shell script phases for "actions" sections.
 
684
    for action in spec_actions:
 
685
      # There's no need to write anything into the script to ensure that the
 
686
      # output directories already exist, because Xcode will look at the
 
687
      # declared outputs and automatically ensure that they exist for us.
 
688
 
 
689
      # Do we have a message to print when this action runs?
 
690
      message = action.get('message')
 
691
      if message:
 
692
        message = 'echo note: ' + gyp.common.EncodePOSIXShellArgument(message)
 
693
      else:
 
694
        message = ''
 
695
 
 
696
      # Turn the list into a string that can be passed to a shell.
 
697
      action_string = gyp.common.EncodePOSIXShellList(action['action'])
 
698
 
 
699
      # Convert Xcode-type variable references to sh-compatible environment
 
700
      # variable references.
 
701
      message_sh = gyp.xcodeproj_file.ConvertVariablesToShellSyntax(message)
 
702
      action_string_sh = gyp.xcodeproj_file.ConvertVariablesToShellSyntax(
 
703
        action_string)
 
704
 
 
705
      script = ''
 
706
      # Include the optional message
 
707
      if message_sh:
 
708
        script += message_sh + '\n'
 
709
      # Be sure the script runs in exec, and that if exec fails, the script
 
710
      # exits signalling an error.
 
711
      script += 'exec ' + action_string_sh + '\nexit 1\n'
 
712
      ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
 
713
            'inputPaths': action['inputs'],
 
714
            'name': 'Action "' + action['action_name'] + '"',
 
715
            'outputPaths': action['outputs'],
 
716
            'shellScript': script,
 
717
            'showEnvVarsInLog': 0,
 
718
          })
 
719
 
 
720
      if support_xct:
 
721
        support_xct.AppendProperty('buildPhases', ssbp)
 
722
      else:
 
723
        # TODO(mark): this assumes too much knowledge of the internals of
 
724
        # xcodeproj_file; some of these smarts should move into xcodeproj_file
 
725
        # itself.
 
726
        xct._properties['buildPhases'].insert(prebuild_index, ssbp)
 
727
        prebuild_index = prebuild_index + 1
 
728
 
 
729
      # TODO(mark): Should verify that at most one of these is specified.
 
730
      if int(action.get('process_outputs_as_sources', False)):
 
731
        for output in action['outputs']:
 
732
          AddSourceToTarget(output, pbxp, xct)
 
733
 
 
734
      if int(action.get('process_outputs_as_mac_bundle_resources', False)):
 
735
        for output in action['outputs']:
 
736
          AddResourceToTarget(output, pbxp, xct)
 
737
 
 
738
    # tgt_mac_bundle_resources holds the list of bundle resources so
 
739
    # the rule processing can check against it.
 
740
    if is_bundle:
 
741
      tgt_mac_bundle_resources = spec.get('mac_bundle_resources', [])
 
742
    else:
 
743
      tgt_mac_bundle_resources = []
 
744
 
 
745
    # Add custom shell script phases driving "make" for "rules" sections.
 
746
    #
 
747
    # Xcode's built-in rule support is almost powerful enough to use directly,
 
748
    # but there are a few significant deficiencies that render them unusable.
 
749
    # There are workarounds for some of its inadequacies, but in aggregate,
 
750
    # the workarounds added complexity to the generator, and some workarounds
 
751
    # actually require input files to be crafted more carefully than I'd like.
 
752
    # Consequently, until Xcode rules are made more capable, "rules" input
 
753
    # sections will be handled in Xcode output by shell script build phases
 
754
    # performed prior to the compilation phase.
 
755
    #
 
756
    # The following problems with Xcode rules were found.  The numbers are
 
757
    # Apple radar IDs.  I hope that these shortcomings are addressed, I really
 
758
    # liked having the rules handled directly in Xcode during the period that
 
759
    # I was prototyping this.
 
760
    #
 
761
    # 6588600 Xcode compiles custom script rule outputs too soon, compilation
 
762
    #         fails.  This occurs when rule outputs from distinct inputs are
 
763
    #         interdependent.  The only workaround is to put rules and their
 
764
    #         inputs in a separate target from the one that compiles the rule
 
765
    #         outputs.  This requires input file cooperation and it means that
 
766
    #         process_outputs_as_sources is unusable.
 
767
    # 6584932 Need to declare that custom rule outputs should be excluded from
 
768
    #         compilation.  A possible workaround is to lie to Xcode about a
 
769
    #         rule's output, giving it a dummy file it doesn't know how to
 
770
    #         compile.  The rule action script would need to touch the dummy.
 
771
    # 6584839 I need a way to declare additional inputs to a custom rule.
 
772
    #         A possible workaround is a shell script phase prior to
 
773
    #         compilation that touches a rule's primary input files if any
 
774
    #         would-be additional inputs are newer than the output.  Modifying
 
775
    #         the source tree - even just modification times - feels dirty.
 
776
    # 6564240 Xcode "custom script" build rules always dump all environment
 
777
    #         variables.  This is a low-prioroty problem and is not a
 
778
    #         show-stopper.
 
779
    rules_by_ext = {}
 
780
    for rule in spec_rules:
 
781
      rules_by_ext[rule['extension']] = rule
 
782
 
 
783
      # First, some definitions:
 
784
      #
 
785
      # A "rule source" is a file that was listed in a target's "sources"
 
786
      # list and will have a rule applied to it on the basis of matching the
 
787
      # rule's "extensions" attribute.  Rule sources are direct inputs to
 
788
      # rules.
 
789
      #
 
790
      # Rule definitions may specify additional inputs in their "inputs"
 
791
      # attribute.  These additional inputs are used for dependency tracking
 
792
      # purposes.
 
793
      #
 
794
      # A "concrete output" is a rule output with input-dependent variables
 
795
      # resolved.  For example, given a rule with:
 
796
      #   'extension': 'ext', 'outputs': ['$(INPUT_FILE_BASE).cc'],
 
797
      # if the target's "sources" list contained "one.ext" and "two.ext",
 
798
      # the "concrete output" for rule input "two.ext" would be "two.cc".  If
 
799
      # a rule specifies multiple outputs, each input file that the rule is
 
800
      # applied to will have the same number of concrete outputs.
 
801
      #
 
802
      # If any concrete outputs are outdated or missing relative to their
 
803
      # corresponding rule_source or to any specified additional input, the
 
804
      # rule action must be performed to generate the concrete outputs.
 
805
 
 
806
      # concrete_outputs_by_rule_source will have an item at the same index
 
807
      # as the rule['rule_sources'] that it corresponds to.  Each item is a
 
808
      # list of all of the concrete outputs for the rule_source.
 
809
      concrete_outputs_by_rule_source = []
 
810
 
 
811
      # concrete_outputs_all is a flat list of all concrete outputs that this
 
812
      # rule is able to produce, given the known set of input files
 
813
      # (rule_sources) that apply to it.
 
814
      concrete_outputs_all = []
 
815
 
 
816
      # messages & actions are keyed by the same indices as rule['rule_sources']
 
817
      # and concrete_outputs_by_rule_source.  They contain the message and
 
818
      # action to perform after resolving input-dependent variables.  The
 
819
      # message is optional, in which case None is stored for each rule source.
 
820
      messages = []
 
821
      actions = []
 
822
 
 
823
      for rule_source in rule.get('rule_sources', []):
 
824
        rule_source_basename = posixpath.basename(rule_source)
 
825
        (rule_source_root, rule_source_ext) = \
 
826
            posixpath.splitext(rule_source_basename)
 
827
 
 
828
        # These are the same variable names that Xcode uses for its own native
 
829
        # rule support.  Because Xcode's rule engine is not being used, they
 
830
        # need to be expanded as they are written to the makefile.
 
831
        rule_input_dict = {
 
832
          'INPUT_FILE_BASE':   rule_source_root,
 
833
          'INPUT_FILE_SUFFIX': rule_source_ext,
 
834
          'INPUT_FILE_NAME':   rule_source_basename,
 
835
          'INPUT_FILE_PATH':   rule_source,
 
836
        }
 
837
 
 
838
        concrete_outputs_for_this_rule_source = []
 
839
        for output in rule.get('outputs', []):
 
840
          # Fortunately, Xcode and make both use $(VAR) format for their
 
841
          # variables, so the expansion is the only transformation necessary.
 
842
          # Any remaning $(VAR)-type variables in the string can be given
 
843
          # directly to make, which will pick up the correct settings from
 
844
          # what Xcode puts into the environment.
 
845
          concrete_output = ExpandXcodeVariables(output, rule_input_dict)
 
846
          concrete_outputs_for_this_rule_source.append(concrete_output)
 
847
 
 
848
          # Add all concrete outputs to the project.
 
849
          pbxp.AddOrGetFileInRootGroup(concrete_output)
 
850
 
 
851
        concrete_outputs_by_rule_source.append( \
 
852
            concrete_outputs_for_this_rule_source)
 
853
        concrete_outputs_all.extend(concrete_outputs_for_this_rule_source)
 
854
 
 
855
        # TODO(mark): Should verify that at most one of these is specified.
 
856
        if int(rule.get('process_outputs_as_sources', False)):
 
857
          for output in concrete_outputs_for_this_rule_source:
 
858
            AddSourceToTarget(output, pbxp, xct)
 
859
 
 
860
        # If the file came from the mac_bundle_resources list or if the rule
 
861
        # is marked to process outputs as bundle resource, do so.
 
862
        was_mac_bundle_resource = rule_source in tgt_mac_bundle_resources
 
863
        if was_mac_bundle_resource or \
 
864
            int(rule.get('process_outputs_as_mac_bundle_resources', False)):
 
865
          for output in concrete_outputs_for_this_rule_source:
 
866
            AddResourceToTarget(output, pbxp, xct)
 
867
 
 
868
        # Do we have a message to print when this rule runs?
 
869
        message = rule.get('message')
 
870
        if message:
 
871
          message = gyp.common.EncodePOSIXShellArgument(message)
 
872
          message = '@echo note: ' + ExpandXcodeVariables(message,
 
873
                                                          rule_input_dict)
 
874
        messages.append(message)
 
875
 
 
876
        # Turn the list into a string that can be passed to a shell.
 
877
        action_string = gyp.common.EncodePOSIXShellList(rule['action'])
 
878
 
 
879
        action = ExpandXcodeVariables(action_string, rule_input_dict)
 
880
        actions.append(action)
 
881
 
 
882
      if len(concrete_outputs_all) > 0:
 
883
        # TODO(mark): There's a possibilty for collision here.  Consider
 
884
        # target "t" rule "A_r" and target "t_A" rule "r".
 
885
        makefile_name = '%s_%s.make' % (target_name, rule['rule_name'])
 
886
        makefile_path = os.path.join(xcode_projects[build_file].path,
 
887
                                     makefile_name)
 
888
        # TODO(mark): try/close?  Write to a temporary file and swap it only
 
889
        # if it's got changes?
 
890
        makefile = open(makefile_path, 'wb')
 
891
 
 
892
        # make will build the first target in the makefile by default.  By
 
893
        # convention, it's called "all".  List all (or at least one)
 
894
        # concrete output for each rule source as a prerequisite of the "all"
 
895
        # target.
 
896
        makefile.write('all: \\\n')
 
897
        for concrete_output_index in \
 
898
            xrange(0, len(concrete_outputs_by_rule_source)):
 
899
          # Only list the first (index [0]) concrete output of each input
 
900
          # in the "all" target.  Otherwise, a parallel make (-j > 1) would
 
901
          # attempt to process each input multiple times simultaneously.
 
902
          # Otherwise, "all" could just contain the entire list of
 
903
          # concrete_outputs_all.
 
904
          concrete_output = \
 
905
              concrete_outputs_by_rule_source[concrete_output_index][0]
 
906
          if concrete_output_index == len(concrete_outputs_by_rule_source) - 1:
 
907
            eol = ''
 
908
          else:
 
909
            eol = ' \\'
 
910
          makefile.write('    %s%s\n' % (concrete_output, eol))
 
911
 
 
912
        for (rule_source, concrete_outputs, message, action) in \
 
913
            zip(rule['rule_sources'], concrete_outputs_by_rule_source,
 
914
                messages, actions):
 
915
          makefile.write('\n')
 
916
 
 
917
          # Add a rule that declares it can build each concrete output of a
 
918
          # rule source.  Collect the names of the directories that are
 
919
          # required.
 
920
          concrete_output_dirs = []
 
921
          for concrete_output_index in xrange(0, len(concrete_outputs)):
 
922
            concrete_output = concrete_outputs[concrete_output_index]
 
923
            if concrete_output_index == 0:
 
924
              bol = ''
 
925
            else:
 
926
              bol = '    '
 
927
            makefile.write('%s%s \\\n' % (bol, concrete_output))
 
928
 
 
929
            concrete_output_dir = posixpath.dirname(concrete_output)
 
930
            if (concrete_output_dir and
 
931
                concrete_output_dir not in concrete_output_dirs):
 
932
              concrete_output_dirs.append(concrete_output_dir)
 
933
 
 
934
          makefile.write('    : \\\n')
 
935
 
 
936
          # The prerequisites for this rule are the rule source itself and
 
937
          # the set of additional rule inputs, if any.
 
938
          prerequisites = [rule_source]
 
939
          prerequisites.extend(rule.get('inputs', []))
 
940
          for prerequisite_index in xrange(0, len(prerequisites)):
 
941
            prerequisite = prerequisites[prerequisite_index]
 
942
            if prerequisite_index == len(prerequisites) - 1:
 
943
              eol = ''
 
944
            else:
 
945
              eol = ' \\'
 
946
            makefile.write('    %s%s\n' % (prerequisite, eol))
 
947
 
 
948
          # Make sure that output directories exist before executing the rule
 
949
          # action.
 
950
          # TODO(mark): quote the list of concrete_output_dirs.
 
951
          if len(concrete_output_dirs) > 0:
 
952
            makefile.write('\tmkdir -p %s\n' % ' '.join(concrete_output_dirs))
 
953
 
 
954
          # The rule message and action have already had the necessary variable
 
955
          # substitutions performed.
 
956
          if message:
 
957
            makefile.write('\t%s\n' % message)
 
958
          makefile.write('\t%s\n' % action)
 
959
 
 
960
        makefile.close()
 
961
 
 
962
        # It might be nice to ensure that needed output directories exist
 
963
        # here rather than in each target in the Makefile, but that wouldn't
 
964
        # work if there ever was a concrete output that had an input-dependent
 
965
        # variable anywhere other than in the leaf position.
 
966
 
 
967
        # Don't declare any inputPaths or outputPaths.  If they're present,
 
968
        # Xcode will provide a slight optimization by only running the script
 
969
        # phase if any output is missing or outdated relative to any input.
 
970
        # Unfortunately, it will also assume that all outputs are touched by
 
971
        # the script, and if the outputs serve as files in a compilation
 
972
        # phase, they will be unconditionally rebuilt.  Since make might not
 
973
        # rebuild everything that could be declared here as an output, this
 
974
        # extra compilation activity is unnecessary.  With inputPaths and
 
975
        # outputPaths not supplied, make will always be called, but it knows
 
976
        # enough to not do anything when everything is up-to-date.
 
977
 
 
978
        # To help speed things up, pass -j COUNT to make so it does some work
 
979
        # in parallel.  Don't use ncpus because Xcode will build ncpus targets
 
980
        # in parallel and if each target happens to have a rules step, there
 
981
        # would be ncpus^2 things going.  With a machine that has 2 quad-core
 
982
        # Xeons, a build can quickly run out of processes based on
 
983
        # scheduling/other tasks, and randomly failing builds are no good.
 
984
        script = \
 
985
"""JOB_COUNT="$(/usr/sbin/sysctl -n hw.ncpu)"
 
986
if [ "${JOB_COUNT}" -gt 4 ]; then
 
987
  JOB_COUNT=4
 
988
fi
 
989
exec "${DEVELOPER_BIN_DIR}/make" -f "${PROJECT_FILE_PATH}/%s" -j "${JOB_COUNT}"
 
990
exit 1
 
991
""" % makefile_name
 
992
        ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
 
993
              'name': 'Rule "' + rule['rule_name'] + '"',
 
994
              'shellScript': script,
 
995
              'showEnvVarsInLog': 0,
 
996
            })
 
997
 
 
998
        if support_xct:
 
999
          support_xct.AppendProperty('buildPhases', ssbp)
 
1000
        else:
 
1001
          # TODO(mark): this assumes too much knowledge of the internals of
 
1002
          # xcodeproj_file; some of these smarts should move into xcodeproj_file
 
1003
          # itself.
 
1004
          xct._properties['buildPhases'].insert(prebuild_index, ssbp)
 
1005
          prebuild_index = prebuild_index + 1
 
1006
 
 
1007
      # Extra rule inputs also go into the project file.  Concrete outputs were
 
1008
      # already added when they were computed.
 
1009
      for group in ['inputs', 'inputs_excluded']:
 
1010
        for item in rule.get(group, []):
 
1011
          pbxp.AddOrGetFileInRootGroup(item)
 
1012
 
 
1013
    # Add "sources".
 
1014
    for source in spec.get('sources', []):
 
1015
      (source_root, source_extension) = posixpath.splitext(source)
 
1016
      if source_extension[1:] not in rules_by_ext:
 
1017
        # AddSourceToTarget will add the file to a root group if it's not
 
1018
        # already there.
 
1019
        AddSourceToTarget(source, pbxp, xct)
 
1020
      else:
 
1021
        pbxp.AddOrGetFileInRootGroup(source)
 
1022
 
 
1023
    # Add "mac_bundle_resources" if it's a bundle of any type.
 
1024
    if is_bundle:
 
1025
      for resource in tgt_mac_bundle_resources:
 
1026
        (resource_root, resource_extension) = posixpath.splitext(resource)
 
1027
        if resource_extension[1:] not in rules_by_ext:
 
1028
          AddResourceToTarget(resource, pbxp, xct)
 
1029
        else:
 
1030
          pbxp.AddOrGetFileInRootGroup(resource)
 
1031
 
 
1032
    # Add "copies".
 
1033
    for copy_group in spec.get('copies', []):
 
1034
      pbxcp = gyp.xcodeproj_file.PBXCopyFilesBuildPhase({
 
1035
            'name': 'Copy to ' + copy_group['destination']
 
1036
          },
 
1037
          parent=xct)
 
1038
      dest = copy_group['destination']
 
1039
      if dest[0] not in ('/', '$'):
 
1040
        # Relative paths are relative to $(SRCROOT).
 
1041
        dest = '$(SRCROOT)/' + dest
 
1042
      pbxcp.SetDestination(dest)
 
1043
 
 
1044
      # TODO(mark): The usual comment about this knowing too much about
 
1045
      # gyp.xcodeproj_file internals applies.
 
1046
      xct._properties['buildPhases'].insert(prebuild_index, pbxcp)
 
1047
 
 
1048
      for file in copy_group['files']:
 
1049
        pbxcp.AddFile(file)
 
1050
 
 
1051
    # Excluded files can also go into the project file.
 
1052
    for key in ['sources', 'mac_bundle_resources']:
 
1053
      excluded_key = key + '_excluded'
 
1054
      for item in spec.get(excluded_key, []):
 
1055
        pbxp.AddOrGetFileInRootGroup(item)
 
1056
 
 
1057
    # So can "inputs" and "outputs" sections of "actions" groups.
 
1058
    for action in spec.get('actions', []):
 
1059
      groups = ['inputs', 'inputs_excluded', 'outputs', 'outputs_excluded']
 
1060
      for group in groups:
 
1061
        for item in action.get(group, []):
 
1062
          # Exclude anything in BUILT_PRODUCTS_DIR.  They're products, not
 
1063
          # sources.
 
1064
          if not item.startswith('$(BUILT_PRODUCTS_DIR)/'):
 
1065
            pbxp.AddOrGetFileInRootGroup(item)
 
1066
 
 
1067
    for postbuild in spec.get('postbuilds', []):
 
1068
      action_string_sh = gyp.common.EncodePOSIXShellList(postbuild['action'])
 
1069
      script = 'exec ' + action_string_sh + '\nexit 1\n'
 
1070
      ssbp = gyp.xcodeproj_file.PBXShellScriptBuildPhase({
 
1071
            'name': 'Postbuild "' + postbuild['postbuild_name'] + '"',
 
1072
            'shellScript': script,
 
1073
            'showEnvVarsInLog': 0,
 
1074
          })
 
1075
      xct.AppendProperty('buildPhases', ssbp)
 
1076
 
 
1077
    # Add dependencies before libraries, because adding a dependency may imply
 
1078
    # adding a library.  It's preferable to keep dependencies listed first
 
1079
    # during a link phase so that they can override symbols that would
 
1080
    # otherwise be provided by libraries, which will usually include system
 
1081
    # libraries.  On some systems, ld is finicky and even requires the
 
1082
    # libraries to be ordered in such a way that unresolved symbols in
 
1083
    # earlier-listed libraries may only be resolved by later-listed libraries.
 
1084
    # The Mac linker doesn't work that way, but other platforms do, and so
 
1085
    # their linker invocations need to be constructed in this way.  There's
 
1086
    # no compelling reason for Xcode's linker invocations to differ.
 
1087
 
 
1088
    if 'dependencies' in spec:
 
1089
      for dependency in spec['dependencies']:
 
1090
        xct.AddDependency(xcode_targets[dependency])
 
1091
        # The support project also gets the dependencies (in case they are
 
1092
        # needed for the actions/rules to work).
 
1093
        if support_xct:
 
1094
          support_xct.AddDependency(xcode_targets[dependency])
 
1095
 
 
1096
    if 'libraries' in spec:
 
1097
      for library in spec['libraries']:
 
1098
        xct.FrameworksPhase().AddFile(library)
 
1099
        # Add the library's directory to LIBRARY_SEARCH_PATHS if necessary.
 
1100
        # I wish Xcode handled this automatically.
 
1101
        # TODO(mark): this logic isn't right.  There are certain directories
 
1102
        # that are always searched, we should check to see if the library is
 
1103
        # in one of those directories, and if not, we should do the
 
1104
        # AppendBuildSetting thing.
 
1105
        if not posixpath.isabs(library) and not library.startswith('$'):
 
1106
          # TODO(mark): Need to check to see if library_dir is already in
 
1107
          # LIBRARY_SEARCH_PATHS.
 
1108
          library_dir = posixpath.dirname(library)
 
1109
          xct.AppendBuildSetting('LIBRARY_SEARCH_PATHS', library_dir)
 
1110
 
 
1111
    for configuration_name in configuration_names:
 
1112
      configuration = spec['configurations'][configuration_name]
 
1113
      xcbc = xct.ConfigurationNamed(configuration_name)
 
1114
      for include_dir in configuration.get('mac_framework_dirs', []):
 
1115
        xcbc.AppendBuildSetting('FRAMEWORK_SEARCH_PATHS', include_dir)
 
1116
      for include_dir in configuration.get('include_dirs', []):
 
1117
        xcbc.AppendBuildSetting('HEADER_SEARCH_PATHS', include_dir)
 
1118
      if 'defines' in configuration:
 
1119
        for define in configuration['defines']:
 
1120
          set_define = EscapeXCodeArgument(define)
 
1121
          xcbc.AppendBuildSetting('GCC_PREPROCESSOR_DEFINITIONS', set_define)
 
1122
      if 'xcode_settings' in configuration:
 
1123
        for xck, xcv in configuration['xcode_settings'].iteritems():
 
1124
          xcbc.SetBuildSetting(xck, xcv)
 
1125
 
 
1126
  build_files = []
 
1127
  for build_file, build_file_dict in data.iteritems():
 
1128
    if build_file.endswith('.gyp'):
 
1129
      build_files.append(build_file)
 
1130
 
 
1131
  for build_file in build_files:
 
1132
    xcode_projects[build_file].Finalize1(xcode_targets, serialize_all_tests)
 
1133
 
 
1134
  for build_file in build_files:
 
1135
    xcode_projects[build_file].Finalize2(xcode_targets,
 
1136
                                         xcode_target_to_target_dict)
 
1137
 
 
1138
  for build_file in build_files:
 
1139
    xcode_projects[build_file].Write()