Skip to content

Conversation

@marcprux
Copy link
Contributor

@marcprux marcprux commented Nov 1, 2025

This PR generates a SSDK4A going back to API 23 (down from 28). This is a result of discussions at https://forums.swift.org/t/android-api-minimum-for-the-swift-sdk-for-android/82874, and also see the development discussion around this at swift-android-sdk#16

The PR itself adds some light patching of FoundationEssentials (to remove posix_spawn_) and FoundationEssentials (to remove getgrgid_r and getgrnam_r). It also unified the application of patches into a single spot in scripts/fetch-source.sh (the previous patching of Testing was duplicated in two separate scripts, build-local and build-docker).

In terms of upstreaming the fixes versus the patching being performed here, we would ideally gate these functions on the #available(Android, …) support added in swiftlang/swift#84574. However, this requires NDK 28, and for the time being, we're supporting the LTS NDK 27 release.

* Add Android workflow

* Build Android image (#1)

* Build Android image

* Checkout without ssh

* Retry build if it fails

* Swift 6.1 Release Dockerfiles (swiftlang#456)

* Change binutils-gold package dependency on Debian 12 to binutils (swiftlang#457)

* Update installed packages after nightly platform expansion (swiftlang#458)

* update nightly-6.1 dependencies

* update nightly-main dependencies

* fix ubuntu images

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Fedora 41 Dockerfile (swiftlang#464)

* Build Android image

* Build Android image

* Swift 6.1 Release Dockerfiles (swiftlang#456)

* Change binutils-gold package dependency on Debian 12 to binutils (swiftlang#457)

* Build Android image

* Build Android image

* Build Android image

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Android SDK build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Androd build

* Swift Android build

* Swift Android build

* Swift Android build

* Swift Android build

---------

Co-authored-by: Mishal Shah <[email protected]>
Co-authored-by: Chris McGee <[email protected]>
Co-authored-by: Justice Adams <[email protected]>
Co-authored-by: Andrew Sukach <[email protected]>

* Swift Android build 6.2 (#2)

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Start splitting NDK out from the rest of the SDK

* Start splitting NDK out from the rest of the SDK

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2

* Swift Android build 6.2 (#3)

* Swift Android build 6.2

* Swift Android build 6.2

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Build SDK in Docker container (#4)

* Add static libraries to post-install script

* Add post-install script to SDK bundle

* Add post-install script to SDK bundle

* Update submodules

* Checkout patches repo instead of using a git submodule

* Update libcurl to 8.13.0

* Remove resources that we no longer use

* Update libcurl to 8.13.0

* Update libxml2 to 2.14.2

* Build libxml2, libcurl, and boringssl with support for Android 16kb page sizes

* Add build-script --extra-cmake-options=-DCMAKE_EXTRA_LINK_FLAGS=-Wl,-z,max-page-size=16384

* Add 16KB page size linker flags to linker flags in swift-toolset.json

* Add 16KB page size linker flags to linker flags in swift-toolset.json

* Build with ndk-r28b

* Revert to building with ndk-r27c

* Use official endpoints for discovering latest Swift release/devel/trunk tags

* Typo fix in version script

* Cleanup for PR

* Change BUILD_VERSION to BUILD_SCHEME and have it match release, swift-*-branch, or development

* Update Android README

* Update how patches are applied

* Fix source directory for patch target

* Harmonize timestamps in artifactbundle with the swift source tag date for build reproducibility

* Simplify toolchain-vars.sh

* Run the compiler validation suite for Android (#8)

* Run the compiler validation suite for Android

* Add --build-compiler option

* Add --cross-compile-build-swift-tools=0 from swiftlang/swift#38441

* Build with --build-llvm=0

* Check out Yams for Swift 6.1.1 build

* Add --llvm-ninja-targets-for-cross-compile-hosts=help

* Install pre-requisites

* Install build prerequisites

* Only setup local toolchain if build-compiler is not 0

* Fix Yams version checkout

* Quote arguments to build scripts

* Permit empty host-toolchain argument in build.sh

* Fix check for BUILD_COMPILER

* Re-order Docker PATH to system clang is used before Swift toolchain clang

* Use --host-test to skip attempt to test on connected device/emulator

* Install clang in Dockerfile

* Add --skip-test-linux flag to build

* Remove more folders to free up space

* Update patches

* Add docker-specific CI variants that run the compiler validation tests

* meaningless edit

* Update pull_request.yml to stop the 6.1 release builds

* Update pull_request.yml to really only build the full docker compiler with tests

* Update README.md with meaningless edit to bump build

* Update build.sh to skip testing XCTest for linux, as a handful of the linux tests fail for some reason

* Update build.sh to disable building libTesting and for 16K memory pages, as both don't work yet

* Update build.sh to skip testing Foundation for linux, which requires building SwiftPM from source first

* Update build.sh to not build each arch in a separate build directory

* Update Dockerfile to use clang 19 instead

* Add self-hosted runner CI variant

* Fix CI workflow syntax

* Update CI runner config

* Update CI runner config

* Fix name of self-hosted CI runner

* Re-order arch run sequence

* Update CI for self-hosted runner

* Build for self-hosted runner

* Add swift-6.2-branch to self-hosted run matrix

* Update build.sh to disable aarch64 temporarily, as we know it built fine

* Update pull_request.yml to disable non-compiler builds, as they all work, and comment out self-hosted till we know armv7 works

* Run compiler validation on self-hosted with increased timeout

* Update build-docker to only build for armv7

* Update pull_request.yml to disable self-hosted builds temporarily

* Update pull_request.yml to build the full compiler in github runners with the armv7 stdlib and tests

* Update build-docker to build for aarch64 first

* Update toolchain-vars.sh to use older trunk snapahot toolchain that didn't crash

* Update toolchain-vars.sh to only download the latest release compiler if building the Swift compiler from source

* Update build-docker to only build for armv7 again

* Update toolchain-vars.sh to fix setting branch variable

* Update build-docker to build for all three supported arches

* Update pull_request.yml to try self-hosted runs again

* Update build.sh to only install SDK components and remove linux stdlib

* Tolerate missing linux folder when attempting to clean up unnecessary build artifacts

* Fix extraCLIOptions in swift-toolset.json

* gcpdw

* Fix patch application

* Update apply-patches.sh to remove unused changes

* Update swift-android-testing-except-release.patch to add Testing fix

* Update swift-android.patch to remove unneeded patches

* Update build.sh to try and fix Testing and clean up the bundle more

* Fix README.md

---------

Co-authored-by: finagolfin <[email protected]>

* Build compiler-validated bundles from latest branch commits, not older tags

Also update patches, particularly to disable failing tests from the compiler validation suite

* gcpd 'Update patches'

* Update patches and build matrix

* Disable compiler validated builds on self-hosted

* Centralize cmake variable for 6.2 in patches, as done for trunk upstream

* Update disabled tests

* Try running Docker build on macOS host

* Remove separate checks of libxml2, curl, and yams

* Remove upstreamed 6.2 branch patches

* Use command-line flags to work around CMake 3.30+ linker flag bug, rather than patching files

* Create an Android CMake toolchain file instead to cross-compile Testing and add
16 KB page linker flag to 6.2 branch

* Install the native host LLVM tools for full compiler builds

* Extend `--cross-compile-build-swift-tools=False` to disable building and installing LLVM
and the Testing macros for cross-compiled hosts, then enable building and using the
Testing macros for the native host.

* Don't copy any libraries from the NDK: link the NDK's clang resource directory in the post-install script instead

* Wildcard the clang version link in the post-install script in order to accomodate NDK 27 and 28

* Switch generated shell header to use #!/usr/bin/env bash

* SBOM and nits (#14)

* Address nits

* Eliminate unnecessaryt pushds and use run cmake install instead of ninja

* Generate SBOM with SDK dependencies

* Add example of command for building locally

* Remove upstreamed patches and turn the last modifications into perl substitutions instead.

* Remove self-hosted build on github CI

* Remove android_build job from pull_request workflow

* Add license headers to Android build scripts

* Remove unnecessary .gitignore

* Add license header to Dockerfile

* Switch over to using explicit tags and branches when invoking build-docker/local

Also, force the full Swift compiler to be built from source when building from a
branch, since there are no existing toolchain builds of arbitrary commits that
we can download.

* Fix build script arguments to use false instead of False due to true_false not handling the capitalized form

* Build swift-6.2-RELEASE

* Apply patch from swiftlang/swift#84061 for 6.2.0 Android SDK release

* Update patch application for 6.2

* Change 6.2 to perform local build

* Change 6.2 to perform local build on ubuntu-24.04

* Switch back to building 6.2 from a tag

* Switch back to building 6.2 from a tag

* Switch back to building 6.2 from a tag

* Build the Swift 6.2 release self-hosted with compiler validation

* Set minimum Android API to 24

* Build against swift-DEVELOPMENT-SNAPSHOT-2025-10-16-

* Disable building libxml2 tests to handle missing glob function in Android 24

* Disable building libxml2 tests to handle missing glob function in Android 24

* Build this PR without compiler validation

* Build this PR without compiler validation

* Disable posix_spawnattr_ for Android 24

* Reduce Android API from 24 to 23

* Restore Android minimum API to 24

* Add swift-nio to tests cases being run

* Reduce Android API from 24 to 23

* Try building swift-collections

* Try testing against emulator API level 23

* Fix backtrace call

* Fail with an error for posix_spawn

* Fix error for posix_spawn

* Remove temporary swift-docker Android build workflow

---------

Co-authored-by: Mishal Shah <[email protected]>
Co-authored-by: Chris McGee <[email protected]>
Co-authored-by: Justice Adams <[email protected]>
Co-authored-by: Andrew Sukach <[email protected]>
Co-authored-by: finagolfin <[email protected]>
@marcprux marcprux requested a review from shahmishal as a code owner November 1, 2025 14:19
--products-dir "/products" \
--host-toolchain "/usr/local/swift" \
--build-compiler "${BUILD_COMPILER}" \
--android-api "${ANDROID_API}" \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The android-api is just set in the default in build.sh; we never override it from build-docker or build-local, so I removed it so where is just a single place where we define the default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the idea was that people using these scripts could pass in ANDROID_API and build the SDK for other API levels too.

perl -pi -g -we "s#(call rm ... \".\{LIBDISPATCH_BUILD_DIR\}\"\n(\s+)fi\n)#\1\2if [[ -d \"\\\${ANDROID_NDK}\" ]]; then call ln -sf \"\\\${SWIFT_BUILD_PATH}/lib/swift\" \"\\\${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib\"; fi#" swift/utils/build-script-impl

# fix optional result value in backtrace() call
perl -pi -e 's;.init\(clamping: addresses.count\)\)\);.init\(clamping: addresses.count\)\) ?? 0\);g' swift-testing/Sources/Testing/SourceAttribution/Backtrace.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this cause a problem? We're building fine everywhere without it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got an error when I initially tried it without the optional guard. Regardless, this whole line is being patched out anyway, so it could likely just be removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unusual, as both the official and my CI build fine without this patch.

@madsodgaard
Copy link

madsodgaard commented Nov 1, 2025

Thanks for tackling this! definitely will be mean a greater adoption possibility imo.

I still think we should also handle the FILE pointer issue, that is on API 23, as it is probably a common thing.
A lot of Swift code does this:

#if os(Android)
typealias CFilePointer = OpaquePointer
#else
typealias CFilePointer = UnsafeMutablePointer<FILE>
#endif

however, on API 23, the FILE struct is actually visible, so the above would not compile on API 23.

I "fixed" it by patching the NDK header, such that is also imports it as OpaquePointer on 23.

https://github.com/madsodgaard/swift-android-sdk/blob/6cf42e93b59ad95a62409f73cd7d0868ee74db7b/.github/workflows/sdks.yml#L171

@marcprux
Copy link
Contributor Author

marcprux commented Nov 1, 2025

I "fixed" it by patching the NDK header, such that is also imports it as OpaquePointer on 23.

We can't patch the NDK. Unlike @finagolfin's fork you are using, we don't bundle the NDK but rather make a link to the system's external NDK which I don't think we can/should mess with.

How was Foundation building against API 23 in my CI build without this patch? I think we assume CFilePointer = OpaquePointer throughout all of Foundation when os(Android). I was also able to build swift-system against aarch64-unknown-linux-android23.

@finagolfin
Copy link
Member

Yeah, we can't modify the NDK once it's external.

I do not think we should make the API 23 changes in this pull, until someone steps up to maintain APIs 23-27 on the CI, as I asked for in the forum five days ago.

Unless you want to handle that, @marcprux?

@marcprux
Copy link
Contributor Author

marcprux commented Nov 1, 2025

I do not think we should make the API 23 changes in this pull, until someone steps up to maintain APIs 23-27 on the CI, as I asked for in the forum five days ago.

I thought we felt it might be worthwhile to ship with support for API 23 while only "officially" supporting API 28+. In other words, the default CI will build against 28 by default, but individual packages can opt into building against a lower API level themselves (which is already supported as a customization option for the workflow).

Happy to continue discussing this either here or there, but my personal feeling is "why not?", especially if it helps other developers who might otherwise be forced to build and use their own bespoke SDK.

@madsodgaard
Copy link

madsodgaard commented Nov 1, 2025

@marcprux alright - that makes sense.

I am guessing the reason those two built fine is that they never make assumptions about the imported type and store it, like other libraries do. It's basically this issue in swift-log: apple/swift-log#374

The correct solution might be to add something upstream in the compiler or find a better implementation for libraries, with #available. I can take a look into it.

# Disable posix_spawnattr_* calls for Android API 23
perl -pi -e 's;try _throwIfPosixError\(posix_spawnattr_init;throw NSError\(domain: NSPOSIXErrorDomain, code: .init\(ENOEXEC\), userInfo: [ NSLocalizedFailureReasonErrorKey: "Process unavailable on Android" ]\) //try _throwIfPosixError\(posix_spawnattr_init;g' swift-corelibs-foundation/Sources/Foundation/Process.swift
perl -pi -e 's;try _throwIfPosixError\(posix_spawnattr_setflags;//try _throwIfPosixError\(posix_spawnattr_setflags;g' swift-corelibs-foundation/Sources/Foundation/Process.swift
perl -pi -e 's;posix_spawnattr_destroy;//posix_spawnattr_destroy;g' swift-corelibs-foundation/Sources/Foundation/Process.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should stub this out, as it will then change this behavior and fail the associated test when we start running them, even on API 28 and above where it works. Instead, if somebody wants to push this bundle back to API 23, they should add polyfills for these methods too, as there already are for most of the posix_spawn APIs in swift-corelibs-foundation.

@finagolfin
Copy link
Member

Happy to continue discussing this either here or there, but my personal feeling is "why not?", especially if it helps other developers who might otherwise be forced to build and use their own bespoke SDK.

The problem is that all compiled methods in the SDK are then built against those older Bionic functions from API 23 only, then run on all Android API devices, which I don't know how well it will work, as I have only supported API 24 for years now. If a couple of you want to commit to support and debugging those potential issues with older Android APIs 23-27 and adding polyfills for those few posix_spawn functions that were added later, we can consider it, as I said on the forum. If not, I think API 28 is old enough.

The correct solution might be to add something upstream in the compiler or find a better implementation for libraries, with #available. I can take a look into it.

Just thought of another option, maybe an API note to make them all opaque pointers, like these we already have?

@madsodgaard
Copy link

madsodgaard commented Nov 1, 2025

The problem is that all compiled methods in the SDK are then built against those older Bionic functions from API 23 only, then run on all Android API devices

Hm, I guess the compiled methods are built against the NDK, which we can control independently of the API version and that should not make a difference regardless of which Android version you deploy to, as the system libraries are dynamically linked on the device?

Just thought of another option, maybe an API note to make them all opaque pointers, like these we already have?

Yeah, good call, I thought of that as well. A lot of functions, but should do it. Would be easier if we could just tell the compiler to interpret the FILE struct as OpaquePointer on API 23 🤔

@finagolfin
Copy link
Member

Hm, I guess the compiled methods are built against the NDK, which we can control independently of the API version and that should not make a difference regardless of which Android version you deploy to, as the system libraries are dynamically linked on the device?

I don't think the NDK is relevant, but the API version built against does matter, because of all the Bionic APIs that were only added in later API levels. Obviously, the base set of APIs available in API 23 should all be fine, since Android is backward-compatible.

The problem is what replacements we put in when building this SDK against Bionic APIs that were only added in API 24 and later, eg the posix_spawn polyfills in swift-corelibs-foundation. Now that this SDK is built against API 28, we don't use those polyfills at all, whereas we have to rely on those instead if we build against API 23 or 24.

I have been building and more importantly running all the tests of this Android SDK against API 24 for years now, so I'm fairly confident with that level, just not API 23. If you all feel 23 is not a step too far, feel free to volunteer to support it then. 😉

Yeah, good call, I thought of that as well. A lot of functions, but should do it. Would be easier if we could just tell the compiler to interpret the FILE struct as OpaquePointer on API 23

Oh, you'd have to modify all functions that use it, not just the struct declaration itself? Yeah, clearly not worth it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants