Add aarch64-linux-android support (#860)

This commit is contained in:
Divy Srivastava 2022-01-05 02:02:48 +05:30 committed by GitHub
parent b3e09e69a0
commit a29740df6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1719 additions and 12 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[target.aarch64-linux-android]
linker = "./third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++"

View File

@ -24,30 +24,42 @@ jobs:
- os: macOS-latest
target: x86_64-apple-darwin
variant: debug
cargo: cargo
- os: macOS-latest
target: x86_64-apple-darwin
variant: release
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'ubuntu-latest-xl' || 'ubuntu-18.04' }}
target: x86_64-unknown-linux-gnu
variant: debug
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'ubuntu-latest-xl' || 'ubuntu-18.04' }}
target: x86_64-unknown-linux-gnu
variant: release
cargo: cargo
- os: windows-2019
target: x86_64-pc-windows-msvc
variant: release # Note: we do not support windows debug builds.
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'ubuntu-latest-xl' || 'ubuntu-18.04' }}
target: aarch64-unknown-linux-gnu
variant: debug
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'ubuntu-latest-xl' || 'ubuntu-18.04' }}
target: aarch64-unknown-linux-gnu
variant: release
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'ubuntu-latest-xl' || 'ubuntu-18.04' }}
target: aarch64-linux-android
variant: release # Note: v8 debug builds on QEMU is buggy.
cargo: cross
env:
V8_FROM_SOURCE: true
@ -158,14 +170,24 @@ jobs:
. $basename/sccache --start-server
echo "$(pwd)/$basename" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install cross
if: matrix.config.target == 'aarch64-linux-android'
run: cargo install cross
- name: Build cross docker image
if: matrix.config.target == 'aarch64-linux-android'
run: docker build -t cross:aarch64-linux-android-0.2.1 .
- name: Test
env:
SCCACHE_IDLE_TIMEOUT: 0
run:
cargo test -vv --all-targets --locked ${{ env.CARGO_VARIANT_FLAG }}
${{ matrix.config.cargo }} test -vv --all-targets --locked ${{ env.CARGO_VARIANT_FLAG }}
--target ${{ matrix.config.target }}
- name: Clippy
run:
cargo clippy --all-targets --locked ${{ env.CARGO_VARIANT_FLAG }}
${{ matrix.config.cargo }} clippy --all-targets --locked ${{ env.CARGO_VARIANT_FLAG }}
--target ${{ matrix.config.target }} -- -D clippy::all
- name: Rustfmt

5
.gitignore vendored
View File

@ -6,3 +6,8 @@
/.cache
/target/
/compile_commands.json
third_party/android_ndk
third_party/android_platform
third_party/catapult
third_party/llvm-build

1358
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ description = "Rust bindings to V8"
readme = "README.md"
authors = ["the Deno authors"]
license = "MIT"
edition = "2018"
edition = "2021"
repository = "https://github.com/denoland/rusty_v8"
exclude = [
@ -84,9 +84,15 @@ which = "4.1.0"
fslock = "0.1.6"
[dev-dependencies]
trybuild = "1.0.42"
trybuild = "1.0.53"
align-data = "0.1.0"
[target.'cfg(target_os = "android")'.dependencies]
winit = "0.26"
pixels = "0.8.0"
ndk = "0.3.0"
ndk-glue = { version = "0.5.0", features = ["logger"] }
[[example]]
name = "hello_world"
@ -95,3 +101,7 @@ name = "shell"
[[example]]
name = "process"
[[example]]
name = "android_fractal"
crate-type = ["cdylib"]

11
Cross.toml Normal file
View File

@ -0,0 +1,11 @@
[target.aarch64-linux-android]
image = "cross:aarch64-linux-android-0.2.1"
[build.env]
passthrough = [
"V8_FROM_SOURCE",
"SCCACHE_ERROR_LOG",
"SCCACHE_LOG",
"SCCACHE_DIR",
"SCCACHE_IDLE_TIMEOUT"
]

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM rustembedded/cross:aarch64-linux-android-0.2.1
RUN apt update && \
apt install -y curl && \
curl -L https://github.com/mozilla/sccache/releases/download/v0.2.15/sccache-v0.2.15-x86_64-unknown-linux-musl.tar.gz | tar xzf -
ENV TZ=Etc/UTC
COPY ./build/*.sh /chromium_build/
RUN \
DEBIAN_FRONTEND=noninteractive \
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \
&& apt-get update && apt-get install -y lsb-release sudo \
&& /chromium_build/install-build-deps-android.sh \
&& rm -rf /chromium_build \
&& rm -rf /var/lib/apt/lists/*
RUN chmod +x /sccache-v0.2.15-x86_64-unknown-linux-musl/sccache
ENV SCCACHE=/sccache-v0.2.15-x86_64-unknown-linux-musl/sccache
ENV SCCACHE_DIR=./target/sccache

View File

@ -103,6 +103,7 @@ fn build_v8() {
// On windows, rustc cannot link with a V8 debug build.
let mut gn_args = if is_debug() && !cfg!(target_os = "windows") {
// Note: When building for Android aarch64-qemu, use release instead of debug.
vec!["is_debug=true".to_string()]
} else {
vec!["is_debug=false".to_string()]
@ -125,8 +126,14 @@ fn build_v8() {
// we can't use chromiums clang plugins with a system clang
gn_args.push("clang_use_chrome_plugins=false".to_string());
} else {
println!("using Chromiums clang");
let clang_base_path = clang_download();
gn_args.push(format!("clang_base_path={:?}", clang_base_path));
if cfg!(target_os = "android") && cfg!(target_arch = "aarch64") {
gn_args.push("clang_use_chrome_plugins=false".to_string());
gn_args.push("treat_warnings_as_errors=false".to_string());
}
}
if let Some(p) = env::var_os("SCCACHE") {
@ -151,12 +158,43 @@ fn build_v8() {
// check if the target triple describes a non-native environment
if target_triple != env::var("HOST").unwrap() {
// cross-compilation setup
if target_triple == "aarch64-unknown-linux-gnu" {
if target_triple == "aarch64-unknown-linux-gnu"
|| target_triple == "aarch64-linux-android"
{
gn_args.push(r#"target_cpu="arm64""#.to_string());
gn_args.push("use_sysroot=true".to_string());
maybe_install_sysroot("arm64");
maybe_install_sysroot("amd64");
};
if target_triple == "aarch64-linux-android" {
gn_args.push("is_component_build=false".to_string());
gn_args.push(r#"v8_target_cpu="arm64""#.to_string());
gn_args.push(r#"target_os="android""#.to_string());
gn_args.push("treat_warnings_as_errors=false".to_string());
// NDK 23 and above removes libgcc entirely.
// https://github.com/rust-lang/rust/pull/85806
maybe_clone_repo(
"./third_party/android_ndk",
"https://github.com/denoland/android_ndk.git",
);
static CHROMIUM_URI: &str = "https://chromium.googlesource.com";
maybe_clone_repo(
"./third_party/android_platform",
&format!(
"{}/chromium/src/third_party/android_platform.git",
CHROMIUM_URI
),
);
maybe_clone_repo(
"./third_party/catapult",
&format!("{}/catapult.git", CHROMIUM_URI),
);
};
}
if target_triple.starts_with("i686-") {
@ -171,6 +209,19 @@ fn build_v8() {
build("rusty_v8", None);
}
fn maybe_clone_repo(dest: &str, repo: &str) {
if !Path::new(&dest).exists() {
assert!(Command::new("git")
.arg("clone")
.arg("--depth=1")
.arg(repo)
.arg(dest)
.status()
.unwrap()
.success());
}
}
fn maybe_install_sysroot(arch: &str) {
let sysroot_path = format!("build/linux/debian_sid_{}-sysroot", arch);
if !PathBuf::from(sysroot_path).is_dir() {
@ -387,6 +438,10 @@ fn is_compatible_clang_version(clang_path: &Path) -> bool {
}
fn find_compatible_system_clang() -> Option<PathBuf> {
if cfg!(target_os = "android") {
return None;
}
if let Ok(p) = env::var("CLANG_BASE_PATH") {
let base_path = Path::new(&p);
let clang_path = base_path.join("bin").join("clang");
@ -395,7 +450,6 @@ fn find_compatible_system_clang() -> Option<PathBuf> {
}
}
println!("using Chromiums clang");
None
}

View File

@ -0,0 +1,69 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
function DrawFrame(frameLen) {
const u8 = new Uint8Array(frameLen);
const width = 800;
const height = 800;
let x = y = 0;
for (let i = 0; i < u8.byteLength; i += 4) {
if (x == width) {
y++;
x = 0;
}
x += 1;
let r = Math.floor(0.3 * x);
let b = Math.floor(0.3 * y);
u8.set([r, 0x00, b, 0xff], i);
}
let scale_x = 3.0 / width;
let scale_y = 3.0 / height;
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let cx = y * scale_x - 1.5;
let cy = x * scale_y - 1.5;
let c = new Complex(-0.4, 0.6);
let z = new Complex(cx, cy);
let i = 0;
while (i < 100 && z.abs() < 2) {
z = z.mul(z).add(c);
i++;
}
u8.set([0x00, i, 0x00, 0xff], (y * width + x) * 4);
}
}
return u8.buffer;
}
class Complex {
constructor(real, imag) {
this.real = real;
this.imag = imag;
}
mul(other) {
return new Complex(
this.real * other.real - this.imag * other.imag,
this.real * other.imag + this.imag * other.real,
);
}
add(other) {
return new Complex(
this.real + other.real,
this.imag + other.imag,
);
}
abs() {
return Math.sqrt(this.real * this.real + this.imag * this.imag);
}
}

153
examples/android_fractal.rs Normal file
View File

@ -0,0 +1,153 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
// Don't run on non-Android targets.
#![cfg(target_os = "android")]
// Don't run this as a test in `--all-targets` mode.
#![cfg(not(test))]
use pixels::Pixels;
use pixels::SurfaceTexture;
use std::cell::Cell;
use winit::platform::run_return::EventLoopExtRunReturn;
#[ndk_glue::main(
backtrace = "on",
logger(level = "debug", tag = "android_fractal")
)]
fn main() {
let mut event_loop = winit::event_loop::EventLoop::new();
let window = winit::window::WindowBuilder::new()
.with_title("rusty_v8 android_fractal")
.build(&event_loop)
.unwrap();
// Initialize V8.
let platform = v8::new_default_platform(0, false).make_shared();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let source =
v8::String::new(&mut scope, include_str!("android_fractal.js")).unwrap();
let context = v8::Context::new(&mut scope);
let mut context_scope = v8::ContextScope::new(&mut scope, context);
execute_script(&mut context_scope, source);
let draw_str = v8::String::new(&mut context_scope, "DrawFrame").unwrap();
let draw_fn = context
.global(&mut context_scope)
.get(&mut context_scope, draw_str.into())
.expect("missing function DrawFrame");
let draw_fn =
v8::Local::<v8::Function>::try_from(draw_fn).expect("function expected");
let mut allowed = false;
loop {
event_loop.run_return(|event, _, control_flow| {
*control_flow = winit::event_loop::ControlFlow::Wait;
match event {
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::CloseRequested,
..
} => *control_flow = winit::event_loop::ControlFlow::Exit,
// Drawing on android must only happen before Event::Suspended and
// after Event::Resumed.
//
// https://github.com/rust-windowing/winit/issues/1588
winit::event::Event::Resumed => {
allowed = true;
}
winit::event::Event::Suspended => {
allowed = false;
}
winit::event::Event::RedrawRequested(_) => {
if !allowed {
return;
}
let surface_texture = SurfaceTexture::new(800, 800, &window);
let mut pixels = Pixels::new(800, 800, surface_texture).unwrap();
draw(&mut context_scope, draw_fn, pixels.get_frame());
if pixels.render().is_err() {
*control_flow = winit::event_loop::ControlFlow::Exit;
return;
}
}
_ => {}
}
window.request_redraw();
});
}
}
fn execute_script(
context_scope: &mut v8::ContextScope<v8::HandleScope>,
script: v8::Local<v8::String>,
) {
let scope = &mut v8::HandleScope::new(context_scope);
let try_catch = &mut v8::TryCatch::new(scope);
let script = v8::Script::compile(try_catch, script, None)
.expect("failed to compile script");
if script.run(try_catch).is_none() {
let exception_string = try_catch
.stack_trace()
.or_else(|| try_catch.exception())
.map(|value| value.to_rust_string_lossy(try_catch))
.unwrap_or_else(|| "no stack trace".into());
panic!("{}", exception_string);
}
}
fn draw(
context_scope: &mut v8::ContextScope<v8::HandleScope>,
draw_fn: v8::Local<v8::Function>,
frame: &mut [u8],
) {
let scope = &mut v8::HandleScope::new(context_scope);
let recv = v8::undefined(scope);
let try_catch = &mut v8::TryCatch::new(scope);
let len = frame.len();
let frame_len = v8::Integer::new(try_catch, len as i32);
let ab = match draw_fn.call(try_catch, recv.into(), &[frame_len.into()]) {
Some(ab) => ab,
None => {
let exception_string = try_catch
.stack_trace()
.or_else(|| try_catch.exception())
.map(|value| value.to_rust_string_lossy(try_catch))
.unwrap_or_else(|| "no stack trace".into());
panic!("{}", exception_string);
}
};
let ab =
v8::Local::<v8::ArrayBuffer>::try_from(ab).expect("array buffer expected");
let bs = ab.get_backing_store();
let js_frame = unsafe { get_backing_store_slice(&bs, 0, len) };
frame.copy_from_slice(js_frame.as_ref());
}
unsafe fn get_backing_store_slice(
backing_store: &v8::SharedRef<v8::BackingStore>,
byte_offset: usize,
byte_length: usize,
) -> &[u8] {
let cells: *const [Cell<u8>] =
&backing_store[byte_offset..byte_offset + byte_length];
let bytes = cells as *const [u8];
&*bytes
}

View File

@ -121,7 +121,6 @@ pub struct CompiledWasmModule(*mut InternalCompiledWasmModule);
impl CompiledWasmModule {
/// Get the (wasm-encoded) wire bytes that were used to compile this module.
pub fn get_wire_bytes_ref(&self) -> &[u8] {
use std::convert::TryInto;
let mut len = 0isize;
unsafe {
let ptr = v8__CompiledWasmModule__GetWireBytesRef(self.0, &mut len);

View File

@ -857,6 +857,9 @@ fn thread_safe_handle_drop_after_isolate() {
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 0);
}
// QEMU doesn't like when we spawn threads
// This works just fine on real hardware
#[cfg(not(target_os = "android"))]
#[test]
fn terminate_execution() {
let _setup_guard = setup();
@ -4838,6 +4841,8 @@ fn value_serializer_not_implemented() {
);
}
// Flaky on aarch64-qemu (Stack corruption).
#[cfg(not(target_os = "android"))]
#[test]
fn clear_kept_objects() {
let _setup_guard = setup();
@ -5529,6 +5534,7 @@ fn counter_lookup_callback() {
assert_ne!(count, 0);
}
#[cfg(not(target_os = "android"))]
#[test]
fn compiled_wasm_module() {
let _setup_guard = setup();

View File

@ -1,3 +1,7 @@
// This is flaky on cross (QEMU bug)
// but otherwise works fine on real device.
#![cfg(not(target_os = "android"))]
use std::iter::repeat_with;
use std::thread;

View File

@ -1,5 +1,7 @@
use std::env;
// Don't run UI tests on emulated environment.
#[cfg(not(target_os = "android"))]
#[test]
fn ui() {
// This environment variable tells build.rs that we're running trybuild tests,