From c653c1aa475d746f135b2d3defd6bc61279b2ea1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 1 Apr 2025 14:31:02 -0400 Subject: FindXCTest: Pass -bundle_loader linker flag for test module more reliably --- Modules/FindXCTest.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index d143118..05ef36c 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake @@ -171,8 +171,8 @@ function(xctest_add_bundle target testee) LIBRARY_OUTPUT_DIRECTORY "${_output_directory}") endif() else() - target_link_libraries(${target} - PRIVATE -bundle_loader $) + target_link_options(${target} + PRIVATE "SHELL:-bundle_loader \"$\"") endif() else() -- cgit v0.12 From 2e59cee9225f1a698d3f6cf537dc32675dc56fb8 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 2 Apr 2025 09:27:27 -0400 Subject: FindXCTest: Fix test module generation for Xcode 16 In Xcode 7.3 and above, the `TEST_HOST` setting causes Xcode to implicitly place the test module inside the executable bundle regardless of the module's own location settings. Since commit a364d2513a (Xcode: Fixup XCTest bundle location for Xcode 7.3, 2016-03-25, v3.5.2~6^2) we explicitly tell CMake to put the test module in the same location so that generator expressions used by `xctest_add_test` agree with where Xcode actually puts it. In Xcode 16 and above, our explicit location settings for the test module conflict with Xcode's `TEST_HOST` rules, causing errors about multiple commands producing the same path. Fix this by dropping CMake's explicit location for the test module unless needed to match a project-specified location for the testee. Instead, teach `xctest_add_test` to express the xctest module location selected by `TEST_HOST` by using generator expressions referencing the testee bundle. Fixes: #26301 Fixes: #26514 --- .gitlab/ci/ctest_exclusions.cmake | 7 ---- Modules/FindXCTest.cmake | 39 ++++++++++++++-------- .../XcodeProject-Device/RunCMakeTest.cmake | 1 + .../XcodeProject-Device/XCTestAddBundle.cmake | 21 ++++++++---- Tests/XCTest/CMakeLists.txt | 15 +++++++++ 5 files changed, 56 insertions(+), 27 deletions(-) diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake index f151bf6..89a5ace 100644 --- a/.gitlab/ci/ctest_exclusions.cmake +++ b/.gitlab/ci/ctest_exclusions.cmake @@ -13,13 +13,6 @@ if (CTEST_CMAKE_GENERATOR MATCHES "Visual Studio") "^ExternalProjectUpdateSetup$") endif () -if (CTEST_CMAKE_GENERATOR MATCHES "Xcode") - list(APPEND test_exclusions - # FIXME(#26301): The XCTest fails with Xcode 16.0. - "^XCTest$" - ) -endif () - if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "_asan") list(APPEND test_exclusions CTestTest2 # crashes on purpose diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index 05ef36c..e6c846b 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake @@ -158,18 +158,13 @@ function(xctest_add_bundle target testee) 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() + # TEST_HOST overrides ${target}'s artifact path, but the relative + # path from TEST_HOST to ${testee}'s PlugIns folder must not leave + # ${target}'s TARGET_BUILD_DIR. If the project sets an explicit + # RUNTIME_OUTPUT_DIRECTORY for ${testee}, put ${target} there too. + # If not, just suppress the project's CMAKE_LIBRARY_OUTPUT_DIRECTORY. + get_property(testee_RUNTIME_OUTPUT_DIRECTORY TARGET ${testee} PROPERTY RUNTIME_OUTPUT_DIRECTORY) + set_property(TARGET ${target} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${testee_RUNTIME_OUTPUT_DIRECTORY}) else() target_link_options(${target} PRIVATE "SHELL:-bundle_loader \"$\"") @@ -210,6 +205,24 @@ function(xctest_add_test name bundle) 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) + + # Determine the path to the test module artifact on disk. + set(_test_bundle_dir "$") + if(XCODE AND _testee_type STREQUAL "EXECUTABLE" AND _testee_macosx_bundle) + # Xcode's TEST_HOST setting places the test module inside the testee bundle. + 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(_test_bundle_dir "$") + else() + set(_test_bundle_dir "$/PlugIns") + endif() + string(APPEND _test_bundle_dir "/$") + endif() + endif() # register test @@ -217,7 +230,7 @@ function(xctest_add_test name bundle) # here for CMP0178. add_test( NAME ${name} - COMMAND ${XCTest_EXECUTABLE} $) + COMMAND ${XCTest_EXECUTABLE} ${_test_bundle_dir}) # point loader to testee in case rpath is disabled diff --git a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake index b61b7fc..64abfb5 100644 --- a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake @@ -307,6 +307,7 @@ if (XCODE_VERSION VERSION_GREATER_EQUAL 7.3) if(BuildSystemVersion) set(RunCMake_GENERATOR_TOOLSET "buildsystem=${BuildSystemVersion}") endif() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${SystemName}-${SDK}") run_cmake(XCTestAddBundle) endfunction() diff --git a/Tests/RunCMake/XcodeProject-Device/XCTestAddBundle.cmake b/Tests/RunCMake/XcodeProject-Device/XCTestAddBundle.cmake index 9114fee..519dbe5 100644 --- a/Tests/RunCMake/XcodeProject-Device/XCTestAddBundle.cmake +++ b/Tests/RunCMake/XcodeProject-Device/XCTestAddBundle.cmake @@ -10,13 +10,20 @@ add_executable(TestedApp MACOSX_BUNDLE dummy_main.swift) xctest_add_bundle(TestingAppBundle TestedApp dummy_main.swift) -get_target_property(_lib_output_dir TestingAppBundle LIBRARY_OUTPUT_DIRECTORY) +macro(add_test NAME name COMMAND xctest arg) + set(actual_arg "${arg}" PARENT_SCOPE) +endmacro() -if (NOT DEFINED TEST_EXPECTED_OUTPUT_DIR) - message(FATAL_ERROR "Testing variable TEST_EXPECTED_OUTPUT_DIR is not set") -endif() +xctest_add_test(TestedApp.TestingAppBundle TestingAppBundle) -if (NOT _lib_output_dir STREQUAL TEST_EXPECTED_OUTPUT_DIR) - message(SEND_ERROR "Property LIBRARY_OUTPUT_DIRECTORY is expected to be ${TEST_EXPECTED_OUTPUT_DIR} " - "but was ${_lib_output_dir}") +if(NOT DEFINED TEST_EXPECTED_OUTPUT_DIR) + message(FATAL_ERROR "Testing variable TEST_EXPECTED_OUTPUT_DIR is not set") +endif() +set(expect_arg "${TEST_EXPECTED_OUTPUT_DIR}/$") +if(NOT "${actual_arg}" STREQUAL "${expect_arg}") + message(FATAL_ERROR "xctest argument expected to be:\n" + " ${expect_arg}\n" + "but was:\n" + " ${actual_arg}\n" + ) endif() diff --git a/Tests/XCTest/CMakeLists.txt b/Tests/XCTest/CMakeLists.txt index e028108..fdda826 100644 --- a/Tests/XCTest/CMakeLists.txt +++ b/Tests/XCTest/CMakeLists.txt @@ -60,6 +60,21 @@ xctest_add_bundle(CocoaExampleTests CocoaExample xctest_add_test(XCTest.CocoaExample CocoaExampleTests) +# Cocoa App Bundle with explicit artifact location. +block() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") + add_executable(CocoaExample2 MACOSX_BUNDLE + CocoaExample/main.m + CocoaExample/AppDelegate.m + CocoaExample/AppDelegate.h + CocoaExample/MainMenu.xib + ) + target_link_libraries(CocoaExample2 PRIVATE "-framework Foundation" "-framework AppKit") + xctest_add_bundle(CocoaExample2Tests CocoaExample2 CocoaExampleTests/CocoaExampleTests.m) + xctest_add_test(XCTest.CocoaExample2 CocoaExample2Tests) +endblock() + # Static lib add_library(StaticLibExample STATIC -- cgit v0.12