Building the Swift stdlib for Android: Linking ICU libraries fails

Hi everyone,

I am trying to build the Swift compiler for Android on Ubuntu as described in swift/Android.md at main · apple/swift · GitHub, but am getting linker errors for the ICU libraries:

$ utils/build-script -R --android --android-ndk $NDK_PATH --android-arch armv7 --android-api-level 21 --android-icu-uc $ARM_DIR/libicuucswift.so --android-icu-uc-include $ARM_DIR/icu/source/common --android-icu-i18n $ARM_DIR/libicui18nswift.so --android-icu-i18n-include $ARM_DIR/icu/source/i18n --android-icu-data $ARM_DIR/libicudataswift.so
...
[14/91][ 15%][0.253s] Linking C shared library lib/swift/android/armv7/libswiftCore.so
FAILED: lib/swift/android/armv7/libswiftCore.so
: && .../swift-project/build/Ninja-ReleaseAssert/llvm-linux-x86_64/./bin/clang ... -licui18nswift  -licuucswift  /usr/lib/x86_64-linux-gnu/libicuuc.so  /usr/lib/x86_64-linux-gnu/libicui18n.so && :
.../android-ndk-r21e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold: error: /usr/lib/x86_64-linux-gnu/libicuuc.so: incompatible target
.../android-ndk-r21e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold: error: /usr/lib/x86_64-linux-gnu/libicui18n.so: incompatible target
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)

Any ideas why it searches /usr/lib for these libraries rather than $ARM_DIR as specified in the build flags?

Impossible to know for sure without more details on how you built ICU. It is possible libicuuc.so is just an alias and it requires the actual library file at libicuuc.so.67.1 or similar.

The guide you are reading is very old and probably out of date. However, I can vouch for this succeeding. The details of each step can be found in the neighbouring directory. If you are working with the 5.3.3 release instead of the development state of Swift, look in the toolchain‐5.3 branch instead.

Thank you for the link, the workflow targets x86_64 Android though as far as I can tell, which would cause natively installed ICU libraries (e.g. from /usr/lib) to link successfully, assuming the runner is also on x86_64. I am, however, trying to target armv7 Android.

The ICU libraries for armv7 that I am trying to use were built with the build-swift.sh script from GitHub - SwiftAndroid/libiconv-libicu-android: Port of libiconv and libicu to Android. I don't think the issue is with the ICU libraries themselves though, since they built fine, the problem seems to be with the Swift compiler's build script which looks for the libraries in the wrong place.

Since the error message displays /usr/lib/x86_64-linux-gnu/libicuuc.so in the clang invocation, I would assume that the build script somehow fails to respect the --android-icu-uc flag.

Does $ARM_DIR have a value? It needs to be pointing to that directory armeabi-v7a from Step 1.

Yes, it points to .../libiconv-libicu-android/armeabi-v7a as described in the guide.

It sounds like it's not detecting the libicu directory you gave it and looking for the system libicu too: try passing in absolute paths for all those libicu flags instead.

The guide you are reading is very old and probably out of date.

I've been updating that doc over the last year, so most of it should work, but I don't cross-compile libicu from source (using the prebuilt Termux packages instead) so that section hasn't been checked.

Yeah, the part I meant was old was the ICU section. The repository it directs you to hasn’t had a commit in almost three years, and the four different unofficial SDKs I am aware of in the wild do not use it for their build. If the ICU set‐up produced by that repository no longer matches what the build script expects, it might have been pointing into thin air. However, later comments suggest that isn’t the issue here.

Passing absolute paths still yields the same error, unfortunately. I got it to work by symlinking the cross-compiled armv7 ICU libraries into /usr/lib/x86_64-linux-gnu, that feels like a hack though.

If you are using utils/build-script over and over, it might not be reconfiguring the CMakeCache.txt file that ends up with those values. One option is passing --reconfigure to the build-script, which should rebuild those files. You can also delete build/.../swift-linux-x86_64/CMakeCache.txt which should do the same effect. Without reconfigure you are using the first configuration over and over.

Good point, thanks, I didn't know about --reconfigure. Unfortunately, the flag combinations I tried before still lead to the same error.

Delete build/Ninja-Release/swift-linux-x86_64 and the symlinks, run the command with absolute paths, and paste the command and CMake output you see in your terminal in a github gist for us to look at. None of us are seeing this problem, so we're clearly missing a bug or a way to hold it wrong, which we can't figure out from the limited data so far.

Sure, should have added this earlier, sorry.

Oh crap, no, this is our fault. This has been broken for awhile, but it's been months since I cross-compiled the Android stdlib that way, so I forgot what the error was. Apply that pull before building with this command and it should work. Alternately, use the Android preset not mentioned in this doc, that the community Android CI uses, and that should work too.

I've been planning to update the docs once I get the Swift corelibs all cross-compiling properly from the official source alone, so they're a bit off atm.

1 Like

You might want to try adding --libicu into your flags. It might be that when ICU is built for the build host, instead of using the system provided one, the Android SDK doesn't try to use the system one. I don't know if one of the PRs from Buttaface will help, I haven't looked at them with detail.