Files
cmake/Tests/RunCMake/XcodeProject/DeterministicProductIds-check.cmake
lapfelix 2cc75c6072 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.
2026-06-17 09:59:31 -04:00

50 lines
1.7 KiB
CMake

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