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
This commit is contained in:
Alexandra Cherdantseva
2025-02-21 21:51:56 +02:00
committed by Brad King
parent b18513105c
commit 2dc8c1d45f
6 changed files with 70 additions and 3 deletions

View File

@@ -1814,9 +1814,37 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
// create prelink phase
preLinkPhase =
this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink);
std::vector<std::string> 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<cmSourceFile*> classes;
if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) {
@@ -2030,7 +2058,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
std::string const& name, cmGeneratorTarget const* gt,
std::vector<cmCustomCommand> const& commands)
std::vector<cmCustomCommand> const& commands,
std::vector<std::string> const& depends)
{
if (commands.empty()) {
return nullptr;
@@ -2065,6 +2094,13 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
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);

View File

@@ -296,7 +296,8 @@ private:
cmCustomCommand const& cc);
cmXCodeObject* CreateRunScriptBuildPhase(
std::string const& name, cmGeneratorTarget const* gt,
std::vector<cmCustomCommand> const& commands);
std::vector<cmCustomCommand> const& commands,
std::vector<std::string> const& depends = {});
std::string ConstructScript(cmCustomCommandGenerator const& ccg);
void CreateReRunCMakeFile(cmLocalGenerator* root,
std::vector<cmLocalGenerator*> const& gens);

View File

@@ -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()

View File

@@ -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)

View File

@@ -0,0 +1,4 @@
int main()
{
return 0;
}

View File

@@ -0,0 +1,4 @@
int fw()
{
return 0;
}