forked from rust-mobile/android-rs-glue
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.rs
277 lines (244 loc) · 9.74 KB
/
config.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
use cargo::core::Workspace;
use cargo::ops;
use cargo::util::errors::CargoError;
use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs;
use std::fs::File;
use std::io::Read;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use toml;
use toml::Parser as TomlParser;
pub struct AndroidConfig {
/// Path to the root of the Android SDK.
pub sdk_path: PathBuf,
/// Path to the root of the Android NDK.
pub ndk_path: PathBuf,
/// How to invoke `gradle`.
pub gradle_command: String,
/// Name that the package will have on the Android machine.
/// This is the key that Android uses to identify your package, so it should be unique for
/// for each application and should contain the vendor's name.
pub package_name: String,
/// Name of the project to feed to the SDK. This will be the name of the APK file.
/// Should be a "system-ish" name, like `my-project`.
pub project_name: String,
/// Label for the package.
pub package_label: String,
/// Name of the launcher icon.
/// Versions of this icon with different resolutions have to reside in the res folder
pub package_icon: Option<String>,
/// List of targets to build the app for. Eg. `arm-linux-androideabi`.
pub build_targets: Vec<String>,
/// Version of android for which to compile. TODO: ensure that >=18 because Rustc only supports 18+
pub android_version: u32,
/// Version of android:targetSdkVersion (optional). Default Value = android_version
pub target_sdk_version: u32,
/// Version of android:minSdkVersion (optional). Default Value = android_version
pub min_sdk_version: u32,
/// Version of the build tools to use
pub build_tools_version: String,
/// If `Some`, a path that contains the list of assets to ship as part of the package.
///
/// The assets can later be loaded with the runtime library.
pub assets_path: Option<PathBuf>,
/// If `Some`, a path that contains the list of resources to ship as part of the package.
///
/// The resources can later be loaded with the runtime library.
/// This folder contains for example the launcher icon, the styles and resolution dependent images.
pub res_path: Option<PathBuf>,
/// Should we build in release mode?
pub release: bool,
/// Should this app be in fullscreen mode (hides the title bar)?
pub fullscreen: bool,
/// Appends this string to the application attributes in the AndroidManifest.xml
pub application_attributes: Option<String>,
/// Appends this string to the activity attributes in the AndroidManifest.xml
pub activity_attributes: Option<String>,
/// The OpenGL ES major version in the AndroidManifest.xml
pub opengles_version_major: u8,
/// The OpenGL ES minor version in the AndroidManifest.xml
pub opengles_version_minor: u8,
}
pub fn load(
workspace: &Workspace,
flag_package: &Option<String>,
) -> Result<AndroidConfig, CargoError> {
// Find out the package requested by the user.
let package = {
let packages = Vec::from_iter(flag_package.iter().cloned());
let spec = ops::Packages::Packages(packages);
match spec {
ops::Packages::Default => unreachable!("cargo apk supports single package only"),
ops::Packages::All => unreachable!("cargo apk supports single package only"),
ops::Packages::OptOut(_) => unreachable!("cargo apk supports single package only"),
ops::Packages::Packages(xs) => match xs.len() {
0 => workspace.current()?,
1 => workspace
.members()
.find(|pkg| *pkg.name() == xs[0])
.ok_or_else(|| {
format_err!("package `{}` is not a member of the workspace", xs[0])
})?,
_ => unreachable!("cargo apk supports single package only"),
},
}
};
// Determine the name of the package and the Android-specific metadata from the Cargo.toml
let (package_name, manifest_content) = {
// Load Cargo.toml & parse
let content = {
let mut file = File::open(package.manifest_path()).unwrap();
let mut content = String::new();
file.read_to_string(&mut content).unwrap();
content
};
let toml = TomlParser::new(&content).parse().unwrap();
let decoded: TomlPackage = toml::decode(toml["package"].clone()).unwrap();
let package_name = decoded.name.clone();
(package_name, decoded.metadata.and_then(|m| m.android))
};
// Determine the gradle command from the env variables
let gradle_command = env::var("CARGO_APK_GRADLE_COMMAND")
.ok()
.unwrap_or("gradle".to_owned());
let ndk_path = env::var("NDK_HOME").expect(
"Please set the path to the Android NDK with the \
$NDK_HOME environment variable.",
);
let sdk_path = {
let mut try = env::var("ANDROID_SDK_HOME").ok();
if try.is_none() {
try = env::var("ANDROID_HOME").ok();
}
try.expect(
"Please set the path to the Android SDK with either the $ANDROID_SDK_HOME or \
the $ANDROID_HOME environment variable.",
)
};
// Find the highest build tools.
let build_tools_version = {
let mut dir = fs::read_dir(Path::new(&sdk_path).join("build-tools"))
.expect("Android SDK has no build-tools directory");
let mut versions = Vec::new();
while let Some(next) = dir.next() {
let next = next.unwrap();
let meta = next.metadata().unwrap();
if !meta.is_dir() {
continue;
}
let file_name = next.file_name().into_string().unwrap();
if !file_name.chars().next().unwrap().is_digit(10) {
continue;
}
versions.push(file_name);
}
versions.sort_by(|a, b| b.cmp(&a));
versions.into_iter().next().unwrap_or("26.0.0".to_owned())
};
// Determine the Sdk versions (compile, target, min)
let android_version = manifest_content
.as_ref()
.and_then(|a| a.android_version)
.unwrap_or(18);
let target_sdk_version = manifest_content
.as_ref()
.and_then(|a| a.target_sdk_version)
.unwrap_or(android_version);
let min_sdk_verision = manifest_content
.as_ref()
.and_then(|a| a.min_sdk_version)
.unwrap_or(android_version);
// For the moment some fields of the config are dummies.
Ok(AndroidConfig {
sdk_path: Path::new(&sdk_path).to_owned(),
ndk_path: Path::new(&ndk_path).to_owned(),
gradle_command: gradle_command,
package_name: manifest_content
.as_ref()
.and_then(|a| a.package_name.clone())
.unwrap_or_else(|| format!("rust.{}", package_name)),
project_name: package_name.clone(),
package_label: manifest_content
.as_ref()
.and_then(|a| a.label.clone())
.unwrap_or_else(|| package_name.clone()),
package_icon: manifest_content.as_ref().and_then(|a| a.icon.clone()),
build_targets: manifest_content
.as_ref()
.and_then(|a| a.build_targets.clone())
.unwrap_or(vec!["arm-linux-androideabi".to_owned()]),
android_version: android_version,
target_sdk_version: target_sdk_version,
min_sdk_version: min_sdk_verision,
build_tools_version: build_tools_version,
assets_path: manifest_content
.as_ref()
.and_then(|a| a.assets.as_ref())
.map(|p| package.manifest_path().parent().unwrap().join(p)),
res_path: manifest_content
.as_ref()
.and_then(|a| a.res.as_ref())
.map(|p| package.manifest_path().parent().unwrap().join(p)),
release: false,
fullscreen: manifest_content
.as_ref()
.and_then(|a| a.fullscreen.clone())
.unwrap_or(false),
application_attributes: manifest_content
.as_ref()
.and_then(|a| map_to_string(a.application_attributes.clone())),
activity_attributes: manifest_content
.as_ref()
.and_then(|a| map_to_string(a.activity_attributes.clone())),
opengles_version_major: manifest_content
.as_ref()
.and_then(|a| a.opengles_version_major)
.unwrap_or(2),
opengles_version_minor: manifest_content
.as_ref()
.and_then(|a| a.opengles_version_minor)
.unwrap_or(0),
})
}
fn map_to_string(input_map: Option<BTreeMap<String, String>>) -> Option<String> {
// TODO rewrite this in functional style
if let Some(map) = input_map {
let mut result = String::new();
for (key, val) in map {
result.push_str(&format!("\n{}=\"{}\"", key, val))
}
Some(result)
} else {
None
}
}
#[derive(Debug, Clone, RustcDecodable)]
struct TomlPackage {
name: String,
metadata: Option<TomlMetadata>,
}
#[derive(Debug, Clone, RustcDecodable)]
struct TomlMetadata {
android: Option<TomlAndroid>,
}
#[derive(Debug, Clone, RustcDecodable)]
struct TomlAndroid {
package_name: Option<String>,
label: Option<String>,
icon: Option<String>,
assets: Option<String>,
res: Option<String>,
android_version: Option<u32>,
target_sdk_version: Option<u32>,
min_sdk_version: Option<u32>,
fullscreen: Option<bool>,
application_attributes: Option<BTreeMap<String, String>>,
activity_attributes: Option<BTreeMap<String, String>>,
build_targets: Option<Vec<String>>,
gradle_command: Option<String>,
opengles_version_major: Option<u8>,
opengles_version_minor: Option<u8>,
}