diff --git a/apk/src/lib.rs b/apk/src/lib.rs index 04d54732..88655eb4 100644 --- a/apk/src/lib.rs +++ b/apk/src/lib.rs @@ -99,14 +99,10 @@ impl Apk { } pub fn add_lib(&mut self, target: Target, path: &Path) -> Result<()> { - let name = path - .file_name() - .context("invalid path")? - .to_str() - .context("invalid path")?; + let name = path.file_name().context("invalid path")?; self.zip.add_file( path, - &Path::new("lib").join(target.android_abi()).join(name), + &Path::new("lib").join(target.as_str()).join(name), ZipFileOptions::Compressed, ) } diff --git a/apk/src/utils.rs b/apk/src/utils.rs index 592d049c..ff266848 100644 --- a/apk/src/utils.rs +++ b/apk/src/utils.rs @@ -11,7 +11,7 @@ pub enum Target { impl Target { /// Identifier used in the NDK to refer to the ABI - pub fn android_abi(self) -> &'static str { + pub fn as_str(self) -> &'static str { match self { Self::Arm64V8a => "arm64-v8a", Self::ArmV7a => "armeabi-v7a", diff --git a/xbuild/src/command/build.rs b/xbuild/src/command/build.rs index 9e7020f7..9bb32b7d 100644 --- a/xbuild/src/command/build.rs +++ b/xbuild/src/command/build.rs @@ -2,7 +2,7 @@ use crate::cargo::CrateType; use crate::download::DownloadManager; use crate::task::TaskRunner; use crate::{BuildEnv, Format, Opt, Platform}; -use anyhow::{Context, Result}; +use anyhow::{ensure, Context, Result}; use apk::Apk; use appbundle::AppBundle; use appimage::AppImage; @@ -71,116 +71,97 @@ pub fn build(env: &BuildEnv) -> Result<()> { } Platform::Android => { let out = platform_dir.join(format!("{}.{}", env.name(), env.target().format())); - if env.config().android().gradle { - crate::gradle::build(env, &out)?; - runner.end_verbose_task(); - return Ok(()); - } else { - let mut apk = Apk::new( - out, - env.config().android().manifest.clone(), - env.target().opt() != Opt::Debug, - )?; - apk.add_res(env.icon(), &env.android_jar())?; - - for asset in &env.config().android().assets { - let path = env.cargo().package_root().join(asset.path()); - - if !asset.optional() || path.exists() { - apk.add_asset(&path, asset.alignment().to_zip_file_options())? + ensure!(has_lib, "Android APKs/AABs require a library"); + + let mut libraries = vec![]; + + for target in env.target().compile_targets() { + let arch_dir = platform_dir.join(target.arch().to_string()); + let cargo_dir = arch_dir.join("cargo"); + let lib = env.cargo_artefact(&cargo_dir, target, CrateType::Cdylib)?; + + let ndk = env.android_ndk(); + + let deps_dir = { + let arch_dir = if target.is_host()? { + cargo_dir.to_path_buf() + } else { + cargo_dir.join(target.rust_triple()?) + }; + let opt_dir = arch_dir.join(target.opt().to_string()); + opt_dir.join("deps") + }; + + let mut search_paths = env + .cargo() + .lib_search_paths(&cargo_dir, target) + .with_context(|| { + format!( + "Finding libraries in `{}` for {:?}", + cargo_dir.display(), + target + ) + })?; + search_paths.push(deps_dir); + let search_paths = search_paths.iter().map(AsRef::as_ref).collect::>(); + + let ndk_sysroot_libs = ndk.join("usr/lib").join(target.ndk_triple()); + let provided_libs_paths = [ + ndk_sysroot_libs.as_path(), + &*ndk_sysroot_libs.join( + // Use libraries (symbols) from the lowest NDK that is supported by the application, + // to prevent inadvertently making newer APIs available: + // https://developer.android.com/ndk/guides/sdk-versions + env.config() + .android() + .manifest + .sdk + .min_sdk_version + .unwrap() + .to_string(), + ), + ]; + + let mut explicit_libs = vec![lib]; + + // Collect the libraries the user wants to include + for runtime_lib_path in env.config().runtime_libs(env.target().platform()) { + let abi_dir = env + .cargo() + .package_root() + .join(runtime_lib_path) + .join(target.android_abi().as_str()); + let entries = std::fs::read_dir(abi_dir)?; + for entry in entries { + let entry = entry?; + let path = entry.path(); + if !path.is_dir() && path.extension() == Some(OsStr::new("so")) { + explicit_libs.push(path); + } } } - if has_lib { - for target in env.target().compile_targets() { - let arch_dir = platform_dir.join(target.arch().to_string()); - let cargo_dir = arch_dir.join("cargo"); - let lib = env.cargo_artefact(&cargo_dir, target, CrateType::Cdylib)?; - - let ndk = env.android_ndk(); - - let deps_dir = { - let arch_dir = if target.is_host()? { - cargo_dir.to_path_buf() - } else { - cargo_dir.join(target.rust_triple()?) - }; - let opt_dir = arch_dir.join(target.opt().to_string()); - opt_dir.join("deps") - }; - - let mut search_paths = env - .cargo() - .lib_search_paths(&cargo_dir, target) - .with_context(|| { - format!( - "Finding libraries in `{}` for {:?}", - cargo_dir.display(), - target - ) - })?; - search_paths.push(deps_dir); - let search_paths = - search_paths.iter().map(AsRef::as_ref).collect::>(); - - let ndk_sysroot_libs = ndk.join("usr/lib").join(target.ndk_triple()); - let provided_libs_paths = [ - ndk_sysroot_libs.as_path(), - &*ndk_sysroot_libs.join( - // Use libraries (symbols) from the lowest NDK that is supported by the application, - // to prevent inadvertently making newer APIs available: - // https://developer.android.com/ndk/guides/sdk-versions - env.config() - .android() - .manifest - .sdk - .min_sdk_version - .unwrap() - .to_string(), - ), - ]; - - let mut explicit_libs = vec![lib]; - - // Collect the libraries the user wants to include - for runtime_lib_path in env.config().runtime_libs(env.target().platform()) { - let abi_dir = env - .cargo() - .package_root() - .join(runtime_lib_path) - .join(target.android_abi().android_abi()); - let entries = std::fs::read_dir(abi_dir)?; - for entry in entries { - let entry = entry?; - let path = entry.path(); - if !path.is_dir() && path.extension() == Some(OsStr::new("so")) { - explicit_libs.push(path); - } - } - } + // Collect the names of libraries provided by the user, and assume these + // are available for other dependencies to link to, too. + let mut included_libs = explicit_libs + .iter() + .map(|p| p.file_name().unwrap().to_owned()) + .collect::>(); - // Collect the names of libraries provided by the user, and assume these - // are available for other dependencies to link to, too. - let mut included_libs = explicit_libs - .iter() - .map(|p| p.file_name().unwrap().to_owned()) - .collect::>(); - - // Collect the names of all libraries that are available on Android - for provided_libs_path in provided_libs_paths { - included_libs - .extend(xcommon::llvm::find_libs_in_dir(provided_libs_path)?); - } + // Collect the names of all libraries that are available on Android + for provided_libs_path in provided_libs_paths { + included_libs.extend(xcommon::llvm::find_libs_in_dir(provided_libs_path)?); + } - // libc++_shared is bundled with the NDK but not available on-device - included_libs.remove(OsStr::new("libc++_shared.so")); + // libc++_shared is bundled with the NDK but not available on-device + included_libs.remove(OsStr::new("libc++_shared.so")); - let mut needs_cpp_shared = false; + let mut needs_cpp_shared = false; - for lib in explicit_libs { - apk.add_lib(target.android_abi(), &lib)?; + for lib in explicit_libs { + libraries.push((target.android_abi(), lib.clone())); - let (extra_libs, cpp_shared) = xcommon::llvm::list_needed_libs_recursively( + let (extra_libs, cpp_shared) = xcommon::llvm::list_needed_libs_recursively( &lib, &search_paths, &included_libs, @@ -193,18 +174,41 @@ pub fn build(env: &BuildEnv) -> Result<()> { search_paths ) })?; - needs_cpp_shared |= cpp_shared; - for lib in &extra_libs { - apk.add_lib(target.android_abi(), lib)?; - } - } - if needs_cpp_shared { - let cpp_shared = ndk_sysroot_libs.join("libc++_shared.so"); - apk.add_lib(target.android_abi(), &cpp_shared)?; - } + needs_cpp_shared |= cpp_shared; + for lib in extra_libs { + libraries.push((target.android_abi(), lib)); + } + } + if needs_cpp_shared { + let cpp_shared = ndk_sysroot_libs.join("libc++_shared.so"); + libraries.push((target.android_abi(), cpp_shared)); + } + } + + if env.config().android().gradle { + crate::gradle::build(env, libraries, &out)?; + runner.end_verbose_task(); + return Ok(()); + } else { + let mut apk = Apk::new( + out, + env.config().android().manifest.clone(), + env.target().opt() != Opt::Debug, + )?; + apk.add_res(env.icon(), &env.android_jar())?; + + for asset in &env.config().android().assets { + let path = env.cargo().package_root().join(asset.path()); + + if !asset.optional() || path.exists() { + apk.add_asset(&path, asset.alignment().to_zip_file_options())? } } + for (target, lib) in libraries { + apk.add_lib(target, &lib)?; + } + apk.finish(env.target().signer().cloned())?; } } diff --git a/xbuild/src/gradle/mod.rs b/xbuild/src/gradle/mod.rs index 013bbdca..ec27f1e0 100644 --- a/xbuild/src/gradle/mod.rs +++ b/xbuild/src/gradle/mod.rs @@ -1,7 +1,7 @@ -use crate::cargo::CrateType; use crate::{task, BuildEnv, Format, Opt}; -use anyhow::Result; -use std::path::Path; +use anyhow::{Context, Result}; +use apk::Target; +use std::path::{Path, PathBuf}; use std::process::Command; static BUILD_GRADLE: &[u8] = include_bytes!("./build.gradle"); @@ -33,7 +33,7 @@ pub fn prepare(env: &BuildEnv) -> Result<()> { Ok(()) } -pub fn build(env: &BuildEnv, out: &Path) -> Result<()> { +pub fn build(env: &BuildEnv, libraries: Vec<(Target, PathBuf)>, out: &Path) -> Result<()> { let platform_dir = env.platform_dir(); let gradle = platform_dir.join("gradle"); let app = gradle.join("app"); @@ -146,13 +146,11 @@ pub fn build(env: &BuildEnv, out: &Path) -> Result<()> { } } - for target in env.target().compile_targets() { - let arch_dir = platform_dir.join(target.arch().to_string()); - let lib = env.cargo_artefact(&arch_dir.join("cargo"), target, CrateType::Cdylib)?; - let lib_name = lib.file_name().unwrap(); - let lib_dir = jnilibs.join(target.android_abi().android_abi()); + for (target, lib) in libraries { + let name = lib.file_name().context("invalid path")?; + let lib_dir = jnilibs.join(target.as_str()); std::fs::create_dir_all(&lib_dir)?; - std::fs::copy(&lib, lib_dir.join(lib_name))?; + std::fs::copy(&lib, lib_dir.join(name))?; } let opt = env.target().opt();