Ryan Prichard | 7aea7e9 | 2022-01-13 17:30:17 -0800 | [diff] [blame] | 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| 2 | # file Copyright.txt or https://cmake.org/licensing for details. |
| 3 | |
| 4 | #[=======================================================================[.rst: |
| 5 | FindXCTest |
| 6 | ---------- |
| 7 | |
| 8 | .. versionadded:: 3.3 |
| 9 | |
| 10 | Functions to help creating and executing XCTest bundles. |
| 11 | |
| 12 | An XCTest bundle is a CFBundle with a special product-type |
| 13 | and bundle extension. The Mac Developer Library provides more |
| 14 | information in the `Testing with Xcode`_ document. |
| 15 | |
| 16 | .. _Testing with Xcode: http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/ |
| 17 | |
| 18 | Module Functions |
| 19 | ^^^^^^^^^^^^^^^^ |
| 20 | |
| 21 | .. command:: xctest_add_bundle |
| 22 | |
| 23 | The ``xctest_add_bundle`` function creates a XCTest bundle named |
| 24 | <target> which will test the target <testee>. Supported target types |
| 25 | for testee are Frameworks and App Bundles:: |
| 26 | |
| 27 | xctest_add_bundle( |
| 28 | <target> # Name of the XCTest bundle |
| 29 | <testee> # Target name of the testee |
| 30 | ) |
| 31 | |
| 32 | .. command:: xctest_add_test |
| 33 | |
| 34 | The ``xctest_add_test`` function adds an XCTest bundle to the |
| 35 | project to be run by :manual:`ctest(1)`. The test will be named |
| 36 | <name> and tests <bundle>:: |
| 37 | |
| 38 | xctest_add_test( |
| 39 | <name> # Test name |
| 40 | <bundle> # Target name of XCTest bundle |
| 41 | ) |
| 42 | |
| 43 | Module Variables |
| 44 | ^^^^^^^^^^^^^^^^ |
| 45 | |
| 46 | The following variables are set by including this module: |
| 47 | |
| 48 | .. variable:: XCTest_FOUND |
| 49 | |
| 50 | True if the XCTest Framework and executable were found. |
| 51 | |
| 52 | .. variable:: XCTest_EXECUTABLE |
| 53 | |
| 54 | The path to the xctest command line tool used to execute XCTest bundles. |
| 55 | |
| 56 | .. variable:: XCTest_INCLUDE_DIRS |
| 57 | |
| 58 | The directory containing the XCTest Framework headers. |
| 59 | |
| 60 | .. variable:: XCTest_LIBRARIES |
| 61 | |
| 62 | The location of the XCTest Framework. |
| 63 | |
| 64 | #]=======================================================================] |
| 65 | |
| 66 | set(_PRESERVED_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}") |
| 67 | |
| 68 | if(CMAKE_EFFECTIVE_SYSTEM_NAME STREQUAL "Apple" |
| 69 | AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") |
| 70 | # Non-macos systems set the CMAKE_FIND_ROOT_PATH_MODE to "ONLY" which |
| 71 | # restricts the search paths too much to find XCTest.framework. In |
| 72 | # contrast to the regular system frameworks which reside within the |
| 73 | # SDK direectory the XCTest framework is located in the respective |
| 74 | # platform directory which is not added to the CMAKE_FIND_ROOT_PATH |
| 75 | # (only to CMAKE_SYSTEM_FRAMEWORK_PATH) and therefore not searched. |
| 76 | # |
| 77 | # Until this is properly addressed, temporaily add the platform |
| 78 | # directory to CMAKE_FIND_ROOT_PATH. |
| 79 | list(APPEND CMAKE_FIND_ROOT_PATH "${_CMAKE_OSX_SYSROOT_PATH}/../..") |
| 80 | endif() |
| 81 | |
| 82 | find_path(XCTest_INCLUDE_DIR |
| 83 | NAMES "XCTest/XCTest.h" |
| 84 | DOC "XCTest include directory") |
| 85 | mark_as_advanced(XCTest_INCLUDE_DIR) |
| 86 | |
| 87 | find_library(XCTest_LIBRARY |
| 88 | NAMES XCTest |
| 89 | DOC "XCTest Framework library") |
| 90 | mark_as_advanced(XCTest_LIBRARY) |
| 91 | |
| 92 | set(CMAKE_FIND_ROOT_PATH "${_PRESERVED_CMAKE_FIND_ROOT_PATH}") |
| 93 | unset(_PRESERVED_CMAKE_FIND_ROOT_PATH) |
| 94 | |
| 95 | execute_process( |
| 96 | COMMAND xcrun --find xctest |
| 97 | OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE |
| 98 | ERROR_VARIABLE _xcrun_err) |
| 99 | if(_xcrun_out) |
| 100 | set(XCTest_EXECUTABLE "${_xcrun_out}" CACHE FILEPATH "XCTest executable") |
| 101 | mark_as_advanced(XCTest_EXECUTABLE) |
| 102 | endif() |
| 103 | |
| 104 | include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) |
| 105 | find_package_handle_standard_args(XCTest |
| 106 | FOUND_VAR XCTest_FOUND |
| 107 | REQUIRED_VARS XCTest_LIBRARY XCTest_INCLUDE_DIR XCTest_EXECUTABLE) |
| 108 | |
| 109 | if(XCTest_FOUND) |
| 110 | set(XCTest_INCLUDE_DIRS "${XCTest_INCLUDE_DIR}") |
| 111 | set(XCTest_LIBRARIES "${XCTest_LIBRARY}") |
| 112 | endif(XCTest_FOUND) |
| 113 | |
| 114 | |
| 115 | function(xctest_add_bundle target testee) |
| 116 | if(NOT XCTest_FOUND) |
| 117 | message(FATAL_ERROR "XCTest is required to create a XCTest Bundle.") |
| 118 | endif(NOT XCTest_FOUND) |
| 119 | |
| 120 | if(NOT CMAKE_OSX_SYSROOT) |
| 121 | message(FATAL_ERROR "Adding XCTest bundles requires CMAKE_OSX_SYSROOT to be set.") |
| 122 | endif() |
| 123 | |
| 124 | add_library(${target} MODULE ${ARGN}) |
| 125 | |
| 126 | set_target_properties(${target} PROPERTIES |
| 127 | BUNDLE TRUE |
| 128 | XCTEST TRUE |
| 129 | XCTEST_TESTEE ${testee}) |
| 130 | |
| 131 | target_link_libraries(${target} PRIVATE "-framework Foundation") |
| 132 | target_link_libraries(${target} PRIVATE ${XCTest_LIBRARIES}) |
| 133 | target_include_directories(${target} PRIVATE ${XCTest_INCLUDE_DIRS}) |
| 134 | |
| 135 | # retrieve testee target type |
| 136 | if(NOT TARGET ${testee}) |
| 137 | message(FATAL_ERROR "${testee} is not a target.") |
| 138 | endif() |
| 139 | get_property(_testee_type TARGET ${testee} PROPERTY TYPE) |
| 140 | get_property(_testee_framework TARGET ${testee} PROPERTY FRAMEWORK) |
| 141 | get_property(_testee_macosx_bundle TARGET ${testee} PROPERTY MACOSX_BUNDLE) |
| 142 | |
| 143 | if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework) |
| 144 | # testee is a Framework |
| 145 | target_link_libraries(${target} PRIVATE ${testee}) |
| 146 | |
| 147 | elseif(_testee_type STREQUAL "STATIC_LIBRARY") |
| 148 | # testee is a static library |
| 149 | target_link_libraries(${target} PRIVATE ${testee}) |
| 150 | |
| 151 | elseif(_testee_type STREQUAL "EXECUTABLE" AND _testee_macosx_bundle) |
| 152 | # testee is an App Bundle |
| 153 | add_dependencies(${target} ${testee}) |
| 154 | if(XCODE) |
| 155 | set_target_properties(${target} PROPERTIES |
| 156 | XCODE_ATTRIBUTE_BUNDLE_LOADER "$(TEST_HOST)" |
| 157 | XCODE_ATTRIBUTE_TEST_HOST "$<TARGET_FILE:${testee}>") |
| 158 | if(XCODE_VERSION VERSION_GREATER_EQUAL 7.3) |
| 159 | # The Xcode "new build system" used a different path until Xcode 12.5. |
| 160 | if(CMAKE_XCODE_BUILD_SYSTEM EQUAL 12 AND |
| 161 | XCODE_VERSION VERSION_LESS 12.5 AND |
| 162 | NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") |
| 163 | set(_output_directory "$<TARGET_BUNDLE_CONTENT_DIR:${testee}>") |
| 164 | else() |
| 165 | set(_output_directory "$<TARGET_BUNDLE_CONTENT_DIR:${testee}>/PlugIns") |
| 166 | endif() |
| 167 | set_target_properties(${target} PROPERTIES |
| 168 | LIBRARY_OUTPUT_DIRECTORY "${_output_directory}") |
| 169 | endif() |
| 170 | else(XCODE) |
| 171 | target_link_libraries(${target} |
| 172 | PRIVATE -bundle_loader $<TARGET_FILE:${testee}>) |
| 173 | endif(XCODE) |
| 174 | |
| 175 | else() |
| 176 | message(FATAL_ERROR "Testee ${testee} is of unsupported type.") |
| 177 | endif() |
| 178 | endfunction(xctest_add_bundle) |
| 179 | |
| 180 | |
| 181 | function(xctest_add_test name bundle) |
| 182 | if(NOT XCTest_EXECUTABLE) |
| 183 | message(FATAL_ERROR "XCTest executable is required to register a test.") |
| 184 | endif() |
| 185 | |
| 186 | # check that bundle is a XCTest Bundle |
| 187 | |
| 188 | if(NOT TARGET ${bundle}) |
| 189 | message(FATAL_ERROR "${bundle} is not a target.") |
| 190 | endif(NOT TARGET ${bundle}) |
| 191 | |
| 192 | get_property(_test_type TARGET ${bundle} PROPERTY TYPE) |
| 193 | get_property(_test_bundle TARGET ${bundle} PROPERTY BUNDLE) |
| 194 | get_property(_test_xctest TARGET ${bundle} PROPERTY XCTEST) |
| 195 | |
| 196 | if(NOT _test_type STREQUAL "MODULE_LIBRARY" |
| 197 | OR NOT _test_xctest OR NOT _test_bundle) |
| 198 | message(FATAL_ERROR "Test ${bundle} is not an XCTest Bundle") |
| 199 | endif() |
| 200 | |
| 201 | # get and check testee properties |
| 202 | |
| 203 | get_property(_testee TARGET ${bundle} PROPERTY XCTEST_TESTEE) |
| 204 | if(NOT TARGET ${_testee}) |
| 205 | message(FATAL_ERROR "${_testee} is not a target.") |
| 206 | endif() |
| 207 | |
| 208 | get_property(_testee_type TARGET ${_testee} PROPERTY TYPE) |
| 209 | get_property(_testee_framework TARGET ${_testee} PROPERTY FRAMEWORK) |
| 210 | |
| 211 | # register test |
| 212 | |
| 213 | add_test( |
| 214 | NAME ${name} |
| 215 | COMMAND ${XCTest_EXECUTABLE} $<TARGET_BUNDLE_DIR:${bundle}>) |
| 216 | |
| 217 | # point loader to testee in case rpath is disabled |
| 218 | |
| 219 | if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework) |
| 220 | set_property(TEST ${name} APPEND PROPERTY |
| 221 | ENVIRONMENT DYLD_FRAMEWORK_PATH=$<TARGET_LINKER_FILE_DIR:${_testee}>/..) |
| 222 | endif() |
| 223 | endfunction(xctest_add_test) |