From 12d5c80e5444a67c5ef1614a53e9671576922639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=8Ans=20Bernhardt?= Date: Sat, 9 May 2026 18:33:44 +0200 Subject: [PATCH 1/4] Add Android (aarch64) cross-compile support to SwiftTimecodeCore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing Linux/Glibc/Musl arms in the libc imports cover Linux but not Android, whose Swift SDK exposes its libc as the `Android` module (Bionic). Without an `#elseif canImport(Android)` arm, the imports fall through to no-import on Android and the unqualified `pow`/`powf`/`trunc` calls + the `ceiling` / `floor` qualified calls fail to resolve. Verified against the Swift 6.3 open-source toolchain (org.swift.630202603201a) with the Android Swift SDK artifact bundle (android28+) — `swift build --target SwiftTimecodeCore --swift-sdk aarch64-unknown-linux-android28` produces an ELF aarch64 .o. Also adds a GitHub Actions job that runs the same cross-compile in CI so future regressions surface immediately. Files changed: - Sources/SwiftTimecodeCore/Utilities/Outsourced/FloatingPoint and Darwin.swift: add canImport(Android) → import Android in 3 places (top imports, `ceiling`, `floor`). - Sources/SwiftTimecodeCore/Timecode/Protocol Adoptions/Strideable.swift: add canImport(Android) → import Android in the imports block. - .github/workflows/build.yml: new `android` job builds SwiftTimecodeCore for aarch64-android28 using vapor/swiftly-action to install Swift 6.3 + the Android SDK artifact bundle. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build.yml | 23 +++++++++++++++++++ .../Protocol Adoptions/Strideable.swift | 2 ++ .../Outsourced/FloatingPoint and Darwin.swift | 6 +++++ 3 files changed, 31 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b93a640..48c892a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,3 +116,26 @@ jobs: run: swift build - name: Unit Tests run: swift test + + android: + name: Build (Android) + runs-on: ubuntu-latest + timeout-minutes: 30 + # Cross-compiles SwiftTimecodeCore for Android (aarch64) using the Swift.org Android SDK + # bundle. Requires the open-source Swift 6.3 toolchain (NOT Xcode's bundled Swift) and the + # Android Swift SDK artifact bundle, which provides the cross-compiled stdlib + Foundation + # for android28+. Tests are not executed (would require a device/emulator); the build alone + # is enough to catch missing canImport(Android) arms or unconditional Apple-only imports. + steps: + - uses: actions/checkout@main + - name: Install Swift 6.3 toolchain (open-source) + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.3" + - name: Install Android Swift SDK + run: | + swift sdk install \ + https://download.swift.org/swift-6.3-release/android-sdk/swift-6.3-RELEASE/swift-6.3-RELEASE_android.artifactbundle.tar.gz \ + --checksum 2f2942c4bcea7965a08665206212c66991dabe23725aeec7c4365fc91acad088 + - name: Build for Android (aarch64) + run: swift build --target SwiftTimecodeCore --swift-sdk aarch64-unknown-linux-android28 diff --git a/Sources/SwiftTimecodeCore/Timecode/Protocol Adoptions/Strideable.swift b/Sources/SwiftTimecodeCore/Timecode/Protocol Adoptions/Strideable.swift index 40f22838..ff2325ac 100644 --- a/Sources/SwiftTimecodeCore/Timecode/Protocol Adoptions/Strideable.swift +++ b/Sources/SwiftTimecodeCore/Timecode/Protocol Adoptions/Strideable.swift @@ -10,6 +10,8 @@ import Darwin import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(Android) +import Android #endif extension Timecode: Strideable { diff --git a/Sources/SwiftTimecodeCore/Utilities/Outsourced/FloatingPoint and Darwin.swift b/Sources/SwiftTimecodeCore/Utilities/Outsourced/FloatingPoint and Darwin.swift index 6ad48d66..ae893624 100644 --- a/Sources/SwiftTimecodeCore/Utilities/Outsourced/FloatingPoint and Darwin.swift +++ b/Sources/SwiftTimecodeCore/Utilities/Outsourced/FloatingPoint and Darwin.swift @@ -15,6 +15,8 @@ import Darwin import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(Android) +import Android #endif // MARK: - ceiling / floor @@ -30,6 +32,8 @@ extension FloatingPoint { Glibc.ceil(self) #elseif canImport(Musl) Musl.ceil(self) + #elseif canImport(Android) + Android.ceil(self) #endif } @@ -43,6 +47,8 @@ extension FloatingPoint { Glibc.floor(self) #elseif canImport(Musl) Musl.floor(self) + #elseif canImport(Android) + Android.floor(self) #endif } } From 3211ebd259739850bf7f9507bab9367715c688d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=8Ans=20Bernhardt?= Date: Mon, 11 May 2026 10:34:42 +0200 Subject: [PATCH 2/4] CI: pin host swift toolchain to 6.3.0 to match Android SDK bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `vapor/swiftly-action@v0.2 toolchain: "6.3"` is a floating selector — when Swift 6.3.1 shipped, CI started picking that up and failing to load the Android SDK's pre-built `.swiftmodule` files (which were compiled by 6.3.0). Swift's stable ABI covers the runtime, not the swiftmodule binary format — that requires an exact-version compiler match. Pin to "6.3.0" to track the Android SDK artifactbundle (also pinned to 6.3-RELEASE). Bump both in lockstep when swift.org publishes a 6.3.1 or 6.4 Android SDK bundle (currently 404). --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48c892a9..f51e72b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -129,9 +129,15 @@ jobs: steps: - uses: actions/checkout@main - name: Install Swift 6.3 toolchain (open-source) + # Pinned to "6.3.0" rather than the floating "6.3" — the Android SDK artifactbundle + # (also pinned to 6.3-RELEASE) ships pre-built `.swiftmodule` files which can ONLY + # be loaded by the exact same compiler version. With "6.3" floating, swiftly picks + # up 6.3.1 once it's released, and CI breaks with: + # "module compiled with Swift 6.3 cannot be imported by the Swift 6.3.1 compiler" + # Bump this in lockstep when a 6.3.1 (or 6.4) Android SDK bundle is published. uses: vapor/swiftly-action@v0.2 with: - toolchain: "6.3" + toolchain: "6.3.0" - name: Install Android Swift SDK run: | swift sdk install \ From ce78c6c3f164205cd5f0d1805e302c6c8702fc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=8Ans=20Bernhardt?= Date: Mon, 11 May 2026 10:49:12 +0200 Subject: [PATCH 3/4] CI: install Android NDK r27d and run SDK setup script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous swift sdk install was incomplete — it only fetches Swift-side artifacts (stdlib, Foundation .swiftmodule files). C interop in SwiftOverlayShims needs the NDK's C sysroot (semaphore.h, libc, etc.), and the SDK bundle's destination JSON needs ANDROID_NDK_HOME baked in by the setup-android-sdk.sh script that ships with it. Without these, the build errors mid-compile with: 'semaphore.h' file not found … could not build C module 'SwiftOverlayShims' NDK r27d is paired with swift-6.3-RELEASE Android SDK — bump both together when swift.org publishes a newer SDK bundle. --- .github/workflows/build.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f51e72b3..03bb9f02 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,10 +138,33 @@ jobs: uses: vapor/swiftly-action@v0.2 with: toolchain: "6.3.0" + - name: Install Android NDK r27d + # `swift sdk install` only fetches the Swift-side stdlib + Foundation for Android. + # The NDK supplies the C sysroot (libc, libm, semaphore.h, …) that the SwiftOverlayShims + # C module needs to compile. Without these the cross-compile errors with + # 'semaphore.h' file not found … could not build C module 'SwiftOverlayShims' + # mid-build. r27d matches the swift-6.3-RELEASE Android SDK bundle's expected NDK + # version — bump in lockstep with the SDK URL above. + run: | + wget -q "https://dl.google.com/android/repository/android-ndk-r27d-linux.zip" -O /tmp/ndk.zip + unzip -q /tmp/ndk.zip -d /opt + echo "ANDROID_NDK_HOME=/opt/android-ndk-r27d" >> "$GITHUB_ENV" - name: Install Android Swift SDK run: | swift sdk install \ https://download.swift.org/swift-6.3-release/android-sdk/swift-6.3-RELEASE/swift-6.3-RELEASE_android.artifactbundle.tar.gz \ --checksum 2f2942c4bcea7965a08665206212c66991dabe23725aeec7c4365fc91acad088 + # The bundle ships a setup script that wires NDK_HOME paths into the SDK's + # destination JSON. Without running it, the compiler can find the Swift + # stdlib but not the NDK's C headers — yielding the SwiftOverlayShims error. + # On ubuntu-latest runners the bundle lands under ~/.config/swiftpm/swift-sdks/ + # (vs. ~/.swiftpm/swift-sdks/ in containerised runs). + SETUP_SCRIPT=$(find "$HOME/.config/swiftpm/swift-sdks" "$HOME/.swiftpm/swift-sdks" -name "setup-android-sdk.sh" 2>/dev/null | head -1) + if [ -z "$SETUP_SCRIPT" ]; then + echo "setup-android-sdk.sh not found. Searched paths:" + find "$HOME/.config/swiftpm" "$HOME/.swiftpm" 2>/dev/null | head -20 + exit 1 + fi + bash "$SETUP_SCRIPT" - name: Build for Android (aarch64) run: swift build --target SwiftTimecodeCore --swift-sdk aarch64-unknown-linux-android28 From 9718b197bde0b03a5132479f1fb011f7024286ea Mon Sep 17 00:00:00 2001 From: Steffan Andrews Date: Fri, 15 May 2026 17:59:32 -0700 Subject: [PATCH 4/4] Updated GitHub CI Android build job --- .github/workflows/build.yml | 50 +++++-------------------------------- 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03bb9f02..ba89f0e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,50 +121,12 @@ jobs: name: Build (Android) runs-on: ubuntu-latest timeout-minutes: 30 - # Cross-compiles SwiftTimecodeCore for Android (aarch64) using the Swift.org Android SDK - # bundle. Requires the open-source Swift 6.3 toolchain (NOT Xcode's bundled Swift) and the - # Android Swift SDK artifact bundle, which provides the cross-compiled stdlib + Foundation - # for android28+. Tests are not executed (would require a device/emulator); the build alone - # is enough to catch missing canImport(Android) arms or unconditional Apple-only imports. + env: + TARGET_TRIPLE: aarch64-unknown-linux-android28 steps: - uses: actions/checkout@main - - name: Install Swift 6.3 toolchain (open-source) - # Pinned to "6.3.0" rather than the floating "6.3" — the Android SDK artifactbundle - # (also pinned to 6.3-RELEASE) ships pre-built `.swiftmodule` files which can ONLY - # be loaded by the exact same compiler version. With "6.3" floating, swiftly picks - # up 6.3.1 once it's released, and CI breaks with: - # "module compiled with Swift 6.3 cannot be imported by the Swift 6.3.1 compiler" - # Bump this in lockstep when a 6.3.1 (or 6.4) Android SDK bundle is published. - uses: vapor/swiftly-action@v0.2 + - uses: orchetect/setup-swift-android-sdk@v1 with: - toolchain: "6.3.0" - - name: Install Android NDK r27d - # `swift sdk install` only fetches the Swift-side stdlib + Foundation for Android. - # The NDK supplies the C sysroot (libc, libm, semaphore.h, …) that the SwiftOverlayShims - # C module needs to compile. Without these the cross-compile errors with - # 'semaphore.h' file not found … could not build C module 'SwiftOverlayShims' - # mid-build. r27d matches the swift-6.3-RELEASE Android SDK bundle's expected NDK - # version — bump in lockstep with the SDK URL above. - run: | - wget -q "https://dl.google.com/android/repository/android-ndk-r27d-linux.zip" -O /tmp/ndk.zip - unzip -q /tmp/ndk.zip -d /opt - echo "ANDROID_NDK_HOME=/opt/android-ndk-r27d" >> "$GITHUB_ENV" - - name: Install Android Swift SDK - run: | - swift sdk install \ - https://download.swift.org/swift-6.3-release/android-sdk/swift-6.3-RELEASE/swift-6.3-RELEASE_android.artifactbundle.tar.gz \ - --checksum 2f2942c4bcea7965a08665206212c66991dabe23725aeec7c4365fc91acad088 - # The bundle ships a setup script that wires NDK_HOME paths into the SDK's - # destination JSON. Without running it, the compiler can find the Swift - # stdlib but not the NDK's C headers — yielding the SwiftOverlayShims error. - # On ubuntu-latest runners the bundle lands under ~/.config/swiftpm/swift-sdks/ - # (vs. ~/.swiftpm/swift-sdks/ in containerised runs). - SETUP_SCRIPT=$(find "$HOME/.config/swiftpm/swift-sdks" "$HOME/.swiftpm/swift-sdks" -name "setup-android-sdk.sh" 2>/dev/null | head -1) - if [ -z "$SETUP_SCRIPT" ]; then - echo "setup-android-sdk.sh not found. Searched paths:" - find "$HOME/.config/swiftpm" "$HOME/.swiftpm" 2>/dev/null | head -20 - exit 1 - fi - bash "$SETUP_SCRIPT" - - name: Build for Android (aarch64) - run: swift build --target SwiftTimecodeCore --swift-sdk aarch64-unknown-linux-android28 + target-triple: ${{ env.TARGET_TRIPLE }} + - name: Build + run: swift build --swift-sdk "$TARGET_TRIPLE" --static-swift-stdlib