Adds generator support for Xcode 5 xctest targets.

Update the xcode generator to set the target productType in the pbxproj file to "com.apple.product-type.bundle.unit-test", if the target sets the "xctest_bundle" property.

Patch by Jon Wall <jonwall@google.com>

BUG=
R=mark@chromium.org

Review URL: https://codereview.chromium.org/51413002

git-svn-id: http://gyp.googlecode.com/svn/trunk@1774 78cadc50-ecff-11dd-a971-7dbc132099af
diff --git a/pylib/gyp/generator/xcode.py b/pylib/gyp/generator/xcode.py
index 7e5f79f..a532cc3 100644
--- a/pylib/gyp/generator/xcode.py
+++ b/pylib/gyp/generator/xcode.py
@@ -72,6 +72,7 @@
   'mac_bundle_resources',
   'mac_framework_headers',
   'mac_framework_private_headers',
+  'mac_xctest_bundle',
   'xcode_create_dependents_test_runner',
 ]
 
@@ -642,6 +643,7 @@
       'static_library':         'com.apple.product-type.library.static',
       'executable+bundle':      'com.apple.product-type.application',
       'loadable_module+bundle': 'com.apple.product-type.bundle',
+      'loadable_module+xctest': 'com.apple.product-type.bundle.unit-test',
       'shared_library+bundle':  'com.apple.product-type.framework',
     }
 
@@ -651,11 +653,18 @@
     }
 
     type = spec['type']
-    is_bundle = int(spec.get('mac_bundle', 0))
+    is_xctest = int(spec.get('mac_xctest_bundle', 0))
+    is_bundle = int(spec.get('mac_bundle', 0)) or is_xctest
     if type != 'none':
       type_bundle_key = type
-      if is_bundle:
+      if is_xctest:
+        type_bundle_key += '+xctest'
+        assert type == 'loadable_module', (
+            'mac_xctest_bundle targets must have type loadable_module '
+            '(target %s)' % target_name)
+      elif is_bundle:
         type_bundle_key += '+bundle'
+
       xctarget_type = gyp.xcodeproj_file.PBXNativeTarget
       try:
         target_properties['productType'] = _types[type_bundle_key]
@@ -668,6 +677,9 @@
       assert not is_bundle, (
           'mac_bundle targets cannot have type none (target "%s")' %
           target_name)
+      assert not is_xctest, (
+          'mac_xctest_bundle targets cannot have type none (target "%s")' %
+          target_name)
 
     target_product_name = spec.get('product_name')
     if target_product_name is not None:
diff --git a/pylib/gyp/xcodeproj_file.py b/pylib/gyp/xcodeproj_file.py
index fd74086..6e4a1dc 100644
--- a/pylib/gyp/xcodeproj_file.py
+++ b/pylib/gyp/xcodeproj_file.py
@@ -2238,20 +2238,22 @@
   #  prefix : the prefix for the file name
   #  suffix : the suffix for the filen ame
   _product_filetypes = {
-    'com.apple.product-type.application':     ['wrapper.application',
-                                               '', '.app'],
-    'com.apple.product-type.bundle':          ['wrapper.cfbundle',
-                                               '', '.bundle'],
-    'com.apple.product-type.framework':       ['wrapper.framework',
-                                               '', '.framework'],
-    'com.apple.product-type.library.dynamic': ['compiled.mach-o.dylib',
-                                               'lib', '.dylib'],
-    'com.apple.product-type.library.static':  ['archive.ar',
-                                               'lib', '.a'],
-    'com.apple.product-type.tool':            ['compiled.mach-o.executable',
-                                               '', ''],
-    'com.googlecode.gyp.xcode.bundle':        ['compiled.mach-o.dylib',
-                                               '', '.so'],
+    'com.apple.product-type.application':       ['wrapper.application',
+                                                 '', '.app'],
+    'com.apple.product-type.bundle':            ['wrapper.cfbundle',
+                                                 '', '.bundle'],
+    'com.apple.product-type.framework':         ['wrapper.framework',
+                                                 '', '.framework'],
+    'com.apple.product-type.library.dynamic':   ['compiled.mach-o.dylib',
+                                                 'lib', '.dylib'],
+    'com.apple.product-type.library.static':    ['archive.ar',
+                                                 'lib', '.a'],
+    'com.apple.product-type.tool':              ['compiled.mach-o.executable',
+                                                 '', ''],
+    'com.apple.product-type.bundle.unit-test':  ['wrapper.cfbundle',
+                                                 '', '.xctest'],
+    'com.googlecode.gyp.xcode.bundle':          ['compiled.mach-o.dylib',
+                                                 '', '.so'],
   }
 
   def __init__(self, properties=None, id=None, parent=None,
@@ -2303,6 +2305,11 @@
           if force_extension is None:
             force_extension = suffix[1:]
 
+        if self._properties['productType'] == \
+           'com.apple.product-type-bundle.unit.test':
+          if force_extension is None:
+            force_extension = suffix[1:]
+
         if force_extension is not None:
           # If it's a wrapper (bundle), set WRAPPER_EXTENSION.
           if filetype.startswith('wrapper.'):
diff --git a/test/mac/gyptest-xctest.py b/test/mac/gyptest-xctest.py
new file mode 100644
index 0000000..a46a5fb
--- /dev/null
+++ b/test/mac/gyptest-xctest.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that xctest targets are correctly configured.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+  test = TestGyp.TestGyp(formats=['xcode'])
+
+  # Ignore this test if Xcode 5 is not installed
+  import subprocess
+  job = subprocess.Popen(['xcodebuild', '-version'],
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.STDOUT)
+  out, err = job.communicate()
+  if job.returncode != 0:
+    raise Exception('Error %d running xcodebuild' % job.returncode)
+  xcode_version, build_number = out.splitlines()
+  # Convert the version string from 'Xcode 5.0' to ['5','0'].
+  xcode_version = xcode_version.split()[-1].split('.')
+  if xcode_version < ['5']:
+    test.pass_test()
+
+  CHDIR = 'xctest'
+  test.run_gyp('test.gyp', chdir=CHDIR)
+  test.build('test.gyp', chdir=CHDIR, arguments=['-scheme', 'classes', 'test'])
+
+  test.built_file_must_match('tests.xctest/Contents/Resources/resource.txt',
+                             'foo\n', chdir=CHDIR)
+  test.pass_test()
diff --git a/test/mac/xctest/MyClass.h b/test/mac/xctest/MyClass.h
new file mode 100644
index 0000000..dde13aa
--- /dev/null
+++ b/test/mac/xctest/MyClass.h
@@ -0,0 +1,8 @@
+// Copyright (c) 2013 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+@interface MyClass : NSObject
+@end
diff --git a/test/mac/xctest/MyClass.m b/test/mac/xctest/MyClass.m
new file mode 100644
index 0000000..df11471
--- /dev/null
+++ b/test/mac/xctest/MyClass.m
@@ -0,0 +1,8 @@
+// Copyright (c) 2013 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "MyClass.h"
+
+@implementation MyClass
+@end
diff --git a/test/mac/xctest/TestCase.m b/test/mac/xctest/TestCase.m
new file mode 100644
index 0000000..36846a1
--- /dev/null
+++ b/test/mac/xctest/TestCase.m
@@ -0,0 +1,16 @@
+// Copyright (c) 2013 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <XCTest/XCTest.h>
+#import "MyClass.h"
+
+@interface TestCase : XCTestCase
+@end
+
+@implementation TestCase
+- (void)testFoo {
+  MyClass *foo = [[MyClass alloc] init];
+  XCTAssertNotNil(foo, @"expected non-nil object");
+}
+@end
diff --git a/test/mac/xctest/resource.txt b/test/mac/xctest/resource.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/test/mac/xctest/resource.txt
@@ -0,0 +1 @@
+foo
diff --git a/test/mac/xctest/test.gyp b/test/mac/xctest/test.gyp
new file mode 100644
index 0000000..ac25656
--- /dev/null
+++ b/test/mac/xctest/test.gyp
@@ -0,0 +1,47 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'classes',
+      'type': 'static_library',
+      'sources': [
+        'MyClass.h',
+        'MyClass.m',
+      ],
+      'link_settings': {
+        'libraries': [
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+        ],
+      },
+    },
+    {
+      'target_name': 'tests',
+      'type': 'loadable_module',
+      'mac_xctest_bundle': 1,
+      'sources': [
+        'TestCase.m',
+      ],
+      'dependencies': [
+        'classes',
+      ],
+      'mac_bundle_resources': [
+        'resource.txt',
+      ],
+      'xcode_settings': {
+        'WRAPPER_EXTENSION': 'xctest',
+        'FRAMEWORK_SEARCH_PATHS': [
+          '$(inherited)',
+          '$(DEVELOPER_FRAMEWORKS_DIR)',
+        ],
+        'OTHER_LDFLAGS': [
+          '$(inherited)',
+          '-ObjC',
+        ],
+      },
+    },
+  ],
+}
+
diff --git a/test/mac/xctest/test.xcodeproj/xcshareddata/xcschemes/classes.xcscheme b/test/mac/xctest/test.xcodeproj/xcshareddata/xcschemes/classes.xcscheme
new file mode 100644
index 0000000..6bd1bb9
--- /dev/null
+++ b/test/mac/xctest/test.xcodeproj/xcshareddata/xcschemes/classes.xcscheme
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0500"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D3B79173B4570A3C70A902FF"
+               BuildableName = "libclasses.a"
+               BlueprintName = "classes"
+               ReferencedContainer = "container:test.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Default">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "2ACDAB234B9E5D65CACBCF9C"
+               BuildableName = "tests.xctest"
+               BlueprintName = "tests"
+               ReferencedContainer = "container:test.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Default"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Default"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Default">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Default"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>