Skip to content

Commit

Permalink
Update JSON config files and doc to Swift 5.5
Browse files Browse the repository at this point in the history
  • Loading branch information
finagolfin committed Sep 23, 2021
1 parent 38886af commit 96517eb
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 89 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/sdks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ jobs:
tar xf ~/swift-${{ matrix.version }}*-android-${{ matrix.arch }}*-${ANDROID_API_LEVEL}-sdk.tar.xz
SDK_NAME=$(ls | grep swift-${{ matrix.version }}.*-sdk)
sed -i "s%/home/butta/swift-5.4.2-android-${{ matrix.arch }}-${ANDROID_API_LEVEL}-sdk%`pwd`/${SDK_NAME}%" android-${{ matrix.arch }}.json
sed -i "s%/home/butta/swift-5.5-android-${{ matrix.arch }}-${ANDROID_API_LEVEL}-sdk%`pwd`/${SDK_NAME}%" android-${{ matrix.arch }}.json
sed -i "s%/home/butta/android-ndk-r23%`pwd`/android-ndk-r23%" android-${{ matrix.arch }}.json
- name: Get Swift Argument Parser package
Expand Down
142 changes: 57 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,55 @@

All patches used to build these SDKs are open source and listed below.

To build with a Swift 5.4.2 SDK, first download [the latest Android LTS NDK
23](https://developer.android.com/ndk/downloads) and [Swift 5.4.3
To build with a Swift 5.5 SDK, first download [the latest Android LTS NDK
23](https://developer.android.com/ndk/downloads) and [Swift 5.5
compiler](https://swift.org/download/#releases) (make sure to install the Swift
compiler's dependencies listed there). Unpack these archives and the SDK.

I will write up a Swift script to do this SDK configuration, but you will need
to do it manually for now. [You can see how I do it on the CI for a concrete
example](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L154).
example](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L150).

The SDK will need to be modified with the path to your NDK and Swift compiler
in the following ways (I'll show aarch64 below, the same will need to be done
for the armv7 or x86_64 SDKs):

1. Change all paths in `swift-5.4.2-android-aarch64-24-sdk/usr/lib/swift/android/aarch64/glibc.modulemap`
1. Change all paths in `swift-5.5-android-aarch64-24-sdk/usr/lib/swift/android/aarch64/glibc.modulemap`
from `/home/butta/android-ndk-r23` to the path to your NDK, ie something
like `/home/yourname/android-ndk-r23`.

2. There's a single line pointing to a header in the SDK itself, so change it
from `/home/butta/swift-5.4.2-android-aarch64-24-sdk` in `glibc.modulemap` to the
path where you unpacked this SDK, such as `/home/yourname/swift-5.4.2-android-aarch64-24-sdk`.
from `/home/butta/swift-5.5-android-aarch64-24-sdk` in `glibc.modulemap` to the
path where you unpacked this SDK, such as `/home/yourname/swift-5.5-android-aarch64-24-sdk`.

3. Change the symbolic link at `swift-5.4.2-android-aarch64-24-sdk/usr/lib/swift/clang`
3. Change the symbolic link at `swift-5.5-android-aarch64-24-sdk/usr/lib/swift/clang`
to point to the clang headers next to your swift compiler, eg

```
ln -sf /home/yourname/swift-5.4.3-RELEASE-ubuntu20.04/usr/lib/clang/10.0.0
swift-5.4.2-android-aarch64-24-sdk/usr/lib/swift/clang
ln -sf /home/yourname/swift-5.5-RELEASE-ubuntu20.04/usr/lib/clang/10.0.0
swift-5.5-android-aarch64-24-sdk/usr/lib/swift/clang
```
The new NDK 23 needs to have a header modified: change the line from this file,
`android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/stdlib.h`,
that reads
`__noreturn void abort(void) __attribute__((__nomerge__));`
to
`__noreturn void abort(void);`, ie comment or remove that attribute that the
Swift 5.4 compiler cannot handle (the upcoming Swift 5.5 compiler does not
require this workaround).

Finally, modify the cross-compilation JSON file in this repo similarly:

1. All paths to the NDK should change from `/home/butta/android-ndk-r23`
to the path to your NDK, `/home/yourname/android-ndk-r23`.

2. The path to the compiler should change from `/home/butta/swift-5.4.3-RELEASE-ubuntu20.04`
to the path to your Swift compiler, `/home/yourname/swift-5.4.3-RELEASE-centos8`.
2. The path to the compiler should change from `/home/butta/swift-5.5-RELEASE-ubuntu20.04`
to the path to your Swift compiler, `/home/yourname/swift-5.5-RELEASE-centos8`.

3. The path to the Android SDK should change from `/home/butta/swift-5.4.2-android-aarch64-24-sdk`
to the path where you unpacked the Android SDK, `/home/yourname/swift-5.4.2-android-aarch64-24-sdk`.
3. The path to the Android SDK should change from `/home/butta/swift-5.5-android-aarch64-24-sdk`
to the path where you unpacked the Android SDK, `/home/yourname/swift-5.5-android-aarch64-24-sdk`.

Now you're ready to cross-compile a Swift package with the cross-compilation
configuration JSON file, android-aarch64.json, and run its tests on Android.
I'll demonstrate with the swift-argument-parser package:
```
git clone --depth 1 https://github.com/apple/swift-argument-parser.git
cd swift-argument-parser/
/home/yourname/swift-5.4.3-RELEASE-ubuntu20.04/usr/bin/swift build --build-tests
/home/yourname/swift-5.5-RELEASE-ubuntu20.04/usr/bin/swift build --build-tests
--enable-test-discovery --destination ~/swift-android-sdk/android-aarch64.json
-Xlinker -rpath -Xlinker \$ORIGIN/swift-5.4.2-android-aarch64-24-sdk/usr/lib/swift/android
-Xlinker -rpath -Xlinker \$ORIGIN/swift-5.5-android-aarch64-24-sdk/usr/lib/swift/android
```
This will cross-compile the package for Android aarch64 and produce a test
runner executable with the `.xctest` extension, in this case at
Expand All @@ -72,13 +64,13 @@ same build directory. Other packages use `#file` to point at test data in the
repo: I've had success moving this data with the test runner, after modifying
the test source so it has the path to this test data in the Android test
environment. See the example of [swift-crypto on the
CI](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L303).
CI](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L296).

You can copy these executables and the SDK to [an emulator or a USB
debugging-enabled device with adb](https://github.com/apple/swift/blob/release/5.4/docs/Android.md#4-deploying-the-build-products-to-the-device),
debugging-enabled device with adb](https://github.com/apple/swift/blob/release/5.5/docs/Android.md#4-deploying-the-build-products-to-the-device),
or put them on an Android device with [a terminal emulator app like Termux](https://termux.com).
I test aarch64 with Termux so I'll show how to run the test runner there, but
the process is similar with adb, [as can be seen on the CI](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L337).
the process is similar with adb, [as can be seen on the CI](https://github.com/buttaface/swift-android-sdk/blob/main/.github/workflows/sdks.yml#L330).

Copy the test executables to the same directory as the SDK:
```
Expand All @@ -91,10 +83,10 @@ uname -m # check if you're running on the right architecture, should say `aarch6
cd # move to the Termux app's home directory
pkg install openssh
scp [email protected]:{swift-5.4.2-android-aarch64-24-sdk.tar.xz,
scp [email protected]:{swift-5.5-android-aarch64-24-sdk.tar.xz,
swift-argument-parserPackageTests.xctest,math,repeat,roll} .
tar xf swift-5.4.2-android-aarch64-24-sdk.tar.xz
tar xf swift-5.5-android-aarch64-24-sdk.tar.xz
./swift-argument-parserPackageTests.xctest
```
Expand All @@ -109,47 +101,46 @@ mode.

# Building the Android SDKs

Download the Swift 5.4.3 compiler and Android NDK 23 as above. Check out this
Download the Swift 5.5 compiler and Android NDK 23 as above. Check out this
repo and run
`SWIFT_TAG=swift-5.4.3-RELEASE ANDROID_ARCH=aarch64 swift get-packages-and-swift-source.swift`
`SWIFT_TAG=swift-5.5-RELEASE ANDROID_ARCH=aarch64 swift get-packages-and-swift-source.swift`
to get some prebuilt Android libraries and the Swift source to build the SDK. If
you pass in a different tag like `swift-5.5-DEVELOPMENT-SNAPSHOT-2021-08-28-a`
for the latest Swift 5.5 snapshot and pass in the path to the corresponding
you pass in a different tag like `swift-DEVELOPMENT-SNAPSHOT-2021-09-18-a`
for the latest Swift trunk snapshot and pass in the path to the corresponding
official prebuilt Swift toolchain to `build-script` below, you can build a Swift
5.5 SDK too, as seen on the CI.
trunk SDK too, as seen on the CI.

After making sure [needed build tools like python 3, CMake, and ninja](https://github.com/apple/swift/blob/release/5.4/docs/HowToGuides/GettingStarted.md#ubuntu-linux)
After making sure [needed build tools like python 3, CMake, and ninja](https://github.com/apple/swift/blob/release/5.5/docs/HowToGuides/GettingStarted.md#ubuntu-linux)
are installed, run the following `build-script` command with your local paths
substituted instead:
```
./swift/utils/build-script -RA --skip-build-cmark --build-llvm=0 --android
--android-ndk /home/butta/android-ndk-r23/ --android-arch aarch64 --android-api-level 24
--android-icu-uc /home/butta/swift-5.4.3-android-aarch64-24-sdk/usr/lib/libicuuc.so
--android-icu-uc-include /home/butta/swift-5.4.3-android-aarch64-24-sdk/usr/include/
--android-icu-i18n /home/butta/swift-5.4.3-android-aarch64-24-sdk/usr/lib/libicui18n.so
--android-icu-i18n-include /home/butta/swift-5.4.3-android-aarch64-24-sdk/usr/include/
--android-icu-data /home/butta/swift-5.4.3-android-aarch64-24-sdk/usr/lib/libicudata.so
--build-swift-tools=0 --native-swift-tools-path=/home/butta/swift-5.4.3-RELEASE-ubuntu20.04/usr/bin/
--android-icu-uc /home/butta/swift-5.5-android-aarch64-24-sdk/usr/lib/libicuuc.so
--android-icu-uc-include /home/butta/swift-5.5-android-aarch64-24-sdk/usr/include/
--android-icu-i18n /home/butta/swift-5.5-android-aarch64-24-sdk/usr/lib/libicui18n.so
--android-icu-i18n-include /home/butta/swift-5.5-android-aarch64-24-sdk/usr/include/
--android-icu-data /home/butta/swift-5.5-android-aarch64-24-sdk/usr/lib/libicudata.so
--build-swift-tools=0 --native-swift-tools-path=/home/butta/swift-5.5-RELEASE-ubuntu20.04/usr/bin/
--native-clang-tools-path=/home/butta/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/bin
--host-cc=/usr/bin/clang-11 --host-cxx=/usr/bin/clang++-11
--cross-compile-hosts=android-aarch64 --cross-compile-deps-path=/home/butta/swift-5.4.3-android-aarch64-24-sdk
--cross-compile-hosts=android-aarch64 --cross-compile-deps-path=/home/butta/swift-5.5-android-aarch64-24-sdk
--skip-local-build --xctest --swift-install-components='clang-resource-dir-symlink;license;stdlib;sdk-overlay'
--install-swift --install-libdispatch --install-foundation --install-xctest
--install-destdir=/home/butta/swift-5.4.3-android-aarch64-24-sdk
--common-swift-flags="-Xlinker -rpath -Xlinker \\\$\$ORIGIN/../.."
--swift-cmake-options=-DCMAKE_SHARED_LINKER_FLAGS='-Wl,-rpath,"\$ORIGIN/../.."' -j9
--install-destdir=/home/butta/swift-5.5-android-aarch64-24-sdk -j9
```
Make sure you have an up-to-date CMake and not something old like 3.16. The
`--host-cc` and `--host-cxx` flags are not needed if you have a `clang` and
`clang++` in your `PATH` already, but I don't and they're unused for this build
anyway but required by `build-script`. Substitute armv7 or x86_64 for aarch64
into these two commands to build for those architectures instead.
into these commands to build for those architectures instead.

Finally, copy `libc++_shared.so` from the NDK and modify the cross-compiled
`libdispatch.so` to include `$ORIGIN` in its rpath:
`libdispatch.so` and Swift corelibs to include `$ORIGIN` and other relative
directories in their rpaths:
```
cp /home/yourname/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so swift-5.4.3-android-aarch64-24-sdk/usr/lib
patchelf --set-rpath \$ORIGIN swift-5.4.3-android-aarch64-24-sdk/usr/lib/swift/android/libdispatch.so
cp /home/yourname/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so swift-5.5-android-aarch64-24-sdk/usr/lib
patchelf --set-rpath \$ORIGIN/../..:\$ORIGIN swift-5.5-android-aarch64-24-sdk/usr/lib/swift/android/lib*.so
```

Here is a description of what the above Swift script is doing:
Expand All @@ -161,32 +152,32 @@ API 24. Specifically, it downloads the libicu, libicu-static, libandroid-spawn,
libcurl, and libxml2 packages from the [Termux package
repository](https://packages.termux.org/apt/termux-main/pool/main/).

Each one is unpacked with `ar x libicu_68.2-1_aarch64.deb; tar xf data.tar.xz` and
the resulting files moved to a newly-created Swift 5.4.3 SDK directory:
Each one is unpacked with `ar x libicu_69.1_aarch64.deb; tar xf data.tar.xz` and
the resulting files moved to a newly-created Swift 5.5 SDK directory:
```
mkdir swift-5.4.3-android-aarch64-24-sdk
mv data/data/com.termux/files/usr swift-5.4.3-android-aarch64-24-sdk
mkdir swift-5.5-android-aarch64-24-sdk
mv data/data/com.termux/files/usr swift-5.5-android-aarch64-24-sdk
```
It removes two config scripts in `usr/bin`, runs `patchelf` to remove the
Termux rpath from all Termux shared libraries, and modifies the ICU libraries
to get rid of the versioning and symlinks (three libicu libraries are removed
since they're unused by Swift):
```
rm swift-5.4.3-android-aarch64-24-sdk/usr/bin/*-config
cd swift-5.4.3-android-aarch64-24-sdk/usr/lib
rm swift-5.5-android-aarch64-24-sdk/usr/bin/*-config
cd swift-5.5-android-aarch64-24-sdk/usr/lib
rm libicu{io,test,tu}*
patchelf --set-rpath \$ORIGIN libandroid-spawn.so libcurl.so libicu*so.68.2 libxml2.so
patchelf --set-rpath \$ORIGIN libandroid-spawn.so libcurl.so libicu*so.69.1 libxml2.so
# repeat the following for libicui18n.so and libicudata.so, as needed
rm libicuuc.so libicuuc.so.68
readelf -d libicuuc.so.68.2
mv libicuuc.so.68.2 libicuuc.so
rm libicuuc.so libicuuc.so.69
readelf -d libicuuc.so.69.1
mv libicuuc.so.69.1 libicuuc.so
patchelf --set-soname libicuuc.so libicuuc.so
patchelf --replace-needed libicudata.so.68 libicudata.so libicuuc.so
patchelf --replace-needed libicudata.so.69 libicudata.so libicuuc.so
```
The libcurl and libxml2 packages are [only needed for the FoundationNetworking
and FoundationXML libraries respectively](https://github.com/apple/swift-corelibs-foundation/blob/release/5.4/Docs/ReleaseNotes_Swift5.md),
and FoundationXML libraries respectively](https://github.com/apple/swift-corelibs-foundation/blob/release/5.5/Docs/ReleaseNotes_Swift5.md),
so you don't have to deploy them on the Android device if you don't use those
extra Foundation libraries.

Expand All @@ -198,35 +189,16 @@ libraries, you will have to track down those other libcurl/xml2 dependencies and
include them yourself.

The libicu dependency can be [cross-compiled for Android from scratch using
these instructions](https://github.com/apple/swift/blob/release/5.4/docs/Android.md#1-downloading-or-building-the-swift-android-stdlib-dependencies)
these instructions](https://github.com/apple/swift/blob/release/5.5/docs/Android.md#1-downloading-or-building-the-swift-android-stdlib-dependencies)
instead, so this Swift SDK for Android could be built without using
any prebuilt Termux packages, if you're willing to put in the effort to
cross-compile them yourself, for example, against a different Android API.

Next, it gets [the 5.4.3 source](https://github.com/apple/swift/releases/tag/swift-5.4.3-RELEASE)
Next, it gets [the 5.5 source](https://github.com/apple/swift/releases/tag/swift-5.5-RELEASE)
tarballs for five Swift repos and renames them to `llvm-project/`, `swift/`,
`swift-corelibs-libdispatch`, `swift-corelibs-foundation`, and
`swift-corelibs-xctest`, as required by the Swift `build-script`. After creating
an empty directory, `mkdir cmark`, it downloads six patches that have been
backported to build the Termux package for Swift 5.4.3 (all Termux patches are
available under the [same license as the Termux package, the Apache license used
by Swift in this case](https://github.com/termux/termux-packages/blob/master/LICENSE.md#license-for-package-patches))
and applies each of them:

- [Only build the stdlib for Android, not linux](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-host.patch)
- [Native clang path](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-native-tools.patch)
- [Pass cross-compilation Swift flags to the corelibs](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-utils-build-script-impl-cross.patch)
- [Link Foundation against the android-spawn wrapper](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-corelibs-foundation-Sources-Foundation-CMakeLists.txt.patch)
- [Libdispatch fixes for Android](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-corelibs-libdispatch-arm.patch)
- [XCTest rpath](https://github.com/termux/termux-packages/blob/master/packages/swift/swift-corelibs-xctest-CMakeLists.txt.patch)

Four of the patches have been merged upstream, with only the tiny Foundation and
XCTest patches that are specific to Android not upstreamed.

For armv7 before Swift 5.4.2, I also had to patch the compiler source with
[this pull](https://github.com/apple/swift/pull/36658) and build it for the
linux x86_64 host first, as opposed to using the official prebuilt Swift
compiler to cross-compile for Android aarch64 and x86_64.

Finally, it applies the `swift-android-5.4.patch` from this repo: these are all build
`swift-corelibs-xctest`, as required by the Swift `build-script`, and creates
an empty directory, `mkdir cmark`.

Finally, it applies the `swift-android-5.5.patch` from this repo: these are all build
configuration tweaks specific to building this Android SDK.
2 changes: 1 addition & 1 deletion android-aarch64.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"-fPIC"
],
"extra-swiftc-flags": [
"-resource-dir", "/home/butta/swift-5.4.2-android-aarch64-24-sdk/usr/lib/swift",
"-resource-dir", "/home/butta/swift-5.5-android-aarch64-24-sdk/usr/lib/swift",
"-tools-directory", "/home/butta/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/bin",
"-Xclang-linker", "--target=aarch64-linux-android24", "-use-ld=lld",
],
Expand Down
2 changes: 1 addition & 1 deletion android-armv7.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"-fPIC"
],
"extra-swiftc-flags": [
"-resource-dir", "/home/butta/swift-5.4.2-android-armv7-24-sdk/usr/lib/swift",
"-resource-dir", "/home/butta/swift-5.5-android-armv7-24-sdk/usr/lib/swift",
"-tools-directory", "/home/butta/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/bin",
"-Xclang-linker", "--target=armv7-linux-android24", "-use-ld=lld",
],
Expand Down
2 changes: 1 addition & 1 deletion android-x86_64.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"-fPIC"
],
"extra-swiftc-flags": [
"-resource-dir", "/home/butta/swift-5.4.2-android-x86_64-24-sdk/usr/lib/swift",
"-resource-dir", "/home/butta/swift-5.5-android-x86_64-24-sdk/usr/lib/swift",
"-tools-directory", "/home/butta/android-ndk-r23/toolchains/llvm/prebuilt/linux-x86_64/bin",
"-Xclang-linker", "--target=x86_64-linux-android24", "-use-ld=lld",
],
Expand Down
1 change: 1 addition & 0 deletions get-packages-and-swift-source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ let sdkPath = cwd.appendingPathComponent(sdkDir)
if !fmd.fileExists(atPath: sdkPath) {
try fmd.removeItem(atPath: cwd.appendingPathComponent("data.tar.xz"))
try fmd.removeItem(atPath: cwd.appendingPathComponent("control.tar.gz"))
try fmd.removeItem(atPath: cwd.appendingPathComponent("control.tar.xz"))
try fmd.removeItem(atPath: cwd.appendingPathComponent("debian-binary"))

try fmd.createDirectory(atPath: sdkPath, withIntermediateDirectories: false)
Expand Down

0 comments on commit 96517eb

Please sign in to comment.