From 2dc8c1d45f1eff74db57346cff86c90c08cf78e6 Mon Sep 17 00:00:00 2001 From: Alexandra Cherdantseva Date: Fri, 21 Feb 2025 21:51:56 +0200 Subject: Xcode: Fix POST_BUILD order in bundle targets on Xcode 16+ XCode 16+ no longer delays our POST_BUILD phase until after bundle files like `Info.plist` are generated. Teach the Xcode generator to add explicit dependencies to the POST_BUILD phase in bundle targets to ensure the files are created before it runs. Fixes: #26656 --- Source/cmGlobalXCodeGenerator.cxx | 40 +++++++++++++++++++++-- Source/cmGlobalXCodeGenerator.h | 3 +- Tests/RunCMake/XcodeProject/BundlePostBuild.cmake | 15 +++++++++ Tests/RunCMake/XcodeProject/RunCMakeTest.cmake | 7 ++++ Tests/RunCMake/XcodeProject/app.cxx | 4 +++ Tests/RunCMake/XcodeProject/fw.cxx | 4 +++ 6 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 Tests/RunCMake/XcodeProject/BundlePostBuild.cmake create mode 100644 Tests/RunCMake/XcodeProject/app.cxx create mode 100644 Tests/RunCMake/XcodeProject/fw.cxx diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index f8a2b58..6037732 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1814,9 +1814,37 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( // create prelink phase preLinkPhase = this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink); + + std::vector depends; + if (gtgt->IsBundleOnApple()) { + // In Xcode 16+ the POST_BUILD phase needs explicit dependencies to + // ensure it runs after certain bundle files are generated. + depends = { + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/" + "Contents/Resources/DWARF/${PRODUCT_NAME}", + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/" + "Contents/Info.plist", + "$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)", + "$(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)", + }; + if (resourceBuildPhase) { + auto resourceFiles = resourceBuildPhase->GetAttribute("files"); + for (auto xsf : resourceFiles->GetObjectList()) { + auto fileRef = xsf->GetAttribute("fileRef"); + auto fileObj = fileRef->GetObject(); + auto path = fileObj->GetAttribute("path"); + auto fileName = cmSystemTools::GetFilenameName(path->GetString()); + if (cmSystemTools::GetFilenameLastExtension(fileName) == ".plist") { + depends.push_back( + "$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/" + + fileName); + } + } + } + } // create postbuild phase postBuildPhase = this->CreateRunScriptBuildPhase("CMake PostBuild Rules", - gtgt, postbuild); + gtgt, postbuild, depends); } else { std::vector classes; if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) { @@ -2030,7 +2058,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( std::string const& name, cmGeneratorTarget const* gt, - std::vector const& commands) + std::vector const& commands, + std::vector const& depends) { if (commands.empty()) { return nullptr; @@ -2066,6 +2095,13 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( buildPhase->AddAttribute("shellScript", this->CreateString(shellScript)); buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); { + cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST); + for (std::string const& s : depends) { + inputPaths->AddUniqueObject(this->CreateString(s)); + } + buildPhase->AddAttribute("inputPaths", inputPaths); + } + { cmXCodeObject* outputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST); for (std::string const& o : allConfigOutputs) { diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index dff8520..3282e0b 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -296,7 +296,8 @@ private: cmCustomCommand const& cc); cmXCodeObject* CreateRunScriptBuildPhase( std::string const& name, cmGeneratorTarget const* gt, - std::vector const& commands); + std::vector const& commands, + std::vector const& depends = {}); std::string ConstructScript(cmCustomCommandGenerator const& ccg); void CreateReRunCMakeFile(cmLocalGenerator* root, std::vector const& gens); diff --git a/Tests/RunCMake/XcodeProject/BundlePostBuild.cmake b/Tests/RunCMake/XcodeProject/BundlePostBuild.cmake new file mode 100644 index 0000000..fb5c48b --- /dev/null +++ b/Tests/RunCMake/XcodeProject/BundlePostBuild.cmake @@ -0,0 +1,15 @@ +enable_language(CXX) +add_executable(app MACOSX_BUNDLE app.cxx) +add_library(fw SHARED fw.cxx) +set_property(TARGET fw PROPERTY FRAMEWORK 1) +foreach(target IN ITEMS app fw) + set_property(TARGET ${target} PROPERTY XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf-with-dsym) + add_custom_command( + TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E sha256sum + [["$DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME/Contents/Resources/DWARF/$PRODUCT_NAME"]] + [["$DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME/Contents/Info.plist"]] + [["$TARGET_BUILD_DIR/$EXECUTABLE_PATH"]] + [["$TARGET_BUILD_DIR/$INFOPLIST_PATH"]] + ) +endforeach() diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index 5e22666..0515ec3 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake @@ -91,6 +91,13 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 12) XcodeObjectLibsInTwoProjectsMacOS() + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/BundlePostBuild-build) + run_cmake(BundlePostBuild) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(BundlePostBuild-build ${CMAKE_COMMAND} --build . --config Debug) + endblock() + endif() function(XcodeSchemaGeneration) diff --git a/Tests/RunCMake/XcodeProject/app.cxx b/Tests/RunCMake/XcodeProject/app.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/XcodeProject/app.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/XcodeProject/fw.cxx b/Tests/RunCMake/XcodeProject/fw.cxx new file mode 100644 index 0000000..e08ce02 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/fw.cxx @@ -0,0 +1,4 @@ +int fw() +{ + return 0; +} -- cgit v0.12