Skip to content

Commit 165e3c0

Browse files
authored
apk: Embed configurable assets in asset/ directory (#122)
Android apps might want to ship with extra files - assets - to complement the native build. These could be single files or folders that are included recursively. the `file_name()` of the given path is added directly to the `assets/` directory, and any directory structure beyond that point is maintained. Assets are specified in the following way in `manifest.yaml`: android: assets: - some/path - { path: some/path } # Same as above - { path: some/path, optional: true } # Do not error if this path doesn't exist - { path: some/path, alignment: 4096 } # Page-aligned for optimal use with AASSET_MODE_BUFFER and AAsset_getBuffer - { path: some/path, alignment: unaligned } - { path: some/path, alignment: compressed }
1 parent 4e2c8e9 commit 165e3c0

File tree

5 files changed

+134
-22
lines changed

5 files changed

+134
-22
lines changed

apk/src/lib.rs

+16-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::res::Chunk;
33
use anyhow::{Context, Result};
44
use std::io::Cursor;
55
use std::path::{Path, PathBuf};
6-
use xcommon::{Scaler, ScalerOpts, Zip, ZipFile, ZipFileOptions};
6+
use xcommon::{Scaler, ScalerOpts, Zip, ZipFileOptions};
77

88
mod compiler;
99
pub mod manifest;
@@ -77,16 +77,27 @@ impl Apk {
7777
Ok(())
7878
}
7979

80+
pub fn add_asset(&mut self, asset: &Path, opts: ZipFileOptions) -> Result<()> {
81+
let file_name = asset
82+
.file_name()
83+
.context("Asset must have file_name component")?;
84+
let dest = Path::new("assets").join(file_name);
85+
if asset.is_dir() {
86+
tracing::info!("Embedding asset directory `{}`", asset.display());
87+
self.zip.add_directory(asset, &dest, opts)
88+
} else {
89+
tracing::info!("Embedding asset file `{}`", asset.display());
90+
self.zip.add_file(asset, &dest, opts)
91+
}
92+
.with_context(|| format!("While embedding asset `{}`", asset.display()))
93+
}
94+
8095
pub fn add_dex(&mut self, dex: &Path) -> Result<()> {
8196
self.zip
8297
.add_file(dex, Path::new("classes.dex"), ZipFileOptions::Compressed)?;
8398
Ok(())
8499
}
85100

86-
pub fn add_zip_file(&mut self, f: ZipFile) -> Result<()> {
87-
self.zip.add_zip_file(f)
88-
}
89-
90101
pub fn add_lib(&mut self, target: Target, path: &Path) -> Result<()> {
91102
let name = path
92103
.file_name()
@@ -100,14 +111,6 @@ impl Apk {
100111
)
101112
}
102113

103-
pub fn add_file(&mut self, source: &Path, dest: &Path, opts: ZipFileOptions) -> Result<()> {
104-
self.zip.add_file(source, dest, opts)
105-
}
106-
107-
pub fn add_directory(&mut self, source: &Path, dest: &Path) -> Result<()> {
108-
self.zip.add_directory(source, dest)
109-
}
110-
111114
pub fn finish(self, signer: Option<Signer>) -> Result<()> {
112115
self.zip.finish()?;
113116
crate::sign::sign(&self.path, signer)?;

msix/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,13 @@ impl Msix {
7373
self.zip.add_file(source, dest, opts)
7474
}
7575

76-
pub fn add_directory(&mut self, source: &Path, dest: &Path) -> Result<()> {
77-
self.zip.add_directory(source, dest)
76+
pub fn add_directory(
77+
&mut self,
78+
source: &Path,
79+
dest: &Path,
80+
opts: ZipFileOptions,
81+
) -> Result<()> {
82+
self.zip.add_directory(source, dest, opts)
7883
}
7984

8085
pub fn finish(mut self, signer: Option<Signer>) -> Result<()> {

xbuild/src/command/build.rs

+9
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ pub fn build(env: &BuildEnv) -> Result<()> {
8383
)?;
8484
apk.add_res(env.icon(), &env.android_jar())?;
8585

86+
for asset in &env.config().android().assets {
87+
let path = env.cargo().package_root().join(asset.path());
88+
89+
if !asset.optional() || path.exists() {
90+
apk.add_asset(&path, asset.alignment().to_zip_file_options())?
91+
}
92+
}
93+
8694
if has_lib {
8795
for target in env.target().compile_targets() {
8896
let arch_dir = platform_dir.join(target.arch().to_string());
@@ -256,6 +264,7 @@ pub fn build(env: &BuildEnv) -> Result<()> {
256264
ipa.add_directory(
257265
&app,
258266
&Path::new("Payload").join(format!("{}.app", env.name())),
267+
ZipFileOptions::Compressed,
259268
)?;
260269
ipa.finish()?;
261270
}

xbuild/src/config.rs

+87
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use msix::AppxManifest;
88
use serde::Deserialize;
99
use std::collections::HashMap;
1010
use std::path::{Path, PathBuf};
11+
use xcommon::ZipFileOptions;
1112

1213
#[derive(Clone, Debug, Default)]
1314
pub struct Config {
@@ -313,7 +314,84 @@ impl Config {
313314
}
314315
}
315316

317+
#[derive(Clone, Copy, Debug, Default, Deserialize)]
318+
#[serde(rename_all = "snake_case")]
319+
pub enum UnalignedCompressed {
320+
/// Don't align this file
321+
Unaligned,
322+
/// Compressed files do not need to be aligned, as they have to be unpacked and decompressed anyway
323+
#[default]
324+
Compressed,
325+
}
326+
327+
#[derive(Clone, Copy, Debug, Deserialize)]
328+
#[serde(untagged)]
329+
pub enum ZipAlignmentOptions {
330+
/// Align this file to the given number of bytes
331+
Aligned(u16),
332+
/// Used to wrap a tagged enum with an untagged alignment value
333+
UnalignedCompressed(UnalignedCompressed),
334+
}
335+
336+
impl Default for ZipAlignmentOptions {
337+
fn default() -> Self {
338+
Self::UnalignedCompressed(UnalignedCompressed::Compressed)
339+
}
340+
}
341+
342+
impl ZipAlignmentOptions {
343+
pub fn to_zip_file_options(self) -> ZipFileOptions {
344+
match self {
345+
Self::Aligned(a) => ZipFileOptions::Aligned(a),
346+
Self::UnalignedCompressed(UnalignedCompressed::Unaligned) => ZipFileOptions::Unaligned,
347+
Self::UnalignedCompressed(UnalignedCompressed::Compressed) => {
348+
ZipFileOptions::Compressed
349+
}
350+
}
351+
}
352+
}
353+
354+
#[derive(Clone, Debug, Deserialize)]
355+
#[serde(untagged, deny_unknown_fields)]
356+
pub enum AssetPath {
357+
Path(PathBuf),
358+
Extended {
359+
path: PathBuf,
360+
#[serde(default)]
361+
optional: bool,
362+
#[serde(default)]
363+
alignment: ZipAlignmentOptions,
364+
},
365+
}
366+
367+
impl AssetPath {
368+
#[inline]
369+
pub fn path(&self) -> &Path {
370+
match self {
371+
AssetPath::Path(path) => path,
372+
AssetPath::Extended { path, .. } => path,
373+
}
374+
}
375+
376+
#[inline]
377+
pub fn optional(&self) -> bool {
378+
match self {
379+
AssetPath::Path(_) => false,
380+
AssetPath::Extended { optional, .. } => *optional,
381+
}
382+
}
383+
384+
#[inline]
385+
pub fn alignment(&self) -> ZipAlignmentOptions {
386+
match self {
387+
AssetPath::Path(_) => Default::default(),
388+
AssetPath::Extended { alignment, .. } => *alignment,
389+
}
390+
}
391+
}
392+
316393
#[derive(Deserialize)]
394+
#[serde(deny_unknown_fields)]
317395
struct RawConfig {
318396
#[serde(flatten)]
319397
generic: Option<GenericConfig>,
@@ -325,13 +403,15 @@ struct RawConfig {
325403
}
326404

327405
#[derive(Clone, Debug, Default, Deserialize)]
406+
#[serde(deny_unknown_fields)]
328407
pub struct GenericConfig {
329408
icon: Option<PathBuf>,
330409
#[serde(default)]
331410
runtime_libs: Vec<PathBuf>,
332411
}
333412

334413
#[derive(Clone, Debug, Default, Deserialize)]
414+
#[serde(deny_unknown_fields)]
335415
pub struct AndroidDebugConfig {
336416
/// Forward remote (phone) socket connection to local (host)
337417
#[serde(default)]
@@ -342,6 +422,7 @@ pub struct AndroidDebugConfig {
342422
}
343423

344424
#[derive(Clone, Debug, Default, Deserialize)]
425+
#[serde(deny_unknown_fields)]
345426
pub struct AndroidConfig {
346427
#[serde(flatten)]
347428
generic: GenericConfig,
@@ -353,12 +434,15 @@ pub struct AndroidConfig {
353434
pub gradle: bool,
354435
#[serde(default)]
355436
pub wry: bool,
437+
#[serde(default)]
438+
pub assets: Vec<AssetPath>,
356439
/// Debug configuration for `x run`
357440
#[serde(default)]
358441
pub debug: AndroidDebugConfig,
359442
}
360443

361444
#[derive(Clone, Debug, Default, Deserialize)]
445+
#[serde(deny_unknown_fields)]
362446
pub struct IosConfig {
363447
#[serde(flatten)]
364448
generic: GenericConfig,
@@ -367,19 +451,22 @@ pub struct IosConfig {
367451
}
368452

369453
#[derive(Clone, Debug, Default, Deserialize)]
454+
#[serde(deny_unknown_fields)]
370455
pub struct MacosConfig {
371456
#[serde(flatten)]
372457
generic: GenericConfig,
373458
pub info: InfoPlist,
374459
}
375460

376461
#[derive(Clone, Debug, Default, Deserialize)]
462+
#[serde(deny_unknown_fields)]
377463
pub struct LinuxConfig {
378464
#[serde(flatten)]
379465
generic: GenericConfig,
380466
}
381467

382468
#[derive(Clone, Debug, Default, Deserialize)]
469+
#[serde(deny_unknown_fields)]
383470
pub struct WindowsConfig {
384471
#[serde(flatten)]
385472
generic: GenericConfig,

xcommon/src/lib.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,20 @@ impl Zip {
286286
}
287287

288288
pub fn add_file(&mut self, source: &Path, dest: &Path, opts: ZipFileOptions) -> Result<()> {
289-
let mut f = File::open(source)?;
289+
let mut f = File::open(source)
290+
.with_context(|| format!("While opening file `{}`", source.display()))?;
290291
self.start_file(dest, opts)?;
291292
std::io::copy(&mut f, &mut self.zip)?;
292293
Ok(())
293294
}
294295

295-
pub fn add_directory(&mut self, source: &Path, dest: &Path) -> Result<()> {
296-
add_recursive(self, source, dest)?;
296+
pub fn add_directory(
297+
&mut self,
298+
source: &Path,
299+
dest: &Path,
300+
opts: ZipFileOptions,
301+
) -> Result<()> {
302+
add_recursive(self, source, dest, opts)?;
297303
Ok(())
298304
}
299305

@@ -335,17 +341,19 @@ impl Zip {
335341
}
336342
}
337343

338-
fn add_recursive(zip: &mut Zip, source: &Path, dest: &Path) -> Result<()> {
339-
for entry in std::fs::read_dir(source)? {
344+
fn add_recursive(zip: &mut Zip, source: &Path, dest: &Path, opts: ZipFileOptions) -> Result<()> {
345+
for entry in std::fs::read_dir(source)
346+
.with_context(|| format!("While reading directory `{}`", source.display()))?
347+
{
340348
let entry = entry?;
341349
let file_name = entry.file_name();
342350
let source = source.join(&file_name);
343351
let dest = dest.join(&file_name);
344352
let file_type = entry.file_type()?;
345353
if file_type.is_dir() {
346-
add_recursive(zip, &source, &dest)?;
354+
add_recursive(zip, &source, &dest, opts)?;
347355
} else if file_type.is_file() {
348-
zip.add_file(&source, &dest, ZipFileOptions::Compressed)?;
356+
zip.add_file(&source, &dest, opts)?;
349357
}
350358
}
351359
Ok(())

0 commit comments

Comments
 (0)