To bring up more or less the same question. Is there a solid way to cross-compile for Android with a Swift SDK without using SwiftPM? If not, is there any work towards it?
So far, I couldn't find a single way to make swiftc work with the Swift Android SDK, so I wonder if this is even possible. CMake is unavoidable for complex mixed-language libraries, so I need to resort to two build systems, which is a maintenance burden. I'd love to avoid SwiftPM, but I can't find a way that's not by rebuilding the entire Swift toolchain for Android.
Switching out the similar Android triple and target config, plus adding CMAKE_SYSTEM_VERSION and CMAKE_ANDROID_NDK as shown in my above link, will get you most of the way there, but the Swift compiler also needs to know the location of the Android NDK sysroot and the Swift resource directory in your Swift SDK for Android, which can be passed in using CMAKE_Swift_FLAGS.
You will probably also have to link that bundle's clang resource directory to your NDK clang resource directory, which is one of the steps the setup-android-sdk.sh script we provide does for you when used with SwiftPM:
I know it seems complicated, but that's why SwiftPM saves you so much trouble, particularly since CMake's Swift support is still evolving.
We had some trouble with SwiftPM also, as this is the first Swift SDK bundle not to ship with the C/C++ headers and libraries for the target platform included, ie you have to download the Android C/C++ NDK separately. There are some Swift configuration bugs with such an external C/C++ platform SDK that we avoid with that setup-android-sdk.sh script. I'm looking at fixing those now and hopefully we can remove that script in the coming months.
I tried this CMake config recently and was able to successfully cross-compile the swift-argument-parser package for Android using the official trunk snapshot of the Swift SDK for Android, so let me know if you have any problems.
Great, and thanks for the update. I'm working on something else right now, and I haven't had the time to try it yet (my setup is not trivial enough to give it a quick shot), but I'll post back here as soon as I have a chance.
In your latest post, you say that the --static-swift-stdlib flag "doesnât help with Foundation either because itâs not part of the standard Swift library," but that isn't true, at least on linux and Android: the flag is misnamed and will try to statically link all Swift runtime libraries, eg XCTest too if you're testing. We should probably rename the flag to describe what it actually does.
Regarding the broader issues you see with C/C++ interfacing or other SwiftPM difficulties, I encourage you to file your specific error output under existing issues or open your own. There are plans for better CMake integration and so on that would benefit from your concrete feedback.
Good to know, but I prefer to stay clear of anything that has inconsistent behavior across platforms, and static linking appears to be one of those things. I'd rather not rely on it. In fact, I intend to get rid of Foundation soon.
You know, I wouldn't like to report issues without being 100% sure that there is an issue, and gaining such certainty can be very time-consuming in experimental endeavors. Writing those posts is my sweet spot between yapping and reporting official issues, but the need for my project to be production-ready today leaves me with very little room for further analysis, as in "I can't afford to figure out" why SwiftPM doesn't work for my use case. In a sense, picking CMake over SwiftPM is a necessity more than a preference.
Nevertheless, you can tell from my convoluted Package.swift how much I experimented with it before throwing in the towel. :D
Back on topic, let's see if this helps others looking to resolve a similar problem.
Context
To give a better context, my library is a mixed Swift/C codebase, cross-platform, targeted at all supported Swift platforms, including Android with the proper Swift SDK. The library is quite complex in that it has several third-party dependencies, written in other languages (C, C++, Go, and whatnot), and built with other build systems. SwiftPM makes this tricky, so I resorted to CMake.
Swift SDKs suit SwiftPM best. Initially, I built third parties with CMake, and the Swift side with SwiftPM plus an insane amount of unsafe flags. This was obnoxious, hence the question of this post.
Toolchain file
With the suggestions from @Finagolfin, I drafted the following toolchain file for CMake, feel free to reuse it. For example, aarch64 could also be made a variable:
The toolchain relies on a few environment variables:
I develop on macOS, and when I use the Swift for Android SDK, I hit compatibility issues with the swiftc compiler from Xcode. My workaround is to use swiftly run swiftc instead, but for this to work I had to feed a simple wrapper script to CMake:
Superproject
The CMake project is a superbuild made of these subprojects:
The core Swift/C library, which maintains the Sources/Tests layout for interop with SwiftPM
OpenSSL (C)
WireGuard (Go)
Wintun (a prebuilt .dll for Windows)
The Android toolchain defines the ANDROID variable so that each third party can act accordingly. For example, OpenSSL requires additional flags for Android targets:
if(ANDROID)
set(OPENSSL_TARGET "android-arm64") # I could hardcode arm64 here
set(OPENSSL_SYMBOLS "-D__ANDROID_API__=${CMAKE_SYSTEM_VERSION}")
else()
set(OPENSSL_TARGET "")
set(OPENSSL_SYMBOLS "")
endif()
With the same approach, I could eventually integrate my library into Passepartout, the app that uses it. Here I describe the overall layout:
The Android project is just a stub, yet it has a working example of how the Swift library is invoked via JNI and @_cdecl. Look for passepartoutExampleSum and passepartoutExampleJSON to get into a deep rabbit hole.