[cmake-developers] [PATCH 1/2] Add handling for XCTest bundles

Gregor Jasny gjasny at googlemail.com
Tue Dec 9 06:02:24 EST 2014


An XCTest bundle is a CFBundle with a special product-type and bundle
extension. It gets loaded directly into the AppBundle or Framework
it should test. For more information about XCTest visit the Mac
Developer library at:

http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/

To build a XCTest bundle, you need to create a CFBundle and set the
XCTEST property. Additionally you need to specify the application
or framework target to test within the XCTEST_HOST property. CMake
and Xcode will then setup the correct target dependencies.

add_library(CocoaExampleTest MODULE ...)
set_target_properties(CocoaExampleTest PROPERTIES
  XCTEST TRUE
  XCTEST_HOST CocoaExample)

Signed-off-by: Gregor Jasny <gjasny at googlemail.com>
---
 Help/manual/cmake-properties.7.rst |  2 ++
 Help/prop_tgt/XCTEST.rst           |  9 ++++++
 Help/prop_tgt/XCTEST_HOST.rst      |  5 +++
 Source/cmGlobalXCodeGenerator.cxx  | 62 ++++++++++++++++++++++++++++++++++++--
 Source/cmTarget.cxx                | 19 ++++++++++--
 Source/cmTarget.h                  |  3 ++
 6 files changed, 95 insertions(+), 5 deletions(-)
 create mode 100644 Help/prop_tgt/XCTEST.rst
 create mode 100644 Help/prop_tgt/XCTEST_HOST.rst

diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index cca6d28..7b8aa66 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -242,6 +242,8 @@ Properties on Targets
    /prop_tgt/VS_WINRT_REFERENCES
    /prop_tgt/WIN32_EXECUTABLE
    /prop_tgt/XCODE_ATTRIBUTE_an-attribute
+   /prop_tgt/XCTEST
+   /prop_tgt/XCTEST_HOST
 
 Properties on Tests
 ===================
diff --git a/Help/prop_tgt/XCTEST.rst b/Help/prop_tgt/XCTEST.rst
new file mode 100644
index 0000000..8a60ad8
--- /dev/null
+++ b/Help/prop_tgt/XCTEST.rst
@@ -0,0 +1,9 @@
+XCTEST
+------
+
+This target is a XCTest CFBundle on the Mac.
+
+If a module library target has this property set to true it will be
+built as a CFBundle when built on the mac.  It will have the directory
+structure required for a CFBundle and will be suitable to be used for
+creating Browser Plugins or other application resources.
diff --git a/Help/prop_tgt/XCTEST_HOST.rst b/Help/prop_tgt/XCTEST_HOST.rst
new file mode 100644
index 0000000..064dbf7
--- /dev/null
+++ b/Help/prop_tgt/XCTEST_HOST.rst
@@ -0,0 +1,5 @@
+XCTEST_HOST
+-----------
+
+XCTest works by injecting an XCTest CFBundle directly into an AppBundle
+or Framework. This property names this destination target under test.
\ No newline at end of file
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index d222288..a88350f 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -493,6 +493,15 @@ cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root,
         allbuild->AddUtility(target.GetName());
         }
 
+      if(target.IsXCTestOnApple())
+        {
+        const char *testHostName = target.GetProperty("XCTEST_HOST");
+        if(testHostName && this->CurrentMakefile->FindTargetToUse(testHostName))
+          {
+          target.AddUtility(testHostName);
+          }
+        }
+
       // Refer to the build configuration file for easy editing.
       listfile = lg->GetMakefile()->GetStartDirectory();
       listfile += "/";
@@ -761,6 +770,10 @@ GetSourcecodeValueFromFileExtension(const std::string& _ext,
     {
     sourcecode = "compiled.mach-o.objfile";
     }
+  else if(ext == "xctest")
+    {
+    sourcecode = "wrapper.cfbundle";
+    }
   else if(ext == "xib")
     {
     keepLastKnownFileType = true;
@@ -1964,7 +1977,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
     {
     buildSettings->AddAttribute("LIBRARY_STYLE",
                                 this->CreateString("BUNDLE"));
-    if (target.GetPropertyAsBool("BUNDLE"))
+    if (target.IsCFBundleOnApple())
       {
       // It turns out that a BUNDLE is basically the same
       // in many ways as an application bundle, as far as
@@ -2360,6 +2373,45 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
                                 this->CreateString(vso.str().c_str()));
     }
+
+  if(target.IsXCTestOnApple())
+    {
+    if(this->XcodeVersion < 50)
+      {
+      this->CurrentMakefile->IssueMessage(cmake::WARNING,
+        "Xcode 5.0 or later is required for XCTEST support.");
+      }
+
+    const char *testHostName = target.GetProperty("XCTEST_HOST");
+    if(!testHostName)
+      {
+      cmSystemTools::Error("target has XCTEST property "
+                           "but no XCTEST_HOST one.");
+      return;
+      }
+
+    cmTarget *testHostTarget =
+      this->CurrentMakefile->FindTargetToUse(testHostName);
+    if(!testHostTarget)
+      {
+      cmSystemTools::Error("Cannot find XCTEST_HOST target: ", testHostName);
+      return;
+      }
+
+    if (!testHostTarget->IsAppBundleOnApple() &&
+        !testHostTarget->IsFrameworkOnApple())
+      {
+      cmSystemTools::Error("XCTEST_HOST target is not a Framework "
+                           "or App Bundle: ", testHostName);
+      return;
+      }
+
+    buildSettings->AddAttribute("BUNDLE_LOADER",
+      this->CreateString("$(TEST_HOST)"));
+    buildSettings->AddAttribute("TEST_HOST",
+      this->CreateString(testHostTarget->GetLocationForBuild()));
+    }
+
   // put this last so it can override existing settings
   // Convert "XCODE_ATTRIBUTE_*" properties directly.
   {
@@ -2558,7 +2610,9 @@ const char* cmGlobalXCodeGenerator::GetTargetFileType(cmTarget& cmtarget)
     case cmTarget::STATIC_LIBRARY:
       return "archive.ar";
     case cmTarget::MODULE_LIBRARY:
-      if (cmtarget.IsCFBundleOnApple())
+      if (cmtarget.IsXCTestOnApple())
+      	return "wrapper.cfbundle";
+      else if (cmtarget.IsCFBundleOnApple())
         return "wrapper.plug-in";
       else
         return ((this->XcodeVersion >= 22)?
@@ -2582,7 +2636,9 @@ const char* cmGlobalXCodeGenerator::GetTargetProductType(cmTarget& cmtarget)
     case cmTarget::STATIC_LIBRARY:
       return "com.apple.product-type.library.static";
     case cmTarget::MODULE_LIBRARY:
-      if (cmtarget.IsCFBundleOnApple())
+      if (cmtarget.IsXCTestOnApple())
+      	return "com.apple.product-type.bundle.unit-test";
+      else if (cmtarget.IsCFBundleOnApple())
         return "com.apple.product-type.bundle";
       else
         return ((this->XcodeVersion >= 22)?
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index f0957d0..ff52d4a 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -624,7 +624,15 @@ bool cmTarget::IsCFBundleOnApple() const
 {
   return (this->GetType() == cmTarget::MODULE_LIBRARY &&
           this->Makefile->IsOn("APPLE") &&
-          this->GetPropertyAsBool("BUNDLE"));
+          (this->GetPropertyAsBool("BUNDLE") ||
+           this->GetPropertyAsBool("XCTEST")));
+}
+
+//----------------------------------------------------------------------------
+bool cmTarget::IsXCTestOnApple() const
+{
+  return (this->IsCFBundleOnApple() &&
+          this->GetPropertyAsBool("XCTEST"));
 }
 
 //----------------------------------------------------------------------------
@@ -4061,7 +4069,14 @@ void cmTarget::GetFullNameInternal(const std::string& config,
     const char *ext = this->GetProperty("BUNDLE_EXTENSION");
     if (!ext)
       {
-      ext = "bundle";
+      if (this->IsXCTestOnApple())
+        {
+        ext = "xctest";
+        }
+      else
+        {
+        ext = "bundle";
+        }
       }
     fw_prefix += ext;
     fw_prefix += "/Contents/MacOS/";
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index a3ecca0..5786d78 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -526,6 +526,9 @@ public:
   /** Return whether this target is a CFBundle (plugin) on Apple.  */
   bool IsCFBundleOnApple() const;
 
+  /** Return whether this target is a XCTest on Apple.  */
+  bool IsXCTestOnApple() const;
+
   /** Return whether this target is an executable Bundle on Apple.  */
   bool IsAppBundleOnApple() const;
 
-- 
1.9.3 (Apple Git-50)



More information about the cmake-developers mailing list