diff --git a/ci/android-sdk.sh b/ci/android-sdk.sh new file mode 100755 index 000000000..aee133e3a --- /dev/null +++ b/ci/android-sdk.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env sh +# Copyright 2016 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +set -ex + +# Prep the SDK and emulator +# +# Note that the update process requires that we accept a bunch of licenses, and +# we can't just pipe `yes` into it for some reason, so we take the same strategy +# located in https://github.com/appunite/docker by just wrapping it in a script +# which apparently magically accepts the licenses. + +SDK=4333796 +mkdir sdk +curl --retry 20 https://dl.google.com/android/repository/sdk-tools-linux-${SDK}.zip -O +unzip -q -d sdk sdk-tools-linux-${SDK}.zip + +case "$1" in + arm | armv7) + api=24 + image="system-images;android-${api};google_apis;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" + exit 1 + ;; +esac; + +# Try to fix warning about missing file. +# See https://askubuntu.com/a/1078784 +mkdir -p /root/.android/ +echo '### User Sources for Android SDK Manager' >> /root/.android/repositories.cfg +echo '#Fri Nov 03 10:11:27 CET 2017 count=0' >> /root/.android/repositories.cfg + +# Print all available packages +# yes | ./sdk/tools/bin/sdkmanager --list --verbose + +# --no_https avoids +# javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found +# +# | grep -v = || true removes the progress bar output from the sdkmanager +# which produces an insane amount of output. +yes | ./sdk/tools/bin/sdkmanager --licenses --no_https | grep -v = || true +yes | ./sdk/tools/bin/sdkmanager --no_https \ + "emulator" \ + "platform-tools" \ + "platforms;android-${api}" \ + "${image}" | grep -v = || true + +echo "no" | + ./sdk/tools/bin/avdmanager create avd \ + --name "${1}" \ + --package "${image}" | grep -v = || true + diff --git a/ci/docker/arm-linux-androideabi/Dockerfile b/ci/docker/arm-linux-androideabi/Dockerfile index 10799974e..7cfdae639 100644 --- a/ci/docker/arm-linux-androideabi/Dockerfile +++ b/ci/docker/arm-linux-androideabi/Dockerfile @@ -11,8 +11,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY android-ndk.sh / RUN /android-ndk.sh arm -ENV PATH=$PATH:/android-toolchain/bin +WORKDIR /android +COPY android-sdk.sh /android/sdk.sh +RUN ./sdk.sh arm +RUN mv /root/.android /tmp +RUN chmod 777 -R /tmp/.android +RUN chmod 755 /android/sdk/tools/* /android/sdk/emulator/qemu/linux-x86_64/* +ENV PATH=$PATH:/android-toolchain/bin:/android/sdk/platform-tools # TODO: run tests in an emulator eventually ENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ - CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER="true" + CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=/tmp/runtest \ + HOME=/tmp + +ADD runtest-android.rs /tmp/runtest.rs +ENTRYPOINT [ \ + "bash", \ + "-c", \ + # set SHELL so android can detect a 64bits system, see + # http://stackoverflow.com/a/41789144 + "SHELL=/bin/dash /android/sdk/emulator/emulator @arm -no-window & \ + /rust/bin/rustc /tmp/runtest.rs -o /tmp/runtest && \ + exec \"$@\"", \ + "--" \ +] diff --git a/ci/runtest-android.rs b/ci/runtest-android.rs new file mode 100644 index 000000000..dc70121dc --- /dev/null +++ b/ci/runtest-android.rs @@ -0,0 +1,50 @@ +use std::env; +use std::process::Command; +use std::path::{Path, PathBuf}; + +fn main() { + let args = env::args_os() + .skip(1) + .filter(|arg| arg != "--quiet") + .collect::>(); + assert_eq!(args.len(), 1); + let test = PathBuf::from(&args[0]); + let dst = Path::new("/data/local/tmp").join(test.file_name().unwrap()); + + println!("waiting for device to come online..."); + let status = Command::new("adb") + .arg("wait-for-device") + .status() + .expect("failed to run: adb wait-for-device"); + assert!(status.success()); + + println!("pushing executable..."); + let status = Command::new("adb") + .arg("push") + .arg(&test) + .arg(&dst) + .status() + .expect("failed to run: adb pushr"); + assert!(status.success()); + + println!("executing tests..."); + let output = Command::new("adb") + .arg("shell") + .arg(&dst) + .output() + .expect("failed to run: adb shell"); + assert!(status.success()); + + println!("status: {}\nstdout ---\n{}\nstderr ---\n{}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr)); + + let stdout = String::from_utf8_lossy(&output.stdout); + stdout.lines().find(|l| + (l.starts_with("PASSED ") && l.contains(" tests")) || + l.starts_with("test result: ok") + ).unwrap_or_else(|| { + panic!("failed to find successful test run"); + }); +} diff --git a/crates/backtrace-sys/build.rs b/crates/backtrace-sys/build.rs index f025dae68..517293000 100644 --- a/crates/backtrace-sys/build.rs +++ b/crates/backtrace-sys/build.rs @@ -56,10 +56,11 @@ fn main() { build.define("BACKTRACE_SUPPORTS_DATA", "0"); File::create(out_dir.join("config.h")).unwrap(); - if !target.contains("apple-ios") + if target.contains("android") { + maybe_enable_dl_iterate_phdr_android(&mut build); + } else if !target.contains("apple-ios") && !target.contains("solaris") && !target.contains("redox") - && !target.contains("android") && !target.contains("haiku") && !target.contains("vxworks") { @@ -122,3 +123,32 @@ fn main() { build.compile("backtrace"); } + +// The `dl_iterate_phdr` API was added in Android API 21+ (according to #227), +// so if we can dynamically detect an appropriately configured C compiler for +// that then let's enable the `dl_iterate_phdr` API, otherwise Android just +// won't have any information. +fn maybe_enable_dl_iterate_phdr_android(build: &mut cc::Build) { + let expansion = cc::Build::new().file("src/android-api.c").expand(); + let expansion = match std::str::from_utf8(&expansion) { + Ok(s) => s, + Err(_) => return, + }; + println!("expanded android version detection:\n{}", expansion); + let marker = "APIVERSION"; + let i = match expansion.find(marker) { + Some(i) => i, + None => return, + }; + let version = match expansion[i + marker.len() + 1..].split_whitespace().next() { + Some(s) => s, + None => return, + }; + let version = match version.parse::() { + Ok(n) => n, + Err(_) => return, + }; + if version >= 21 { + build.define("HAVE_DL_ITERATE_PHDR", "1"); + } +} diff --git a/crates/backtrace-sys/src/android-api.c b/crates/backtrace-sys/src/android-api.c new file mode 100644 index 000000000..1bfeadf5b --- /dev/null +++ b/crates/backtrace-sys/src/android-api.c @@ -0,0 +1,4 @@ +// Used from the build script to detect the value of the `__ANDROID_API__` +// builtin #define + +APIVERSION __ANDROID_API__