Skip to content

Commit 1bc3591

Browse files
authored
Add Swift SDK for Android to swift_package_test workflow (#172)
1 parent f1ef0dc commit 1bc3591

File tree

3 files changed

+265
-21
lines changed

3 files changed

+265
-21
lines changed

.github/workflows/pull_request.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ jobs:
4444
cd MyPackage
4545
swift package init --type library
4646
enable_wasm_sdk_build: true
47+
# Android
48+
android_sdk_pre_build_command: |
49+
mkdir MyPackage
50+
cd MyPackage
51+
swift package init --type library
52+
enable_android_sdk_build: true
4753
# Windows
4854
windows_build_command: |
4955
mkdir MyPackage
@@ -58,6 +64,12 @@ jobs:
5864
with:
5965
# Skip Linux which doesn't currently support docker-less workflow
6066
enable_linux_checks: false
67+
# Android
68+
android_sdk_pre_build_command: |
69+
mkdir MyPackage
70+
cd MyPackage
71+
swift package init --type library
72+
enable_android_sdk_build: true
6173
# Windows
6274
windows_build_command: |
6375
mkdir MyPackage

.github/workflows/scripts/install-and-build-with-sdk.sh

Lines changed: 166 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ error() { printf -- "** ERROR: %s\n" "$*" >&2; }
1818
fatal() { error "$@"; exit 1; }
1919

2020
# Parse command line options
21+
INSTALL_ANDROID=false
2122
INSTALL_STATIC_LINUX=false
2223
INSTALL_WASM=false
2324
BUILD_EMBEDDED_WASM=false
@@ -27,6 +28,18 @@ SWIFT_BUILD_COMMAND="swift build"
2728

2829
while [[ $# -gt 0 ]]; do
2930
case $1 in
31+
--android)
32+
INSTALL_ANDROID=true
33+
shift
34+
;;
35+
--android-ndk-version=*)
36+
ANDROID_NDK_VERSION="${1#*=}"
37+
shift
38+
;;
39+
--android-sdk-triple=*)
40+
ANDROID_SDK_TRIPLE="${1#*=}"
41+
shift
42+
;;
3043
--static)
3144
INSTALL_STATIC_LINUX=true
3245
shift
@@ -64,32 +77,32 @@ done
6477

6578
# Validate arguments
6679
if [[ -z "$SWIFT_VERSION_INPUT" ]]; then
67-
fatal "Usage: $0 [--static] [--wasm] [--flags=\"<build-flags>\"] [--build-command=\"<build-command>\"] <swift-version>"
80+
fatal "Usage: $0 [--android] [--static] [--wasm] [--flags=\"<build-flags>\"] [--build-command=\"<build-command>\"] <swift-version>"
6881
fi
6982

70-
if [[ "$INSTALL_STATIC_LINUX" == false && "$INSTALL_WASM" == false ]]; then
71-
fatal "At least one of --static or --wasm must be specified"
83+
if [[ "$INSTALL_ANDROID" == false && "$INSTALL_STATIC_LINUX" == false && "$INSTALL_WASM" == false ]]; then
84+
fatal "At least one of --android or --static or --wasm must be specified"
7285
fi
7386

7487
log "Requested Swift version: $SWIFT_VERSION_INPUT"
88+
log "Install Android Swift SDK: $INSTALL_ANDROID"
7589
log "Install Static Linux Swift SDK: $INSTALL_STATIC_LINUX"
7690
log "Install Wasm Swift SDK: $INSTALL_WASM"
7791
if [[ -n "$SWIFT_BUILD_FLAGS" ]]; then
7892
log "Additional build flags: $SWIFT_BUILD_FLAGS"
7993
fi
8094

81-
# Detect package manager
82-
if command -v apt >/dev/null 2>&1; then
83-
INSTALL_PACKAGE_COMMAND="apt update -q && apt install -yq"
84-
elif command -v dnf >/dev/null 2>&1; then
85-
INSTALL_PACKAGE_COMMAND="dnf install -y"
86-
elif command -v yum >/dev/null 2>&1; then
87-
INSTALL_PACKAGE_COMMAND="yum install -y"
88-
else
89-
fatal "No supported package manager found"
90-
fi
91-
9295
install_package() {
96+
# Detect package manager
97+
if command -v apt >/dev/null 2>&1; then
98+
INSTALL_PACKAGE_COMMAND="apt update -q && apt install -yq"
99+
elif command -v dnf >/dev/null 2>&1; then
100+
INSTALL_PACKAGE_COMMAND="dnf install -y"
101+
elif command -v yum >/dev/null 2>&1; then
102+
INSTALL_PACKAGE_COMMAND="yum install -y"
103+
else
104+
fatal "No supported package manager found"
105+
fi
93106
eval "$INSTALL_PACKAGE_COMMAND $1"
94107
}
95108

@@ -103,7 +116,7 @@ SWIFT_API_INSTALL_ROOT="https://www.swift.org/api/v1/install"
103116
# and gets the checksum for the patch version's Static Linux and/or Wasm Swift SDK.
104117
#
105118
# $1 (string): A minor Swift version, e.g. "6.1"
106-
# Output: A string of the form "<patch-version>|<static-checksum>|<wasm-checksum>
119+
# Output: A string of the form "<patch-version>|<android-checksum>|<static-checksum>|<wasm-checksum>
107120
find_latest_swift_version() {
108121
local minor_version="$1"
109122

@@ -128,6 +141,23 @@ find_latest_swift_version() {
128141

129142
log "Found latest patch version: $latest_version"
130143

144+
local android_sdk_checksum=""
145+
if [[ "$INSTALL_ANDROID" == true ]]; then
146+
android_sdk_checksum=$(echo "$releases_json" | jq -r --arg version "$latest_version" '
147+
.[]
148+
| select(.name == $version)
149+
| .platforms[]
150+
| select(.platform == "android")
151+
| .checksum
152+
')
153+
154+
if [[ -z "$android_sdk_checksum" ]]; then
155+
fatal "No Android Swift SDK checksum found for Swift $latest_version"
156+
fi
157+
158+
log "Found Android Swift SDK checksum: ${android_sdk_checksum:0:12}..."
159+
fi
160+
131161
local static_linux_sdk_checksum=""
132162
if [[ "$INSTALL_STATIC_LINUX" == true ]]; then
133163
static_linux_sdk_checksum=$(echo "$releases_json" | jq -r --arg version "$latest_version" '
@@ -162,14 +192,15 @@ find_latest_swift_version() {
162192
log "Found Swift SDK for Wasm checksum: ${wasm_sdk_checksum:0:12}..."
163193
fi
164194

165-
echo "${latest_version}|${static_linux_sdk_checksum}|${wasm_sdk_checksum}"
195+
echo "${latest_version}|${android_sdk_checksum}|${static_linux_sdk_checksum}|${wasm_sdk_checksum}"
166196
}
167197

168-
# Finds the latest Static Linux or Wasm Swift SDK development snapshot
169-
# for the inputted Swift version and its checksum.
198+
# Finds the latest Android or Static Linux or Wasm
199+
# Swift SDK development snapshot for the inputted
200+
# Swift version and its checksum.
170201
#
171202
# $1 (string): Nightly Swift version, e.g. "6.2" or "main"
172-
# $2 (string): "static" or "wasm"
203+
# $2 (string): "android" or "static" or "wasm"
173204
# Output: A string of the form "<snapshot>|<sdk-checksum>",
174205
# e.g. "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-07-29-a|<sdk-checksum>"
175206
find_latest_sdk_snapshot() {
@@ -206,6 +237,12 @@ find_latest_sdk_snapshot() {
206237
}
207238

208239
SWIFT_VERSION_BRANCH=""
240+
ANDROID_SDK_TAG=""
241+
ANDROID_SDK_CHECKSUM=""
242+
# TODO: we will be removing the "-0.1" suffix in a future nightly
243+
ANDROID_SDK_PATH_SUFFIX="-0.1"
244+
ANDROID_SDK_PATH_SEP="-"
245+
209246
STATIC_LINUX_SDK_TAG=""
210247
STATIC_LINUX_SDK_CHECKSUM=""
211248
WASM_SDK_TAG=""
@@ -220,6 +257,13 @@ if [[ "$SWIFT_VERSION_INPUT" == nightly-* ]]; then
220257
SWIFT_VERSION_BRANCH="swift-${version}-branch"
221258
fi
222259

260+
if [[ "$INSTALL_ANDROID" == true ]]; then
261+
android_sdk_info=$(find_latest_sdk_snapshot "$version" "android")
262+
263+
ANDROID_SDK_TAG=$(echo "$android_sdk_info" | cut -d'|' -f1)
264+
ANDROID_SDK_CHECKSUM=$(echo "$android_sdk_info" | cut -d'|' -f2)
265+
fi
266+
223267
if [[ "$INSTALL_STATIC_LINUX" == true ]]; then
224268
static_linux_sdk_info=$(find_latest_sdk_snapshot "$version" "static")
225269

@@ -239,14 +283,21 @@ else
239283
latest_version=$(echo "$latest_version_info" | cut -d'|' -f1)
240284
SWIFT_VERSION_BRANCH="swift-${latest_version}-release"
241285

286+
ANDROID_SDK_TAG="swift-${latest_version}-RELEASE"
287+
ANDROID_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f2)
288+
242289
STATIC_LINUX_SDK_TAG="swift-${latest_version}-RELEASE"
243-
STATIC_LINUX_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f2)
290+
STATIC_LINUX_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f3)
244291

245292
WASM_SDK_TAG="swift-${latest_version}-RELEASE"
246-
WASM_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f3)
293+
WASM_SDK_CHECKSUM=$(echo "$latest_version_info" | cut -d'|' -f4)
247294
fi
248295

249296
# Validate that required Swift SDK tags are set
297+
if [[ "$INSTALL_ANDROID" == true && -z "$ANDROID_SDK_TAG" ]]; then
298+
fatal "ANDROID_SDK_TAG is not set but Android Swift SDK installation was requested"
299+
fi
300+
250301
if [[ "$INSTALL_STATIC_LINUX" == true && -z "$STATIC_LINUX_SDK_TAG" ]]; then
251302
fatal "STATIC_LINUX_SDK_TAG is not set but Static Linux Swift SDK installation was requested"
252303
fi
@@ -439,9 +490,25 @@ download_and_extract_toolchain() {
439490
}
440491

441492
INSTALLED_SWIFT_TAG=$(get_installed_swift_tag)
493+
SWIFT_EXECUTABLE_FOR_ANDROID_SDK=""
442494
SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK=""
443495
SWIFT_EXECUTABLE_FOR_WASM_SDK=""
444496

497+
if [[ "$INSTALL_ANDROID" == true ]]; then
498+
if [[ "$INSTALLED_SWIFT_TAG" == "$ANDROID_SDK_TAG" ]]; then
499+
log "Current toolchain matches Android Swift SDK snapshot: $ANDROID_SDK_TAG"
500+
SWIFT_EXECUTABLE_FOR_ANDROID_SDK="swift"
501+
else
502+
log "Installing Swift toolchain to match Android Swift SDK snapshot: $ANDROID_SDK_TAG"
503+
initialize_os_info
504+
SWIFT_EXECUTABLE_FOR_ANDROID_SDK=$(download_and_extract_toolchain "$ANDROID_SDK_TAG")
505+
if [[ $? -eq $EXIT_TOOLCHAIN_NOT_FOUND ]]; then
506+
# Don't fail the workflow if we can't find the right toolchain
507+
exit 0
508+
fi
509+
fi
510+
fi
511+
445512
if [[ "$INSTALL_STATIC_LINUX" == true ]]; then
446513
if [[ "$INSTALLED_SWIFT_TAG" == "$STATIC_LINUX_SDK_TAG" ]]; then
447514
log "Current toolchain matches Static Linux Swift SDK snapshot: $STATIC_LINUX_SDK_TAG"
@@ -472,9 +539,57 @@ if [[ "$INSTALL_WASM" == true ]]; then
472539
fi
473540
fi
474541

542+
ANDROID_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/android-sdk"
475543
STATIC_LINUX_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/static-sdk"
476544
WASM_SDK_DOWNLOAD_ROOT="${SWIFT_DOWNLOAD_ROOT}/${SWIFT_VERSION_BRANCH}/wasm-sdk"
477545

546+
install_android_sdk() {
547+
# Check if the Android Swift SDK is already installed
548+
if "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" sdk list 2>/dev/null | grep -q "^${ANDROID_SDK_TAG}_android"; then
549+
log "✅ Android Swift SDK ${ANDROID_SDK_TAG} is already installed, skipping installation"
550+
return 0
551+
fi
552+
553+
log "Installing Android Swift SDK: $ANDROID_SDK_TAG"
554+
555+
local android_sdk_name="${ANDROID_SDK_TAG}_android${ANDROID_SDK_PATH_SUFFIX}"
556+
local android_sdk_bundle_name="${android_sdk_name}.artifactbundle"
557+
# TODO: remove once the next nightly changes the "-" to "_" in the name
558+
local android_sdk_bundle_dir="${android_sdk_bundle_name//_android/${ANDROID_SDK_PATH_SEP}android}"
559+
local android_sdk_filename="${android_sdk_bundle_name}.tar.gz"
560+
local sdk_url="${ANDROID_SDK_DOWNLOAD_ROOT}/${ANDROID_SDK_TAG}/${android_sdk_filename}"
561+
562+
log "Running: ${SWIFT_EXECUTABLE_FOR_ANDROID_SDK} sdk install ${sdk_url} --checksum ${ANDROID_SDK_CHECKSUM}"
563+
564+
if "$SWIFT_EXECUTABLE_FOR_ANDROID_SDK" sdk install "$sdk_url" --checksum "$ANDROID_SDK_CHECKSUM"; then
565+
log "✅ Android Swift SDK installed successfully"
566+
else
567+
fatal "Failed to install Android Swift SDK"
568+
fi
569+
570+
# now setup the link to the local ANDROID_NDK_HOME
571+
swift sdk configure --show-configuration "$(swift sdk list | grep android | tail -n 1)"
572+
573+
# guess some common places where the swift-sdks file lives
574+
cd ~/Library/org.swift.swiftpm || cd ~/.config/swiftpm || cd ~/.local/swiftpm || cd ~/.swiftpm || cd /root/.swiftpm
575+
576+
# Download and install the Android NDK.
577+
# Note that we could use the system package manager, but it is
578+
# named different things for different distributions
579+
# (e.g., "google-android-ndk-r26-installer" on Debian)
580+
if [[ ! -d "${ANDROID_NDK_HOME:-}" ]]; then
581+
# permit the "--android-ndk" flag to override the default
582+
local android_ndk_version="${ANDROID_NDK_VERSION:-r27d}"
583+
curl -fsSL -o ndk.zip --retry 3 https://dl.google.com/android/repository/android-ndk-"${android_ndk_version}"-"$(uname -s)".zip
584+
unzip -q ndk.zip
585+
rm ndk.zip
586+
export ANDROID_NDK_HOME="${PWD}"/android-ndk-"${android_ndk_version}"
587+
fi
588+
589+
./swift-sdks/"${android_sdk_bundle_dir}"/swift-android/scripts/setup-android-sdk.sh
590+
cd -
591+
}
592+
478593
install_static_linux_sdk() {
479594
# Check if the Static Linux Swift SDK is already installed
480595
if "$SWIFT_EXECUTABLE_FOR_STATIC_LINUX_SDK" sdk list 2>/dev/null | grep -q "^${STATIC_LINUX_SDK_TAG}_static-linux-0.0.1"; then
@@ -518,6 +633,11 @@ install_wasm_sdk() {
518633
}
519634

520635
install_sdks() {
636+
if [[ "$INSTALL_ANDROID" == true ]]; then
637+
log "Starting install of Swift ${SWIFT_VERSION_INPUT} Android Swift SDK"
638+
install_android_sdk
639+
fi
640+
521641
if [[ "$INSTALL_STATIC_LINUX" == true ]]; then
522642
log "Starting install of Swift ${SWIFT_VERSION_INPUT} Static Linux Swift SDK"
523643
install_static_linux_sdk
@@ -533,6 +653,31 @@ build() {
533653
# Enable alias expansion to use a 'swift' alias for the executable path
534654
shopt -s expand_aliases
535655

656+
if [[ "$INSTALL_ANDROID" == true ]]; then
657+
log "Running Swift build with Android Swift SDK"
658+
659+
local sdk_name="${ANDROID_SDK_TAG}${ANDROID_SDK_PATH_SEP}android${ANDROID_SDK_PATH_SUFFIX}"
660+
661+
alias swift='$SWIFT_EXECUTABLE_FOR_ANDROID_SDK'
662+
local build_command="$SWIFT_BUILD_COMMAND --swift-sdk ${ANDROID_SDK_TRIPLE:-$sdk_name}"
663+
if [[ -n "$SWIFT_BUILD_FLAGS" ]]; then
664+
build_command="$build_command $SWIFT_BUILD_FLAGS"
665+
fi
666+
667+
log "Running: $build_command"
668+
669+
# clear the ANDROID_NDK_ROOT environment variable if it is set
670+
# due to https://github.com/swiftlang/swift-driver/pull/1879
671+
# otherwise build error: missing required module 'SwiftAndroid'
672+
export ANDROID_NDK_ROOT=""
673+
674+
if eval "$build_command"; then
675+
log "✅ Swift build with Android Swift SDK completed successfully"
676+
else
677+
fatal "Swift build with Android Swift SDK failed"
678+
fi
679+
fi
680+
536681
if [[ "$INSTALL_STATIC_LINUX" == true ]]; then
537682
log "Running Swift build with Static Linux Swift SDK"
538683

0 commit comments

Comments
 (0)