Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manifest (de)serialization #106

Merged
merged 17 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cargo-apk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Breaking, uses `ndk-build`'s new (de)serialized `Manifest` struct to properly serialize a toml's `[package.metadata.android]` to an `AndroidManifest.xml`. The `[package.metadata.android]` now closely resembles the structure of [`an android manifest file`](https://developer.android.com/guide/topics/manifest/manifest-element). See [README](README.md) for an example of the new `[package.metadata.android]` structure and all manifest attributes that are currently supported.
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved

# 0.5.6 (2020-11-25)

- Use `dunce::simplified` when extracting the manifest's assets and resource folder
Expand Down
155 changes: 101 additions & 54 deletions cargo-apk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,81 +26,128 @@ cargo install --path .
Following configuration options are supported by `cargo apk` under `[package.metadata.android]`:

```toml
# Name of your APK as shown in the app drawer and in the app switcher
apk_label = "APK Name"

# The target Android API level.
target_sdk_version = 29
min_sdk_version = 26

# Virtual path your application's icon for any mipmap level.
# If not specified, an icon will not be included in the APK.
icon = "@mipmap/ic_launcher"

# If set to true, makes the app run in full-screen, by adding the following line
# as an XML attribute to the manifest's <application> tag :
# android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen
# Defaults to false.
fullscreen = false
[package.metadata.android]
# Specifies the array of targets to build for.
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android" ]

# Set the instruction of how the activity should be launched
# See https://developer.android.com/guide/topics/manifest/activity-element#lmode
# Defaults to standard.
launch_mode = "standard"
# Path to your application's resources folder.
# If not specified, resources will not be included in the APK.
resources = "path/to/resources_folder"

# Set the minimum required OpenGL ES version.
# Defaults to [3, 1]
opengles_version = [3, 0]
# Path to the folder containing your application's assets.
# If not specified, assets will not be included in the APK.
assets = "path/to/assets_folder"

# Sets the applications screenOrientation.
# See https://developer.android.com/guide/topics/manifest/activity-element
# and look for `android:screenOrientation` for possible values
# Defaults to "unspecified" which makes the system pick an orientation and
# doesn't give you help with rotation.
orientation = "sensorLandscape"
# See https://developer.android.com/guide/topics/manifest/uses-sdk-element
#
# Defaults to a `min_sdk_version` of 23 and `target_sdk_version` is based on the ndk's default platform.
[package.metadata.android.sdk]
min_sdk_version = 16
target_sdk_version = 29
max_sdk_version = 29

# Adds a uses-feature element to the manifest
# Supported keys: name, required
# See https://developer.android.com/guide/topics/manifest/uses-feature-element
#
# Note: there can be multiple .feature entries.
[[package.metadata.android.feature]]
name = "android.hardware.camera"
name = "android.hardware.vulkan.level"
required = true
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved

# If no `opengles_version` is specified in any feature, belows feature is added as a default
# `opengles_version` feature.
[[package.metadata.android.feature]]
name = "android.hardware.vulkan.level"
version = "1"
opengles_version = [3, 1]
required = true

# Adds a uses-permission element to the manifest.
# Note that android_version 23 and higher, Android requires the application to request permissions at runtime.
# There is currently no way to do this using a pure NDK based application.
# See https://developer.android.com/guide/topics/manifest/uses-permission-element
#
# Note: there can be multiple .permission entries.
[[package.metadata.android.permission]]
name = "android.permission.WRITE_EXTERNAL_STORAGE"
max_sdk_version = 18

# Specifies the array of targets to build for.
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android" ]

# Path to your application's resources folder.
# If not specified, resources will not be included in the APK
res = "path/to/res_folder"
# See https://developer.android.com/guide/topics/manifest/application-element
[package.metadata.android.application]

# Path to the folder containing your application's assets.
# If not specified, assets will not be included in the APK
assets = "path/to/assets_folder"
# See https://developer.android.com/guide/topics/manifest/application-element#debug
#
# Defaults to false.
debuggable = false

# See https://developer.android.com/guide/topics/manifest/application-element#theme
#
# Example shows setting the theme of an application to fullscreen.
theme = "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"

# Virtual path your application's icon for any mipmap level.
# If not specified, an icon will not be included in the APK.
icon = "@mipmap/ic_launcher"

# See https://developer.android.com/guide/topics/manifest/application-element#label
#
# Defaults to the compiled artifact's name.
label = "Application Name"

# Adds application metadata to the manifest
# Note that there can be several application_metadatas entries
# this will add: <meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
[[package.metadata.android.application_metadatas]]
# See https://developer.android.com/guide/topics/manifest/meta-data-element
#
# Note: there can be several .metadata entries.
# Note: the `resource` attribute is currently not supported.
[[package.metadata.android.application.metadata]]
name = "com.samsung.android.vr.application.mode"
value = "vr_only"

# Adds activity metadata to the manifest
# Note that there can be several activity_metadatas entries
# this will add: <meta-data android:name="com.oculus.vr.focusaware" android:value="true"/>
[[package.metadata.android.activity_metadatas]]
# See https://developer.android.com/guide/topics/manifest/activity-element
[package.metadata.android.application.activity]

# See https://developer.android.com/guide/topics/manifest/activity-element#config
#
# Defaults to "orientation|keyboardHidden|screenSize".
config_changes = "orientation"

# See https://developer.android.com/guide/topics/manifest/activity-element#label
#
# Defaults to the application's label.
label = "Activity Name"

# See https://developer.android.com/guide/topics/manifest/activity-element#lmode
#
# Defaults to "standard".
launch_mode = "singleTop"

# See https://developer.android.com/guide/topics/manifest/activity-element#screen
#
# Defaults to "unspecified".
orientation = "landscape"

# See https://developer.android.com/guide/topics/manifest/meta-data-element
#
# Note: there can be several .metadata entries.
# Note: the `resource` attribute is currently not supported.
[[package.metadata.android.application.activity.metadata]]
name = "com.oculus.vr.focusaware"
value = "true"

# See https://developer.android.com/guide/topics/manifest/intent-filter-element
#
# Note: there can be several .intent_filter entries.
[[package.metadata.android.application.activity.intent_filter]]
# See https://developer.android.com/guide/topics/manifest/action-element
actions = ["android.intent.action.VIEW", "android.intent.action.WEB_SEARCH"]
# See https://developer.android.com/guide/topics/manifest/category-element
categories = ["android.intent.category.DEFAULT", "android.intent.category.BROWSABLE"]

# See https://developer.android.com/guide/topics/manifest/data-element
#
# Note: there can be several .data entries.
# Note: not specifying an attribute excludes it from the final data specification.
[[package.metadata.android.application.activity.intent_filter.data]]
scheme = "https"
host = "github.com"
port = "8080"
path = "/rust-windowing/android-ndk-rs/tree/master/cargo-apk"
path_prefix = "/rust-windowing/"
mime_type = "image/jpeg"
```

TODO: intent filters
If a manifest attribute is not supported by `cargo apk` feel free to create a PR that adds the missing attribute.
100 changes: 58 additions & 42 deletions cargo-apk/src/apk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use crate::manifest::Manifest;
use cargo_subcommand::{Artifact, CrateType, Profile, Subcommand};
use ndk_build::apk::{Apk, ApkConfig};
use ndk_build::cargo::{cargo_apk, VersionCode};
use ndk_build::config::Config;
use ndk_build::dylibs::get_libs_search_paths;
use ndk_build::error::NdkError;
use ndk_build::manifest::MetaData;
use ndk_build::ndk::Ndk;
use ndk_build::target::Target;
use std::path::PathBuf;
Expand Down Expand Up @@ -47,49 +47,64 @@ impl<'a> ApkBuilder<'a> {
Artifact::Root(name) => format!("rust.{}", name.replace("-", "_")),
Artifact::Example(name) => format!("rust.example.{}", name.replace("-", "_")),
};
let package_label = self
.manifest
.metadata
.apk_label
.as_deref()
.unwrap_or(artifact.name())
.to_string();

let config = Config {
// Set default Android manifest values
let mut manifest = self.manifest.android_manifest.clone();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it cleaner to fill this in directly after loading the file in fn from_subcommand?

manifest.package = package_name;
manifest.version_name = Some(self.manifest.version.clone());
manifest.version_code = Some(VersionCode::from_semver(&self.manifest.version)?.to_code(1));
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved

manifest.sdk.target_sdk_version = manifest
.sdk
.target_sdk_version
.or(Some(self.ndk.default_platform()));
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved
manifest.application.debuggable = manifest
.application
.debuggable
.or(Some(*self.cmd.profile() == Profile::Dev));
manifest.application.has_code = Some(false);
if manifest.application.label.is_empty() {
manifest.application.label = artifact.name().to_string();
}
let default_meta_data = MetaData {
name: "android.app.lib_name".to_string(),
value: artifact.name().replace("-", "_"),
};
if let Some(meta_datas) = &mut manifest.application.activity.meta_datas {
meta_datas.push(default_meta_data);
} else {
manifest.application.activity.meta_datas = Some(vec![default_meta_data]);
};
let assets = self.manifest.assets.as_ref().map(|assets| {
dunce::simplified(
&self
.cmd
.manifest()
.parent()
.expect("invalid manifest path")
.join(&assets),
)
.to_owned()
});
let resources = self.manifest.resources.as_ref().map(|res| {
dunce::simplified(
&self
.cmd
.manifest()
.parent()
.expect("invalid manifest path")
.join(&res),
)
.to_owned()
});

let config = ApkConfig {
ndk: self.ndk.clone(),
build_dir: self.build_dir.join(artifact),
package_name,
package_label,
version_name: self.manifest.version.clone(),
version_code: VersionCode::from_semver(&self.manifest.version)?.to_code(1),
split: None,
target_name: artifact.name().replace("-", "_"),
debuggable: *self.cmd.profile() == Profile::Dev,
assets: self.manifest.assets.as_ref().map(|assets| {
dunce::simplified(
&self
.cmd
.manifest()
.parent()
.expect("invalid manifest path")
.join(&assets),
)
.to_owned()
}),
res: self.manifest.res.as_ref().map(|res| {
dunce::simplified(
&self
.cmd
.manifest()
.parent()
.expect("invalid manifest path")
.join(&res),
)
.to_owned()
}),
assets,
resources,
manifest,
};

let config = ApkConfig::from_config(config, self.manifest.metadata.clone());
let apk = config.create_apk()?;

for target in &self.build_targets {
Expand All @@ -101,7 +116,7 @@ impl<'a> ApkBuilder<'a> {
.join(artifact)
.join(artifact.file_name(CrateType::Cdylib, &triple));

let target_sdk_version = config.manifest.target_sdk_version;
let target_sdk_version = config.manifest.sdk.target_sdk_version.unwrap();

let mut cargo = cargo_apk(&config.ndk, *target, target_sdk_version)?;
cargo.arg("build");
Expand Down Expand Up @@ -153,7 +168,8 @@ impl<'a> ApkBuilder<'a> {
let ndk = Ndk::from_env()?;
let target_sdk_version = self
.manifest
.metadata
.android_manifest
.sdk
.target_sdk_version
.unwrap_or_else(|| ndk.default_platform());
for target in &self.build_targets {
Expand Down
14 changes: 7 additions & 7 deletions cargo-apk/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::error::Error;
use ndk_build::config::Metadata;
use ndk_build::manifest::AndroidManifest;
use ndk_build::target::Target;
use serde::Deserialize;
use std::path::Path;

pub struct Manifest {
pub version: String,
pub metadata: Metadata,
pub android_manifest: AndroidManifest,
pub build_targets: Vec<Target>,
pub assets: Option<String>,
pub res: Option<String>,
pub resources: Option<String>,
}

impl Manifest {
Expand All @@ -24,10 +24,10 @@ impl Manifest {
.unwrap_or_default();
Ok(Self {
version: toml.package.version,
metadata: metadata.metadata,
android_manifest: metadata.android_manifest,
build_targets: metadata.build_targets.unwrap_or_default(),
assets: metadata.assets,
res: metadata.res,
resources: metadata.resources,
})
}
}
Expand All @@ -51,8 +51,8 @@ struct PackageMetadata {
#[derive(Clone, Debug, Default, Deserialize)]
struct AndroidMetadata {
#[serde(flatten)]
metadata: Metadata,
android_manifest: AndroidManifest,
build_targets: Option<Vec<Target>>,
assets: Option<String>,
res: Option<String>,
resources: Option<String>,
}
5 changes: 4 additions & 1 deletion ndk-build/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Unreleased

- Breaking, refactored `Manifest` into a proper (de)serialization struct. `Manifest` now closely matches [`an android manifest file`](https://developer.android.com/guide/topics/manifest/manifest-element).
maxded marked this conversation as resolved.
Show resolved Hide resolved
- Breaking, removed `Config` in favor of using the new `Manifest` struct directly. Instead of using `Config::from_config` to create a `Manifest`, now you instantiate `Manifest` directly using, almost all, the same values.
maxded marked this conversation as resolved.
Show resolved Hide resolved

# 0.1.4 (2020-11-25)

- On Windows, fixed UNC path handling for resource folder
- On Windows, fixed UNC path handling for resource folder.

# 0.1.3 (2020-11-21)

Expand Down
1 change: 1 addition & 0 deletions ndk-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ repository = "https://github.com/rust-windowing/android-ndk-rs"
dirs = "2.0.2"
dunce = "1.0"
serde = { version = "1.0.104", features = ["derive"] }
quick-xml = { version = "0.20.0", features = ["serialize"] }
thiserror = "1.0"
which = "4.0"
Loading