Skip to content

Commit 4905c1c

Browse files
authored
Add enable_android_sdk_checks option for Android emulator testing (#215)
* Add enable_android_sdk_checks option for Android emulator testing * Update build flags * Pull request enable_android_sdk_checks: true * Fix script location * Fix script permissions * Download the Android SDK * Update the Android SDK * Install Android SDK using package manager * Update the Android SDK * Install Java * Install Java * Install commandline tools in correct directory * Update Android SDK * Update Android SDK * Update Android SDK * Use different Android emulator device * Update path to Android emulator * Update path to Android emulator * Update path to Android emulator * Fix check for JAVA_HOME * Install sudo * Better JAVA_HOME check * Try not enabling KVM * Try not enabling KVM * Fix install path for Android AVD * Try to install KVM * Try to install KVM * Just run tests on a single config * Try installing KVM * Try installing KVM * Try installing KVM * Try running emulator with -no-accel * Try running tests * Try running tests * Try running tests * Try freeing disk space * Try freeing disk space * Try freeing disk space * Try freeing disk space * Use x86_64-linux-android for test cases * Use x86_64-linux-android for test cases * Fix AVD partition size * Fix AVD partition size * Fix AVD partition size * Fix location of libc++_shared.so * Fix location of libc++_shared.so * Fix location of libc++_shared.so * Fix location of libc++_shared.so * Fix location of libc++_shared.so * Fix location of libc++_shared.so * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Fix location of Swift SDK for Android * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Set AVD partition size * Run without a docker container * Run without a docker container * Run without a docker container * Run without a docker container * Fix ANDROID_SDK_TRIPLE * Fix ANDROID_AVD_HOME * Fix ANDROID_AVD_HOME * Cleanup copy of test resources and executables * Cleanup copy of test resources and executables * Cleanup copy of test resources and executables * Break up build and test run into two separate steps * Add explicit --testing-library arg * Check with ANDROID_SDK_TRIPLES * Check with ANDROID_SDK_TRIPLES * Cleanup * Quote ANDROID_EMULATOR_TIMEOUT for shellcheck * Add log message about which triple is under test * Add warning when Package.swift contains macros * Fix syntax for shellcheck * Add input parameter description for android_sdk_triples clarifying that the last triple in the list is used for emulator testing
1 parent 0c89531 commit 4905c1c

File tree

3 files changed

+175
-26
lines changed

3 files changed

+175
-26
lines changed

.github/workflows/pull_request.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ jobs:
3737
wasm_sdk_pre_build_command: |
3838
cd tests/TestPackage
3939
enable_wasm_sdk_build: true
40-
# Android
41-
android_sdk_pre_build_command: |
42-
cd tests/TestPackage
43-
enable_android_sdk_build: true
4440
# Windows
4541
windows_build_command: |
4642
cd tests/TestPackage
@@ -56,7 +52,7 @@ jobs:
5652
# Android
5753
android_sdk_pre_build_command: |
5854
cd tests/TestPackage
59-
enable_android_sdk_build: true
55+
enable_android_sdk_checks: true
6056
# Windows
6157
windows_build_command: |
6258
cd tests/TestPackage
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/bin/bash
2+
##===----------------------------------------------------------------------===##
3+
##
4+
## This source file is part of the Swift.org open source project
5+
##
6+
## Copyright (c) 2025 Apple Inc. and the Swift project authors
7+
## Licensed under Apache License v2.0 with Runtime Library Exception
8+
##
9+
## See https://swift.org/LICENSE.txt for license information
10+
## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
11+
##
12+
##===----------------------------------------------------------------------===##
13+
14+
set -euo pipefail
15+
16+
log() { printf -- "** %s\n" "$*" >&2; }
17+
error() { printf -- "** ERROR: %s\n" "$*" >&2; }
18+
fatal() { error "$@"; exit 1; }
19+
20+
ANDROID_PROFILE="Nexus 10"
21+
ANDROID_EMULATOR_TIMEOUT=300
22+
23+
SWIFTPM_HOME="${XDG_CONFIG_HOME}"/swiftpm
24+
# e.g., "${SWIFTPM_HOME}"/swift-sdks/swift-DEVELOPMENT-SNAPSHOT-2025-12-11-a_android.artifactbundle/
25+
SWIFT_ANDROID_SDK_HOME=$(find "${SWIFTPM_HOME}"/swift-sdks -maxdepth 1 -name 'swift-*android.artifactbundle' | tail -n 1)
26+
27+
ANDROID_SDK_TRIPLE="x86_64-unknown-linux-android28"
28+
29+
while [[ $# -gt 0 ]]; do
30+
case $1 in
31+
--android-sdk-triple=*)
32+
ANDROID_SDK_TRIPLE="${1#*=}"
33+
shift
34+
;;
35+
--android-profile=*)
36+
ANDROID_PROFILE="${1#*=}"
37+
shift
38+
;;
39+
--android-emulator-timeout=*)
40+
ANDROID_EMULATOR_TIMEOUT="${1#*=}"
41+
shift
42+
;;
43+
-*)
44+
fatal "Unknown option: $1"
45+
;;
46+
*)
47+
if [[ -z "$SWIFT_VERSION_INPUT" ]]; then
48+
SWIFT_VERSION_INPUT="$1"
49+
else
50+
fatal "Multiple Swift versions specified: $SWIFT_VERSION_INPUT and $1"
51+
fi
52+
shift
53+
;;
54+
esac
55+
done
56+
57+
# extract the API level from the end of the triple
58+
ANDROID_API="${ANDROID_SDK_TRIPLE/*-unknown-linux-android/}"
59+
60+
# extract the build arch from the beginning of the triple
61+
ANDROID_EMULATOR_ARCH="${ANDROID_SDK_TRIPLE/-unknown-linux-android*/}"
62+
63+
# x86_64=x86_64, armv7=arm
64+
ANDROID_EMULATOR_ARCH_TRIPLE="${ANDROID_EMULATOR_ARCH}"
65+
66+
log "Running tests for ${ANDROID_SDK_TRIPLE}"
67+
68+
EMULATOR_SPEC="system-images;android-${ANDROID_API};default;${ANDROID_EMULATOR_ARCH}"
69+
70+
log "SWIFT_ANDROID_SDK_HOME=${SWIFT_ANDROID_SDK_HOME}"
71+
72+
# install and start an Android emulator
73+
log "Listing installed Android SDKs"
74+
export PATH="${PATH}:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/build-tools/latest:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin"
75+
sdkmanager --list_installed
76+
77+
log "Updating Android SDK licenses"
78+
yes | sdkmanager --licenses > /dev/null || true
79+
80+
log "Installing Android emulator"
81+
sdkmanager --install "emulator" "platform-tools" "platforms;android-${ANDROID_API}" "${EMULATOR_SPEC}"
82+
83+
log "Creating Android emulator"
84+
export ANDROID_AVD_HOME=${XDG_CONFIG_HOME:-$HOME}/.android/avd
85+
ANDROID_EMULATOR_NAME="swiftemu"
86+
avdmanager create avd --force -n "${ANDROID_EMULATOR_NAME}" --package "${EMULATOR_SPEC}" --device "${ANDROID_PROFILE}"
87+
88+
log "Configuring Android emulators"
89+
emulator -list-avds
90+
91+
log "Check Hardware Acceleration (KVM)"
92+
emulator -accel-check
93+
94+
log "Starting Android emulator"
95+
# launch the emulator in the background
96+
nohup emulator -no-metrics -partition-size 1024 -memory 4096 -wipe-data -no-window -no-snapshot -noaudio -no-boot-anim -avd "${ANDROID_EMULATOR_NAME}" &
97+
98+
log "Waiting for Android emulator startup"
99+
timeout "${ANDROID_EMULATOR_TIMEOUT}" adb wait-for-any-device
100+
101+
log "Prepare Swift test package"
102+
# create a staging folder where we copy the test executable
103+
# and all the dependent libraries to copy over to the emulator
104+
STAGING_DIR="swift-android-test"
105+
rm -rf "${STAGING_DIR}"
106+
mkdir "${STAGING_DIR}"
107+
108+
BUILD_DIR=.build/"${ANDROID_SDK_TRIPLE}"/debug
109+
110+
find "${BUILD_DIR}" -name '*.xctest' -exec cp -av {} "${STAGING_DIR}" \;
111+
find "${BUILD_DIR}" -name '*.resources' -exec cp -av {} "${STAGING_DIR}" \;
112+
113+
# copy over the required library dependencies
114+
cp -av "${SWIFT_ANDROID_SDK_HOME}"/swift-android/swift-resources/usr/lib/swift-"${ANDROID_EMULATOR_ARCH_TRIPLE}"/android/*.so "${STAGING_DIR}"
115+
cp -av "${SWIFT_ANDROID_SDK_HOME}"/swift-android/ndk-sysroot/usr/lib/"${ANDROID_EMULATOR_ARCH_TRIPLE}"-linux-android/libc++_shared.so "${STAGING_DIR}"
116+
117+
# for the common case of tests referencing
118+
# their own files as hardwired paths instead of resources
119+
if [[ -d Tests ]]; then
120+
cp -a Tests "${STAGING_DIR}"
121+
fi
122+
123+
# warn about macros in packages, as per
124+
# https://github.com/swiftlang/github-workflows/pull/215#discussion_r2621335245
125+
! grep -lq '\.macro(' Package.swift || log "WARNING: Packages with macros are known to have issues with cross-compilation: https://github.com/swiftlang/swift-package-manager/issues/8094"
126+
127+
log "Copy Swift test package to emulator"
128+
129+
ANDROID_TMP_FOLDER="/data/local/tmp/${STAGING_DIR}"
130+
adb push "${STAGING_DIR}" "${ANDROID_TMP_FOLDER}"
131+
132+
TEST_CMD="./*.xctest"
133+
TEST_SHELL="cd ${ANDROID_TMP_FOLDER}"
134+
TEST_SHELL="${TEST_SHELL} && ${TEST_CMD} --testing-library xctest"
135+
136+
# Run test cases a second time with the Swift Testing library
137+
# We additionally need to handle the special exit code
138+
# EXIT_NO_TESTS_FOUND (69 on Android), which can happen
139+
# when the tests link to Testing, but no tests are executed
140+
# see: https://github.com/swiftlang/swift-package-manager/blob/main/Sources/Commands/SwiftTestCommand.swift#L1571
141+
TEST_SHELL="${TEST_SHELL} && ${TEST_CMD} --testing-library swift-testing && [ \$? -eq 0 ] || [ \$? -eq 69 ]"
142+
143+
log "Run Swift package tests"
144+
145+
# run the test executable
146+
adb shell "${TEST_SHELL}"

.github/workflows/swift_package_test.yml

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ on:
149149
default: "swift build"
150150
android_sdk_triples:
151151
type: string
152-
description: "The triples to use when building with the Swift SDK for Android"
152+
description: "The triples to use when building with the Swift SDK for Android. The final triple in the list will be used for the emulator testing and should match the host architecture."
153153
default: "[\"aarch64-unknown-linux-android28\", \"x86_64-unknown-linux-android28\"]"
154154
android_ndk_versions:
155155
type: string
@@ -198,6 +198,10 @@ on:
198198
type: boolean
199199
description: "Boolean to enable building with the Swift SDK for Android. Defaults to false"
200200
default: false
201+
enable_android_sdk_checks:
202+
type: boolean
203+
description: "Boolean to enable testing with the Swift SDK for Android. Defaults to false"
204+
default: false
201205
enable_macos_checks:
202206
type: boolean
203207
description: "Boolean to enable macOS testing. Defaults to false"
@@ -598,45 +602,29 @@ jobs:
598602
${{ steps.script_path.outputs.root }}/.github/workflows/scripts/install-and-build-with-sdk.sh --embedded-wasm --flags="$BUILD_FLAGS" ${{ matrix.swift_version }}
599603
600604
android-sdk-build:
601-
name: Swift SDK for Android Build (${{ matrix.swift_version }} - ${{ matrix.os_version }} - NDK ${{ matrix.ndk_version }})
605+
name: Swift SDK for Android Build (${{ matrix.swift_version }} - NDK ${{ matrix.ndk_version }})
602606
runs-on: ubuntu-latest
603607
strategy:
604608
fail-fast: false
605609
matrix:
606610
swift_version: ${{ fromJson(inputs.android_sdk_versions) }}
607611
ndk_version: ${{ fromJson(inputs.android_ndk_versions) }}
608-
os_version: ${{ fromJson(inputs.linux_os_versions) }}
609612
exclude:
610613
- ${{ fromJson(inputs.android_exclude_swift_versions) }}
611-
- ${{ fromJson((!inputs.enable_android_sdk_build && inputs.android_sdk_versions) || '[]') }}
612-
- ${{ fromJson((!inputs.enable_android_sdk_build && inputs.android_ndk_versions) || '[]') }}
613-
- ${{ fromJson((!inputs.enable_android_sdk_build && inputs.linux_os_versions) || '[]') }}
614-
container:
615-
image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }}
614+
- ${{ fromJson((!(inputs.enable_android_sdk_build || inputs.enable_android_sdk_checks) && inputs.android_sdk_versions) || '[]') }}
615+
- ${{ fromJson((!(inputs.enable_android_sdk_build || inputs.enable_android_sdk_checks) && inputs.android_ndk_versions) || '[]') }}
616616
steps:
617617
- name: Swift version
618618
run: swift --version
619619
- name: Clang version
620620
run: clang --version
621621
- name: Checkout repository
622622
uses: actions/checkout@v4
623-
if: ${{ matrix.os_version != 'amazonlinux2' }}
624-
- name: Checkout repository
625-
uses: actions/checkout@v1
626-
if: ${{ matrix.os_version == 'amazonlinux2' }}
627623
- name: Checkout swiftlang/github-workflows repository
628-
if: ${{ matrix.os_version != 'amazonlinux2' && github.repository != 'swiftlang/github-workflows' }}
629624
uses: actions/checkout@v4
630625
with:
631626
repository: swiftlang/github-workflows
632627
path: github-workflows
633-
- name: Checkout swiftlang/github-workflows repository
634-
if: ${{ matrix.os_version == 'amazonlinux2' && github.repository != 'swiftlang/github-workflows' }}
635-
uses: actions/checkout@v1
636-
with:
637-
repository: swiftlang/github-workflows
638-
path: ${{ github.event.repository.name }}/github-workflows
639-
ref: main
640628
- name: Determine script-root path
641629
id: script_path
642630
run: |
@@ -660,10 +648,29 @@ jobs:
660648
run: ${{ inputs.linux_pre_build_command }}
661649
- name: Install Swift SDK for Android and build
662650
env:
663-
BUILD_FLAGS: ${{ (contains(matrix.swift_version, 'nightly') && inputs.swift_nightly_flags) || inputs.swift_flags }}
651+
BUILD_FLAGS: ${{ inputs.enable_android_sdk_checks && '--build-tests' }} ${{ (contains(matrix.swift_version, 'nightly') && inputs.swift_nightly_flags) || inputs.swift_flags }}
652+
shell: bash
664653
run: |
665654
${{ inputs.android_sdk_pre_build_command }}
666655
${{ steps.script_path.outputs.root }}/.github/workflows/scripts/install-and-build-with-sdk.sh --android --flags="$BUILD_FLAGS" --build-command="${{ inputs.android_sdk_build_command }}" --android-sdk-triple=${{ join(fromJson(inputs.android_sdk_triples), ' --android-sdk-triple=') }} --android-ndk-version="${{ matrix.ndk_version }}" ${{ matrix.swift_version }}
656+
- name: Enable KVM and free disk space
657+
if: ${{ inputs.enable_android_sdk_checks }}
658+
run: |
659+
# enable KVM on Linux for tests, else error on emulator launch:
660+
# CPU acceleration status: This user doesn't have permissions to use KVM (/dev/kvm).
661+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
662+
sudo udevadm control --reload-rules
663+
sudo udevadm trigger --name-match=kvm
664+
665+
# need to free space or the emulator runs out
666+
sudo rm -rf /opt/microsoft /opt/google /opt/az /opt/ghc /usr/share/dotnet /usr/local/share/boost /opt/hostedtoolcache /usr/local/share/chromium
667+
df -h
668+
- name: Install Android Emulator and run tests
669+
if: ${{ inputs.enable_android_sdk_checks }}
670+
shell: bash
671+
run: |
672+
${{ inputs.android_sdk_pre_build_command }}
673+
${{ steps.script_path.outputs.root }}/.github/workflows/scripts/android/android-emulator-tests.sh --android-sdk-triple=${{ join(fromJson(inputs.android_sdk_triples), ' --android-sdk-triple=') }}
667674
668675
windows-build:
669676
name: Windows (${{ matrix.swift_version }} - ${{ matrix.os_version }})

0 commit comments

Comments
 (0)