This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
|
PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||||
|
export PATH
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 --output ABSOLUTE_DIR --target-dir ABSOLUTE_DIR" >&2
|
echo "usage: $0 --output ABSOLUTE_DIR --target-dir ABSOLUTE_DIR" >&2
|
||||||
@@ -26,10 +28,41 @@ done
|
|||||||
|
|
||||||
test -n "$output" || usage
|
test -n "$output" || usage
|
||||||
test -n "$target_dir" || usage
|
test -n "$target_dir" || usage
|
||||||
case "$output:$target_dir" in
|
ROOT=$(CDPATH= cd -P -- "$(dirname "$0")/../.." && pwd -P)
|
||||||
/*:/*) ;;
|
CORE="$ROOT/core"
|
||||||
|
|
||||||
|
resolve_new_path() {
|
||||||
|
requested=$1
|
||||||
|
label=$2
|
||||||
|
case "$requested" in
|
||||||
|
/*) ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
|
if test -e "$requested" || test -L "$requested"; then
|
||||||
|
echo "$label must not exist: $requested" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
parent=$(dirname -- "$requested")
|
||||||
|
name=$(basename -- "$requested")
|
||||||
|
case "$name" in
|
||||||
|
''|.|..) usage ;;
|
||||||
|
esac
|
||||||
|
physical_parent=$(CDPATH= cd -P -- "$parent" 2>/dev/null && pwd -P) || {
|
||||||
|
echo "$label parent must already exist: $parent" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
candidate="$physical_parent/$name"
|
||||||
|
case "$candidate" in
|
||||||
|
"$ROOT"|"$ROOT"/*)
|
||||||
|
echo "$label must be outside the repository: $requested" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
printf '%s\n' "$candidate"
|
||||||
|
}
|
||||||
|
|
||||||
|
output=$(resolve_new_path "$output" output)
|
||||||
|
target_dir=$(resolve_new_path "$target_dir" "target directory")
|
||||||
case "$output/:$target_dir/" in
|
case "$output/:$target_dir/" in
|
||||||
"$target_dir/"*:*|*:"$output/"*)
|
"$target_dir/"*:*|*:"$output/"*)
|
||||||
echo "output and target directory must be separate" >&2
|
echo "output and target directory must be separate" >&2
|
||||||
@@ -37,55 +70,164 @@ case "$output/:$target_dir/" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
ROOT=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
mkdir "$output"
|
||||||
case "$output" in
|
if ! mkdir "$target_dir"; then
|
||||||
|
rmdir "$output"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
output=$(CDPATH= cd -P -- "$output" && pwd -P)
|
||||||
|
target_dir=$(CDPATH= cd -P -- "$target_dir" && pwd -P)
|
||||||
|
for reserved_path in "$output" "$target_dir"; do
|
||||||
|
case "$reserved_path" in
|
||||||
"$ROOT"|"$ROOT"/*)
|
"$ROOT"|"$ROOT"/*)
|
||||||
echo "output must be outside the repository" >&2
|
echo "reserved packaging path resolved inside the repository: $reserved_path" >&2
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
case "$target_dir" in
|
done
|
||||||
"$ROOT"|"$ROOT"/*)
|
case "$output/:$target_dir/" in
|
||||||
echo "target directory must be outside the repository" >&2
|
"$target_dir/"*:*|*:"$output/"*)
|
||||||
|
echo "reserved output and target directory must be separate" >&2
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
test ! -e "$output" || {
|
|
||||||
echo "output already exists: $output" >&2
|
test "$(/usr/bin/uname -s)" = Darwin || {
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
test ! -e "$target_dir" || {
|
|
||||||
echo "target directory already exists: $target_dir" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
test "$(uname -s)" = Darwin || {
|
|
||||||
echo "XCFramework packaging requires macOS" >&2
|
echo "XCFramework packaging requires macOS" >&2
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
|
test "$(/usr/bin/uname -m)" = arm64 || {
|
||||||
|
echo "XCFramework packaging requires an arm64 host" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
source_epoch=$(git -C "$ROOT" show -s --format=%ct HEAD)
|
user_record=$(/usr/bin/dscacheutil -q user -a name "$(/usr/bin/id -un)")
|
||||||
export CARGO_INCREMENTAL=0
|
trusted_home=$(printf '%s\n' "$user_record" | awk '$1 == "dir:" { print $2; exit }')
|
||||||
export CARGO_TARGET_DIR="$target_dir"
|
rustup="$trusted_home/.cargo/bin/rustup"
|
||||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
test -x "$rustup" || {
|
||||||
export RUSTFLAGS="--remap-path-prefix=$ROOT=."
|
echo "rustup not found at trusted user path" >&2
|
||||||
export SOURCE_DATE_EPOCH="$source_epoch"
|
exit 2
|
||||||
export ZERO_AR_DATE=1
|
}
|
||||||
|
cargo_bin=$(/usr/bin/env -i \
|
||||||
|
HOME="$trusted_home" \
|
||||||
|
PATH="$PATH" \
|
||||||
|
RUSTUP_HOME="$trusted_home/.rustup" \
|
||||||
|
"$rustup" which --toolchain 1.97.1 cargo)
|
||||||
|
rustc_bin=$(/usr/bin/env -i \
|
||||||
|
HOME="$trusted_home" \
|
||||||
|
PATH="$PATH" \
|
||||||
|
RUSTUP_HOME="$trusted_home/.rustup" \
|
||||||
|
"$rustup" which --toolchain 1.97.1 rustc)
|
||||||
|
rustc_version=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" "$rustc_bin" --version)
|
||||||
|
cargo_version=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" "$cargo_bin" --version)
|
||||||
|
test "$rustc_version" = 'rustc 1.97.1 (8bab26f4f 2026-07-14)' || {
|
||||||
|
echo "unexpected rustc identity: $rustc_version" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
test "$cargo_version" = 'cargo 1.97.1 (c980f4866 2026-06-30)' || {
|
||||||
|
echo "unexpected cargo identity: $cargo_version" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
xcodebuild=$(/usr/bin/env -i PATH="$PATH" /usr/bin/xcrun --find xcodebuild)
|
||||||
|
xcode_version=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" "$xcodebuild" -version)
|
||||||
|
test "$xcode_version" = 'Xcode 26.6
|
||||||
|
Build version 17F113' || {
|
||||||
|
echo "unexpected Xcode identity: $xcode_version" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
clang=$(/usr/bin/env -i PATH="$PATH" /usr/bin/xcrun --find clang)
|
||||||
|
ar=$(/usr/bin/env -i PATH="$PATH" /usr/bin/xcrun --find ar)
|
||||||
|
clang_identity=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" "$clang" --version)
|
||||||
|
clang_version=$(printf '%s\n' "$clang_identity" | sed -n '1p')
|
||||||
|
sdk=$(/usr/bin/env -i PATH="$PATH" /usr/bin/xcrun --sdk macosx --show-sdk-path)
|
||||||
|
|
||||||
cargo build \
|
config_dir=$CORE
|
||||||
--manifest-path "$ROOT/core/Cargo.toml" \
|
while :; do
|
||||||
|
if test -e "$config_dir/.cargo/config" || test -e "$config_dir/.cargo/config.toml"; then
|
||||||
|
echo "Cargo config is not permitted in the packaging path: $config_dir/.cargo" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
test "$config_dir" = / && break
|
||||||
|
config_dir=$(dirname "$config_dir")
|
||||||
|
done
|
||||||
|
|
||||||
|
source_commit=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" \
|
||||||
|
/usr/bin/git -C "$ROOT" rev-parse HEAD)
|
||||||
|
source_epoch=$(/usr/bin/env -i HOME="$trusted_home" PATH="$PATH" \
|
||||||
|
/usr/bin/git -C "$ROOT" show -s --format=%ct HEAD)
|
||||||
|
rustflags="--remap-path-prefix=$ROOT=. --remap-path-prefix=$target_dir=/cargo-target"
|
||||||
|
cflags="-fdebug-prefix-map=$ROOT=. -ffile-prefix-map=$ROOT=. -fdebug-prefix-map=$target_dir=/cargo-target -ffile-prefix-map=$target_dir=/cargo-target"
|
||||||
|
cargo_home="$target_dir/cargo-home"
|
||||||
|
build_tmp="$target_dir/tmp"
|
||||||
|
mkdir "$cargo_home" "$build_tmp"
|
||||||
|
for cache in registry git; do
|
||||||
|
if test -e "$trusted_home/.cargo/$cache"; then
|
||||||
|
ln -s "$trusted_home/.cargo/$cache" "$cargo_home/$cache"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
(
|
||||||
|
cd "$CORE"
|
||||||
|
/usr/bin/env -i \
|
||||||
|
AR="$ar" \
|
||||||
|
AR_aarch64_apple_darwin="$ar" \
|
||||||
|
CARGO_HOME="$cargo_home" \
|
||||||
|
CARGO_INCREMENTAL=0 \
|
||||||
|
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \
|
||||||
|
CARGO_PROFILE_RELEASE_INCREMENTAL=false \
|
||||||
|
CARGO_PROFILE_RELEASE_LTO=fat \
|
||||||
|
CARGO_PROFILE_RELEASE_PANIC=abort \
|
||||||
|
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER="$clang" \
|
||||||
|
CARGO_TARGET_DIR="$target_dir" \
|
||||||
|
CC="$clang" \
|
||||||
|
CC_aarch64_apple_darwin="$clang" \
|
||||||
|
CFLAGS="$cflags" \
|
||||||
|
CFLAGS_aarch64_apple_darwin="$cflags" \
|
||||||
|
HOME="$trusted_home" \
|
||||||
|
MACOSX_DEPLOYMENT_TARGET=14.0 \
|
||||||
|
PATH="$PATH" \
|
||||||
|
RUSTC="$rustc_bin" \
|
||||||
|
RUSTFLAGS="$rustflags" \
|
||||||
|
SDKROOT="$sdk" \
|
||||||
|
SOURCE_DATE_EPOCH="$source_epoch" \
|
||||||
|
TMPDIR="$build_tmp" \
|
||||||
|
ZERO_AR_DATE=1 \
|
||||||
|
"$cargo_bin" build \
|
||||||
--target aarch64-apple-darwin \
|
--target aarch64-apple-darwin \
|
||||||
--release \
|
--release \
|
||||||
--locked
|
--frozen
|
||||||
|
)
|
||||||
|
|
||||||
headers="$target_dir/xcframework-headers"
|
headers="$target_dir/xcframework-headers"
|
||||||
mkdir -p "$headers" "$output"
|
mkdir -p "$headers" "$output"
|
||||||
cp "$ROOT/core/include/versevdi_core.h" "$headers/"
|
cp "$ROOT/core/include/versevdi_core.h" "$headers/"
|
||||||
cp "$ROOT/core/include/module.modulemap" "$headers/"
|
cp "$ROOT/core/include/module.modulemap" "$headers/"
|
||||||
|
|
||||||
xcodebuild -create-xcframework \
|
/usr/bin/env -i \
|
||||||
|
HOME="$trusted_home" \
|
||||||
|
PATH="$PATH" \
|
||||||
|
TMPDIR="$build_tmp" \
|
||||||
|
"$xcodebuild" -create-xcframework \
|
||||||
-library "$target_dir/aarch64-apple-darwin/release/libversevdi_core.a" \
|
-library "$target_dir/aarch64-apple-darwin/release/libversevdi_core.a" \
|
||||||
-headers "$headers" \
|
-headers "$headers" \
|
||||||
-output "$output/VerseVDICore.xcframework"
|
-output "$output/VerseVDICore.xcframework"
|
||||||
|
|
||||||
timestamp=$(date -r "$source_epoch" +%Y%m%d%H%M.%S)
|
timestamp=$(date -r "$source_epoch" +%Y%m%d%H%M.%S)
|
||||||
find "$output/VerseVDICore.xcframework" -exec touch -h -t "$timestamp" {} +
|
find "$output/VerseVDICore.xcframework" -exec touch -h -t "$timestamp" {} +
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'rustc=%s\n' "$rustc_version"
|
||||||
|
printf 'cargo=%s\n' "$cargo_version"
|
||||||
|
printf 'cargo_path=%s\n' "$cargo_bin"
|
||||||
|
printf 'rustc_path=%s\n' "$rustc_bin"
|
||||||
|
printf 'xcode=%s\n' "$xcode_version"
|
||||||
|
printf 'clang=%s\n' "$clang_version"
|
||||||
|
printf 'sdk=%s\n' "$sdk"
|
||||||
|
printf 'cargo_config=isolated-home-and-no-project-config\n'
|
||||||
|
printf 'target=aarch64-apple-darwin\n'
|
||||||
|
printf 'deployment_target=14.0\n'
|
||||||
|
printf 'source_commit=%s\n' "$source_commit"
|
||||||
|
printf 'source_epoch=%s\n' "$source_epoch"
|
||||||
|
printf 'rustflags=--remap-path-prefix=<repository>=. --remap-path-prefix=<target>=/cargo-target\n'
|
||||||
|
printf 'cflags=-fdebug-prefix-map/-ffile-prefix-map for <repository> and <target>\n'
|
||||||
|
} >"$output/build-environment.txt"
|
||||||
|
touch -t "$timestamp" "$output/build-environment.txt" "$output"
|
||||||
|
|||||||
+133
-20
@@ -18,23 +18,91 @@ test -x "$BUILDER"
|
|||||||
WORK=$(mktemp -d "${TMPDIR:-/tmp}/versevdi-core-package.XXXXXX")
|
WORK=$(mktemp -d "${TMPDIR:-/tmp}/versevdi-core-package.XXXXXX")
|
||||||
trap 'rm -rf "$WORK"' EXIT HUP INT TERM
|
trap 'rm -rf "$WORK"' EXIT HUP INT TERM
|
||||||
|
|
||||||
|
expect_path_rejected() {
|
||||||
|
error=$1
|
||||||
|
shift
|
||||||
|
if "$BUILDER" "$@" >"$WORK/path.stdout" 2>"$WORK/path.stderr"; then
|
||||||
|
echo "unsafe packaging path was accepted" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -F "$error" "$WORK/path.stderr" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir "$WORK/parents" "$WORK/physical-parent"
|
||||||
|
mkdir "$WORK/existing-output"
|
||||||
|
expect_path_rejected "must not exist" \
|
||||||
|
--output "$WORK/existing-output" --target-dir "$WORK/parents/existing-target"
|
||||||
|
ln -s "$WORK/physical-parent" "$WORK/parent-alias"
|
||||||
|
ln -s "$WORK/physical-parent" "$WORK/second-parent-alias"
|
||||||
|
ln -s "$WORK/missing" "$WORK/dangling-output"
|
||||||
|
expect_path_rejected "must not exist" \
|
||||||
|
--output "$WORK/dangling-output" --target-dir "$WORK/parents/dangling-target"
|
||||||
|
expect_path_rejected "must be separate" \
|
||||||
|
--output "$WORK/parents/same" --target-dir "$WORK/parents/same"
|
||||||
|
expect_path_rejected "must be separate" \
|
||||||
|
--output "$WORK/parent-alias/aliased-same" \
|
||||||
|
--target-dir "$WORK/second-parent-alias/aliased-same"
|
||||||
|
ln -s "$ROOT" "$WORK/repository-alias"
|
||||||
|
expect_path_rejected "outside the repository" \
|
||||||
|
--output "$WORK/repository-alias/core/forbidden-output" \
|
||||||
|
--target-dir "$WORK/parents/repository-alias-target"
|
||||||
|
|
||||||
"$BUILDER" --output "$WORK/one" --target-dir "$WORK/target-one"
|
"$BUILDER" --output "$WORK/one" --target-dir "$WORK/target-one"
|
||||||
"$BUILDER" --output "$WORK/two" --target-dir "$WORK/target-two"
|
mkdir "$WORK/hostile-bin"
|
||||||
|
mkdir -p "$WORK/hostile-cargo-home" "$WORK/hostile-home/.cargo"
|
||||||
|
ln -s /usr/bin/false "$WORK/hostile-bin/cargo"
|
||||||
|
ln -s /usr/bin/false "$WORK/hostile-bin/rustup"
|
||||||
|
ln -s /usr/bin/false "$WORK/hostile-bin/xcodebuild"
|
||||||
|
cat >"$WORK/hostile-cargo-home/config.toml" <<'EOF'
|
||||||
|
[build]
|
||||||
|
rustc-wrapper = "/usr/bin/false"
|
||||||
|
EOF
|
||||||
|
cat >"$WORK/hostile-home/.cargo/config.toml" <<'EOF'
|
||||||
|
[build]
|
||||||
|
rustc = "/usr/bin/false"
|
||||||
|
EOF
|
||||||
|
env \
|
||||||
|
AR=/usr/bin/false \
|
||||||
|
CARGO_BUILD_RUSTC=/usr/bin/false \
|
||||||
|
CARGO_BUILD_RUSTC_WRAPPER=/usr/bin/false \
|
||||||
|
CARGO_ENCODED_RUSTFLAGS=--cfghostile \
|
||||||
|
CARGO_HOME="$WORK/hostile-cargo-home" \
|
||||||
|
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=/usr/bin/false \
|
||||||
|
CC=/usr/bin/false \
|
||||||
|
DEVELOPER_DIR="$WORK/hostile-xcode" \
|
||||||
|
HOME="$WORK/hostile-home" \
|
||||||
|
PATH="$WORK/hostile-bin:/usr/bin:/bin" \
|
||||||
|
RUSTC=/usr/bin/false \
|
||||||
|
RUSTC_WRAPPER=/usr/bin/false \
|
||||||
|
RUSTFLAGS=--cfg=hostile \
|
||||||
|
RUSTUP_HOME="$WORK/hostile-rustup-home" \
|
||||||
|
RUSTUP_TOOLCHAIN=bogus \
|
||||||
|
"$BUILDER" \
|
||||||
|
--output "$WORK/parent-alias/two" \
|
||||||
|
--target-dir "$WORK/parents/target-two"
|
||||||
|
|
||||||
framework_one="$WORK/one/VerseVDICore.xcframework"
|
framework_one="$WORK/one/VerseVDICore.xcframework"
|
||||||
framework_two="$WORK/two/VerseVDICore.xcframework"
|
framework_two="$WORK/physical-parent/two/VerseVDICore.xcframework"
|
||||||
library_one=$(find "$framework_one" -type f -name libversevdi_core.a -print)
|
library_one=$(find "$framework_one" -type f -name libversevdi_core.a -print)
|
||||||
library_two=$(find "$framework_two" -type f -name libversevdi_core.a -print)
|
library_two=$(find "$framework_two" -type f -name libversevdi_core.a -print)
|
||||||
test "$(printf '%s\n' "$library_one" | grep -c .)" -eq 1
|
test "$(printf '%s\n' "$library_one" | grep -c .)" -eq 1
|
||||||
test "$(printf '%s\n' "$library_two" | grep -c .)" -eq 1
|
test "$(printf '%s\n' "$library_two" | grep -c .)" -eq 1
|
||||||
|
|
||||||
test "$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedPlatform' "$framework_one/Info.plist")" = macos
|
platform=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedPlatform' "$framework_one/Info.plist")
|
||||||
test "$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedArchitectures:0' "$framework_one/Info.plist")" = arm64
|
architecture=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedArchitectures:0' "$framework_one/Info.plist")
|
||||||
test "$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries' "$framework_one/Info.plist" | grep -c 'Dict {')" -eq 1
|
available_libraries=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries' "$framework_one/Info.plist")
|
||||||
test "$(xcrun lipo -archs "$library_one")" = arm64
|
test "$platform" = macos
|
||||||
file "$library_one" | grep -F 'current ar archive' >/dev/null
|
test "$architecture" = arm64
|
||||||
|
test "$(printf '%s\n' "$available_libraries" | grep -c 'Dict {')" -eq 1
|
||||||
|
library_archs=$(xcrun lipo -archs "$library_one")
|
||||||
|
library_identity=$(file "$library_one")
|
||||||
|
test "$library_archs" = arm64
|
||||||
|
printf '%s\n' "$library_identity" | grep -F 'current ar archive' >/dev/null
|
||||||
|
|
||||||
consumer="$WORK/consumer"
|
consumer="$WORK/consumer"
|
||||||
|
link_consumer() {
|
||||||
|
output=$1
|
||||||
|
library=$2
|
||||||
xcrun clang \
|
xcrun clang \
|
||||||
-arch arm64 \
|
-arch arm64 \
|
||||||
-mmacosx-version-min=14.0 \
|
-mmacosx-version-min=14.0 \
|
||||||
@@ -44,19 +112,37 @@ xcrun clang \
|
|||||||
-fmodules-cache-path="$WORK/module-cache" \
|
-fmodules-cache-path="$WORK/module-cache" \
|
||||||
-I"$(dirname "$library_one")/Headers" \
|
-I"$(dirname "$library_one")/Headers" \
|
||||||
"$ROOT/core/tests/ffi/abi_smoke.c" \
|
"$ROOT/core/tests/ffi/abi_smoke.c" \
|
||||||
"$library_one" \
|
-Wl,-force_load,"$library" \
|
||||||
-framework Security \
|
-framework Security \
|
||||||
-framework SystemConfiguration \
|
-framework SystemConfiguration \
|
||||||
-framework CoreFoundation \
|
-framework CoreFoundation \
|
||||||
-lresolv \
|
-lresolv \
|
||||||
-o "$consumer"
|
-o "$output"
|
||||||
test "$(xcrun lipo -archs "$consumer")" = arm64
|
}
|
||||||
file "$consumer" | grep -F 'Mach-O 64-bit executable arm64' >/dev/null
|
|
||||||
|
link_consumer "$consumer" "$library_one"
|
||||||
|
consumer_archs=$(xcrun lipo -archs "$consumer")
|
||||||
|
consumer_identity=$(file "$consumer")
|
||||||
|
test "$consumer_archs" = arm64
|
||||||
|
printf '%s\n' "$consumer_identity" | grep -F 'Mach-O 64-bit executable arm64' >/dev/null
|
||||||
|
|
||||||
symbols=$(xcrun nm -gjU "$consumer")
|
symbols=$(xcrun nm -gjU "$consumer")
|
||||||
actual_exports=$(printf '%s\n' "$symbols" | grep '^_verse_core_' | LC_ALL=C sort -u)
|
actual_exports=$(printf '%s\n' "$symbols" | grep '^_verse_core_' | LC_ALL=C sort -u)
|
||||||
test "$actual_exports" = "$EXPECTED_EXPORTS"
|
test "$actual_exports" = "$EXPECTED_EXPORTS"
|
||||||
|
|
||||||
|
cat >"$WORK/unexpected.c" <<'EOF'
|
||||||
|
void verse_core_unexpected_v1(void) {}
|
||||||
|
EOF
|
||||||
|
xcrun clang -arch arm64 -mmacosx-version-min=14.0 -c "$WORK/unexpected.c" -o "$WORK/unexpected.o"
|
||||||
|
cp "$library_one" "$WORK/libunexpected.a"
|
||||||
|
ZERO_AR_DATE=1 xcrun ar -r "$WORK/libunexpected.a" "$WORK/unexpected.o"
|
||||||
|
link_consumer "$WORK/unexpected-consumer" "$WORK/libunexpected.a"
|
||||||
|
unexpected_symbols=$(xcrun nm -gjU "$WORK/unexpected-consumer")
|
||||||
|
if test "$(printf '%s\n' "$unexpected_symbols" | grep '^_verse_core_' | LC_ALL=C sort -u)" = "$EXPECTED_EXPORTS"; then
|
||||||
|
echo "force-loaded export inspection missed an unreferenced ABI symbol" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
dependencies=$(xcrun otool -L "$consumer")
|
dependencies=$(xcrun otool -L "$consumer")
|
||||||
unexpected_dependencies=$(printf '%s\n' "$dependencies" | tail -n +2 | awk '{print $1}' | grep -Ev '^(/usr/lib/(libSystem\.B|libresolv\.9)\.dylib|/System/Library/Frameworks/(CoreFoundation|Security|SystemConfiguration)\.framework/Versions/A/[^/]+)$' || true)
|
unexpected_dependencies=$(printf '%s\n' "$dependencies" | tail -n +2 | awk '{print $1}' | grep -Ev '^(/usr/lib/(libSystem\.B|libresolv\.9)\.dylib|/System/Library/Frameworks/(CoreFoundation|Security|SystemConfiguration)\.framework/Versions/A/[^/]+)$' || true)
|
||||||
test -z "$unexpected_dependencies"
|
test -z "$unexpected_dependencies"
|
||||||
@@ -76,31 +162,58 @@ done
|
|||||||
canonical_tree() {
|
canonical_tree() {
|
||||||
(
|
(
|
||||||
cd "$1"
|
cd "$1"
|
||||||
find . -type f -print | LC_ALL=C sort | while IFS= read -r path; do
|
find . -print >"$WORK/tree.entries"
|
||||||
digest=$(shasum -a 256 "$path" | awk '{print $1}')
|
LC_ALL=C sort "$WORK/tree.entries" >"$WORK/tree.sorted"
|
||||||
printf '%s %s\n' "$digest" "$path"
|
while IFS= read -r path; do
|
||||||
done
|
metadata=$(stat -f '%HT|%Sp' "$path")
|
||||||
|
case "$metadata" in
|
||||||
|
'Regular File|'*) digest=$(sha256_file "$path") ;;
|
||||||
|
'Symbolic Link|'*) digest=$(readlink "$path") ;;
|
||||||
|
*) digest=- ;;
|
||||||
|
esac
|
||||||
|
printf '%s|%s|%s\n' "$metadata" "$digest" "$path"
|
||||||
|
done <"$WORK/tree.sorted"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sha256_file() {
|
||||||
|
checksum=$(shasum -a 256 "$1")
|
||||||
|
set -- $checksum
|
||||||
|
printf '%s\n' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
canonical_tree "$framework_one" >"$WORK/one.tree"
|
canonical_tree "$framework_one" >"$WORK/one.tree"
|
||||||
canonical_tree "$framework_two" >"$WORK/two.tree"
|
canonical_tree "$framework_two" >"$WORK/two.tree"
|
||||||
cmp "$WORK/one.tree" "$WORK/two.tree"
|
cmp "$WORK/one.tree" "$WORK/two.tree"
|
||||||
test "$(shasum -a 256 "$library_one" | awk '{print $1}')" = "$(shasum -a 256 "$library_two" | awk '{print $1}')"
|
test "$(sha256_file "$library_one")" = "$(sha256_file "$library_two")"
|
||||||
|
|
||||||
source_epoch=$(git -C "$ROOT" show -s --format=%ct HEAD)
|
source_epoch=$(git -C "$ROOT" show -s --format=%ct HEAD)
|
||||||
find "$framework_one" -exec stat -f '%m' {} \; | while IFS= read -r epoch; do
|
find "$WORK/one" "$WORK/physical-parent/two" -exec stat -f '%m' {} \; >"$WORK/mtimes"
|
||||||
|
while IFS= read -r epoch; do
|
||||||
test "$epoch" = "$source_epoch"
|
test "$epoch" = "$source_epoch"
|
||||||
done
|
done <"$WORK/mtimes"
|
||||||
|
|
||||||
|
expected_rustc='rustc 1.97.1 (8bab26f4f 2026-07-14)'
|
||||||
|
expected_cargo='cargo 1.97.1 (c980f4866 2026-06-30)'
|
||||||
|
expected_xcode='Xcode 26.6
|
||||||
|
Build version 17F113'
|
||||||
|
grep -Fx "rustc=$expected_rustc" "$WORK/one/build-environment.txt" >/dev/null
|
||||||
|
grep -Fx "cargo=$expected_cargo" "$WORK/one/build-environment.txt" >/dev/null
|
||||||
|
test "$(sed -n 's/^xcode=//p; /^Build version /p' "$WORK/one/build-environment.txt")" = "$expected_xcode"
|
||||||
|
cmp "$WORK/one/build-environment.txt" "$WORK/physical-parent/two/build-environment.txt"
|
||||||
|
|
||||||
strings "$library_one" >"$WORK/library.strings"
|
strings "$library_one" >"$WORK/library.strings"
|
||||||
if grep -F "$ROOT" "$WORK/library.strings" >/dev/null; then
|
if grep -F "$ROOT" "$WORK/library.strings" >/dev/null; then
|
||||||
echo "repository path leaked into static archive" >&2
|
echo "repository path leaked into static archive" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if grep -F "$WORK/target-one" "$WORK/library.strings" >/dev/null; then
|
||||||
|
echo "target directory path leaked into static archive" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
archive_members=$(xcrun ar -tv "$library_one")
|
archive_members=$(xcrun ar -tv "$library_one")
|
||||||
printf '%s\n' "$archive_members" | awk '$7 != "1970" { exit 1 }'
|
printf '%s\n' "$archive_members" | awk '$7 != "1970" { exit 1 }'
|
||||||
|
|
||||||
printf 'framework_sha256=%s\n' "$(shasum -a 256 "$WORK/one.tree" | awk '{print $1}')"
|
printf 'framework_sha256=%s\n' "$(sha256_file "$WORK/one.tree")"
|
||||||
printf 'library_sha256=%s\n' "$(shasum -a 256 "$library_one" | awk '{print $1}')"
|
printf 'library_sha256=%s\n' "$(sha256_file "$library_one")"
|
||||||
|
|||||||
Reference in New Issue
Block a user