-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AArch64 Android support #13065
AArch64 Android support #13065
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. 👍
We should add this target to the smoke tests GHA.
I'd prefer to extract the change to System::User#name
implementation to a separate PR. It's a requirement to support bionic, but it's a general change not directly related to android.
Maybe also a refactor of the StaticFileHandler
time frame.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make sense to add a full test (even if at nightlies). Most of the specs run IIUC.
@@ -1,4 +1,4 @@ | |||
{% skip_file if flag?(:win32) %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this spec being run today in some ararch64 platforms? Why exclude it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is by sheer coincidence that it doesn't break on some AArch64 targets. To this date I don't think the situation has ever improved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean because of LLVM, or something Crystal specific? In the first case, maybe it got fixed (I couldn't find any open bug there)? In the second case, we should be tracking it. It doesn't work there in your M2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could implement the whole VaList
thing ourselves if we want to, similar to Rust (#9422 (comment)); until then we should not assume that VaList
is supported on any AArch64 targets at all. (This refers to the whole VaList
type and not just the @[Primitive(:va_arg)]
part.)
If by full test you mean a separate CI workflow (Android emulator runners do exist), I don't know if it is really worth the effort. |
Adding CI infra is out of scope for this PR. This is just platform support. |
👍
Sure. It was more of an exploratory question about how supported this platform could be. |
Adds bindings for Android's Bionic C runtime library for AArch64 targets. This is a more polished version of this branch, with all the lib funs manually verified against the Android headers. This allows building standard ELF executables that run on an Android shell.
Compiler and standard library changes
{{ flag?(:android) }}
returns true if the target system is Android. The directory name underlib_c
is now<architecture>-android
.LibC::ANDROID_API
(see below).System::User#name
is unavailable on Bionic, so that field now defaults to#username
.Android API level
The target Android API level, accessible via
LibC::ANDROID_API
, can be configured by either theANDROID_PLATFORM
or theANDROID_NATIVE_API_LEVEL
environment variable (the names come from the official CMake toolchain file), and should be one of the values from this list, optionally preceded byandroid-
. If neither environment variable is defined, the default API level is 31 (Android 12 / S). This is a fairly recent, but appropriate value, as all newly published Android apps must target this level too.The constant is defined in
src/lib_c.cr
, corresponding to__ANDROID_API__
being a built-in#define
with the Android toolchains. Bionic depends on this API level, and so does the Android NDK. The actual minimum version fully supported in this PR is 28; below this level, certain C functions are defined inline in the Bionic headers rather than exported, and this PR does not recreate them in Crystal.Simple cross-compilation
After rebuilding Crystal itself on a host with this PR applied, the following test program should demonstrate that cross-compilation is indeed working:
Compile with:
where
$ANDROID_NDK_ROOT
points to the root directory of your Android NDK installation,$HOST_TAG
would be one of the values listed here according to your host architecture, and$API_LEVEL
should match the declared API level in Crystal. The resultinga.out
should then run successfully under an ADB shell with an output like:Default prelude
Cross-compiling using the default prelude is a bit more involved because all the minimum runtime dependencies must also be cross-compiled. The following script builds Boehm GC, PCRE2, and libevent for Android:
This time we will build a more "complex" program:
Compile a statically linked binary with:
The output is just
Hello world from aarch64-apple-darwin22.2.0
this time.A dynamically linked library can be obtained by using
"-L.../dynamic"
instead. The .so files themselves should also be copied to somewhere on the device; since the system library directories are protected, they are ideally accessed viaLD_LIBRARY_PATH
. (Binaries built within Termux have aRUNPATH
that refers to Termux's own library directory, but the same applies.)Running Crystal on Termux
The Crystal compiler requires a linker, which is not straightforward to set up in a vanilla Android environment; Termux is a preferred choice for setting up a development environment that still uses the Android NDK. First run
bin/crystal build --cross-compile --target=aarch64-linux-android --release src/compiler/crystal.cr
on the host system (this will include playground and interpreter support). Then on Termux run:From here the
bin/crystal
wrapper script will pick up the newly built compiler, andmake install PREFIX=...
will produce a portable Crystal installation which can be subsequently used to rebuild Crystal itself on Termux.A handful of specs are disabled on Android because they are difficult to test on an unrooted device. Additionally, Bionic's iconv supports only UTF-8/16/32 and breaks on null characters. With those limitations in mind,
make std_spec compiler_spec primitives_spec FLAGS=-Duse_libiconv threads=1
is confirmed to pass on Termux.Just give me something that runs on an Android
Then you don't need this PR - Termux's PRoot Distro will be good enough, although that doesn't bind to Bionic. The main goal of this PR is to enable Android app development down the road, getting the compiler to run on Termux is just a bonus.
References