# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindXCTest ---------- .. versionadded:: 3.3 Functions to help creating and executing XCTest bundles. An XCTest bundle is a CFBundle with a special product-type and bundle extension. The Mac Developer Library provides more information in the `Testing with Xcode`_ document. .. _Testing with Xcode: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/ Module Functions ^^^^^^^^^^^^^^^^ .. command:: xctest_add_bundle The ``xctest_add_bundle`` function creates a XCTest bundle named which will test the target . Supported target types for testee are Frameworks and App Bundles:: xctest_add_bundle( # Name of the XCTest bundle # Target name of the testee ) .. command:: xctest_add_test The ``xctest_add_test`` function adds an XCTest bundle to the project to be run by :manual:`ctest(1)`. The test will be named and tests :: xctest_add_test( # Test name # Target name of XCTest bundle ) Module Variables ^^^^^^^^^^^^^^^^ The following variables are set by including this module: .. variable:: XCTest_FOUND True if the XCTest Framework and executable were found. .. variable:: XCTest_EXECUTABLE The path to the xctest command line tool used to execute XCTest bundles. .. variable:: XCTest_INCLUDE_DIRS The directory containing the XCTest Framework headers. .. variable:: XCTest_LIBRARIES The location of the XCTest Framework. #]=======================================================================] set(_PRESERVED_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}") if(CMAKE_EFFECTIVE_SYSTEM_NAME STREQUAL "Apple" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") # Non-macos systems set the CMAKE_FIND_ROOT_PATH_MODE to "ONLY" which # restricts the search paths too much to find XCTest.framework. In # contrast to the regular system frameworks which reside within the # SDK direectory the XCTest framework is located in the respective # platform directory which is not added to the CMAKE_FIND_ROOT_PATH # (only to CMAKE_SYSTEM_FRAMEWORK_PATH) and therefore not searched. # # Until this is properly addressed, temporaily add the platform # directory to CMAKE_FIND_ROOT_PATH. list(APPEND CMAKE_FIND_ROOT_PATH "${_CMAKE_OSX_SYSROOT_PATH}/../..") endif() find_path(XCTest_INCLUDE_DIR NAMES "XCTest/XCTest.h" DOC "XCTest include directory") mark_as_advanced(XCTest_INCLUDE_DIR) find_library(XCTest_LIBRARY NAMES XCTest DOC "XCTest Framework library") mark_as_advanced(XCTest_LIBRARY) set(CMAKE_FIND_ROOT_PATH "${_PRESERVED_CMAKE_FIND_ROOT_PATH}") unset(_PRESERVED_CMAKE_FIND_ROOT_PATH) execute_process( COMMAND xcrun --find xctest OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE _xcrun_err) if(_xcrun_out) set(XCTest_EXECUTABLE "${_xcrun_out}" CACHE FILEPATH "XCTest executable") mark_as_advanced(XCTest_EXECUTABLE) endif() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args(XCTest FOUND_VAR XCTest_FOUND REQUIRED_VARS XCTest_LIBRARY XCTest_INCLUDE_DIR XCTest_EXECUTABLE) if(XCTest_FOUND) set(XCTest_INCLUDE_DIRS "${XCTest_INCLUDE_DIR}") set(XCTest_LIBRARIES "${XCTest_LIBRARY}") endif(XCTest_FOUND) function(xctest_add_bundle target testee) if(NOT XCTest_FOUND) message(FATAL_ERROR "XCTest is required to create a XCTest Bundle.") endif(NOT XCTest_FOUND) if(NOT CMAKE_OSX_SYSROOT) message(FATAL_ERROR "Adding XCTest bundles requires CMAKE_OSX_SYSROOT to be set.") endif() add_library(${target} MODULE ${ARGN}) set_target_properties(${target} PROPERTIES BUNDLE TRUE XCTEST TRUE XCTEST_TESTEE ${testee}) target_link_libraries(${target} PRIVATE "-framework Foundation") target_link_libraries(${target} PRIVATE ${XCTest_LIBRARIES}) target_include_directories(${target} PRIVATE ${XCTest_INCLUDE_DIRS}) # retrieve testee target type if(NOT TARGET ${testee}) message(FATAL_ERROR "${testee} is not a target.") endif() get_property(_testee_type TARGET ${testee} PROPERTY TYPE) get_property(_testee_framework TARGET ${testee} PROPERTY FRAMEWORK) get_property(_testee_macosx_bundle TARGET ${testee} PROPERTY MACOSX_BUNDLE) if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework) # testee is a Framework target_link_libraries(${target} PRIVATE ${testee}) elseif(_testee_type STREQUAL "STATIC_LIBRARY") # testee is a static library target_link_libraries(${target} PRIVATE ${testee}) elseif(_testee_type STREQUAL "EXECUTABLE" AND _testee_macosx_bundle) # testee is an App Bundle add_dependencies(${target} ${testee}) if(XCODE) set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_BUNDLE_LOADER "$(TEST_HOST)" XCODE_ATTRIBUTE_TEST_HOST "$") if(XCODE_VERSION VERSION_GREATER_EQUAL 7.3) # The Xcode "new build system" used a different path until Xcode 12.5. if(CMAKE_XCODE_BUILD_SYSTEM EQUAL 12 AND XCODE_VERSION VERSION_LESS 12.5 AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(_output_directory "$") else() set(_output_directory "$/PlugIns") endif() set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${_output_directory}") endif() else(XCODE) target_link_libraries(${target} PRIVATE -bundle_loader $) endif(XCODE) else() message(FATAL_ERROR "Testee ${testee} is of unsupported type.") endif() endfunction(xctest_add_bundle) function(xctest_add_test name bundle) if(NOT XCTest_EXECUTABLE) message(FATAL_ERROR "XCTest executable is required to register a test.") endif() # check that bundle is a XCTest Bundle if(NOT TARGET ${bundle}) message(FATAL_ERROR "${bundle} is not a target.") endif(NOT TARGET ${bundle}) get_property(_test_type TARGET ${bundle} PROPERTY TYPE) get_property(_test_bundle TARGET ${bundle} PROPERTY BUNDLE) get_property(_test_xctest TARGET ${bundle} PROPERTY XCTEST) if(NOT _test_type STREQUAL "MODULE_LIBRARY" OR NOT _test_xctest OR NOT _test_bundle) message(FATAL_ERROR "Test ${bundle} is not an XCTest Bundle") endif() # get and check testee properties get_property(_testee TARGET ${bundle} PROPERTY XCTEST_TESTEE) if(NOT TARGET ${_testee}) message(FATAL_ERROR "${_testee} is not a target.") endif() get_property(_testee_type TARGET ${_testee} PROPERTY TYPE) get_property(_testee_framework TARGET ${_testee} PROPERTY FRAMEWORK) # register test add_test( NAME ${name} COMMAND ${XCTest_EXECUTABLE} $) # point loader to testee in case rpath is disabled if(_testee_type STREQUAL "SHARED_LIBRARY" AND _testee_framework) set_property(TEST ${name} APPEND PROPERTY ENVIRONMENT DYLD_FRAMEWORK_PATH=$/..) endif() endfunction(xctest_add_test)