Swift for Android: Call for the Community

For a little over a year, I have been collaborating with the open source community in bringing Swift support for Android up to the level that other platforms enjoy (namely Darwin and Linux), mainly thanks to the incredible job others did before me. Sadly, I will not be able to continue dedicating a lot of time to those efforts, and with this post I want to inform the community of people interested in Swift for Android.

About continuous integration.

As part of the Swift Community CI there are two machines running a partial Swift test suite with every commit to master. There's one for ARMv7 and one for AArch64. Those machines are still going to be available for the time being.

These machines do not execute the complete Swift test suite. The complete test suite will test both the compiler and the standard library implementation. In the machines we can test the compiler and some aspects of the stdlib, but we cannot run any compiled code targeting the Android devices, because the machines have no Android device attached to them. This means that by default for Android, instead of running the target check-swift-android-aarch64, the machines run check-swift-only_non_executable-android-aarch64.

A couple of pieces that are not build in these machines are the Swift corelibs (dispatch, XCTest, and Foundation). Limitations in the build-script infrastructure make it impossible to cross-compile those. The products will normally compile, and for the most part they pass the tests and work, but building them in the CI is not possible at the moment (see below to learn who is continuously building those libraries for Android).

Additionally, these machines skip building many products beyond Swift, like SwiftPM, SourceKit-LSP and others, since those only apply to the machine that compiles the code, not the one that run it, and those products are already build by the Linux CI machines, and building them again will be a waste of resources.

What to do when the build breaks

Sadly, there's currently no way set up to execute the same suite of tests before the pull requests are merged, so normally one will notice problems with the builds after the pull requests are merged, and not before. Fortunately there are not that many breakages thanks to the awesome work of the rest of the contributors, but there exist from time to time. Since the machines are quite fast (it takes about an hour to build and test), if the breakages are recent, it is quite easy with Jenkins Console Output and the Jenkins Build Changes to figure out what happened. If the breakages are not recent (so the first build that failed is already discarded from the limited history of 20 last builds), one should focus on the Console Output only.

Most of the breakages this days is a modification on the test files: either someone added a new test, or someone modified a test that didn't need to execute on the target. In this case, the fix is really simple, since it just means adding // REQUIRES: executable_text in the test file. Examples of these kind of fixes are #29235, #28751, or #26759.

In other cases the fixes are more involved. One example is 32 bits compatibility: the main platforms (Darwin and Linux) do not care that much anymore about 32 bits, having evolved to 64 bits in most cases, so in some occasions a test will be written only for 64 bit machines, and have to be adapted for the ARMv7 32 bit processor (for example in #28542). Other examples are missing checks for Android: the test are written for Darwin and Linux (since those are the machines that are checked by the official CI), but are missing other platforms like Android or Windows. These cases are harder to catch, because sometimes they are silent: if the checks are not there, the test harness will not notice any wrong checks (an example of this case is #28422). Most of the other cases will be one off, that might need more thinking or investigation. A good place to start might be searching for "android" in the closed PRs of the Swift project.

A final case for breakage will be that the machine itself broke. This hasn't happen in a long time, but because life has a sense of humor, I found a breakage like that while I was writing these lines (#29244 introduced a new dependency for the build-script which wasn't installed in the machine). In this cases, the only way of fixing these kinds of problems is having access to the machine itself, which means that I will need to be involved. More details about that below. I think there are big changes coming to the build-script, so I will try to keep an eye for the next couple of weeks, just in case anything else breaks.

How to know when the build breaks

Sadly there's no good way to be notified of the build breakages that I know. I used to visit https://ci-external.swift.org/ every morning to check that the builds weren't in the previous 24 hours. I end up using an RSS reader, and subscribed to the RSS feeds of the builds (ARMv7 and AArch64). At least I was getting a visual notification if new builds were happening. It will be ideal to have a push system, but the current system doesn't seem to allow such thing.

How to communicate with me if necessary

In the case that something goes horribly wrong with the machine, or there's some problem that can only be fixed with access to the machine, there will be no other way to fix things than contacting me.

The best way will be these forums: mention me (maybe as a comment to this post) and I will try to answer as soon as I am capable. This might mean that I will normally be able to help on weekday evenings/nights and some weekends (Pacific Coast time zone), and sometimes one is away from a working computer for long periods of time, so please be patient if I don't answer right away. I hope, however, that most things will not need my involvement, and that the community will be able to fix them by themselves.

A note about Azure

Since early last year, @compnerd has been working on an alternative Swift CI hosted in Azure DevOps. It was initially intended for the Swift for Windows builds, but it has grown in the meantime to build many other products.

One of the products is a complete Android SDK, including dispatch, Foundation and XCTest, which are not build in the Community CI. While I think these builds do not test the standard library at all (not even the non executable tests), or the Swift corelibs, it gives a little bit of signal about their health. If you are interested in helping, it might not be bad to have an eye on those builds as well.


Mentioning people that might be interested in this announcement: @v.gorlov, @geordie_j, @Finagolfin, @futurejones (since the work on Linux-AArch64 sometimes overlapped with Android-AArch64).

26 Likes

The build-script and related build support files, in my opinion, a bit overcomplicated. That was the main reason why I created unofficial Toolchain for Android (GitHub - vgorloff/swift-everywhere-toolchain: Automated workflow to compile Swift Toolchain, for making Android apps with Swift.).

The build process of that unofficial Toolchain for Android is fully automated. It includes libDispatch, libFoundation and all needed 3rd-party dependencies. At the moment it not running test suite. But it is planned to support this year later.

It is also planned to update Swift Package Manager to support Android architecture targets without workarounds.

I also want to get this unofficial Toolchain working with Fuchsia and other OS-es (i.e. cross compile build for Linux/Windows on macOS host).

Not official means – same source code, but built using another build scripts .)

btw: I can convert Ruby code, used in unofficial Toolchain, to the Python and integrate it into official Toolchain as well.

5 Likes

@drodriguez Thanks for all your contributions, especially for your help with AArch64.

You should check out the Azure setup. It is actually quite nice. The only bad thing is that, as far as I know, there's no way of executing it locally, which makes it a little bit difficult to test, and not useful for local development.

I tried modifying the Python build script in order to remove the build-script-impl dependency some months ago, but the PRs ended up in review hell (I was the only one looking at them, or at least commenting on them). I tried for two reasons: it will allow more flexibility in order to build more pieces for Android (my intention was the corelibs), and it will also work for Windows (that cannot run the build-script-impl natively).

Sadly improving build-script is the only way that might be accepted by Apple, but changing it is a (very steep) uphill battle in my experience (it will be specially complicated now, that they are modifying it for other reasons).

Just saw this post now, thanks for reviewing some of my Android pulls before.

I've been building Swift master natively on Android in the Termux app about once or twice a month and submitting pulls for the entire toolchain back upstream. I'm down to 11 failing tests from the complete Swift test suite you mention above, one test in Foundation that you already submitted a pull for, swift-corelibs-foundation#2145, and one test in XCTest and three in the Swift Package Manager related to Android incompatibilities that aren't important (Bionic doesn't support module maps yet, for example).

Otherwise, I have libdispatch up through sourcekit-lsp passing their tests on an Android AArch64 device (I don't really try ARMv7 since it's dying off) and have built and tested a pure Swift library package using just the package manager on-device. I will be submitting a pull to the Termux package repo for a Swift toolchain on native Android soon, already got a couple more Swift pulls related to that in the pipeline.

I can pitch in on the AArch64 CI, helping make sure it stays green. I don't know the extent of Android support in Azure, but with AArch64 support in cloud/CI services increasing and something like the just announced Anbox Cloud for Android, it should be possible for a company using Swift on Android to set up much more comprehensive CI, maybe using the native Android support I've been adding.

4 Likes

My main concern here is that it would be unwise to duplicate efforts in making competing toolchains, unless there's a really good reason. I am very much "bought into" the Azure setup that @compnerd is running (we are also donating server hardware for the linux builds) so my preference would be to get that working great for everyone (obviously) but I'm open to other suggestions if they're playing it "by the books". One extremely strong characteristic of @compnerd's efforts is that he has constantly been working to get the changes merged into Apple's swift repos. Our past experience has been much more along the lines of seeing hacks and workarounds that inevitably bit rot, whereas Saleem's efforts seem to be for the long term. (As an aside, given Saleem's recent "promotion" to become a part of the Swift core team, I can also imagine he'll have some say in how Swift for Android moves forwards at an "official" level too).

My colleague @michaelknoch has been busy updating the docs and examples in our swift android toolchain, which provides artefacts from @compnerd's builds in an easy-to-use zip file and is the basis for building our open source version of UIKit for Android. The toolchain and documentation are far from perfect still (we have CMake calling CMake due to Gradle limitations) but hey, they work very well with or without Android Studio, and our app has been in production since mid last year across multiple architectures with that set up. We think a lot of users can get benefit from using CMake to build Swift and integrate with other projects independent of their language (thanks to @compnerd's work here as well!)

As for things left doing: there are many of course, but for me one of the biggest ways people could support would be to get the full Android SDK building on Linux (this would be amazing because the whole thing takes minutes, not hours, to build on the powerful machine we have running there). Right now we're using the toolchain built for Linux / macOS respectively, but use the Android SDK built on Windows.

The main advantage of having everything build on linux would be to improve debugging and testing. I'd like to have tests running on emulators on the CI machine (which seemed to be working last I checked), and with that I'd like to fix some critical bugs in swift-corelibs-foundation, which is currently impractical without a fast build process and automated tests.

2 Likes

This was more or less on my plans last year, but life got in the way and my plans didn't go anyway. I was thinking in using either Amazon EC2 A1 instances (AArch64) if Android can be installed on it, or the Genymotion AMI (x86_64). I think it would be possible to bring up an instance for each of the tests on demand, but all of this were just plans, and I didn't research how easy/difficult it would be.

Right. The Azure setup is good in that it uses many parts of the build system from the official repository (the CMake), and Saleem contributes back the necessary changes in those files. Sadly (in my opinion), having that alternative solution that Saleem and me could modify at will (something that we are limited to in the Community CI) has removed a lot of inertia that I had into improving build-script in the main repo. So while I love what Azure allows, and what already offers, I think the efforts should focus on improving the main repo, but it is a harder and longer path (I don't know if Ted announcement from yesterday will imply more openness with changes in the build-script, though).

I don't understand if you are saying that it is not possible right now. I might misunderstood "Android SDK", but if you mean the Swift+Clang toolchain for Linux, and the runtime, stdlib and corelibs for Android, I have been building in Linux all the time without major problems. This is more or less what the Community CI does (and those machines take around 1 hour to build the things).

First of all, thank you for your efforts in improving the android port for Swift as well as the infrastructure.

The core team is very much invested in ensuring that Swift is viable on the platforms that the community cares about. As @tkremenek quoted from the swift.org website:

Android is certainly part of that vision, and we would like to ensure that this port (and other ones) thrive.

Although currently, build-script is currently the officially documented way to build the Swift project, the Core Team is open to exploring different directions which would satisfy the needs of all the participants of the community.

We would like to foster an environment which encourages collaboration amongst the contributors. There are a number of ports that currently exist and it seems that there may be some growing pains for the project. Although we do not have answers on how to solve all the issues immediately, we are committed into trying to ensure that we can come to a solution which enables the ports to be effectively maintained and grow.

Drawing from the experiences of LLVM, there is certainly value in creating a culture of supporting other platforms and ensuring that individual work does not accidentally break other platforms. We believe that this has worked extremely well there and would like to encourage that in the Swift community as well.

It would be beneficial to clarify the expectations that the community has for supporting the platforms. This would ensure that there is no miscommunication between the contributors to the project. We will create a separate thread to discuss the expectations and create a living document of the expectations of both the port as well as contributors of the project towards the ports in the tree. This would cover the items of building, testing (CI), and general maintenance of the ports.

Saleem
(On behalf of the Core Team)

19 Likes

Coming back to the idea of the Linux builds, I gather it’s possible, but currently not integrated into the cross-platform CMake structure on Saleem’s CI setup.

If we can get that working (which would remove this issue of doubled efforts) then I would be open to do a lot more work on Foundation including getting tests working, writing more tests and fixing some long-standing bugs. I’d also be happy to do work for supporting Android 5 and 6 but without this setup it’s not worth the effort right now.

We had the toolchain (which is the much larger part) building in 5-7mins, so the SDK should take a couple of minutes once it’s working. That would allow for some very fast iteration which currently isn’t possible anywhere else (as far as I know).

It does have Linux builds for Foundation:
https://dev.azure.com/compnerd/swift-build/_build/results?buildId=25248&view=logs&s=abe4fa59-6eba-5195-c69b-a97243ebdadf

and Android builds for Foundation:
https://dev.azure.com/compnerd/swift-build/_build/results?buildId=25202&view=logs&s=4f12b15b-d141-55f8-f2a9-62acd6d01f15

and on Android for the "stable" (read 5.2) branch:
https://dev.azure.com/compnerd/swift-build/_build/results?buildId=25218&view=logs&s=4f12b15b-d141-55f8-f2a9-62acd6d01f15

I'm not sure what exactly you are looking for there. The running of the tests on Android is more challenging, but, enabling testing on Linux is possible (though, there was a regression in the agent that I've reported to Microsoft that would need a little bit of time).

As a first step towards running the Swift toolchain natively on Android by using the Termux app I linked above, I've put together a Termux build script to automate building the Swift 5.1.4 compiler up through the package manager on an Android AArch64 device. If anyone wants to try building the Swift compiler on their Android device and using it, some directions follow for those unfamiliar with Termux. You will want a relatively recent and powerful CPU, ie a Snapdragon 835 or later, and need 5 GBs of free storage.

First, install the Termux app on your Android phone or tablet, available at the official Play Store or other open-source app stores like F-Droid or APKPure. Once installed, open the app and type and run the following commands to get the Swift toolchain built and installed:

cd ~
uname -m     # should say aarch64, otherwise this won't work
pkg install git
git clone -b swift https://github.com/buttaface/termux-packages.git
cd termux-packages/
pkg install clang binutils-gold file patch python cmake ninja python2 perl rsync libandroid-spawn libcurl libicu libsqlite libuuid libxml2 llbuild pkg-config
TERMUX_MAKE_PROCESSES=5 ./build-package.sh swift
dpkg -i debs/swift_5.1.4_aarch64.deb

TERMUX_MAKE_PROCESSES determines how many cores to use, use more if you don't mind the device becoming unresponsive or the build potentially getting killed, as Android is more aggressive about killing such background processes than a desktop OS.

Usually, the Termux app will also show up in the notification tray, where if you press the down arrow next to the Termux name, more options like 'Acquire Wakelock' will show up. It's a good idea to press that option, so that if the device screen turns off, the build can continue in the background.

The build takes three hours on a Snapdragon 835, so be prepared to wait, but once it's done, SPM commands like swift build or swift test should be available and you can try writing and building Swift packages on your Android device. See the Termux pull linked first above for more info.

4 Likes

I've updated the Termux build script to the Swift 5.2 release. These slightly modified instructions will build and install it as above:

cd
uname -m     # should say aarch64, otherwise this won't work
pkg install git
git clone -b swift52 https://github.com/buttaface/termux-packages.git
cd termux-packages/
pkg install clang binutils-gold file patch python cmake ninja python2 perl rsync libandroid-spawn libcurl libicu libsqlite libuuid libxml2 llbuild pkg-config
TERMUX_MAKE_PROCESSES=5 ./build-package.sh -s swift
dpkg -i debs/swift_5.2_aarch64.deb

I'm going to try cross-compiling the Swift compiler itself next, which should hopefully lead to a pre-built Swift 5.2 package for Android AArch64.

1 Like

Took me awhile, but there's now a prebuilt Swift 5.2.4 toolchain available to natively build Swift packages on Android AArch64 phones and tablets. Simply install the Termux app for Android and run pkg install swift and you have a working Swift compiler, along with Foundation and the other corelibs plus the package manager (no lldb REPL or sourcekit-lsp yet):

> pkg install swift
> swift --version
Swift version 5.2.4 (swift-5.2.4-RELEASE)
Target: aarch64-unknown-linux-android

> git clone https://github.com/apple/swift-argument-parser.git
> cd swift-argument-parser/
> time swift build -j 9
[53/53] Linking math

real    0m12.384s
> time swift test -j 9 --enable-test-discovery --parallel
[59/59] Linking swift-argument-parserPackageTests.xctest
[298/298] Testing ArgumentParserEndToEndTests.RepeatingEndToEndTests/testParsing_repeatingArgu


real    0m16.606s

The time command shows how long it takes to build and run the tests of a pure Swift package on mid-range Android hardware.

This native toolchain port builds on the Android stdlib port that started getting merged into mainline 4-5 years ago by Zhuowei Zhang and Brian Gesiak and continued by Amr Aboelela, along with others, before being maintained and kept up to date with an Android stdlib CI by @compnerd and @drodriguez in recent years.

My Swift 5.2 patches are easily accessible, and about a quarter have already been merged into mainline. The remaining mostly patch the build scripts and some of the CMake config, as the corelibs already have good support for cross-compilation (Termux packages are cross-compiled from linux x86_64 using the Android NDK). Some description of the yet unmerged patches follows:

  1. The stdlib CMake config supports cross-compiling the stdlib with a prebuilt Swift compiler, but some pulls last year broke parts of it for Android, so it doesn't work without one build-script flag or doesn't apply other linker flags that it used to. Also, it has never worked if you try to only cross-compile the stdlib and not build the host stdlib. I've submitted a pull to fix these problems, but it has sat in limbo without proper review for three months now.
  2. The current build-script supports cross-compiling the Swift compiler and stdlib, but not the corelibs and package manager, so I added support for cross-compiling those also.
  3. The build-script always builds the toolchain natively for the host first (with the single aforementioned exception of a flag to build the stdlib with a prebuilt Swift compiler instead) and then builds everything else with it: I disabled that and use a prebuilt official release of the Swift 5.2 compiler to cross-compile the corelibs and package manager too.
  4. Several small tweaks like removing unnecessary rpaths from the package manager and configuring Swift to use the Termux clang, instead of its own.

I'm not sure how much interest there is in merging these remaining patches that would be generally useful. If anyone has any problems with this Swift package on Android 7 or 8, let me know, as I do all my testing on Android 9.

5 Likes

Working well on my phone! I struggled for days to get Swift 5.2+ to cross-compile for Android and couldn't get it to work with any of the methods available online. Even the Android.md on the Swift project on GitHub didn't function as expected. This method worked perfectly.

I am trying to get some CI set up around this and wanted to at least have a build and test step in it. I decided to try out Android x86_64 on VMWare to see if I could get these same steps to work. The architecture is x86_64 instead of aarch64, though. Using pkg search I was able to see that llbuild is available but not swift. Is there any way to get this to work? Or will I just need to do my building and testing on the phone for now?

1 Like

I have an 5.3 SDK for cross‐compiling to x86_64 available for download here. I use with the emulator on GitHub actions, so presumably you can use that for your CI. An example fetch‐and‐use script is here.

Working well on my phone!

Good to hear.

I struggled for days to get Swift 5.2+ to cross-compile for Android

Depending on which version of the commands you used, there are still things that can go wrong. I will rewrite the Android doc soon to make that less likely, once the patches I mentioned above are in.

The architecture is x86_64 instead of aarch64

I have not yet bothered to build a Termux package for x86_64. Maybe Jeremy's build will work for you? Note the extra flags he adds when cross-compiling to Android, just as I do when cross-compiling Foundation and the other corelibs.

2 Likes

Excellent! I'll dig into this some more. Looks like this will be a great starting point. Thanks!

We are looking to host Ubuntu or AWS Linux 2 x-compile to Android builds of SPM packaged products in an EC2 instance or docker container. We are currently building Droid from Mac->Droid with a destination JSON using some of the other community's work. Have all of your diffs made it into main so this it is possible to use your Linux->Droid stuff or should we use your fork? We don't currently want to build on device (although we thinks thats super awesome! :-) ) because we have a hosted hybrid native/web IDE for our products, using among other things, sourcekit-lsp. We talk about our work here.

Have all of your diffs made it into main so this it is possible to use your Linux->Droid stuff or should we use your fork?

Most of my remaining diffs have to do with modifying the official Swift build-script, which is a combo of Python and a large bash script, to generate the right CMake commands to build a cross-compilation SDK for Android.

I presume you're using a prebuilt Android SDK on macOS, which could probably be used in the same way with the Swift toolchain for linux right now, ie simply move that prebuilt Android SDK to linux and it should just work with the Swift linux toolchain too. Somebody generated the Android SDK you're using either by making similar changes to the ones I made or by manually specifying all the CMake cross-compilation flags themselves, as can be seen in this guide for cross-compiling the Android SDK on Windows.

Since I'm modifying the cross-platform build-script, my patches are not specific to a linux host, they enable cross-compiling to Android on all hosts, whether Mac or linux or whatever. In fact, my latest build-script pull isn't just for cross-compiling Foundation and the other Swift corelibs for Android, it adds the right CMake flags to enable cross-compilation to any platform, and uses Android as the first example config.

In other words, if Mac->Droid cross-compilation is working well for you already with some prebuilt Android SDK, it should be straightforward to just move what you're already using to Linux->Droid. My diffs are more about making it easier to build that Android SDK itself. I don't maintain a fork for that, but all my patches against Swift 5.3 are online under the Apache license and I keep upstreaming them to the main branch.

We talk about our work here .

Oh nice, good to see Swift being used in such a cross-platform way.

2 Likes

Is there a 5.3 Android SDK you would recommend ? Happy to try SPM on Linux again, with that SDK. Our entire suite uses SPM, and we wrap massive C++ libs as well.