diff --git a/ci/android-install-sdk.sh b/ci/android-install-sdk.sh index 43e1153dcd331..fa1e021ede5b4 100755 --- a/ci/android-install-sdk.sh +++ b/ci/android-install-sdk.sh @@ -14,25 +14,31 @@ mkdir -p sdk/cmdline-tools wget -q --tries=20 "https://dl.google.com/android/repository/commandlinetools-linux-${sdk}_latest.zip" unzip -q -d sdk/cmdline-tools "commandlinetools-linux-${sdk}_latest.zip" -case "$1" in +# The API level is passed in (arg 2) so it stays a single source of truth +# shared with the CC wrapper name in the Dockerfile; only the image variant +# is arch-specific here. +arch="$1" +api="$2" +if [ -z "${api}" ]; then + echo "usage: $0 " + exit 1 +fi + +case "${arch}" in arm | armv7) - api=24 image="system-images;android-${api};default;armeabi-v7a" ;; aarch64) - api=24 image="system-images;android-${api};google_apis;arm64-v8a" ;; i686) - api=28 image="system-images;android-${api};default;x86" ;; x86_64) - api=28 image="system-images;android-${api};default;x86_64" ;; *) - echo "invalid arch: $1" + echo "invalid arch: ${arch}" exit 1 ;; esac @@ -66,7 +72,7 @@ cp /android/android-emulator-package.xml /android/sdk/emulator/package.xml echo "no" | ./sdk/cmdline-tools/tools/bin/avdmanager create avd \ - --name "${1}" \ + --name "${arch}" \ --package "${image}" | grep -v = || true rm -rf "commandlinetools-linux-${sdk}_latest.zip" emulator-linux_x64-9058569.zip diff --git a/ci/docker/aarch64-linux-android/Dockerfile b/ci/docker/aarch64-linux-android/Dockerfile index 91c074150f94d..5d0ee0f9c1e4a 100644 --- a/ci/docker/aarch64-linux-android/Dockerfile +++ b/ci/docker/aarch64-linux-android/Dockerfile @@ -18,18 +18,22 @@ WORKDIR /android/ COPY android* /android/ ENV ANDROID_ARCH=aarch64 +# Single source of truth for the API level: it selects the emulator system +# image (passed to android-install-sdk.sh) and names the CC wrapper below, so +# the toolchain can never drift from the image the tests run on. +ARG ANDROID_API=24 ENV PATH=$PATH:/android/linux-x86_64/bin:/android/sdk/cmdline-tools/tools:/android/sdk/platform-tools RUN /android/android-install-ndk.sh -RUN /android/android-install-sdk.sh $ANDROID_ARCH +RUN /android/android-install-sdk.sh $ANDROID_ARCH $ANDROID_API RUN mv /root/.android /tmp RUN chmod 777 -R /tmp/.android RUN chmod 755 /android/sdk/cmdline-tools/tools/* /android/sdk/emulator/qemu/linux-x86_64/* ENV PATH=$PATH:/rust/bin \ - CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android28-clang \ + CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android${ANDROID_API}-clang \ CARGO_TARGET_AARCH64_LINUX_ANDROID_RUNNER=/tmp/runtest \ - CC_aarch64_linux_android=aarch64-linux-android28-clang \ + CC_aarch64_linux_android=aarch64-linux-android${ANDROID_API}-clang \ AR_aarch64_linux_android=llvm-ar \ HOME=/tmp diff --git a/ci/docker/arm-linux-androideabi/Dockerfile b/ci/docker/arm-linux-androideabi/Dockerfile index 657ff81097c7e..3317d53cbfcaf 100644 --- a/ci/docker/arm-linux-androideabi/Dockerfile +++ b/ci/docker/arm-linux-androideabi/Dockerfile @@ -18,18 +18,22 @@ WORKDIR /android/ COPY android* /android/ ENV ANDROID_ARCH=arm +# Single source of truth for the API level: it selects the emulator system +# image (passed to android-install-sdk.sh) and names the CC wrapper below, so +# the toolchain can never drift from the image the tests run on. +ARG ANDROID_API=24 ENV PATH=$PATH:/android/linux-x86_64/bin:/android/sdk/cmdline-tools/tools:/android/sdk/platform-tools RUN /android/android-install-ndk.sh -RUN /android/android-install-sdk.sh $ANDROID_ARCH +RUN /android/android-install-sdk.sh $ANDROID_ARCH $ANDROID_API RUN mv /root/.android /tmp RUN chmod 777 -R /tmp/.android RUN chmod 755 /android/sdk/cmdline-tools/tools/* /android/sdk/emulator/qemu/linux-x86_64/* ENV PATH=$PATH:/rust/bin \ - CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi28-clang \ + CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi${ANDROID_API}-clang \ CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=/tmp/runtest \ - CC_arm_linux_androideabi=armv7a-linux-androideabi28-clang \ + CC_arm_linux_androideabi=armv7a-linux-androideabi${ANDROID_API}-clang \ AR_arm_linux_androideabi=llvm-ar \ HOME=/tmp diff --git a/ci/docker/x86_64-linux-android/Dockerfile b/ci/docker/x86_64-linux-android/Dockerfile index 62eee968057bc..4b72b8c382fea 100644 --- a/ci/docker/x86_64-linux-android/Dockerfile +++ b/ci/docker/x86_64-linux-android/Dockerfile @@ -21,15 +21,19 @@ RUN curl -fsSL https://us-apt.pkg.dev/doc/repo-signing-key.gpg \ WORKDIR /android/ ENV ANDROID_ARCH=x86_64 +# The API level we compile against: this only names the CC wrapper and the toolchain we use. +# The Android Cuttlefish device is pinned separately and is newer, i.e. capable of testing +# newer APIs. The toolchain wrapper is held at the same baseline as arm/aarch64 for now. +ARG ANDROID_API=24 COPY android-install-ndk.sh /android/ RUN /android/android-install-ndk.sh ENV PATH=$PATH:/rust/bin:/android/linux-x86_64/bin \ ANDROID_SERIAL=127.0.0.1:6520 \ - CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android28-clang \ + CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android${ANDROID_API}-clang \ CARGO_TARGET_X86_64_LINUX_ANDROID_RUNNER=/tmp/runtest \ - CC_x86_64_linux_android=x86_64-linux-android28-clang \ - CXX_x86_64_linux_android=x86_64-linux-android28-clang++ \ + CC_x86_64_linux_android=x86_64-linux-android${ANDROID_API}-clang \ + CXX_x86_64_linux_android=x86_64-linux-android${ANDROID_API}-clang++ \ AR_x86_64_linux_android=llvm-ar \ HOME=/tmp diff --git a/libc-test/build.rs b/libc-test/build.rs old mode 100644 new mode 100755 index 61b71b8475826..b13a8369849c9 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -2060,6 +2060,10 @@ fn test_android(target: &str) { }; let x86 = target.contains("i686") || target.contains("x86_64"); let aarch64 = target.contains("aarch64"); + // The API level the NDK toolchain targets, which caps the available libc + // API surface (see `Versions`). Detection is required, so unwrap and let a + // toolchain we can't read fail loudly rather than silently skip every test. + let android = VERSIONS.android.unwrap(); let mut cfg = ctest_cfg(); cfg.define("_GNU_SOURCE", None); @@ -2251,8 +2255,7 @@ fn test_android(target: &str) { "posix_spawn_file_actions_t" => true, "posix_spawnattr_t" => true, - // Added in API level 24 - "if_nameindex" => true, + "if_nameindex" if android < 24 => true, _ => false, } @@ -2450,8 +2453,11 @@ fn test_android(target: &str) { "reallocarray" => true, "__system_property_wait" => true, - // Added in API level 30, but tests use level 28. - "memfd_create" | "mlock2" | "renameat2" | "statx" | "statx_timestamp" => true, + "memfd_create" | "mlock2" | "renameat2" | "statx" | "statx_timestamp" + if android < 30 => + { + true + } // Added in API level 33, but tests use level 28. "preadv2" | "pwritev2" => true, @@ -2459,34 +2465,23 @@ fn test_android(target: &str) { // Added in glibc 2.25. "getentropy" => true, - // Added in API level 28, but some tests use level 24. - "getrandom" => true, - - // Added in API level 28, but some tests use level 24. - "syncfs" => true, - - // Added in API level 28, but some tests use level 24. - "pthread_attr_getinheritsched" | "pthread_attr_setinheritsched" => true, - // Added in API level 28, but some tests use level 24. - "fread_unlocked" | "fwrite_unlocked" | "fgets_unlocked" | "fflush_unlocked" => true, - - // Added in API level 28, but some tests use level 24. - "aligned_alloc" => true, + "getrandom" | "syncfs" | "aligned_alloc" if android < 28 => true, - // Added in API level 26, but some tests use level 24. - "getgrent" => true, + "pthread_attr_getinheritsched" | "pthread_attr_setinheritsched" if android < 28 => true, - // Added in API level 26, but some tests use level 24. - "setgrent" => true, - - // Added in API level 26, but some tests use level 24. - "endgrent" => true, + "fread_unlocked" | "fwrite_unlocked" | "fgets_unlocked" | "fflush_unlocked" + if android < 28 => + { + true + } - // Added in API level 26, but some tests use level 24. - "getpwent" | "setpwent" | "endpwent" => true, + "getgrent" | "setgrent" | "endgrent" | "getpwent" | "setpwent" | "endpwent" + if android < 26 => + { + true + } - // Added in API level 26, but some tests use level 24. - "getdomainname" | "setdomainname" => true, + "getdomainname" | "setdomainname" if android < 26 => true, // FIXME(android): bad function pointers: "isalnum" | "isalpha" | "iscntrl" | "isdigit" | "isgraph" | "islower" | "isprint" @@ -2504,6 +2499,21 @@ fn test_android(target: &str) { } }); + cfg.skip_fn_ptrcheck(move |func| { + match func { + // termios functions are inlines below API 28, so + // their C-side address never matches the symbol Rust links. + "tcdrain" | "tcflow" | "tcflush" | "tcgetattr" | "tcgetsid" | "tcsendbreak" + | "tcsetattr" | "cfgetispeed" | "cfgetospeed" | "cfmakeraw" | "cfsetispeed" + | "cfsetospeed" | "cfsetspeed" + if android < 28 => + { + true + } + _ => false, + } + }); + cfg.skip_struct_field_type(move |struct_, field| { match (struct_.ident(), field.ident()) { // This is a weird union, don't check the type. @@ -6404,6 +6414,8 @@ struct Versions { macos: Option<(u32, u32)>, emscripten: Option<(u32, u32)>, wasi_sdk: Option<(u32, WasiVersion)>, + /// Android API level (no minor version). + android: Option, } #[derive(Clone, Copy, Debug, Default)] @@ -6432,6 +6444,13 @@ impl Versions { #include "gnu/libc-version.h" #endif + #ifdef __ANDROID__ + /* The clang driver predefines __ANDROID_MIN_SDK_VERSION__ from the API level + * in the target triple; including api-level.h ensures it is defined even on + * toolchains that leave it to the header. */ + #include "android/api-level.h" + #endif + #if defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) \ @@ -6501,6 +6520,11 @@ impl Versions { } "__GLIBC__" => ret.glibc.get_or_insert_default().0 = value.parse().unwrap(), "__GLIBC_MINOR__" => ret.glibc.get_or_insert_default().1 = value.parse().unwrap(), + // Clang 12+ predefines the target API level here as a plain integer. + // Every NDK wrapper we build with carries an API level, so parse it like + // the other version macros above and let a missing value fail loudly at + // the unwrap in `test_android` rather than silently skipping tests. + "__ANDROID_MIN_SDK_VERSION__" => ret.android = Some(value.parse().unwrap()), "__MAC_OS_X_VERSION_MAX_ALLOWED" => { let caps = mac_re.captures(value).unwrap(); let major: u32 = caps[1].parse().unwrap();