use crate::error::NdkError;
use crate::ndk::Ndk;
use crate::target::Target;
use std::path::Path;
use std::process::Command;
pub fn cargo_ndk(
ndk: &Ndk,
target: Target,
sdk_version: u32,
target_dir: impl AsRef<Path>,
) -> Result<Command, NdkError> {
let triple = target.rust_triple();
let clang_target = format!("--target={}{}", target.ndk_llvm_triple(), sdk_version);
let mut cargo = Command::new("cargo");
const SEP: &str = "\x1f";
let mut rustflags = match std::env::var("CARGO_ENCODED_RUSTFLAGS") {
Ok(val) => {
if std::env::var_os("RUSTFLAGS").is_some() {
panic!(
"Both `CARGO_ENCODED_RUSTFLAGS` and `RUSTFLAGS` were found in the environment, please clear one or the other before invoking this script"
);
}
val
}
Err(std::env::VarError::NotPresent) => {
match std::env::var("RUSTFLAGS") {
Ok(val) => {
cargo.env_remove("RUSTFLAGS");
val.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(SEP)
}
Err(std::env::VarError::NotPresent) => String::new(),
Err(std::env::VarError::NotUnicode(_)) => {
panic!("RUSTFLAGS environment variable contains non-unicode characters")
}
}
}
Err(std::env::VarError::NotUnicode(_)) => {
panic!("CARGO_ENCODED_RUSTFLAGS environment variable contains non-unicode characters")
}
};
let (clang, clang_pp) = ndk.clang()?;
cargo.env(format!("CC_{}", triple), &clang);
cargo.env(format!("CFLAGS_{}", triple), &clang_target);
cargo.env(format!("CXX_{}", triple), &clang_pp);
cargo.env(format!("CXXFLAGS_{}", triple), &clang_target);
cargo.env(cargo_env_target_cfg("LINKER", triple), &clang);
if !rustflags.is_empty() {
rustflags.push_str(SEP);
}
rustflags.push_str("-Clink-arg=");
rustflags.push_str(&clang_target);
let ar = ndk.toolchain_bin("ar", target)?;
cargo.env(format!("AR_{}", triple), &ar);
cargo.env(cargo_env_target_cfg("AR", triple), &ar);
if ndk.build_tag() > 7272597 {
let cargo_apk_link_dir = target_dir
.as_ref()
.join("cargo-apk-temp-extra-link-libraries");
std::fs::create_dir_all(&cargo_apk_link_dir)
.map_err(|e| NdkError::IoPathError(cargo_apk_link_dir.clone(), e))?;
let libgcc = cargo_apk_link_dir.join("libgcc.a");
std::fs::write(&libgcc, "INPUT(-lunwind)").map_err(|e| NdkError::IoPathError(libgcc, e))?;
rustflags.push_str(SEP);
rustflags.push_str("-L");
rustflags.push_str(SEP);
rustflags.push_str(
cargo_apk_link_dir
.to_str()
.expect("Target dir must be valid UTF-8"),
);
}
cargo.env("CARGO_ENCODED_RUSTFLAGS", rustflags);
Ok(cargo)
}
fn cargo_env_target_cfg(tool: &str, target: &str) -> String {
let utarget = target.replace('-', "_");
let env = format!("CARGO_TARGET_{}_{}", &utarget, tool);
env.to_uppercase()
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct VersionCode {
major: u8,
minor: u8,
patch: u8,
}
impl VersionCode {
pub fn new(major: u8, minor: u8, patch: u8) -> Self {
Self {
major,
minor,
patch,
}
}
pub fn from_semver(version: &str) -> Result<Self, NdkError> {
let mut iter = version.split(|c1| ['.', '-', '+'].iter().any(|c2| c1 == *c2));
let mut p = || {
iter.next()
.ok_or(NdkError::InvalidSemver)?
.parse()
.map_err(|_| NdkError::InvalidSemver)
};
Ok(Self::new(p()?, p()?, p()?))
}
pub fn to_code(&self, apk_id: u8) -> u32 {
(apk_id as u32) << 24
| (self.major as u32) << 16
| (self.minor as u32) << 8
| self.patch as u32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_semver() {
let v = VersionCode::from_semver("0.0.0").unwrap();
assert_eq!(v, VersionCode::new(0, 0, 0));
let v = VersionCode::from_semver("254.254.254-alpha.fix+2").unwrap();
assert_eq!(v, VersionCode::new(254, 254, 254));
}
}