Skip to content

Commit 45fb46f

Browse files
committed
fix(darwin): re-sign linker-signed macOS native modules while staging
Python extensions from python-build (the stdlib lib-dynload modules) and from many wheels keep the ad-hoc signature the linker attached, whose identifier is the file name including `.so`. Xcode's distribution signing re-signs them with --preserve-metadata=identifier, which ignores linker signatures, so codesign derives a new identifier without the extension, while Xcode can write the designated requirement from the old one. The result, reported with Xcode 26.5, is a signature that fails its own requirement, and App Store Connect rejects the build with 90238 "does not satisfy its designated Requirement" on every such file. prepare_macos.sh (stdlib) and sync_site_packages.sh (site-packages, app) now give every still-linker-signed .so/.dylib a regular ad-hoc signature, whose identifier re-signing preserves. Only the staged dist_macos copies are touched; the source directories and the provider xcframeworks are left as they are. CocoaPods and SwiftPM both build from dist_macos. Refs #250
1 parent a119d59 commit 45fb46f

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Signature normalization for the Mach-O libraries bundled into a macOS app.
2+
#
3+
# replace_linker_signatures <dir>...
4+
#
5+
# Re-signs ad-hoc every `.so`, `.so.*` and `.dylib` under the given directories
6+
# that carries the signature the linker attached when it built the file.
7+
#
8+
# A linker signature names the code after the output file, extension included
9+
# (`_ssl.cpython-314-darwin.so`), and codesign never carries metadata over from
10+
# one: re-signing the file derives a fresh identifier from the file name minus
11+
# its extension (`_ssl.cpython-314-darwin`). Xcode's distribution signing
12+
# (Organizer, `xcodebuild -exportArchive`) can write the designated requirement
13+
# from the identifier it read off the existing signature, so a linker-signed
14+
# library ends up with a requirement its own new signature does not satisfy,
15+
# and App Store Connect rejects the upload (error 90238, "does not satisfy its
16+
# designated Requirement"). A regular ad-hoc signature's identifier is carried
17+
# over by re-signing, so the requirement and the signature agree.
18+
#
19+
# Files are inspected one at a time, because `codesign -d` given several paths
20+
# stops at the first one that is not signed. Anything that is not
21+
# linker-signed -- already re-signed, unsigned, or not Mach-O at all -- is left
22+
# untouched, so a repeated run only re-checks. Ad-hoc signing is deterministic,
23+
# so re-signing a fresh copy of the same file reproduces the same bytes. Paths
24+
# containing newlines are not supported.
25+
#
26+
# Returns non-zero, after printing the error, when a directory cannot be listed
27+
# or a file cannot be re-signed.
28+
replace_linker_signatures() {
29+
local dir files bin err
30+
for dir in "$@"; do
31+
[ -d "$dir" ] || continue
32+
if ! files=$(find "$dir" -type f \( -name '*.so' -o -name '*.so.*' -o -name '*.dylib' \)); then
33+
echo "replace_linker_signatures: cannot list $dir" >&2
34+
return 1
35+
fi
36+
while IFS= read -r bin; do
37+
[ -n "$bin" ] || continue
38+
case $(codesign -d --verbose=1 "$bin" 2>&1) in
39+
*"CodeDirectory "*linker-signed*) ;;
40+
*) continue ;;
41+
esac
42+
if ! err=$(codesign --force --sign - "$bin" 2>&1); then
43+
echo "replace_linker_signatures: codesign failed for $bin: $err" >&2
44+
return 1
45+
fi
46+
done <<EOF
47+
$files
48+
EOF
49+
done
50+
}

‎src/serious_python_darwin/darwin/prepare_macos.sh‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$pb_id" ]; then
5757
echo "$pb_id" > "$marker"
5858
fi
5959

60+
# ---- stdlib native module signatures ----------------------------------------
61+
# Outside the extraction guard, so an already-extracted dist is covered as well.
62+
# The provider xcframeworks are not touched.
63+
. "$script_dir/linker_signatures.sh"
64+
replace_linker_signatures "$dist/stdlib" || exit 1
65+
6066
# ---- flet-dev/dart-bridge (xcframework, same archive for macOS + iOS) -----
6167
# Separate cache guard so a stale $dist from before this change still picks
6268
# up the new artifact on first re-prepare.

‎src/serious_python_darwin/darwin/sync_site_packages.sh‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ script_dir=$(cd "$(dirname "$0")" && pwd -P)
33
# Provider-signature + provider-integrity checks (see xcframework_verify.sh).
44
# Sourced unconditionally so both the iOS and macOS branches can use it.
55
source $script_dir/xcframework_verify.sh
6+
# Linker-signature replacement for the macOS resource trees.
7+
source "$script_dir/linker_signatures.sh"
68

79
# App sources are arch- and platform-independent; stage them as a bare `app/`
810
# resource bundle into BOTH dist trees, regardless of whether site-packages
@@ -16,6 +18,7 @@ if [[ -n "$SERIOUS_PYTHON_APP" && -d "$SERIOUS_PYTHON_APP" ]]; then
1618
mkdir -p "$app_dist/app"
1719
rsync -a --exclude '.pod' "$SERIOUS_PYTHON_APP/" "$app_dist/app/"
1820
done
21+
replace_linker_signatures "$script_dir/dist_macos/app" || exit 1
1922
fi
2023

2124
if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ]]; then
@@ -136,6 +139,9 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ]
136139
# file. .pod is only needed by package_command.dart at packaging
137140
# time to invoke this sync script; it does not belong in the bundle.
138141
rsync -av --delete --exclude '.pod' "$SERIOUS_PYTHON_SITE_PACKAGES/" "$dist/site-packages/"
142+
# The staged copy is re-signed, never the source; each sync restores the
143+
# linker-signed files and re-signing them reproduces the same bytes.
144+
replace_linker_signatures "$dist/site-packages" || exit 1
139145

140146
# macOS has no framework-ization step -- its .so's load flat from the
141147
# resource tree -- so nothing here should ever touch the provider

0 commit comments

Comments
 (0)