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 {
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")
}
}
fn ndk_major_version(ndk_dir: &Path) -> u32 {
let re = Regex::new(r"Pkg.Revision = (\d+)\.(\d+)\.(\d+)").unwrap();
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");
let captures = re
.captures(&buf)
.expect("source.properties did not match the regex");
captures[1].parse().expect("could not parse major version")
}
fn main() {
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
.file("./deps/ada.cpp")
.include("./deps")
.cpp(true)
.std("c++17");
let compile_target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH");
let compile_target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS");
let compile_target_feature = env::var("CARGO_CFG_TARGET_FEATURE");
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 {
let host_toolchain = format!("{}/toolchains/llvm/prebuilt/{}", ndk, host_tag());
build.flag(&format!("--sysroot={}/sysroot", host_toolchain));
}
}
_ => {
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}"
);
build.flag("-fno-exceptions");
build.cpp_set_stdlib("c++");
println!("cargo:rustc-link-lib=c++abi");
if compile_target_os == "unknown" {
build.target("wasm32-wasi");
println!("cargo:rustc-link-lib=c");
build.file("./deps/wasi_to_unknown.cpp");
}
}
let compiler = build.get_compiler();
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");
}