mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-27 04:09:36 +03:00
Xcode: Use deterministic object ids for product file references
Follow-up commit 1fa2ec1bbd (Xcode: Use deterministic object ids for
targets, 2024-04-04, v3.30.0-rc1~263^2). Each target's product
PBXFileReference (the libfoo.a / foo entries under the Products group)
was created with a random object id on every regeneration. When a
CMake-generated Xcode project is embedded in another Xcode project, that
parent keeps the product reference's id in its remoteGlobalIDString; a
random id means the reference no longer resolves and the link falls back
to a bare `-l` flag (e.g. `ld: library 'foo' not found`).
Derive the product PBXFileReference id from the target name and the
build-tree-relative product path, the same hashing path already used for
the target object id. The id is now stable across regenerations and
independent of the absolute build tree location.
This commit is contained in:
@@ -3583,7 +3583,10 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget(
|
||||
target->AddAttribute("name", this->CreateString(gtgt->GetName()));
|
||||
target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
|
||||
|
||||
cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
|
||||
cmXCodeObject* fileRef =
|
||||
this->CreateObject(cmXCodeObject::PBXFileReference,
|
||||
cmStrCat("PBXFileReference:product:", gtgt->GetName(),
|
||||
':', targetBinaryPath));
|
||||
if (char const* fileType = this->GetTargetFileType(gtgt)) {
|
||||
fileRef->AddAttribute("explicitFileType", this->CreateString(fileType));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Collect "path -> object id" for every product PBXFileReference (those under
|
||||
# BUILT_PRODUCTS_DIR) in a generated project.pbxproj.
|
||||
function(read_product_ids pbxproj out_var)
|
||||
if(NOT EXISTS "${pbxproj}")
|
||||
set(RunCMake_TEST_FAILED "Project file does not exist:\n ${pbxproj}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(ids "")
|
||||
file(STRINGS "${pbxproj}" lines)
|
||||
foreach(line IN LISTS lines)
|
||||
if(line MATCHES "isa = PBXFileReference;" AND line MATCHES "sourceTree = BUILT_PRODUCTS_DIR;")
|
||||
string(REGEX MATCH "^[ \t]*([0-9A-F]+) " _ "${line}")
|
||||
set(id "${CMAKE_MATCH_1}")
|
||||
string(REGEX MATCH "path = ([^;]+);" _ "${line}")
|
||||
set(path "${CMAKE_MATCH_1}")
|
||||
list(APPEND ids "${path}=${id}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(SORT ids)
|
||||
set(${out_var} "${ids}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(pbxproj "${RunCMake_TEST_BINARY_DIR}/DeterministicProductIds.xcodeproj/project.pbxproj")
|
||||
read_product_ids("${pbxproj}" this_ids)
|
||||
if(RunCMake_TEST_FAILED)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT this_ids)
|
||||
set(RunCMake_TEST_FAILED "No product PBXFileReference entries found in\n ${pbxproj}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# On the second generation, compare against the first build tree. The ids must
|
||||
# be byte-identical even though the build tree path differs.
|
||||
if(DEFINED DeterministicProductIds_FirstBinaryDir)
|
||||
read_product_ids(
|
||||
"${DeterministicProductIds_FirstBinaryDir}/DeterministicProductIds.xcodeproj/project.pbxproj"
|
||||
first_ids)
|
||||
if(RunCMake_TEST_FAILED)
|
||||
return()
|
||||
endif()
|
||||
if(NOT this_ids STREQUAL first_ids)
|
||||
string(REPLACE ";" "\n " a "${first_ids}")
|
||||
string(REPLACE ";" "\n " b "${this_ids}")
|
||||
set(RunCMake_TEST_FAILED
|
||||
"Product PBXFileReference ids are not deterministic across build trees.\nFirst:\n ${a}\nSecond:\n ${b}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,10 @@
|
||||
enable_language(C)
|
||||
|
||||
# Each target's product PBXFileReference must get a deterministic object id so
|
||||
# that a parent project embedding this one can keep a stable remoteGlobalIDString.
|
||||
add_library(productStatic STATIC empty.c)
|
||||
add_library(productShared SHARED empty.c)
|
||||
add_executable(productExe empty.c)
|
||||
|
||||
# A target in a subdirectory exercises the relative-path component of the key.
|
||||
add_subdirectory(DeterministicProductIdsSub)
|
||||
@@ -0,0 +1 @@
|
||||
add_library(productSubStatic STATIC ../empty.c)
|
||||
@@ -14,6 +14,19 @@ run_cmake_with_options(ArchsStandard "-DCMAKE_OSX_ARCHITECTURES=$(ARCHS_STANDARD
|
||||
run_cmake(ExplicitCMakeLists)
|
||||
run_cmake(ImplicitCMakeLists)
|
||||
run_cmake(InterfaceLibSources)
|
||||
|
||||
function(DeterministicProductIds)
|
||||
# Generate the same project into two separate build trees and verify that the
|
||||
# product PBXFileReference object ids are byte-identical: deterministic and
|
||||
# independent of the build tree location.
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DeterministicProductIds-build1)
|
||||
run_cmake(DeterministicProductIds)
|
||||
set(DeterministicProductIds_FirstBinaryDir ${RunCMake_TEST_BINARY_DIR})
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DeterministicProductIds-build2)
|
||||
run_cmake(DeterministicProductIds)
|
||||
endfunction()
|
||||
DeterministicProductIds()
|
||||
|
||||
run_cmake_with_options(SearchPaths -DCMAKE_CONFIGURATION_TYPES=Debug)
|
||||
run_cmake_with_options(InheritedParameters -DCMake_TEST_Swift=${CMake_TEST_Swift})
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
int productSymbol(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user