mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-25 13:40:19 +00:00
Feat: Android Dynamic Arch Support (#3215)
* feat: android multi-arch support * revision: make krate immutable * revision: minor tweaks * revision: make pub crate * revision: dedup arch access * revision: minor tweaks
This commit is contained in:
parent
5ecbb6594c
commit
af53354b28
10 changed files with 205 additions and 58 deletions
|
@ -745,7 +745,13 @@ impl AppBundle {
|
|||
)?;
|
||||
}
|
||||
|
||||
let output = Command::new("./gradlew")
|
||||
let gradle_exec_name = match cfg!(windows) {
|
||||
true => "gradlew.bat",
|
||||
false => "gradlew",
|
||||
};
|
||||
let gradle_exec = self.build.root_dir().join(gradle_exec_name);
|
||||
|
||||
let output = Command::new(gradle_exec)
|
||||
.arg("assembleDebug")
|
||||
.current_dir(self.build.root_dir())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
|
|
|
@ -114,10 +114,11 @@ impl BuildRequest {
|
|||
// We don't want to overwrite the user's .cargo/config.toml since that gets committed to git
|
||||
// and we want everyone's install to be the same.
|
||||
if self.build.platform() == Platform::Android {
|
||||
let linker = self
|
||||
let ndk = self
|
||||
.krate
|
||||
.android_linker()
|
||||
.android_ndk()
|
||||
.context("Could not autodetect android linker")?;
|
||||
let linker = self.build.target_args.arch().android_linker(&ndk);
|
||||
|
||||
tracing::trace!("Using android linker: {linker:?}");
|
||||
|
||||
|
@ -252,7 +253,7 @@ impl BuildRequest {
|
|||
Some(true) => Some("aarch64-apple-ios"),
|
||||
_ => Some("aarch64-apple-ios-sim"),
|
||||
},
|
||||
Platform::Android => Some("aarch64-linux-android"),
|
||||
Platform::Android => Some(self.build.target_args.arch().android_target_triplet()),
|
||||
Platform::Server => None,
|
||||
// we're assuming we're building for the native platform for now... if you're cross-compiling
|
||||
// the targets here might be different
|
||||
|
@ -536,7 +537,7 @@ impl BuildRequest {
|
|||
.join("src")
|
||||
.join("main")
|
||||
.join("jniLibs")
|
||||
.join("arm64-v8a"),
|
||||
.join(self.build.target_args.arch().android_jnilib()),
|
||||
|
||||
// these are all the same, I think?
|
||||
Platform::Windows
|
||||
|
|
|
@ -134,13 +134,20 @@ impl BuildRequest {
|
|||
/// will do its best to fill in the missing bits by exploring the sdk structure
|
||||
/// IE will attempt to use the Java installed from android studio if possible.
|
||||
pub(crate) async fn verify_android_tooling(&self, _rustup: RustupShow) -> Result<()> {
|
||||
if self.krate.android_linker().is_none() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Android linker not found. Please set the ANDROID_NDK_HOME environment variable to the root of your NDK installation."
|
||||
).into());
|
||||
let result = self
|
||||
.krate
|
||||
.android_ndk()
|
||||
.map(|ndk| self.build.target_args.arch().android_linker(&ndk));
|
||||
|
||||
if let Some(path) = result {
|
||||
if path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Err(anyhow::anyhow!(
|
||||
"Android linker not found. Please set the `ANDROID_NDK_HOME` environment variable to the root of your NDK installation."
|
||||
).into())
|
||||
}
|
||||
|
||||
/// Ensure the right dependencies are installed for linux apps.
|
||||
|
|
|
@ -63,7 +63,7 @@ impl BuildArgs {
|
|||
let krate =
|
||||
DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?;
|
||||
|
||||
self.resolve(&krate)?;
|
||||
self.resolve(&krate).await?;
|
||||
|
||||
let bundle = Builder::start(&krate, self.clone())?.finish().await?;
|
||||
|
||||
|
@ -79,7 +79,7 @@ impl BuildArgs {
|
|||
///
|
||||
/// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the
|
||||
/// fullstack variant even if they omitted the `--fullstack` flag.
|
||||
pub(crate) fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||
pub(crate) async fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||
let default_platform = krate.default_platform();
|
||||
let auto_platform = krate.autodetect_platform();
|
||||
|
||||
|
@ -145,6 +145,34 @@ impl BuildArgs {
|
|||
}
|
||||
}
|
||||
|
||||
// Determine arch if android
|
||||
if self.platform == Some(Platform::Android) && self.target_args.arch.is_none() {
|
||||
tracing::debug!("No android arch provided, attempting to auto detect.");
|
||||
|
||||
let arch = Arch::autodetect().await;
|
||||
|
||||
// Some extra logs
|
||||
let arch = match arch {
|
||||
Some(a) => {
|
||||
tracing::info!(
|
||||
"Autodetected `{}` Android arch.",
|
||||
a.android_target_triplet()
|
||||
);
|
||||
a.to_owned()
|
||||
}
|
||||
None => {
|
||||
let a = Arch::default();
|
||||
tracing::info!(
|
||||
"Could not detect Android arch, defaulting to `{}`",
|
||||
a.android_target_triplet()
|
||||
);
|
||||
a
|
||||
}
|
||||
};
|
||||
|
||||
self.target_args.arch = Some(arch);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ impl Bundle {
|
|||
|
||||
// We always use `release` mode for bundling
|
||||
self.build_arguments.release = true;
|
||||
self.build_arguments.resolve(&krate)?;
|
||||
self.build_arguments.resolve(&krate).await?;
|
||||
|
||||
tracing::info!("Building app...");
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ impl RunArgs {
|
|||
let krate = DioxusCrate::new(&self.build_args.target_args)
|
||||
.context("Failed to load Dioxus workspace")?;
|
||||
|
||||
self.build_args.resolve(&krate)?;
|
||||
self.build_args.resolve(&krate).await?;
|
||||
|
||||
tracing::trace!("Building crate krate data: {:#?}", krate);
|
||||
tracing::trace!("Build args: {:#?}", self.build_args);
|
||||
|
|
|
@ -54,13 +54,13 @@ impl ServeArgs {
|
|||
Ok(StructuredOutput::Success)
|
||||
}
|
||||
|
||||
pub(crate) fn load_krate(&mut self) -> Result<DioxusCrate> {
|
||||
pub(crate) async fn load_krate(&mut self) -> Result<DioxusCrate> {
|
||||
let krate = DioxusCrate::new(&self.build_arguments.target_args)?;
|
||||
self.resolve(&krate)?;
|
||||
self.resolve(&krate).await?;
|
||||
Ok(krate)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||
pub(crate) async fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||
// Enable hot reload.
|
||||
if self.hot_reload.is_none() {
|
||||
self.hot_reload = Some(krate.settings.always_hot_reload.unwrap_or(true));
|
||||
|
@ -82,7 +82,7 @@ impl ServeArgs {
|
|||
}
|
||||
|
||||
// Resolve the build arguments
|
||||
self.build_arguments.resolve(krate)?;
|
||||
self.build_arguments.resolve(krate).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use super::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::path::Path;
|
||||
use tokio::process::Command;
|
||||
|
||||
/// Information about the target to build
|
||||
#[derive(Clone, Debug, Default, Deserialize, Parser)]
|
||||
|
@ -35,13 +38,11 @@ pub(crate) struct TargetArgs {
|
|||
#[clap(long)]
|
||||
pub(crate) no_default_features: bool,
|
||||
|
||||
/// The architecture to build for [default: "native"]
|
||||
///
|
||||
/// Can either be `arm | arm64 | x86 | x86_64 | native`
|
||||
#[clap(long)]
|
||||
pub(crate) arch: Option<String>,
|
||||
/// The architecture to build for.
|
||||
#[clap(long, value_enum)]
|
||||
pub(crate) arch: Option<Arch>,
|
||||
|
||||
/// Are we building for a device or just the simulator
|
||||
/// Are we building for a device or just the simulator.
|
||||
/// If device is false, then we'll build for the simulator
|
||||
#[clap(long)]
|
||||
pub(crate) device: Option<bool>,
|
||||
|
@ -50,3 +51,138 @@ pub(crate) struct TargetArgs {
|
|||
#[clap(long)]
|
||||
pub(crate) target: Option<String>,
|
||||
}
|
||||
|
||||
impl TargetArgs {
|
||||
pub(crate) fn arch(&self) -> Arch {
|
||||
self.arch.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Deserialize, clap::ValueEnum)]
|
||||
#[non_exhaustive]
|
||||
pub(crate) enum Arch {
|
||||
// Android: armv7l, armv7-linux-androideabi
|
||||
Arm,
|
||||
// Android: aarch64, aarch64-linux-android
|
||||
#[default]
|
||||
Arm64,
|
||||
// Android: i386, i686-linux-android
|
||||
X86,
|
||||
// Android: x86_64, x86_64-linux-android
|
||||
X64,
|
||||
}
|
||||
|
||||
impl Arch {
|
||||
pub(crate) async fn autodetect() -> Option<Self> {
|
||||
// Try auto detecting arch through adb.
|
||||
static AUTO_ARCH: OnceCell<Option<Arch>> = OnceCell::new();
|
||||
|
||||
match AUTO_ARCH.get() {
|
||||
Some(a) => *a,
|
||||
None => {
|
||||
// TODO: Wire this up with --device flag. (add `-s serial`` flag before `shell` arg)
|
||||
let output = Command::new("adb")
|
||||
.arg("shell")
|
||||
.arg("uname")
|
||||
.arg("-m")
|
||||
.output()
|
||||
.await;
|
||||
|
||||
let out = match output {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
tracing::debug!("ADB command failed: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse ADB output
|
||||
let Ok(out) = String::from_utf8(out.stdout) else {
|
||||
tracing::debug!("ADB returned unexpected data.");
|
||||
return None;
|
||||
};
|
||||
let trimmed = out.trim().to_string();
|
||||
tracing::trace!("ADB Returned: `{trimmed:?}`");
|
||||
|
||||
// Set the cell
|
||||
let arch = Arch::try_from(trimmed).ok();
|
||||
AUTO_ARCH
|
||||
.set(arch)
|
||||
.expect("the cell should have been checked empty by the match condition");
|
||||
|
||||
arch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn android_target_triplet(&self) -> &'static str {
|
||||
match self {
|
||||
Arch::Arm => "armv7-linux-androideabi",
|
||||
Arch::Arm64 => "aarch64-linux-android",
|
||||
Arch::X86 => "i686-linux-android",
|
||||
Arch::X64 => "x86_64-linux-android",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn android_jnilib(&self) -> &'static str {
|
||||
match self {
|
||||
Arch::Arm => "armeabi-v7a",
|
||||
Arch::Arm64 => "arm64-v8a",
|
||||
Arch::X86 => "x86",
|
||||
Arch::X64 => "x86_64",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn android_clang_triplet(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Arm => "armv7a-linux-androideabi",
|
||||
_ => self.android_target_triplet(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn android_linker(&self, ndk: &Path) -> PathBuf {
|
||||
// "/Users/jonkelley/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang"
|
||||
|
||||
let toolchain_dir = ndk.join("toolchains").join("llvm").join("prebuilt");
|
||||
let triplet = self.android_clang_triplet();
|
||||
let clang_exec = format!("{}24-clang", triplet);
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
// for whatever reason, even on aarch64 macos, the linker is under darwin-x86_64
|
||||
return toolchain_dir
|
||||
.join("darwin-x86_64")
|
||||
.join("bin")
|
||||
.join(clang_exec);
|
||||
}
|
||||
|
||||
if cfg!(target_os = "linux") {
|
||||
return toolchain_dir
|
||||
.join("linux-x86_64")
|
||||
.join("bin")
|
||||
.join(clang_exec);
|
||||
}
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
return toolchain_dir
|
||||
.join("windows-x86_64")
|
||||
.join("bin")
|
||||
.join(format!("{}.cmd", clang_exec));
|
||||
}
|
||||
|
||||
unimplemented!("Unsupported target os for android toolchain auodetection")
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Arch {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
match value.as_str() {
|
||||
"armv7l" => Ok(Self::Arm),
|
||||
"aarch64" => Ok(Self::Arm64),
|
||||
"i386" => Ok(Self::X86),
|
||||
"x86_64" => Ok(Self::X64),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -557,7 +557,7 @@ impl DioxusCrate {
|
|||
krates
|
||||
}
|
||||
|
||||
fn android_ndk(&self) -> Option<PathBuf> {
|
||||
pub(crate) fn android_ndk(&self) -> Option<PathBuf> {
|
||||
// "/Users/jonkelley/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang"
|
||||
static PATH: once_cell::sync::Lazy<Option<PathBuf>> = once_cell::sync::Lazy::new(|| {
|
||||
use std::env::var;
|
||||
|
@ -596,37 +596,6 @@ impl DioxusCrate {
|
|||
PATH.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn android_linker(&self) -> Option<PathBuf> {
|
||||
// "/Users/jonkelley/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang"
|
||||
self.android_ndk().map(|ndk| {
|
||||
let toolchain_dir = ndk.join("toolchains").join("llvm").join("prebuilt");
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
// for whatever reason, even on aarch64 macos, the linker is under darwin-x86_64
|
||||
return toolchain_dir
|
||||
.join("darwin-x86_64")
|
||||
.join("bin")
|
||||
.join("aarch64-linux-android24-clang");
|
||||
}
|
||||
|
||||
if cfg!(target_os = "linux") {
|
||||
return toolchain_dir
|
||||
.join("linux-x86_64")
|
||||
.join("bin")
|
||||
.join("aarch64-linux-android24-clang");
|
||||
}
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
return toolchain_dir
|
||||
.join("windows-x86_64")
|
||||
.join("bin")
|
||||
.join("aarch64-linux-android24-clang.cmd");
|
||||
}
|
||||
|
||||
unimplemented!("Unsupported target os for android toolchain auodetection")
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn mobile_org(&self) -> String {
|
||||
let identifier = self.bundle_identifier();
|
||||
let mut split = identifier.splitn(3, '.');
|
||||
|
|
|
@ -42,7 +42,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
|
|||
let mut tracer = TraceController::redirect();
|
||||
|
||||
// Load the krate and resolve the server args against it - this might log so do it after we turn on the tracer first
|
||||
let krate = args.load_krate()?;
|
||||
let krate = args.load_krate().await?;
|
||||
|
||||
// Note that starting the builder will queue up a build immediately
|
||||
let mut builder = Builder::start(&krate, args.build_args())?;
|
||||
|
@ -158,7 +158,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
|
|||
screen.push_cargo_log(message);
|
||||
}
|
||||
BuildUpdate::BuildFailed { err } => {
|
||||
tracing::error!("Build failed: {}", err);
|
||||
tracing::error!("Build failed: {:?}", err);
|
||||
}
|
||||
BuildUpdate::BuildReady { bundle } => {
|
||||
let handle = runner
|
||||
|
|
Loading…
Reference in a new issue