Skip to content

Commit aa00f19

Browse files
committed
Update doc to Swift 6 and NDK 27b and remove the now unused destination config files
1 parent db148b2 commit aa00f19

File tree

4 files changed

+93
-171
lines changed

4 files changed

+93
-171
lines changed

README.md

Lines changed: 93 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,78 @@
1-
# Swift cross-compilation SDK for Android
1+
# Swift cross-compilation SDK bundle for Android
22

3-
The patch used to build this SDK is open source and listed below. I
3+
The patches used to build this SDK bundle are open source and listed below. I
44
maintain [a daily CI on github Actions](https://github.com/finagolfin/swift-android-sdk/actions?query=event%3Aschedule)
5-
that [cross-compiles the SDK from the release and development source branches of
5+
that [cross-compiles the SDK bundle from the release and development source branches of
66
the Swift toolchain for AArch64, armv7, and x86_64, builds several Swift
77
packages against those SDKs, and then runs their tests in the Android x86_64
88
emulator](https://github.com/finagolfin/swift-android-sdk/blob/main/.github/workflows/sdks.yml).
99

10-
Now that Swift 5.10 supports [the new SDK bundle
11-
format](https://github.com/apple/swift-evolution/blob/main/proposals/0387-cross-compilation-destinations.md),
12-
I plan to distribute an Android SDK bundle in the coming months.
10+
## Cross-compiling and testing Swift packages with the Android SDK buhdle
1311

14-
## Cross-compiling and testing Swift packages with the Android SDK
15-
16-
To build with the Swift 5.10 SDK, first download [the latest Android LTS NDK
17-
27](https://developer.android.com/ndk/downloads) and [Swift 5.10.1
18-
compiler](https://swift.org/download/#releases) (make sure to install the Swift
19-
compiler's dependencies linked there). Unpack these archives and the SDK.
20-
21-
Change the symbolic link at `swift-5.10-android-24-sdk/usr/lib/swift/clang`
22-
to point to the clang headers that come with your swift compiler, eg
12+
To build with the Swift 6 SDK bundle, first download [the official open-source
13+
Swift 6.0.1 toolchain for linux or macOS](https://swift.org/download/#releases)
14+
(make sure to install the Swift dependencies linked there). Install the OSS
15+
toolchain on macOS as detailed in [the instructions for using the static linux
16+
Musl SDK bundle at swift.org](https://www.swift.org/documentation/articles/static-linux-getting-started.html).
17+
On linux, simply download the toolchain, unpack it, and add it to your `PATH`.
2318

19+
Next, install the Android SDK bundle by having the Swift toolchain directly
20+
download it:
2421
```
25-
ln -sf /home/yourname/swift-5.10.1-RELEASE-ubuntu22.04/usr/lib/clang/15.0.0
26-
swift-5.10-android-24-sdk/usr/lib/swift/clang
22+
swift sdk install https://github.com/finagolfin/swift-android-sdk/releases/download/6.0.1/swift-6.0.1-RELEASE-android-24-0.1.artifactbundle.tar.gz --checksum 28d019e91902681e04bf62b9535888441aa7c0cc96902940964013f29020d100
2723
```
24+
You can check if it was properly installed by running `swift sdk list`.
2825

29-
Next, modify the cross-compilation JSON file `android-aarch64.json` in this repo
30-
similarly:
31-
32-
1. All paths to the NDK should change from `/home/finagolfin/android-ndk-r27`
33-
to the path to your NDK, `/home/yourname/android-ndk-r27`.
34-
35-
2. The path to the compiler should change from `/home/finagolfin/swift-5.10.1-RELEASE-ubuntu22.04`
36-
to the path to your Swift compiler, `/home/yourname/swift-5.10.1-RELEASE-ubi9`.
37-
38-
3. The paths to the Android SDK should change from `/home/finagolfin/swift-5.10-android-24-sdk`
39-
to the path where you unpacked the Android SDK, `/home/yourname/swift-5.10-android-24-sdk`.
40-
41-
Now you're ready to cross-compile a Swift package with the cross-compilation
42-
configuration JSON file, `android-aarch64.json`, and run its tests on Android.
26+
Now you're ready to cross-compile a Swift package and run its tests on Android.
4327
I'll demonstrate with the swift-argument-parser package:
4428
```
4529
git clone --depth 1 https://github.com/apple/swift-argument-parser.git
4630
4731
cd swift-argument-parser/
4832
49-
/home/yourname/swift-5.10.1-RELEASE-ubuntu22.04/usr/bin/swift build --build-tests
50-
--destination ~/swift-android-sdk/android-aarch64.json
51-
-Xlinker -rpath -Xlinker \$ORIGIN/swift-5.10-android-24-sdk/usr/lib/aarch64-linux-android
33+
swift build --build-tests --swift-sdk aarch64-unknown-linux-android24
5234
```
53-
This will cross-compile the package for Android aarch64 and produce a test
54-
runner executable with the `.xctest` extension, in this case at
35+
This will cross-compile the package for Android aarch64 at API 24 and produce a
36+
test runner executable with the `.xctest` extension, in this case at
5537
`.build/aarch64-unknown-linux-android24/debug/swift-argument-parserPackageTests.xctest`.
56-
It adds a rpath for where it expects the SDK libraries to be relative to the
57-
test runner when run on Android.
5838

5939
Sometimes the test runner will depend on additional files or executables: this
60-
one depends on the example executables `generate-manual`, `math`, `repeat`, and
61-
`roll` in the same build directory. Other packages use `#file` to point at test
62-
data in the repo: I've had success moving this data with the test runner, after
63-
modifying the test source so it has the path to this test data in the Android
64-
test environment. See the example of [swift-crypto on the
65-
CI](https://github.com/finagolfin/swift-android-sdk/blob/5.10/.github/workflows/sdks.yml#L317).
66-
67-
You can copy these executables and the SDK to [an emulator or a USB
68-
debugging-enabled device with adb](https://github.com/apple/swift/blob/release/5.10/docs/Android.md#3-deploying-the-build-products-to-the-device),
40+
one depends on the example executables `color`, `generate-manual`, `math`,
41+
`repeat`, and `roll` in the same build directory. Other packages use `#file` to
42+
point at test data in the repo: I've had success moving this data with the test
43+
runner, after modifying the test source so it has the path to this test data in
44+
the Android test environment. See the example of [swift-crypto on the
45+
CI](https://github.com/finagolfin/swift-android-sdk/blob/6.0.1/.github/workflows/sdks.yml#L492).
46+
47+
You can copy these executables and the Swift runtime libraries to [an emulator
48+
or a USB debugging-enabled device with adb](https://github.com/swiftlang/swift/blob/release/6.0/docs/Android.md#3-deploying-the-build-products-to-the-device),
6949
or put them on an Android device with [a terminal emulator app like Termux](https://termux.dev/en/).
7050
I test aarch64 with Termux so I'll show how to run the test runner there, but
71-
the process is similar with adb, [as can be seen on the CI](https://github.com/finagolfin/swift-android-sdk/blob/5.10/.github/workflows/sdks.yml#L355).
51+
the process is similar with adb, [as can be seen on the CI](https://github.com/finagolfin/swift-android-sdk/blob/6.0.1/.github/workflows/sdks.yml#L440).
7252

73-
Copy the test executables to the same directory as the SDK:
53+
Copy the test executables to the same directory as the Swift 6 runtime libraries,
54+
removing a few Android stub libraries that aren't needed:
7455
```
75-
cp .build/aarch64-unknown-linux-android24/debug/{swift-argument-parserPackageTests.xctest,generate-manual,math,repeat,roll} ..
56+
cp .build/aarch64-unknown-linux-android24/debug/{swift-argument-parserPackageTests.xctest,color,generate-manual,math,repeat,roll} ..
57+
cp ~/.swiftpm/swift-sdks/swift-6.0.1-RELEASE-android-24-0.1.artifactbundle/swift-6.0.1-release-android-24-sdk/android-27b-sysroot/usr/lib/aarch64-linux-android/24/lib*.so ..
58+
rm ../lib{c,dl,log,m,z}.so
7659
```
77-
You can copy the SDK and test executables to Termux using scp from OpenSSH, run
78-
these commands in Termux on the Android device:
60+
You can copy the test executables and Swift 6 runtime libraries to Termux using
61+
scp from OpenSSH, run these commands in Termux on the Android device:
7962
```
8063
uname -m # check if you're running on the right architecture, should say `aarch64`
8164
cd # move to the Termux app's home directory
8265
pkg install openssh
8366
84-
scp [email protected]:{swift-5.10-android-24-sdk.tar.xz,
85-
swift-argument-parserPackageTests.xctest,generate-manual,math,repeat,roll} .
86-
87-
tar xf swift-5.10-android-24-sdk.tar.xz
67+
scp [email protected]:{lib*.so,swift-argument-parserPackageTests.xctest,color,generate-manual,math,repeat,roll} .
8868
8969
./swift-argument-parserPackageTests.xctest
9070
```
9171
I've tried several Swift packages, including some mostly written in C or C++,
92-
and all the cross-compiled tests passed.
72+
and all the cross-compiled tests passed. Note that while this SDK bundle is
73+
compiled against Android API 24, there was a regression in Swift 6 so that
74+
Foundation can only be run on Android API 29 or later, #175. I will update the
75+
SDK bundle when I find a fix for that new issue.
9376

9477
You can even run armv7 tests on an aarch64 device, though Termux may require
9578
running `unset LD_PRELOAD` before invoking an armv7 test runner on aarch64.
@@ -99,28 +82,25 @@ mode.
9982

10083
## Porting Swift packages to Android
10184

102-
The most commonly needed change is to simply import Glibc for Android too (while
103-
Bionic is the name of the C library on Android, currently Swift uses the name
104-
`Glibc` as a placeholder for most non-Darwin, non-Windows C libraries), so add
105-
or change these two lines for Android:
85+
The most commonly needed change is to import the new Android overlay, so add
86+
these two lines for Android when calling Android's C APIs:
10687
```
107-
#if canImport(Glibc)
108-
import Glibc
88+
#if canImport(Android)
89+
import Android
10990
```
110-
For example, that is all I had to do [to port swift-argument-parser to
111-
Android](https://github.com/apple/swift-argument-parser/pull/14/files).
112-
113-
You may also need to add some Android-specific support using `#if os(Android)`,
91+
You may also need to add some Android-specific support using `#if canImport(Android)`,
11492
for example, since FILE is an opaque struct since Android 7, you will [have to
115-
refer to any FILE pointers like this](https://github.com/apple/swift-tools-support-core/pull/243/files):
93+
refer to any FILE pointers like this](https://github.com/swiftlang/swift-tools-support-core/pull/243/files):
11694
```
117-
#if os(Android)
95+
#if canImport(Android)
11896
typealias FILEPointer = OpaquePointer
11997
```
98+
Those changes are all I had to do [to port swift-argument-parser to
99+
Android](https://github.com/apple/swift-argument-parser/pull/651/files).
120100

121101
## Building an Android app with Swift
122102

123-
Some people have reported an issue with using the libraries from this SDK in
103+
Some people have reported an issue with using previous libraries from this SDK in
124104
their Android app, that the Android toolchain strips `libdispatch.so` and
125105
complains that it has an `empty/missing DT_HASH/DT_GNU_HASH`. You can [work
126106
around this issue by adding the following to your `build.gradle`](https://github.com/finagolfin/swift-android-sdk/issues/67#issuecomment-1227460068):
@@ -132,39 +112,34 @@ packagingOptions {
132112
}
133113
```
134114

135-
Note that the FoundationNetworking and FoundationXML libraries won't actually
136-
run on Android with this SDK, as their dependencies libcurl and libxml2 have other
137-
library dependencies that aren't included. If you want to use either of these
138-
separate Foundation libraries, you will have to track down those other libcurl/xml2
139-
dependencies and include them yourself.
140-
141-
## Building the Android SDKs from source
115+
## Building an Android SDK from source
142116

143-
Download the Swift 5.10.1 compiler and Android NDK 27 as above. Check out this
144-
repo and run
145-
`SWIFT_TAG=swift-5.10.1-RELEASE ANDROID_ARCH=aarch64 swift get-packages-and-swift-source.swift`
146-
to get some prebuilt Android libraries and the Swift source to build the SDK. If
147-
you pass in a different tag like `swift-DEVELOPMENT-SNAPSHOT-2024-03-30-a`
117+
Download the Swift 6.0.1 compiler as above and Android NDK 27b (only building
118+
the Android SDKs on linux works for now). Check out this repo and run
119+
`SWIFT_TAG=swift-6.0.1-RELEASE ANDROID_ARCH=aarch64 swift get-packages-and-swift-source.swift`
120+
to get some prebuilt Android libraries and the Swift source to build an AArch64
121+
SDK. If you pass in a different tag like `swift-DEVELOPMENT-SNAPSHOT-2024-10-08-a`
148122
for the latest Swift trunk snapshot and pass in the path to the corresponding
149123
prebuilt Swift toolchain to `build-script` below, you can build a Swift trunk
150124
SDK too, as seen on the CI.
151125

152126
Next, apply a patch to the Swift source, `swift-android.patch` from this repo,
153-
which adds a dependency for the Foundation core library in this Android SDK, and
154-
four more patches that make modifications for the nullability annotations newly
155-
added in NDK 26:
127+
plus three more patches that make modifications for NDK 27 and [the Foundation
128+
rewrite in Swift 6 that was merged this summer](https://www.swift.org/blog/foundation-preview-now-available/)
129+
and substitute a string for NDK 27:
156130
```
157-
git apply swift-android.patch swift-android-both-ndks.patch swift-android-foundation-ndk26.patch swift-android-stdlib-ndk26.patch swift-android-stdlib-except-trunk.patch
131+
git apply swift-android.patch swift-android-foundation.patch swift-android-foundation-release.patch swift-android-foundation-except-trunk.patch
132+
perl -pi -e 's%r26%r27%' swift/stdlib/cmake/modules/AddSwiftStdlib.cmake
158133
```
159134

160-
After making sure [needed build tools like python 3, CMake, and ninja](https://github.com/apple/swift/blob/release/5.10/docs/HowToGuides/GettingStarted.md#linux)
135+
After making sure [needed build tools like python 3, CMake, and ninja](https://github.com/swiftlang/swift/blob/release/6.0/docs/HowToGuides/GettingStarted.md#linux)
161136
are installed, run the following `build-script` command with your local paths
162137
substituted instead:
163138
```
164139
./swift/utils/build-script -RA --skip-build-cmark --build-llvm=0 --android
165-
--android-ndk /home/finagolfin/android-ndk-r27/ --android-arch aarch64 --android-api-level 24
166-
--build-swift-tools=0 --native-swift-tools-path=/home/finagolfin/swift-5.10.1-RELEASE-ubuntu22.04/usr/bin/
167-
--native-clang-tools-path=/home/finagolfin/swift-5.10.1-RELEASE-ubuntu22.04/usr/bin/
140+
--android-ndk /home/finagolfin/android-ndk-r27b/ --android-arch aarch64 --android-api-level 24
141+
--build-swift-tools=0 --native-swift-tools-path=/home/finagolfin/swift-6.0.1-RELEASE-ubuntu22.04/usr/bin/
142+
--native-clang-tools-path=/home/finagolfin/swift-6.0.1-RELEASE-ubuntu22.04/usr/bin/
168143
--host-cc=/usr/bin/clang-13 --host-cxx=/usr/bin/clang++-13
169144
--cross-compile-hosts=android-aarch64 --cross-compile-deps-path=/home/finagolfin/swift-release-android-aarch64-24-sdk
170145
--skip-local-build --xctest --swift-install-components='clang-resource-dir-symlink;license;stdlib;sdk-overlay'
@@ -176,63 +151,61 @@ Make sure you have an up-to-date CMake and not something old like 3.16. The
176151
`--host-cc` and `--host-cxx` flags are not needed if you have a `clang` and
177152
`clang++` in your `PATH` already, but I don't and they're unused for this build
178153
anyway but required by `build-script`. Substitute armv7 or x86_64 for aarch64
179-
into these commands to build for those architectures instead.
154+
into these commands to build SDKs for those architectures instead.
180155

181156
Finally, copy `libc++_shared.so` from the NDK and modify the cross-compiled
182-
`libdispatch.so` and Swift corelibs to include `$ORIGIN` and other relative
183-
directories in their rpaths:
157+
Swift corelibs to include `$ORIGIN` and other relative directories in their rpaths:
184158
```
185-
cp /home/yourname/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so swift-release-android-aarch64-24-sdk/usr/lib
159+
cp /home/yourname/android-ndk-r27b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so swift-release-android-aarch64-24-sdk/usr/lib
186160
patchelf --set-rpath \$ORIGIN/../..:\$ORIGIN swift-release-android-aarch64-24-sdk/usr/lib/swift/android/lib*.so
187161
```
188162

189163
Here is a description of what the above Swift script is doing:
190164

191165
This prebuilt SDK was compiled against Android API 24, because the Swift
192-
stdlib and corelibs require some libraries like libicu, that are pulled from the
166+
Foundation libraries require some libraries like libcurl, that are pulled from the
193167
prebuilt library packages used by the Termux app, which are built against Android
194-
API 24. Specifically, it downloads the libicu, libicu-static, libandroid-spawn,
195-
libcurl, and libxml2 packages from the [Termux package
168+
API 24. Specifically, it downloads the libandroid-spawn, libcurl, and libxml2
169+
packages and their handful of dependencies from the [Termux package
196170
repository](https://packages.termux.dev/apt/termux-main/pool/main/).
197171

198-
Each one is unpacked with `ar x libicu_74.2_aarch64.deb; tar xf data.tar.xz` and
172+
Each one is unpacked with `ar x libcurl_8.10.1-1_aarch64.deb; tar xf data.tar.xz` and
199173
the resulting files moved to a newly-created Swift release SDK directory:
200174
```
201175
mkdir swift-release-android-aarch64-24-sdk
202176
mv data/data/com.termux/files/usr swift-release-android-aarch64-24-sdk
203177
```
204178
It removes two config scripts in `usr/bin`, runs `patchelf` to remove the
205-
Termux rpath from all Termux shared libraries, and modifies the ICU libraries
206-
to get rid of the versioning and symlinks (three libicu libraries are removed
207-
since they're unused by Swift):
179+
Termux rpath from all Termux shared libraries, removes some unused libraries
180+
and config files, and modifies the libraries to get rid of the versioning and
181+
symlinks, which can't always be used on Android:
208182
```
209183
rm swift-release-android-aarch64-24-sdk/usr/bin/*-config
210184
cd swift-release-android-aarch64-24-sdk/usr/lib
211185
212-
rm libicu{io,test,tu}*
213-
patchelf --set-rpath \$ORIGIN libandroid-spawn.so libcurl.so libicu*so.74.2 libxml2.so
186+
patchelf --set-rpath \$ORIGIN libandroid-spawn.so libcurl.so libxml2.so
214187
215-
# repeat the following for libicui18n.so and libicudata.so, as needed
216-
rm libicuuc.so libicuuc.so.74
217-
readelf -d libicuuc.so.74.2
218-
mv libicuuc.so.74.2 libicuuc.so
219-
patchelf --set-soname libicuuc.so libicuuc.so
220-
patchelf --replace-needed libicudata.so.74 libicudata.so libicuuc.so
188+
# repeat the following for all versioned Termux libraries, as needed
189+
rm libxml2.so libxml2.so.2
190+
readelf -d libxml2.so.2.13.4
191+
mv libxml2.so.2.13.4 libxml2.so
192+
patchelf --set-soname libxml2.so libxml2.so
193+
patchelf --replace-needed libz.so.1 libz.so libxml2.so
221194
```
222195
The libcurl and libxml2 packages are [only needed for the FoundationNetworking
223-
and FoundationXML libraries respectively](https://github.com/apple/swift-corelibs-foundation/blob/release/5.10/Docs/ReleaseNotes_Swift5.md),
196+
and FoundationXML libraries respectively](https://github.com/swiftlang/swift-corelibs-foundation/blob/release/5.10/Docs/ReleaseNotes_Swift5.md),
224197
so you don't have to deploy them on the Android device if you don't use those
225-
extra Foundation libraries. I simply include all four libraries since there's
226-
currently no way to disable building them in the CMake configuration.
198+
extra Foundation libraries.
227199

228-
The libicu dependency can be [cross-compiled for Android from scratch using
229-
these instructions](https://github.com/apple/swift/blob/release/5.5/docs/Android.md#1-downloading-or-building-the-swift-android-stdlib-dependencies)
230-
instead, so this Swift SDK for Android could be built without using
231-
any prebuilt Termux packages, if you're willing to put in the effort to
232-
cross-compile them yourself, for example, against a different Android API.
200+
This Swift SDK for Android could be built without using any prebuilt Termux
201+
packages, by compiling against a more recent Android API that doesn't need the
202+
`libandroid-spawn` backport, and by cross-compiling libcurl/libxml2 and their
203+
dependencies yourself or not using FoundationNetworking and FoundationXML, by
204+
disabling their build.
233205

234-
Finally, it gets [the 5.10.1 source](https://github.com/apple/swift/releases/tag/swift-5.10.1-RELEASE)
235-
tarballs for seven Swift repos and renames them to `llvm-project/`, `swift/`,
206+
Finally, it gets [the 6.0.1 source](https://github.com/swiftlang/swift/releases/tag/swift-6.0.1-RELEASE)
207+
tarballs for ten Swift repos and renames them to `llvm-project/`, `swift/`,
236208
`swift-syntax`, `swift-experimental-string-processing`, `swift-corelibs-libdispatch`,
237-
`swift-corelibs-foundation`, and `swift-corelibs-xctest`, as required by the Swift
209+
`swift-corelibs-foundation`, `swift-collections`, `swift-foundation`,
210+
`swift-foundation-icu`, and `swift-corelibs-xctest`, as required by the Swift
238211
`build-script`.

0 commit comments

Comments
 (0)