Skip to content

Commit

Permalink
fix: android builds (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
triniwiz authored Jan 17, 2024
1 parent a657645 commit 3d49fa0
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 54 deletions.
13 changes: 7 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ serde_json = "1.0"
[build-dependencies]
cc = { version = "1.0.83", features = ["parallel"] }
link_args = "0.6"
regex = { version = "1.10.2", features = [] }

[package.metadata.docs.rs]
features = ["serde"]
Expand Down
223 changes: 176 additions & 47 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,115 @@
use std::env;
use regex::Regex;
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::{env, fmt};

#[derive(Clone, Debug)]
pub struct Target {
pub architecture: String,
pub vendor: String,
pub system: Option<String>,
pub abi: Option<String>,
}

impl Target {
pub fn as_strs(&self) -> (&str, &str, Option<&str>, Option<&str>) {
(
self.architecture.as_str(),
self.vendor.as_str(),
self.system.as_deref(),
self.abi.as_deref(),
)
}
}

impl Display for Target {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}-{}", &self.architecture, &self.vendor)?;

if let Some(ref system) = self.system {
write!(f, "-{}", system)
} else {
Ok(())
}?;

if let Some(ref abi) = self.abi {
write!(f, "-{}", abi)
} else {
Ok(())
}
}
}

pub fn ndk() -> String {
env::var("ANDROID_NDK").expect("ANDROID_NDK variable not set")
}

pub fn target_arch(arch: &str) -> &str {
match arch {
"armv7" => "arm",
"aarch64" => "arm64",
"i686" => "x86",
arch => arch,
}
}

fn host_tag() -> &'static str {
// Because this is part of build.rs, the target_os is actually the host system
if cfg!(target_os = "windows") {
"windows-x86_64"
} else if cfg!(target_os = "linux") {
"linux-x86_64"
} else if cfg!(target_os = "macos") {
"darwin-x86_64"
} else {
panic!("host os is not supported")
}
}

/// Get NDK major version from source.properties
fn ndk_major_version(ndk_dir: &Path) -> u32 {
// Capture version from the line with Pkg.Revision
let re = Regex::new(r"Pkg.Revision = (\d+)\.(\d+)\.(\d+)").unwrap();
// There's a source.properties file in the ndk directory, which contains
let mut source_properties =
File::open(ndk_dir.join("source.properties")).expect("Couldn't open source.properties");
let mut buf = String::new();
source_properties
.read_to_string(&mut buf)
.expect("Could not read source.properties");
// Capture version info
let captures = re
.captures(&buf)
.expect("source.properties did not match the regex");
// Capture 0 is the whole line of text
captures[1].parse().expect("could not parse major version")
}

// Taken from https://github.com/Brooooooklyn/ada-url/blob/main/ada/build.rs
fn main() {
println!("cargo:rerun-if-changed=deps/ada.cpp");
println!("cargo:rerun-if-changed=deps/ada.h");
println!("cargo:rerun-if-changed=deps/ada_c.h");
let target_str = env::var("TARGET").unwrap();
let target: Vec<String> = target_str.split('-').map(|s| s.into()).collect();
assert!(target.len() >= 2, "Failed to parse TARGET {}", target_str);

let abi = if target.len() > 3 {
Some(target[3].clone())
} else {
None
};

let system = if target.len() > 2 {
Some(target[2].clone())
} else {
None
};

let target = Target {
architecture: target[0].clone(),
vendor: target[1].clone(),
system,
abi,
};

let mut build = cc::Build::new();
build
Expand All @@ -20,52 +125,76 @@ fn main() {
let compile_target_feature = env::var("CARGO_CFG_TARGET_FEATURE");
// Except for Emscripten target (which emulates POSIX environment), compile to Wasm via WASI SDK
// which is currently the only standalone provider of stdlib for compilation of C/C++ libraries.
if compile_target_arch.starts_with("wasm") && compile_target_os != "emscripten" {
let wasi_sdk = env::var("WASI_SDK").unwrap_or_else(|_| "/opt/wasi-sdk".to_owned());
assert!(
std::path::Path::new(&wasi_sdk).exists(),
"WASI SDK not found at {wasi_sdk}"
);
build.compiler(format!("{wasi_sdk}/bin/clang++"));
let wasi_sysroot_lib = match compile_target_feature {
Ok(compile_target_feature) if compile_target_feature.contains("atomics") => {
"wasm32-wasi-threads"

match target.system.as_deref() {
Some("android" | "androideabi") => {
let ndk = ndk();
let major = ndk_major_version(Path::new(&ndk));
if major < 22 {
build
.flag(&format!("--sysroot={}/sysroot", ndk))
.flag(&format!(
"-isystem{}/sources/cxx-stl/llvm-libc++/include",
ndk
));
} else {
// NDK versions >= 22 have the sysroot in the llvm prebuilt by
let host_toolchain = format!("{}/toolchains/llvm/prebuilt/{}", ndk, host_tag());
// sysroot is stored in the prebuilt llvm, under the host
build.flag(&format!("--sysroot={}/sysroot", host_toolchain));
}
_ => "wasm32-wasi",
};
println!("cargo:rustc-link-search={wasi_sdk}/share/wasi-sysroot/lib/{wasi_sysroot_lib}");
// Wasm exceptions are new and not yet supported by WASI SDK.
build.flag("-fno-exceptions");
// WASI SDK only has libc++ available.
build.cpp_set_stdlib("c++");
// Explicitly link C++ ABI to avoid linking errors (it takes care of C++ -> C "lowering").
println!("cargo:rustc-link-lib=c++abi");
// Because Ada is a pure parsing library that doesn't need any OS syscalls,
// it's also possible to compile it to wasm32-unknown-unknown.
// This still requires WASI SDK for libc & libc++, but then we need a few hacks / overrides to get a pure Wasm w/o imports instead.
if compile_target_os == "unknown" {
build.target("wasm32-wasi");
println!("cargo:rustc-link-lib=c");
build.file("./deps/wasi_to_unknown.cpp");
}
} else if !(compile_target_os == "windows" && compile_target_env == "msvc") {
build.compiler("clang++");
}

let compiler = build.get_compiler();
// Note: it's possible to use Clang++ explicitly on Windows as well, so this check
// should be specifically for "is target compiler MSVC" and not "is target OS Windows".
if compiler.is_like_msvc() {
build.static_crt(true);
link_args::windows! {
unsafe {
no_default_lib(
"libcmt.lib",
_ => {
if compile_target_arch.starts_with("wasm") && compile_target_os != "emscripten" {
let wasi_sdk = env::var("WASI_SDK").unwrap_or_else(|_| "/opt/wasi-sdk".to_owned());
assert!(
Path::new(&wasi_sdk).exists(),
"WASI SDK not found at {wasi_sdk}"
);
build.compiler(format!("{wasi_sdk}/bin/clang++"));
let wasi_sysroot_lib = match compile_target_feature {
Ok(compile_target_feature) if compile_target_feature.contains("atomics") => {
"wasm32-wasi-threads"
}
_ => "wasm32-wasi",
};
println!(
"cargo:rustc-link-search={wasi_sdk}/share/wasi-sysroot/lib/{wasi_sysroot_lib}"
);
// Wasm exceptions are new and not yet supported by WASI SDK.
build.flag("-fno-exceptions");
// WASI SDK only has libc++ available.
build.cpp_set_stdlib("c++");
// Explicitly link C++ ABI to avoid linking errors (it takes care of C++ -> C "lowering").
println!("cargo:rustc-link-lib=c++abi");
// Because Ada is a pure parsing library that doesn't need any OS syscalls,
// it's also possible to compile it to wasm32-unknown-unknown.
// This still requires WASI SDK for libc & libc++, but then we need a few hacks / overrides to get a pure Wasm w/o imports instead.
if compile_target_os == "unknown" {
build.target("wasm32-wasi");
println!("cargo:rustc-link-lib=c");
build.file("./deps/wasi_to_unknown.cpp");
}
} else if !(compile_target_os == "windows" && compile_target_env == "msvc") {
build.compiler("clang++");
}
};
} else if compiler.is_like_clang() && cfg!(feature = "libcpp") {
build.cpp_set_stdlib("c++");

let compiler = build.get_compiler();
// Note: it's possible to use Clang++ explicitly on Windows as well, so this check
// should be specifically for "is target compiler MSVC" and not "is target OS Windows".
if compiler.is_like_msvc() {
build.static_crt(true);
link_args::windows! {
unsafe {
no_default_lib(
"libcmt.lib",
);
}
}
} else if compiler.is_like_clang() && cfg!(feature = "libcpp") {
build.cpp_set_stdlib("c++");
}
}
}

build.compile("ada");
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,9 @@ impl PartialEq for Url {
}

impl PartialOrd for Url {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(other)) }
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Url {
Expand Down

0 comments on commit 3d49fa0

Please sign in to comment.