From f5b6894ccea840c200c2d0ee1ade40a369c13383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Jos=C3=A9=20Garc=C3=ADa=20Garc=C3=ADa?= Date: Thu, 27 Aug 2026 19:06:16 +0200 Subject: [PATCH] Splice the softfp shims before the sysroot leaves for stage 2 The shims were a dependency of finalize-sdk and of nothing else. A producer does not run finalize-sdk: it builds the sysroot target, tars the target half and hands it to stage 2. So the archives it exported had never been patched, and stage 2 failed the ABI contract test with checking the softfp ABI shims (23 functions) resolve against the shim missing sceGxmSetViewport resolved against the softfp shim wrapper which is the check doing its job two jobs away from the cause. The first softfp build in CI is where this surfaced; the container builds it was verified in are single-stage, and there finalize-sdk runs. The two lists were the problem, not the missing entry. They are mutually exclusive -- a producer exports, everything else finalizes -- so anything added to one and forgotten in the other stays invisible until something reads the result. Name the target half once and let both wait for it; the splice belongs to that list, at the point where the world is known to be softfp. Stage 2 is excluded because it imports the archives already spliced and builds no components, so there is no softfp-shim target there to wait for. The test reads both recipes and refuses either naming the splice itself, which is what let them drift. --- CMakeLists.txt | 21 ++++++++ cmake/recipes/ExportSysroot.cmake | 3 +- cmake/recipes/FinalizeSdk.cmake | 12 +---- tests/cmake/target-half-dependencies.cmake | 56 ++++++++++++++++++++++ 4 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 tests/cmake/target-half-dependencies.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ecb936..213b699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,6 +303,27 @@ if(NOT VITASDK_TARGET_ONLY) endif() include(cmake/recipes/BuildGccFinal.cmake) include(cmake/recipes/Provenance.cmake) + +# What the target half is made of, named once because two things wait for it: +# the export that hands stage 2 a sysroot, and the finalize barrier of a build +# that keeps its own. One of them forgetting a piece is invisible until +# something reads the result -- the softfp shims were spliced by finalize-sdk +# and not by the export, so a staged build shipped stage 2 a sysroot whose +# stub archives had never been patched, and the ABI contract test found it +# there rather than here. +set(target_half_dependencies + ${gcc_final_barrier} + vita-headers + newlib + pthread-embedded + samples + ${version_info_file}) +if(VITASDK_FLOAT_ABI STREQUAL "softfp" AND NOT VITASDK_STAGE1_DIR) + # Stage 2 imports the archives already spliced; there is nothing to splice + # and no softfp-shim target, since the components are not built there. + list(APPEND target_half_dependencies softfp-shim) +endif() + if(VITASDK_TARGET_ONLY) include(cmake/recipes/ExportSysroot.cmake) else() diff --git a/cmake/recipes/ExportSysroot.cmake b/cmake/recipes/ExportSysroot.cmake index dda169d..1b07d77 100644 --- a/cmake/recipes/ExportSysroot.cmake +++ b/cmake/recipes/ExportSysroot.cmake @@ -43,8 +43,7 @@ add_custom_target(sysroot -P ${CMAKE_SOURCE_DIR}/cmake/CopySysroot.cmake COMMAND ${CMAKE_COMMAND} -E tar "cfj" ${CMAKE_BINARY_DIR}/${sysroot_tarball} vitasdk WORKING_DIRECTORY ${sysroot_root} - DEPENDS ${gcc_final_barrier} vita-headers newlib pthread-embedded samples - ${version_info_file} + DEPENDS ${target_half_dependencies} COMMENT "Exporting the target half to ${sysroot_tarball}" VERBATIM ) diff --git a/cmake/recipes/FinalizeSdk.cmake b/cmake/recipes/FinalizeSdk.cmake index 589eaf5..aec562b 100644 --- a/cmake/recipes/FinalizeSdk.cmake +++ b/cmake/recipes/FinalizeSdk.cmake @@ -33,22 +33,12 @@ set(finalize_sdk_dependencies vita-toolchain_${target_suffix} binutils_${target_suffix} gdb_${target_suffix} - vita-headers - newlib - pthread-embedded - samples vdpm vita-makepkg - ${gcc_final_barrier} - ${version_info_file}) + ${target_half_dependencies}) if(BUILD_PACMAN_CLIENT) list(APPEND finalize_sdk_dependencies package-client-configuration) endif() -if(VITASDK_FLOAT_ABI STREQUAL "softfp") - # Splice the softfp shims into the stub archives before finalize-sdk - # strips them (cmake/strip_target_objects.cmake), not after. - list(APPEND finalize_sdk_dependencies softfp-shim) -endif() # Target objects are stripped where they are produced. In stage 2 they arrive # already stripped from stage 1, and the objcopy that would do it belongs to diff --git a/tests/cmake/target-half-dependencies.cmake b/tests/cmake/target-half-dependencies.cmake new file mode 100644 index 0000000..8101315 --- /dev/null +++ b/tests/cmake/target-half-dependencies.cmake @@ -0,0 +1,56 @@ +# The export and the finalize barrier wait for the same target half. +# +# They are mutually exclusive -- a producer exports a sysroot for stage 2, any +# other build finalizes its own -- so a piece added to one and forgotten in the +# other is invisible until something reads the result. The softfp shims were +# spliced by finalize-sdk and not by the export, so a staged build handed +# stage 2 a sysroot whose stub archives had never been patched, and the ABI +# contract test found it two jobs later. + +cmake_minimum_required(VERSION 3.16) + +get_filename_component(repository_root "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +set(failures 0) + +function(expect_contains path needle description) + file(READ "${repository_root}/${path}" contents) + string(FIND "${contents}" "${needle}" position) + if(position EQUAL -1) + message(SEND_ERROR "FAIL: ${description}") + math(EXPR failures "${failures} + 1") + set(failures ${failures} PARENT_SCOPE) + endif() +endfunction() + +function(expect_absent path needle description) + file(READ "${repository_root}/${path}" contents) + string(FIND "${contents}" "${needle}" position) + if(NOT position EQUAL -1) + message(SEND_ERROR "FAIL: ${description}") + endif() +endfunction() + +# Both consumers take the list; neither spells the target half out again. +expect_contains("cmake/recipes/ExportSysroot.cmake" + "DEPENDS \${target_half_dependencies}" + "the sysroot export does not wait for the target half") +expect_contains("cmake/recipes/FinalizeSdk.cmake" + "\${target_half_dependencies})" + "finalize-sdk does not wait for the target half") + +# The splice belongs to the list and nowhere else, or the two drift again. +expect_absent("cmake/recipes/ExportSysroot.cmake" "softfp-shim" + "ExportSysroot.cmake names softfp-shim itself instead of taking the list") +expect_absent("cmake/recipes/FinalizeSdk.cmake" "softfp-shim" + "FinalizeSdk.cmake names softfp-shim itself instead of taking the list") + +# A softfp world whose shims were never spliced is a toolchain that +# miscompiles quietly, so the list carries them exactly when the world is one. +expect_contains("CMakeLists.txt" + "list(APPEND target_half_dependencies softfp-shim)" + "the softfp splice is not part of the target half") +expect_contains("CMakeLists.txt" + "VITASDK_FLOAT_ABI STREQUAL \"softfp\"" + "nothing decides when the splice belongs to the target half") + +message(STATUS "target half dependency tests passed")