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:
1 parent
5ecbb6594c
commit
af53354b28
10 files changed
+205
-58
No files matched your search
@@ -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")
|
.arg("assembleDebug")
|
||||||
.current_dir(self.build.root_dir())
|
.current_dir(self.build.root_dir())
|
||||||
.stderr(std::process::Stdio::piped())
|
.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
|
// 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.
|
// and we want everyone's install to be the same.
|
||||||
if self.build.platform() == Platform::Android {
|
if self.build.platform() == Platform::Android {
|
||||||
let linker = self
|
let ndk = self
|
||||||
.krate
|
.krate
|
||||||
.android_linker()
|
.android_ndk()
|
||||||
.context("Could not autodetect android linker")?;
|
.context("Could not autodetect android linker")?;
|
||||||
|
let linker = self.build.target_args.arch().android_linker(&ndk);
|
||||||
|
|
||||||
tracing::trace!("Using android linker: {linker:?}");
|
tracing::trace!("Using android linker: {linker:?}");
|
||||||
|
|
||||||
@@ -252,7 +253,7 @@ impl BuildRequest {
|
|||||||
Some(true) => Some("aarch64-apple-ios"),
|
Some(true) => Some("aarch64-apple-ios"),
|
||||||
_ => Some("aarch64-apple-ios-sim"),
|
_ => 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,
|
Platform::Server => None,
|
||||||
// we're assuming we're building for the native platform for now... if you're cross-compiling
|
// we're assuming we're building for the native platform for now... if you're cross-compiling
|
||||||
// the targets here might be different
|
// the targets here might be different
|
||||||
@@ -536,7 +537,7 @@ impl BuildRequest {
|
|||||||
.join("src")
|
.join("src")
|
||||||
.join("main")
|
.join("main")
|
||||||
.join("jniLibs")
|
.join("jniLibs")
|
||||||
.join("arm64-v8a"),
|
.join(self.build.target_args.arch().android_jnilib()),
|
||||||
|
|
||||||
// these are all the same, I think?
|
// these are all the same, I think?
|
||||||
Platform::Windows
|
Platform::Windows
|
||||||
|
|||||||
@@ -134,13 +134,20 @@ impl BuildRequest {
|
|||||||
/// will do its best to fill in the missing bits by exploring the sdk structure
|
/// 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.
|
/// IE will attempt to use the Java installed from android studio if possible.
|
||||||
pub(crate) async fn verify_android_tooling(&self, _rustup: RustupShow) -> Result<()> {
|
pub(crate) async fn verify_android_tooling(&self, _rustup: RustupShow) -> Result<()> {
|
||||||
if self.krate.android_linker().is_none() {
|
let result = self
|
||||||
return Err(anyhow::anyhow!(
|
.krate
|
||||||
"Android linker not found. Please set the ANDROID_NDK_HOME environment variable to the root of your NDK installation."
|
.android_ndk()
|
||||||
).into());
|
.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.
|
/// Ensure the right dependencies are installed for linux apps.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ impl BuildArgs {
|
|||||||
let krate =
|
let krate =
|
||||||
DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?;
|
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?;
|
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
|
/// 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.
|
/// 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 default_platform = krate.default_platform();
|
||||||
let auto_platform = krate.autodetect_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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl Bundle {
|
|||||||
|
|
||||||
// We always use `release` mode for bundling
|
// We always use `release` mode for bundling
|
||||||
self.build_arguments.release = true;
|
self.build_arguments.release = true;
|
||||||
self.build_arguments.resolve(&krate)?;
|
self.build_arguments.resolve(&krate).await?;
|
||||||
|
|
||||||
tracing::info!("Building app...");
|
tracing::info!("Building app...");
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ impl RunArgs {
|
|||||||
let krate = DioxusCrate::new(&self.build_args.target_args)
|
let krate = DioxusCrate::new(&self.build_args.target_args)
|
||||||
.context("Failed to load Dioxus workspace")?;
|
.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!("Building crate krate data: {:#?}", krate);
|
||||||
tracing::trace!("Build args: {:#?}", self.build_args);
|
tracing::trace!("Build args: {:#?}", self.build_args);
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ impl ServeArgs {
|
|||||||
Ok(StructuredOutput::Success)
|
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)?;
|
let krate = DioxusCrate::new(&self.build_arguments.target_args)?;
|
||||||
self.resolve(&krate)?;
|
self.resolve(&krate).await?;
|
||||||
Ok(krate)
|
Ok(krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
pub(crate) async fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
|
||||||
// Enable hot reload.
|
// Enable hot reload.
|
||||||
if self.hot_reload.is_none() {
|
if self.hot_reload.is_none() {
|
||||||
self.hot_reload = Some(krate.settings.always_hot_reload.unwrap_or(true));
|
self.hot_reload = Some(krate.settings.always_hot_reload.unwrap_or(true));
|
||||||
@@ -82,7 +82,7 @@ impl ServeArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the build arguments
|
// Resolve the build arguments
|
||||||
self.build_arguments.resolve(krate)?;
|
self.build_arguments.resolve(krate).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use std::path::Path;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
/// Information about the target to build
|
/// Information about the target to build
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Parser)]
|
#[derive(Clone, Debug, Default, Deserialize, Parser)]
|
||||||
@@ -35,13 +38,11 @@ pub(crate) struct TargetArgs {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub(crate) no_default_features: bool,
|
pub(crate) no_default_features: bool,
|
||||||
|
|
||||||
/// The architecture to build for [default: "native"]
|
/// The architecture to build for.
|
||||||
///
|
#[clap(long, value_enum)]
|
||||||
/// Can either be `arm | arm64 | x86 | x86_64 | native`
|
pub(crate) arch: Option<Arch>,
|
||||||
#[clap(long)]
|
|
||||||
pub(crate) arch: Option<String>,
|
|
||||||
|
|
||||||
/// 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
|
/// If device is false, then we'll build for the simulator
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub(crate) device: Option<bool>,
|
pub(crate) device: Option<bool>,
|
||||||
@@ -50,3 +51,138 @@ pub(crate) struct TargetArgs {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub(crate) target: Option<String>,
|
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
|
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"
|
// "/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(|| {
|
static PATH: once_cell::sync::Lazy<Option<PathBuf>> = once_cell::sync::Lazy::new(|| {
|
||||||
use std::env::var;
|
use std::env::var;
|
||||||
@@ -596,37 +596,6 @@ impl DioxusCrate {
|
|||||||
PATH.clone()
|
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 {
|
pub(crate) fn mobile_org(&self) -> String {
|
||||||
let identifier = self.bundle_identifier();
|
let identifier = self.bundle_identifier();
|
||||||
let mut split = identifier.splitn(3, '.');
|
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();
|
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
|
// 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
|
// Note that starting the builder will queue up a build immediately
|
||||||
let mut builder = Builder::start(&krate, args.build_args())?;
|
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);
|
screen.push_cargo_log(message);
|
||||||
}
|
}
|
||||||
BuildUpdate::BuildFailed { err } => {
|
BuildUpdate::BuildFailed { err } => {
|
||||||
tracing::error!("Build failed: {}", err);
|
tracing::error!("Build failed: {:?}", err);
|
||||||
}
|
}
|
||||||
BuildUpdate::BuildReady { bundle } => {
|
BuildUpdate::BuildReady { bundle } => {
|
||||||
let handle = runner
|
let handle = runner
|
||||||
|
|||||||
Reference in new issue
Block a user