Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/api-stability.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
if diff -q "sdk_api_base.json" "sdk_api.json" > /dev/null; then
echo "No Sentry API changes detected."
else
echo "❌ Sentry public API changes are detected. If they're intended run "make generate-public-api" and commit the changes."
echo "❌ Sentry public API changes are detected. If they're intended run 'make generate-public-api' and commit the changes."
diff "sdk_api_base.json" "sdk_api.json" || true
xcrun --sdk iphoneos swift-api-digester \
-diagnose-sdk \
Expand All @@ -75,7 +75,7 @@ jobs:
if diff -q "sdk_api_sentryswiftui_base.json" "sdk_api_sentryswiftui.json" > /dev/null; then
echo "No SentrySwiftUI API changes detected."
else
echo "❌ SentrySwiftUI public API changes are detected. If they're intended run "make generate-public-api" and commit the changes."
echo "❌ SentrySwiftUI public API changes are detected. If they're intended run 'make generate-public-api' and commit the changes."
diff "sdk_api_sentryswiftui_base.json" "sdk_api_sentryswiftui.json" || true
xcrun --sdk iphoneos swift-api-digester \
-diagnose-sdk \
Expand All @@ -87,6 +87,7 @@ jobs:
cat sentryswiftui_result.json
exit 1
fi

- name: Run CI Diagnostics
uses: ./.github/actions/ci-diagnostics
if: failure()
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/assemble-xcframework-variant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ jobs:
shell: bash

- name: Validate XCFramework structure
run: ./scripts/validate-xcframework-format.sh "${{env.XCFRAMEWORK_NAME}}.xcframework"
run: |
./scripts/validate-xcframework-format.sh "${{env.XCFRAMEWORK_NAME}}.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "${{env.XCFRAMEWORK_NAME}}.xcframework"
shell: bash
env:
XCFRAMEWORK_NAME: ${{ env.XCFRAMEWORK_NAME }}
Expand Down
6 changes: 6 additions & 0 deletions scripts/build-xcframework-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ if [ "$variants" = "DynamicOnly" ] || [ "$variants" = "AllVariants" ]; then
begin_group "Sentry-Dynamic"
./scripts/build-xcframework-variant.sh "Sentry" "-Dynamic" "mh_dylib" "" "$sdks" "arm64e"
./scripts/validate-xcframework-format.sh "Sentry-Dynamic.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "Sentry-Dynamic.xcframework"
./scripts/compress-xcframework.sh "$signed" Sentry-Dynamic
mv Sentry-Dynamic.xcframework.zip XCFrameworkBuildPath/Sentry-Dynamic.xcframework.zip
end_group
Expand All @@ -55,6 +56,7 @@ if [ "$variants" = "DynamicWithARM64eOnly" ] || [ "$variants" = "AllVariants" ];
begin_group "Sentry-Dynamic-WithARM64e"
./scripts/build-xcframework-variant.sh "Sentry" "-Dynamic-WithARM64e" "mh_dylib" "" "$sdks" ""
./scripts/validate-xcframework-format.sh "Sentry-Dynamic-WithARM64e.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "Sentry-Dynamic-WithARM64e.xcframework"
./scripts/compress-xcframework.sh "$signed" Sentry-Dynamic-WithARM64e
mv Sentry-Dynamic-WithARM64e.xcframework.zip XCFrameworkBuildPath/Sentry-Dynamic-WithARM64e.xcframework.zip
end_group
Expand All @@ -64,6 +66,7 @@ if [ "$variants" = "StaticOnly" ] || [ "$variants" = "AllVariants" ]; then
begin_group "Sentry-Static"
./scripts/build-xcframework-variant.sh "Sentry" "" "staticlib" "" "$sdks" ""
./scripts/validate-xcframework-format.sh "Sentry.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "Sentry.xcframework"
./scripts/compress-xcframework.sh "$signed" Sentry
mv Sentry.xcframework.zip XCFrameworkBuildPath/Sentry.xcframework.zip
end_group
Expand All @@ -73,6 +76,7 @@ if [ "$variants" = "SwiftUIOnly" ] || [ "$variants" = "AllVariants" ]; then
begin_group "SentrySwiftUI"
./scripts/build-xcframework-variant.sh "SentrySwiftUI" "" "mh_dylib" "" "$sdks" ""
./scripts/validate-xcframework-format.sh "SentrySwiftUI.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "SentrySwiftUI.xcframework"
./scripts/compress-xcframework.sh "$signed" SentrySwiftUI
mv SentrySwiftUI.xcframework.zip XCFrameworkBuildPath/SentrySwiftUI.xcframework.zip
end_group
Expand All @@ -82,6 +86,7 @@ if [ "$variants" = "WithoutUIKitOnly" ] || [ "$variants" = "AllVariants" ]; then
begin_group "Sentry-WithoutUIKitOrAppKit"
./scripts/build-xcframework-variant.sh "Sentry" "-WithoutUIKitOrAppKit" "mh_dylib" "WithoutUIKit" "$sdks" "arm64e"
./scripts/validate-xcframework-format.sh "Sentry-WithoutUIKitOrAppKit.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "Sentry-WithoutUIKitOrAppKit.xcframework"
./scripts/compress-xcframework.sh "$signed" Sentry-WithoutUIKitOrAppKit
mv Sentry-WithoutUIKitOrAppKit.xcframework.zip XCFrameworkBuildPath/Sentry-WithoutUIKitOrAppKit.xcframework.zip
end_group
Expand All @@ -91,6 +96,7 @@ if [ "$variants" = "WithoutUIKitWithARM64eOnly" ] || [ "$variants" = "AllVariant
begin_group "Sentry-WithoutUIKitOrAppKit-WithARM64e"
./scripts/build-xcframework-variant.sh "Sentry" "-WithoutUIKitOrAppKit-WithARM64e" "mh_dylib" "WithoutUIKit" "$sdks" ""
./scripts/validate-xcframework-format.sh "Sentry-WithoutUIKitOrAppKit-WithARM64e.xcframework"
./scripts/validate-xcframework-architectures.sh --xcframework "Sentry-WithoutUIKitOrAppKit-WithARM64e.xcframework"
./scripts/compress-xcframework.sh "$signed" Sentry-WithoutUIKitOrAppKit-WithARM64e
mv Sentry-WithoutUIKitOrAppKit-WithARM64e.xcframework.zip XCFrameworkBuildPath/Sentry-WithoutUIKitOrAppKit-WithARM64e.xcframework.zip
end_group
Expand Down
81 changes: 49 additions & 32 deletions scripts/build-xcframework-slice.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,35 +67,36 @@ fi
rm -rf XCFrameworkBuildPath/DerivedData

## watchos and watchsimulator don't support make_mergeable: ld: unknown option: -make_mergeable
if [[ "$sdk" == "watchos" || "$sdk" == "watchsimulator" ]]; then
OTHER_LDFLAGS=""
elif [ "$MACH_O_TYPE" != "staticlib" ]; then
OTHER_LDFLAGS="-Wl,-make_mergeable"
## For other dynamic frameworks, add -make_mergeable (append to existing flags)
if [[ "$sdk" != "watchos" && "$sdk" != "watchsimulator" ]] && [ "$MACH_O_TYPE" != "staticlib" ]; then
OTHER_LDFLAGS="$OTHER_LDFLAGS -Wl,-make_mergeable"
fi

slice_id="${scheme}${suffix}-${sdk}"

output_xcarchive_path="XCFrameworkBuildPath/archive/${scheme}${suffix}"
sentry_xcarchive_path="$output_xcarchive_path/${sdk}.xcarchive"
log_info " Output archive: $sentry_xcarchive_path"

if [ "$sdk" = "maccatalyst" ]; then
# we can't use the "archive" action here because it doesn't support the -destination option, which we need to build the maccatalyst slice. so we'll have to build it manually and then copy the build product to an xcarchive directory we create.
begin_group "Build ${slice_id} (maccatalyst)"
set -o pipefail && NSUnbufferedIO=YES xcodebuild \
-project Sentry.xcodeproj/ \
-scheme "$scheme" \
-configuration "$resolved_configuration" \
-sdk iphoneos \
-destination 'platform=macOS,variant=Mac Catalyst' \
-derivedDataPath ./XCFrameworkBuildPath/DerivedData \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
MACH_O_TYPE="$MACH_O_TYPE" \
SUPPORTS_MACCATALYST=YES \
ENABLE_CODE_COVERAGE=NO \
GCC_GENERATE_DEBUGGING_SYMBOLS="$GCC_GENERATE_DEBUGGING_SYMBOLS" \
OTHER_LDFLAGS="$OTHER_LDFLAGS" 2>&1 | tee "${slice_id}.maccatalyst.log" | xcbeautify --preserve-unbeautified
maccatalyst_args=(
-project Sentry.xcodeproj/
-scheme "$scheme"
-configuration "$resolved_configuration"
-sdk iphoneos
-destination "generic/platform=macOS,variant=Mac Catalyst"
-derivedDataPath ./XCFrameworkBuildPath/DerivedData
CODE_SIGNING_REQUIRED=NO
SKIP_INSTALL=NO
CODE_SIGN_IDENTITY=
MACH_O_TYPE="$MACH_O_TYPE"
SUPPORTS_MACCATALYST=YES
ENABLE_CODE_COVERAGE=NO
GCC_GENERATE_DEBUGGING_SYMBOLS="$GCC_GENERATE_DEBUGGING_SYMBOLS"
OTHER_LDFLAGS="$OTHER_LDFLAGS"
)
set -o pipefail && NSUnbufferedIO=YES xcodebuild "${maccatalyst_args[@]}" 2>&1 | tee "${slice_id}.maccatalyst.log" | xcbeautify --preserve-unbeautified
end_group

maccatalyst_build_product_directory="XCFrameworkBuildPath/DerivedData/Build/Products/$resolved_configuration-maccatalyst"
Expand All @@ -117,19 +118,35 @@ if [ "$sdk" = "maccatalyst" ]; then
end_group
else
begin_group "Archive ${slice_id}"
set -o pipefail && NSUnbufferedIO=YES xcodebuild archive \
-project Sentry.xcodeproj/ \
-scheme "$scheme" \
-configuration "$resolved_configuration" \
-sdk "$sdk" \
-archivePath "./$sentry_xcarchive_path" \
CODE_SIGNING_REQUIRED=NO \
SKIP_INSTALL=NO \
CODE_SIGN_IDENTITY= \
MACH_O_TYPE="$MACH_O_TYPE" \
ENABLE_CODE_COVERAGE=NO \
GCC_GENERATE_DEBUGGING_SYMBOLS="$GCC_GENERATE_DEBUGGING_SYMBOLS" \
OTHER_LDFLAGS="$OTHER_LDFLAGS" 2>&1 | tee "${slice_id}.log" | xcbeautify --preserve-unbeautified
xcodebuild_args=(
-project Sentry.xcodeproj/
-scheme "$scheme"
-configuration "$resolved_configuration"
-sdk "$sdk"
)

if [ "$sdk" = "macosx" ]; then
xcodebuild_args+=(-destination "generic/platform=macOS")
fi

build_setting_overrides=(
CODE_SIGNING_REQUIRED=NO
SKIP_INSTALL=NO
CODE_SIGN_IDENTITY=
MACH_O_TYPE="$MACH_O_TYPE"
ENABLE_CODE_COVERAGE=NO
GCC_GENERATE_DEBUGGING_SYMBOLS="$GCC_GENERATE_DEBUGGING_SYMBOLS"
OTHER_LDFLAGS="$OTHER_LDFLAGS"
)

archive_args=(
archive
"${xcodebuild_args[@]}"
-archivePath "./$sentry_xcarchive_path"
"${build_setting_overrides[@]}"
)

set -o pipefail && NSUnbufferedIO=YES xcodebuild "${archive_args[@]}" 2>&1 | tee "${slice_id}.log" | xcbeautify --preserve-unbeautified
end_group
fi

Expand Down
16 changes: 14 additions & 2 deletions scripts/build-xcframework-variant.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
#!/bin/bash

#
# Builds all slices for an XCFramework variant
#
# Parameters:
# $1 - scheme (e.g., Sentry, SentryObjC)
# $2 - suffix (optional, e.g., -Dynamic)
# $3 - MACH_O_TYPE (mh_dylib or staticlib)
# $4 - configuration_suffix (optional)
# $5 - sdks_to_build (AllSDKs, iOSOnly, macOSOnly, macCatalystOnly)
# $6 - excluded_archs (optional)
set -eoux pipefail

scheme="$1"
Expand All @@ -15,8 +24,11 @@ elif [ "$sdks_to_build" = "macOSOnly" ]; then
sdks=( macosx )
elif [ "$sdks_to_build" = "macCatalystOnly" ]; then
sdks=( maccatalyst )
else
elif [ -z "$sdks_to_build" ] || [ "$sdks_to_build" = "AllSDKs" ]; then
sdks=( iphoneos iphonesimulator macosx maccatalyst appletvos appletvsimulator watchos watchsimulator xros xrsimulator )
else
# Treat as comma-separated list (e.g. "iphonesimulator" or "iphoneos,iphonesimulator").
IFS=',' read -r -a sdks <<< "$sdks_to_build"
fi

for sdk in "${sdks[@]}"; do
Expand Down
1 change: 1 addition & 0 deletions scripts/compress-xcframework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ if [[ "$should_sign" == true ]]; then
begin_group "Signing $framework"
log_info "Signing with certificate: $sentry_certificate"
codesign --sign "$sentry_certificate" --timestamp --options runtime --deep --force "$framework_path"
codesign --verify --deep --strict --verbose=2 "$framework_path"
end_group
fi

Expand Down
156 changes: 156 additions & 0 deletions scripts/validate-xcframework-architectures.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./ci-utils.sh disable=SC1091
source "$SCRIPT_DIR/ci-utils.sh"

XCFRAMEWORK_PATH=""

usage() {
log_notice "Usage: $0 --xcframework <path>"
log_notice " --xcframework <path> XCFramework bundle to validate (required)"
exit 1
}

while [[ $# -gt 0 ]]; do
case "$1" in
--xcframework)
if [ $# -lt 2 ]; then
usage
fi
XCFRAMEWORK_PATH="$2"
shift 2
;;
*)
usage
;;
esac
done

if [ -z "$XCFRAMEWORK_PATH" ]; then
log_error "Error: --xcframework is required"
usage
fi

if [ ! -d "$XCFRAMEWORK_PATH" ]; then
log_error "XCFramework path does not exist: $XCFRAMEWORK_PATH"
exit 1
fi

info_plist_path="$XCFRAMEWORK_PATH/Info.plist"
if [ ! -f "$info_plist_path" ]; then
log_error "Missing XCFramework Info.plist: $info_plist_path"
exit 1
fi

normalize_archs() {
local archs="$1"

printf "%s\n" "$archs" | tr " " "\n" | sed "/^$/d" | sort | paste -sd " " -
}

binary_path_for_library() {
local library_identifier="$1"
local library_path="$2"
local library_full_path="$XCFRAMEWORK_PATH/$library_identifier/$library_path"
local framework_name=""
local binary_path=""

if [[ "$library_full_path" == *.framework ]]; then
framework_name="$(basename "$library_full_path" .framework)"
binary_path="$library_full_path/$framework_name"
if [ -e "$binary_path" ]; then
printf "%s\n" "$binary_path"
return 0
fi

binary_path="$library_full_path/Versions/A/$framework_name"
if [ -e "$binary_path" ]; then
printf "%s\n" "$binary_path"
return 0
fi

log_error "Missing framework binary for $library_identifier: $library_full_path" >&2
return 1
fi

if [ -f "$library_full_path" ]; then
printf "%s\n" "$library_full_path"
return 0
fi

log_error "Unsupported or missing library path for $library_identifier: $library_full_path" >&2
return 1
}

validate_library_architectures() {
local library_identifier="$1"
local library_path="$2"
local expected_archs="$3"
local binary_path=""
local actual_archs=""
local normalized_expected_archs=""
local normalized_actual_archs=""

if ! binary_path="$(binary_path_for_library "$library_identifier" "$library_path")"; then
return 1
fi

if ! actual_archs="$(lipo -archs "$binary_path" 2>/dev/null)"; then
log_error "Could not read architectures for $library_identifier: $binary_path"
return 1
fi

normalized_expected_archs="$(normalize_archs "$expected_archs")"
normalized_actual_archs="$(normalize_archs "$actual_archs")"

if [ "$normalized_expected_archs" != "$normalized_actual_archs" ]; then
log_error "$library_identifier architecture mismatch: expected [$normalized_expected_archs], got [$normalized_actual_archs]"
log_error "Binary: $binary_path"
return 1
fi

log_notice "$library_identifier architectures: $normalized_actual_archs"
}

begin_group "Validate XCFramework architectures: $XCFRAMEWORK_PATH"

xcframework_json="$(plutil -convert json -o - "$info_plist_path")"
validation_errors=0
processed_libraries=0

library_records="$(
printf "%s\n" "$xcframework_json" \
| jq -r '.AvailableLibraries[] | [.LibraryIdentifier, .LibraryPath, (.SupportedArchitectures | join(" "))] | @tsv'
)" || {
log_error "Could not parse AvailableLibraries from $info_plist_path"
end_group
exit 1
}

while IFS=$'\t' read -r library_identifier library_path expected_archs; do
if [ -z "$library_identifier" ]; then
continue
fi

processed_libraries=$((processed_libraries + 1))

if ! validate_library_architectures "$library_identifier" "$library_path" "$expected_archs"; then
validation_errors=$((validation_errors + 1))
fi
done <<< "$library_records"

if [ "$processed_libraries" -eq 0 ]; then
log_error "XCFramework Info.plist does not contain any AvailableLibraries entries."
validation_errors=$((validation_errors + 1))
fi

end_group

if [ "$validation_errors" -ne 0 ]; then
log_error "XCFramework architecture validation failed with $validation_errors error(s)."
exit 1
fi

log_notice "XCFramework architecture validation passed."
Loading