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

Gregor Jasny gjasny at googlemail.com
Wed Feb 25 15:07:43 EST 2015


An XCTest bundle is a CFBundle with a special product-type and bundle
extension. For more information about XCTest visit the Mac
Developer library at:

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

Signed-off-by: Gregor Jasny <gjasny at googlemail.com>
---
 Help/manual/cmake-modules.7.rst    |   1 +
 Help/manual/cmake-properties.7.rst |   1 +
 Help/module/XCTestUtilities.rst    |   1 +
 Help/prop_tgt/XCTEST.rst           |  13 ++++
 Help/release/dev/xcode-xctest.rst  |   6 ++
 Modules/XCTestUtilities.cmake      | 123 +++++++++++++++++++++++++++++++++++++
 Source/cmGlobalXCodeGenerator.cxx  |  12 +++-
 Source/cmTarget.cxx                |  16 ++++-
 Source/cmTarget.h                  |   3 +
 9 files changed, 173 insertions(+), 3 deletions(-)
 create mode 100644 Help/module/XCTestUtilities.rst
 create mode 100644 Help/prop_tgt/XCTEST.rst
 create mode 100644 Help/release/dev/xcode-xctest.rst
 create mode 100644 Modules/XCTestUtilities.cmake

diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index 2b26cc9..a2a2d24 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -241,3 +241,4 @@ All Modules
    /module/Use_wxWindows
    /module/WriteBasicConfigVersionFile
    /module/WriteCompilerDetectionHeader
+   /module/XCTestUtilities
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 19fdf23..1dff33e 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -243,6 +243,7 @@ Properties on Targets
    /prop_tgt/VS_WINRT_REFERENCES
    /prop_tgt/WIN32_EXECUTABLE
    /prop_tgt/XCODE_ATTRIBUTE_an-attribute
+   /prop_tgt/XCTEST
 
 Properties on Tests
 ===================
diff --git a/Help/module/XCTestUtilities.rst b/Help/module/XCTestUtilities.rst
new file mode 100644
index 0000000..c96f515
--- /dev/null
+++ b/Help/module/XCTestUtilities.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/XCTestUtilities.cmake
diff --git a/Help/prop_tgt/XCTEST.rst b/Help/prop_tgt/XCTEST.rst
new file mode 100644
index 0000000..8a2ef8b
--- /dev/null
+++ b/Help/prop_tgt/XCTEST.rst
@@ -0,0 +1,13 @@
+XCTEST
+------
+
+This target is a XCTest CFBundle on the Mac.
+
+This property will usually get set via the ``add_xctest`` macro in
+:module:`XCTestUtilities` module.
+
+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.
+
+This property depends on :prop_tgt:`BUNDLE` to be effective.
diff --git a/Help/release/dev/xcode-xctest.rst b/Help/release/dev/xcode-xctest.rst
new file mode 100644
index 0000000..af9f705
--- /dev/null
+++ b/Help/release/dev/xcode-xctest.rst
@@ -0,0 +1,6 @@
+Xcode XCTest Support
+--------------------
+
+* CMake gained the abiltity to create XCTest bundles to test
+  Frameworks and App Bundles within Xcode. The :module:`XCTestUtilities`
+  module provides convenience functions to handle :prop_tgt:`XCTEST` bundles.
diff --git a/Modules/XCTestUtilities.cmake b/Modules/XCTestUtilities.cmake
new file mode 100644
index 0000000..0ff648c
--- /dev/null
+++ b/Modules/XCTestUtilities.cmake
@@ -0,0 +1,123 @@
+#.rst:
+# XCTestUtilities
+# ---------------
+#
+# Functions to help creating and executing XCTest bundles.
+#
+# An XCTest bundle is a CFBundle with a special product-type
+# and bundle extension. For more information about XCTest visit
+# the Mac Developer library at:
+# http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/
+#
+# The following functions are provided by this module:
+#
+# ::
+#
+#    add_xctest
+#    add_test_xctest
+#
+# ::
+#
+#   add_xctest(<target> <testee>)
+#
+# Create a XCTest bundle named <target> which will test the target
+# <testee>. Supported target types for testee are Frameworks and
+# App Bundles.
+#
+# ::
+#
+#   add_test_xctest(<name> <target>)
+#
+# Add an XCTest bundle to the project to be run by :manual:`ctest(1)`.
+# The test will be named <name> and tests <target>.
+
+#=============================================================================
+# Copyright 2015 Gregor Jasny
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+function(add_xctest target testee)
+
+  if(NOT CMAKE_OSX_SYSROOT)
+    message(STATUS "Adding XCTest bundles requires CMAKE_OSX_SYSROOT to be set.")
+  endif()
+
+  # check that testee is a valid target type
+  get_target_property(TESTEE_TYPE ${testee} TYPE)
+  get_target_property(TESTEE_FRAMEWORK ${testee} FRAMEWORK)
+  get_target_property(TESTEE_MACOSX_BUNDLE ${testee} MACOSX_BUNDLE)
+
+  if(TESTEE_TYPE STREQUAL "SHARED_LIBRARY" AND TESTEE_FRAMEWORK)
+    # found a framework
+  elseif(TESTEE_TYPE STREQUAL "EXECUTABLE" AND TESTEE_MACOSX_BUNDLE)
+    # found an app bundle
+  else()
+  	message(FATAL_ERROR "Testee ${testee} is of unsupported type: ${TESTEE_TYPE}")
+  endif()
+
+  add_library(${target} MODULE ${ARGN})
+
+  set_target_properties(${target} PROPERTIES
+    BUNDLE TRUE
+    XCTEST TRUE)
+
+  find_library(FOUNDATION_LIBRARY Foundation)
+  if(NOT FOUNDATION_LIBRARY)
+    message(STATUS "Could not find Foundation Framework.")
+  endif()
+
+  find_library(XCTEST_LIBRARY XCTest)
+  if(NOT XCTEST_LIBRARY)
+    message(STATUS "Could not find XCTest Framework.")
+  endif()
+
+  target_link_libraries(${target} PRIVATE ${FOUNDATION_LIBRARY} ${XCTEST_LIBRARY})
+  mark_as_advanced(FOUNDATION_LIBRARY XCTEST_LIBRARY)
+
+  if(TESTEE_TYPE STREQUAL "SHARED_LIBRARY" AND TESTEE_FRAMEWORK)
+    set_target_properties(${testee} PROPERTIES
+      BUILD_WITH_INSTALL_RPATH TRUE
+      INSTALL_NAME_DIR "@rpath")
+
+    target_link_libraries(${target} PRIVATE ${testee})
+  elseif(TESTEE_TYPE STREQUAL "EXECUTABLE" AND TESTEE_MACOSX_BUNDLE)
+    add_dependencies(${target} ${testee})
+    if(XCODE)
+      set_target_properties(${target} PROPERTIES
+        XCODE_ATTRIBUTE_BUNDLE_LOADER "$(TEST_HOST)"
+        XCODE_ATTRIBUTE_TEST_HOST "$<TARGET_FILE:${testee}>")
+    else(XCODE)
+      target_link_libraries(${target} PRIVATE "-bundle_loader $<TARGET_FILE:${testee}>")
+    endif(XCODE)
+  endif()
+endfunction(add_xctest)
+
+function(add_test_xctest name target)
+  get_target_property(TARGET_TYPE ${target} TYPE)
+  get_target_property(TARGET_XCTEST ${target} XCTEST)
+
+  if(NOT TARGET_TYPE STREQUAL "MODULE_LIBRARY" OR NOT TARGET_XCTEST)
+  	message(FATAL_ERROR "Test ${target} is not a XCTest")
+  endif()
+
+  execute_process(
+    COMMAND xcrun --find xctest
+    OUTPUT_VARIABLE XCTEST_EXECUTABLE
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  if(NOT XCTEST_EXECUTABLE)
+    message(STATUS "Unable to finc xctest binary.")
+  endif()
+
+  add_test(
+    NAME ${name}
+    COMMAND ${XCTEST_EXECUTABLE} $<TARGET_LINKER_FILE_DIR:${target}>/../..)
+endfunction(add_test_xctest)
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index aea134e..784bc24 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -804,6 +804,10 @@ GetSourcecodeValueFromFileExtension(const std::string& _ext,
     {
     sourcecode = "compiled.mach-o.objfile";
     }
+  else if(ext == "xctest")
+    {
+    sourcecode = "wrapper.cfbundle";
+    }
   else if(ext == "xib")
     {
     keepLastKnownFileType = true;
@@ -2598,7 +2602,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)?
@@ -2622,7 +2628,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 e046bef..6753895 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -615,6 +615,13 @@ bool cmTarget::IsCFBundleOnApple() const
 }
 
 //----------------------------------------------------------------------------
+bool cmTarget::IsXCTestOnApple() const
+{
+  return (this->IsCFBundleOnApple() &&
+          this->GetPropertyAsBool("XCTEST"));
+}
+
+//----------------------------------------------------------------------------
 bool cmTarget::IsBundleOnApple() const
 {
   return this->IsFrameworkOnApple() || this->IsAppBundleOnApple() ||
@@ -6774,7 +6781,14 @@ std::string cmTarget::GetCFBundleDirectory(const std::string& config,
   const char *ext = this->GetProperty("BUNDLE_EXTENSION");
   if (!ext)
     {
-    ext = "bundle";
+    if (this->IsXCTestOnApple())
+      {
+      ext = "xctest";
+      }
+    else
+      {
+      ext = "bundle";
+      }
     }
   fpath += ext;
   fpath += "/Contents";
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index ddd9859..b58e0a0 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;
 
-- 
2.3.0



More information about the cmake-developers mailing list