1
- # Swift cross-compilation SDK for Android
1
+ # Swift cross-compilation SDK bundle for Android
2
2
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
4
4
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
6
6
the Swift toolchain for AArch64, armv7, and x86_64, builds several Swift
7
7
packages against those SDKs, and then runs their tests in the Android x86_64
8
8
emulator] ( https://github.com/finagolfin/swift-android-sdk/blob/main/.github/workflows/sdks.yml ) .
9
9
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
13
11
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 ` .
23
18
19
+ Next, install the Android SDK bundle by having the Swift toolchain directly
20
+ download it:
24
21
```
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
27
23
```
24
+ You can check if it was properly installed by running ` swift sdk list ` .
28
25
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.
43
27
I'll demonstrate with the swift-argument-parser package:
44
28
```
45
29
git clone --depth 1 https://github.com/apple/swift-argument-parser.git
46
30
47
31
cd swift-argument-parser/
48
32
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
52
34
```
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
55
37
` .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.
58
38
59
39
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 ) ,
69
49
or put them on an Android device with [ a terminal emulator app like Termux] ( https://termux.dev/en/ ) .
70
50
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 ) .
72
52
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:
74
55
```
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
76
59
```
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:
79
62
```
80
63
uname -m # check if you're running on the right architecture, should say `aarch64`
81
64
cd # move to the Termux app's home directory
82
65
pkg install openssh
83
66
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} .
88
68
89
69
./swift-argument-parserPackageTests.xctest
90
70
```
91
71
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.
93
76
94
77
You can even run armv7 tests on an aarch64 device, though Termux may require
95
78
running ` unset LD_PRELOAD ` before invoking an armv7 test runner on aarch64.
@@ -99,28 +82,25 @@ mode.
99
82
100
83
## Porting Swift packages to Android
101
84
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:
106
87
```
107
- #if canImport(Glibc )
108
- import Glibc
88
+ #if canImport(Android )
89
+ import Android
109
90
```
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) ` ,
114
92
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 ) :
116
94
```
117
- #if os (Android)
95
+ #if canImport (Android)
118
96
typealias FILEPointer = OpaquePointer
119
97
```
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 ) .
120
100
121
101
## Building an Android app with Swift
122
102
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
124
104
their Android app, that the Android toolchain strips ` libdispatch.so ` and
125
105
complains that it has an ` empty/missing DT_HASH/DT_GNU_HASH ` . You can [ work
126
106
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 {
132
112
}
133
113
```
134
114
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
142
116
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 `
148
122
for the latest Swift trunk snapshot and pass in the path to the corresponding
149
123
prebuilt Swift toolchain to ` build-script ` below, you can build a Swift trunk
150
124
SDK too, as seen on the CI.
151
125
152
126
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 :
156
130
```
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
158
133
```
159
134
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 )
161
136
are installed, run the following ` build-script ` command with your local paths
162
137
substituted instead:
163
138
```
164
139
./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/
168
143
--host-cc=/usr/bin/clang-13 --host-cxx=/usr/bin/clang++-13
169
144
--cross-compile-hosts=android-aarch64 --cross-compile-deps-path=/home/finagolfin/swift-release-android-aarch64-24-sdk
170
145
--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
176
151
` --host-cc ` and ` --host-cxx ` flags are not needed if you have a ` clang ` and
177
152
` clang++ ` in your ` PATH ` already, but I don't and they're unused for this build
178
153
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.
180
155
181
156
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:
184
158
```
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
186
160
patchelf --set-rpath \$ORIGIN/../..:\$ORIGIN swift-release-android-aarch64-24-sdk/usr/lib/swift/android/lib*.so
187
161
```
188
162
189
163
Here is a description of what the above Swift script is doing:
190
164
191
165
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
193
167
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
196
170
repository] ( https://packages.termux.dev/apt/termux-main/pool/main/ ) .
197
171
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
199
173
the resulting files moved to a newly-created Swift release SDK directory:
200
174
```
201
175
mkdir swift-release-android-aarch64-24-sdk
202
176
mv data/data/com.termux/files/usr swift-release-android-aarch64-24-sdk
203
177
```
204
178
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 :
208
182
```
209
183
rm swift-release-android-aarch64-24-sdk/usr/bin/*-config
210
184
cd swift-release-android-aarch64-24-sdk/usr/lib
211
185
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
214
187
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
221
194
```
222
195
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 ) ,
224
197
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.
227
199
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 .
233
205
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/ ` ,
236
208
` 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
238
211
` build-script ` .
0 commit comments