1use {
2 crate::{error::NdkError, ndk::Ndk, target::Target},
3 std::{
4 env::{VarError, var, var_os},
5 fs::{create_dir_all, write},
6 path::Path,
7 process::Command,
8 },
9};
10
11pub fn cargo_ndk(
13 ndk: &Ndk,
14 target: Target,
15 sdk_version: u32,
16 target_dir: impl AsRef<Path>,
17) -> Result<Command, NdkError> {
18 let triple = target.rust_triple();
19 let clang_target = format!("--target={}{}", target.ndk_llvm_triple(), sdk_version);
20 let mut cargo = Command::new("cargo");
21
22 const SEP: &str = "\x1f";
23
24 let mut rustflags = match var("CARGO_ENCODED_RUSTFLAGS") {
26 Ok(val) => {
27 if var_os("RUSTFLAGS").is_some() {
28 panic!(
29 "Both `CARGO_ENCODED_RUSTFLAGS` and `RUSTFLAGS` were found in the environment, please clear one or the other before invoking this script"
30 );
31 }
32
33 val
34 }
35 Err(VarError::NotPresent) => {
36 match var("RUSTFLAGS") {
37 Ok(val) => {
38 cargo.env_remove("RUSTFLAGS");
39
40 val.split(' ')
43 .map(str::trim)
44 .filter(|s| !s.is_empty())
45 .collect::<Vec<_>>()
46 .join(SEP)
47 }
48 Err(VarError::NotPresent) => String::new(),
49 Err(VarError::NotUnicode(_)) => {
50 panic!("RUSTFLAGS environment variable contains non-unicode characters")
51 }
52 }
53 }
54 Err(VarError::NotUnicode(_)) => {
55 panic!("CARGO_ENCODED_RUSTFLAGS environment variable contains non-unicode characters")
56 }
57 };
58
59 let (clang, clang_pp) = ndk.clang()?;
60
61 cargo.env(format!("CC_{}", triple), &clang);
64 cargo.env(format!("CFLAGS_{}", triple), &clang_target);
65 cargo.env(format!("CXX_{}", triple), &clang_pp);
66 cargo.env(format!("CXXFLAGS_{}", triple), &clang_target);
67
68 cargo.env(cargo_env_target_cfg("LINKER", triple), &clang);
71 if !rustflags.is_empty() {
72 rustflags.push_str(SEP);
73 }
74 rustflags.push_str("-Clink-arg=");
75 rustflags.push_str(&clang_target);
76
77 let ar = ndk.toolchain_bin("ar", target)?;
78 cargo.env(format!("AR_{}", triple), &ar);
79 cargo.env(cargo_env_target_cfg("AR", triple), &ar);
80
81 if ndk.build_tag() > 7272597 {
88 let cargo_apk_link_dir = target_dir
89 .as_ref()
90 .join("cargo-apk-temp-extra-link-libraries");
91 create_dir_all(&cargo_apk_link_dir)
92 .map_err(|e| NdkError::IoPathError(cargo_apk_link_dir.clone(), e))?;
93 let libgcc = cargo_apk_link_dir.join("libgcc.a");
94 write(&libgcc, "INPUT(-lunwind)").map_err(|e| NdkError::IoPathError(libgcc, e))?;
95
96 rustflags.push_str(SEP);
104 rustflags.push_str("-L");
105 rustflags.push_str(SEP);
106 rustflags.push_str(
107 cargo_apk_link_dir
108 .to_str()
109 .expect("Target dir must be valid UTF-8"),
110 );
111 }
112
113 cargo.env("CARGO_ENCODED_RUSTFLAGS", rustflags);
114
115 Ok(cargo)
116}
117
118fn cargo_env_target_cfg(tool: &str, target: &str) -> String {
119 let utarget = target.replace('-', "_");
120 let env = format!("CARGO_TARGET_{}_{}", &utarget, tool);
121 env.to_uppercase()
122}
123
124#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
125pub struct VersionCode {
126 major: u8,
127 minor: u8,
128 patch: u8,
129}
130
131impl VersionCode {
132 pub fn new(major: u8, minor: u8, patch: u8) -> Self {
133 Self {
134 major,
135 minor,
136 patch,
137 }
138 }
139
140 pub fn from_semver(version: &str) -> Result<Self, NdkError> {
141 let mut iter = version.split(|c1| ['.', '-', '+'].iter().any(|c2| c1 == *c2));
142 let mut p = || {
143 iter.next()
144 .ok_or(NdkError::InvalidSemver)?
145 .parse()
146 .map_err(|_| NdkError::InvalidSemver)
147 };
148 Ok(Self::new(p()?, p()?, p()?))
149 }
150
151 pub fn to_code(&self, apk_id: u8) -> u32 {
152 (apk_id as u32) << 24
153 | (self.major as u32) << 16
154 | (self.minor as u32) << 8
155 | self.patch as u32
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn from_semver() {
165 let v = VersionCode::from_semver("0.0.0").unwrap();
166 assert_eq!(v, VersionCode::new(0, 0, 0));
167 let v = VersionCode::from_semver("254.254.254-alpha.fix+2").unwrap();
168 assert_eq!(v, VersionCode::new(254, 254, 254));
169 }
170}