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