From 5905bce08d3186071723da974df01df1dfb4b8cd Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 24 Jun 2025 23:37:45 +0100 Subject: [PATCH 01/25] Add --renderer argument to the CLI Signed-off-by: Nico Burns --- Cargo.toml | 1 - packages/cli/src/build/request.rs | 36 ++++--- packages/cli/src/cli/build.rs | 4 +- packages/cli/src/cli/target.rs | 9 +- packages/cli/src/platform.rs | 158 ++++++++++++++++++++++-------- 5 files changed, 152 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9ad5ae22e..65c4b55ab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -441,7 +441,6 @@ openssl = { version = "0.10", features = ["vendored"] } # To make most examples faster to compile, we split out assets and http-related stuff # This trims off like 270 dependencies, leading to a significant speedup in compilation time [features] -default = ["desktop"] desktop = ["dioxus/desktop"] native = ["dioxus/native"] liveview = ["dioxus/liveview"] diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index bf29f013ef..b9fa4bb1fb 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -316,8 +316,8 @@ //! - xbuild: use crate::{ - AndroidTools, BuildContext, DioxusConfig, Error, LinkAction, LinkerFlavor, Platform, Result, - RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, + AndroidTools, BuildContext, ClientRenderer, DioxusConfig, Error, LinkAction, LinkerFlavor, + Platform, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::Context; @@ -556,20 +556,25 @@ impl BuildRequest { let mut no_default_features = args.no_default_features; let platform: Platform = match args.platform { - Some(platform) => match enabled_platforms.len() { - 0 => platform, + Some(platform_arg) => match enabled_platforms.len() { + 0 => Platform::from(platform_arg), // The user passed --platform XYZ but already has `default = ["ABC"]` in their Cargo.toml or dioxus = { features = ["abc"] } // We want to strip out the default platform and use the one they passed, setting no-default-features _ => { features.extend(Self::platformless_features(main_package)); no_default_features = true; - platform + Platform::from(platform_arg) } }, - None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(), + None if !using_dioxus_explicitly => Platform::TARGET_PLATFORM.unwrap(), None => match enabled_platforms.len() { - 0 => return Err(anyhow::anyhow!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`").into()), + 0 => match args.renderer { + Some(_) => Platform::TARGET_PLATFORM.unwrap(), + None => Platform::TARGET_PLATFORM.unwrap(), + // TODO: should we always have a default + // None => return Err(anyhow::anyhow!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`").into()), + }, 1 => enabled_platforms[0], _ => { return Err(anyhow::anyhow!( @@ -582,7 +587,11 @@ impl BuildRequest { // Add any features required to turn on the client if using_dioxus_explicitly { - features.push(Self::feature_for_platform(main_package, platform)); + features.push(Self::feature_for_platform_and_renderer( + main_package, + platform, + args.renderer, + )); } // Set the profile of the build if it's not already set @@ -2867,9 +2876,13 @@ impl BuildRequest { } /// Get the features required to build for the given platform - fn feature_for_platform(package: &krates::cm::Package, platform: Platform) -> String { + fn feature_for_platform_and_renderer( + package: &krates::cm::Package, + platform: Platform, + renderer: Option, + ) -> String { // Try to find the feature that activates the dioxus feature for the given platform - let dioxus_feature = platform.feature_name(); + let dioxus_feature = platform.feature_name(renderer); let res = package.features.iter().find_map(|(key, features)| { // if the feature is just the name of the platform, we use that @@ -2895,7 +2908,7 @@ impl BuildRequest { }); res.unwrap_or_else(|| { - let fallback = format!("dioxus/{}", platform.feature_name()) ; + let fallback = format!("dioxus/{}", dioxus_feature) ; tracing::debug!( "Could not find explicit feature for platform {platform}, passing `fallback` instead" ); @@ -2953,6 +2966,7 @@ impl BuildRequest { }; // we only trace features 1 level deep.. + // TODO: trace all enabled features, not just default features for feature in default.iter() { // If the user directly specified a platform we can just use that. if feature.starts_with("dioxus/") { diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 2400a52588..c362402f3a 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,5 +1,5 @@ use crate::{cli::*, AppBuilder, BuildRequest, Workspace}; -use crate::{BuildMode, Platform}; +use crate::{BuildMode, Platform, PlatformArg}; use super::target::TargetArgs; @@ -120,7 +120,7 @@ impl CommandWithPlatformOverrides { let main_target = client.main_target.clone(); let mut server_args = server_args.clone(); // The platform in the server build is always set to Server - server_args.platform = Some(Platform::Server); + server_args.platform = Some(PlatformArg::Server); server = Some(BuildRequest::new(&server_args, Some(main_target), workspace.clone()).await?); } diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index c4c98ccf5a..4ca048a6d5 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -1,5 +1,6 @@ use crate::cli::*; -use crate::Platform; +use crate::ClientRenderer; +use crate::PlatformArg; use target_lexicon::Triple; const HELP_HEADING: &str = "Target Options"; @@ -9,7 +10,11 @@ const HELP_HEADING: &str = "Target Options"; pub(crate) struct TargetArgs { /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long, value_enum, help_heading = HELP_HEADING)] - pub(crate) platform: Option, + pub(crate) platform: Option, + + /// Build renderer: support Webview and Native [default: "webview"] + #[clap(long, value_enum, help_heading = HELP_HEADING)] + pub(crate) renderer: Option, /// Build in release mode [default: false] #[clap(long, short, help_heading = HELP_HEADING)] diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 986c306146..2886e50cea 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -17,56 +17,118 @@ use std::str::FromStr; clap::ValueEnum, )] #[non_exhaustive] -pub(crate) enum Platform { +pub(crate) enum PlatformArg { /// Targeting the web platform using WASM #[clap(name = "web")] - #[serde(rename = "web")] #[default] Web, /// Targeting macos desktop - /// When running on macos, you can also use `--platform desktop` to build for the desktop - #[cfg_attr(target_os = "macos", clap(alias = "desktop"))] #[clap(name = "macos")] - #[serde(rename = "macos")] MacOS, /// Targeting windows desktop - /// When running on windows, you can also use `--platform desktop` to build for the desktop - #[cfg_attr(target_os = "windows", clap(alias = "desktop"))] #[clap(name = "windows")] - #[serde(rename = "windows")] Windows, /// Targeting linux desktop - /// When running on linux, you can also use `--platform desktop` to build for the desktop - #[cfg_attr(target_os = "linux", clap(alias = "desktop"))] #[clap(name = "linux")] - #[serde(rename = "linux")] Linux, /// Targeting the ios platform /// /// Can't work properly if you're not building from an Apple device. #[clap(name = "ios")] - #[serde(rename = "ios")] Ios, /// Targeting the android platform #[clap(name = "android")] - #[serde(rename = "android")] Android, + /// Targeting the current platform with the "desktop" renderer + #[clap(name = "desktop")] + Desktop, + + /// Targeting the current platform with the "native" renderer + #[clap(name = "native")] + Native, + /// Targeting the server platform using Axum and Dioxus-Fullstack /// /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply /// means you're only building the server variant without the `.wasm` to serve. #[clap(name = "server")] - #[serde(rename = "server")] Server, /// Targeting the static generation platform using SSR and Dioxus-Fullstack #[clap(name = "liveview")] + Liveview, +} + +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Debug, + clap::ValueEnum, +)] +#[non_exhaustive] +pub(crate) enum ClientRenderer { + /// Targeting webview renderer + #[serde(rename = "webview")] + Webview, + + /// Targeting native renderer + #[serde(rename = "native")] + Native, +} + +#[derive( + Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, +)] +#[non_exhaustive] +pub(crate) enum Platform { + /// Targeting the web platform using WASM + #[serde(rename = "web")] + #[default] + Web, + + /// Targeting macos desktop + #[serde(rename = "macos")] + MacOS, + + /// Targeting windows desktop + #[serde(rename = "windows")] + Windows, + + /// Targeting linux desktop + #[serde(rename = "linux")] + Linux, + + /// Targeting the ios platform + /// + /// Can't work properly if you're not building from an Apple device. + #[serde(rename = "ios")] + Ios, + + /// Targeting the android platform + #[serde(rename = "android")] + Android, + + /// Targeting the server platform using Axum and Dioxus-Fullstack + /// + /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply + /// means you're only building the server variant without the `.wasm` to serve. + #[serde(rename = "server")] + Server, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack #[serde(rename = "liveview")] Liveview, } @@ -113,18 +175,51 @@ impl Display for Platform { } } +impl From for Platform { + fn from(value: PlatformArg) -> Self { + match value { + // Most values map 1:1 + PlatformArg::Web => Platform::Web, + PlatformArg::MacOS => Platform::MacOS, + PlatformArg::Windows => Platform::Windows, + PlatformArg::Linux => Platform::Linux, + PlatformArg::Ios => Platform::Ios, + PlatformArg::Android => Platform::Android, + PlatformArg::Server => Platform::Server, + PlatformArg::Liveview => Platform::Liveview, + + // The alias arguments + PlatformArg::Desktop | PlatformArg::Native => { + Platform::TARGET_PLATFORM.unwrap() + } + } + } +} + impl Platform { - /// Get the feature name for the platform in the dioxus crate - pub(crate) fn feature_name(&self) -> &str { + #[cfg(target_os = "macos")] + pub(crate) const TARGET_PLATFORM: Option = Some(Platform::MacOS); + #[cfg(target_os = "windows")] + pub(crate) const TARGET_PLATFORM: Option = Some(Platform::Windows); + #[cfg(target_os = "linux")] + pub(crate) const TARGET_PLATFORM: Option = Some(Platform::Linux); + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + pub(crate) const TARGET_PLATFORM: Option = None; + + // /// Get the feature name for the platform in the dioxus crate + pub(crate) fn feature_name(&self, renderer: Option) -> &str { match self { Platform::Web => "web", - Platform::MacOS => "desktop", - Platform::Windows => "desktop", - Platform::Linux => "desktop", + Platform::MacOS | Platform::Windows | Platform::Linux => match renderer { + None | Some(ClientRenderer::Webview) => "desktop", + Some(ClientRenderer::Native) => "native", + }, + Platform::Ios | Platform::Android => match renderer { + None | Some(ClientRenderer::Webview) => "mobile", + Some(ClientRenderer::Native) => "native", + }, Platform::Server => "server", Platform::Liveview => "liveview", - Platform::Ios => "mobile", - Platform::Android => "mobile", } } @@ -160,25 +255,7 @@ impl Platform { pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option { match feature { "web" => Some(Platform::Web), - "desktop" | "native" => { - #[cfg(target_os = "macos")] - { - Some(Platform::MacOS) - } - #[cfg(target_os = "windows")] - { - Some(Platform::Windows) - } - #[cfg(target_os = "linux")] - { - Some(Platform::Linux) - } - // Possibly need a something for freebsd? Maybe default to Linux? - #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] - { - None - } - } + "desktop" | "native" => Platform::TARGET_PLATFORM, "mobile" => None, "liveview" => Some(Platform::Liveview), "server" => Some(Platform::Server), @@ -188,6 +265,7 @@ impl Platform { pub(crate) fn profile_name(&self, release: bool) -> String { let base_profile = match self { + // TODO: add native profile? Platform::MacOS | Platform::Windows | Platform::Linux => "desktop", Platform::Web => "web", Platform::Ios => "ios", From 0f0826f73a6e463949e064f5f5dfb62c700babdc Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 30 Jun 2025 16:55:21 +0100 Subject: [PATCH 02/25] Make `--platform native` work Signed-off-by: Nico Burns --- packages/cli/src/build/request.rs | 15 +++++++++++---- packages/cli/src/platform.rs | 4 +--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index b9fa4bb1fb..bc8319875e 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -317,8 +317,8 @@ use crate::{ AndroidTools, BuildContext, ClientRenderer, DioxusConfig, Error, LinkAction, LinkerFlavor, - Platform, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, - DX_RUSTC_WRAPPER_ENV_VAR, + Platform, PlatformArg, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, + Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::Context; use cargo_metadata::diagnostic::Diagnostic; @@ -552,6 +552,13 @@ impl BuildRequest { .iter() .any(|dep| dep.name == "dioxus"); + // Infer the renderer from platform argument if the platform argument is "native" or "desktop" + let renderer = args.renderer.or(match args.platform { + Some(PlatformArg::Desktop) => Some(ClientRenderer::Webview), + Some(PlatformArg::Native) => Some(ClientRenderer::Native), + _ => None, + }); + let mut features = args.features.clone(); let mut no_default_features = args.no_default_features; @@ -569,7 +576,7 @@ impl BuildRequest { }, None if !using_dioxus_explicitly => Platform::TARGET_PLATFORM.unwrap(), None => match enabled_platforms.len() { - 0 => match args.renderer { + 0 => match renderer { Some(_) => Platform::TARGET_PLATFORM.unwrap(), None => Platform::TARGET_PLATFORM.unwrap(), // TODO: should we always have a default @@ -590,7 +597,7 @@ impl BuildRequest { features.push(Self::feature_for_platform_and_renderer( main_package, platform, - args.renderer, + renderer, )); } diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 2886e50cea..529bd0f5a0 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -189,9 +189,7 @@ impl From for Platform { PlatformArg::Liveview => Platform::Liveview, // The alias arguments - PlatformArg::Desktop | PlatformArg::Native => { - Platform::TARGET_PLATFORM.unwrap() - } + PlatformArg::Desktop | PlatformArg::Native => Platform::TARGET_PLATFORM.unwrap(), } } } From 6332252ca190c3963ca1c3cc8414dac427006408 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 1 Jul 2025 16:40:04 -0700 Subject: [PATCH 03/25] Emit `krates` error when cargo metadata fails --- packages/cli/src/error.rs | 3 +++ packages/cli/src/workspace.rs | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/error.rs b/packages/cli/src/error.rs index f703304715..d79bb8be70 100644 --- a/packages/cli/src/error.rs +++ b/packages/cli/src/error.rs @@ -48,6 +48,9 @@ pub(crate) enum Error { #[error("Network connectivity error: {0}")] Network(String), + #[error("Failed to run cargo metadata: {0}")] + Krates(#[from] krates::Error), + #[error(transparent)] Other(#[from] anyhow::Error), } diff --git a/packages/cli/src/workspace.rs b/packages/cli/src/workspace.rs index a334a8fc0c..b07eaa390e 100644 --- a/packages/cli/src/workspace.rs +++ b/packages/cli/src/workspace.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use std::{collections::HashSet, path::Path}; use std::{path::PathBuf, time::Duration}; use target_lexicon::Triple; -use tokio::process::Command; +use tokio::{process::Command, task::JoinHandle}; pub struct Workspace { pub(crate) krates: Krates, @@ -35,7 +35,7 @@ impl Workspace { return Ok(ws.clone()); } - let krates_future = tokio::task::spawn_blocking(|| { + let krates_future: JoinHandle> = tokio::task::spawn_blocking(|| { let manifest_options = crate::logging::VERBOSITY.get().unwrap(); let lock_options = LockOptions { frozen: manifest_options.frozen, @@ -48,9 +48,7 @@ impl Workspace { let mut builder = krates::Builder::new(); builder.workspace(true); - let res = builder - .build(cmd, |_| {}) - .context("Failed to run cargo metadata"); + let res = builder.build(cmd, |_| {})?; if !lock_options.offline { if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") { @@ -58,7 +56,7 @@ impl Workspace { } } - res + Ok(res) }); let spin_future = async move { From c806365d88a389275e8e34905862bb32eb2984ab Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 2 Jul 2025 01:15:36 +0100 Subject: [PATCH 04/25] Reinstate default "desktop" feature for examples --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 65c4b55ab8..f9ad5ae22e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -441,6 +441,7 @@ openssl = { version = "0.10", features = ["vendored"] } # To make most examples faster to compile, we split out assets and http-related stuff # This trims off like 270 dependencies, leading to a significant speedup in compilation time [features] +default = ["desktop"] desktop = ["dioxus/desktop"] native = ["dioxus/native"] liveview = ["dioxus/liveview"] From 72a32a02dd1a78a766f73a48f34e523b151a40a8 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 2 Jul 2025 01:17:42 +0100 Subject: [PATCH 05/25] Remove commented 'no platform specified' error Signed-off-by: Nico Burns --- packages/cli/src/build/request.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index bc8319875e..9f019148a3 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -576,12 +576,7 @@ impl BuildRequest { }, None if !using_dioxus_explicitly => Platform::TARGET_PLATFORM.unwrap(), None => match enabled_platforms.len() { - 0 => match renderer { - Some(_) => Platform::TARGET_PLATFORM.unwrap(), - None => Platform::TARGET_PLATFORM.unwrap(), - // TODO: should we always have a default - // None => return Err(anyhow::anyhow!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`").into()), - }, + 0 => Platform::TARGET_PLATFORM.unwrap(), 1 => enabled_platforms[0], _ => { return Err(anyhow::anyhow!( From 51b259bfbd6f45fac08f4c46ae032cbb51d94cf7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 11:12:53 -0500 Subject: [PATCH 06/25] use the target tripple and bundle format as the source of truth for the build --- packages/cli/src/build/builder.rs | 67 +++--- packages/cli/src/build/request.rs | 313 ++++++++++++-------------- packages/cli/src/cli/build.rs | 8 +- packages/cli/src/cli/bundle.rs | 25 +-- packages/cli/src/cli/run.rs | 40 ++-- packages/cli/src/cli/target.rs | 4 +- packages/cli/src/logging.rs | 26 +-- packages/cli/src/platform.rs | 350 ++++++++++++++++++++---------- packages/cli/src/serve/mod.rs | 26 ++- packages/cli/src/serve/output.rs | 16 +- packages/cli/src/serve/runner.rs | 19 +- packages/cli/src/serve/server.rs | 16 +- packages/cli/src/serve/update.rs | 4 +- 13 files changed, 507 insertions(+), 407 deletions(-) diff --git a/packages/cli/src/build/builder.rs b/packages/cli/src/build/builder.rs index 08f208276d..9f71d99517 100644 --- a/packages/cli/src/build/builder.rs +++ b/packages/cli/src/build/builder.rs @@ -1,5 +1,5 @@ use crate::{ - serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, Platform, + serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, BundleFormat, ProgressRx, ProgressTx, Result, StructuredOutput, }; use anyhow::Context; @@ -16,6 +16,7 @@ use std::{ process::Stdio, }; use subsecond_types::JumpTable; +use target_lexicon::Architecture; use tokio::{ io::{AsyncBufReadExt, BufReader, Lines}, process::{Child, ChildStderr, ChildStdout, Command}, @@ -289,7 +290,13 @@ impl AppBuilder { // for all other platforms, we need to use the ASLR reference to know where to insert the patch. let aslr_reference = match self.aslr_reference { Some(val) => val, - None if self.build.platform == Platform::Web => 0, + None if matches!( + self.build.triple.architecture, + Architecture::Wasm32 | Architecture::Wasm64 + ) => + { + 0 + } None => { tracing::warn!( "Ignoring hotpatch since there is no ASLR reference. Is the client connected?" @@ -502,18 +509,18 @@ impl AppBuilder { ); // We try to use stdin/stdout to communicate with the app - match self.build.platform { + match self.build.bundle { // Unfortunately web won't let us get a proc handle to it (to read its stdout/stderr) so instead // use use the websocket to communicate with it. I wish we could merge the concepts here, // like say, opening the socket as a subprocess, but alas, it's simpler to do that somewhere else. - Platform::Web => { + BundleFormat::Web => { // Only the first build we open the web app, after that the user knows it's running if open_browser { self.open_web(open_address.unwrap_or(devserver_ip)); } } - Platform::Ios => { + BundleFormat::Ios => { if self.build.device { self.codesign_ios().await?; self.open_ios_device().await? @@ -522,16 +529,15 @@ impl AppBuilder { } } - Platform::Android => { + BundleFormat::Android => { self.open_android_sim(false, devserver_ip, envs).await?; } // These are all just basically running the main exe, but with slightly different resource dir paths - Platform::Server - | Platform::MacOS - | Platform::Windows - | Platform::Linux - | Platform::Liveview => self.open_with_main_exe(envs, args)?, + BundleFormat::Server + | BundleFormat::MacOS + | BundleFormat::Windows + | BundleFormat::Linux => self.open_with_main_exe(envs, args)?, }; self.builds_opened += 1; @@ -617,7 +623,7 @@ impl AppBuilder { } // If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext` - if self.build.platform == Platform::Android { + if self.build.bundle == BundleFormat::Android { let bundled_name = PathBuf::from(bundled.bundled_path()); _ = self.copy_file_to_android_tmp(&from, &bundled_name).await; } @@ -628,7 +634,7 @@ impl AppBuilder { let mut jump_table = crate::build::create_jump_table(&new, &triple, cache)?; // If it's android, we need to copy the assets to the device and then change the location of the patch - if self.build.platform == Platform::Android { + if self.build.bundle == BundleFormat::Android { jump_table.lib = self .copy_file_to_android_tmp(&new, &(PathBuf::from(new.file_name().unwrap()))) .await?; @@ -721,7 +727,7 @@ impl AppBuilder { } // If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext` - if self.build.platform == Platform::Android { + if self.build.bundle == BundleFormat::Android { _ = self .copy_file_to_android_tmp(&changed_file, &bundled_name) .await; @@ -1297,18 +1303,14 @@ We checked the folder: {} let mut main_exe = self.build.main_exe(); // The requirement here is based on the platform, not necessarily our current architecture. - let requires_entropy = match self.build.platform { + let requires_entropy = match self.build.bundle { // When running "bundled", we don't need entropy - Platform::Web => false, - Platform::MacOS => false, - Platform::Ios => false, - Platform::Android => false, + BundleFormat::Web | BundleFormat::MacOS | BundleFormat::Ios | BundleFormat::Android => { + false + } // But on platforms that aren't running as "bundled", we do. - Platform::Windows => true, - Platform::Linux => true, - Platform::Server => true, - Platform::Liveview => true, + BundleFormat::Windows | BundleFormat::Linux | BundleFormat::Server => true, }; if requires_entropy || crate::devcfg::should_force_entropy() { @@ -1378,12 +1380,11 @@ We checked the folder: {} } pub(crate) async fn open_debugger(&mut self, server: &WebServer) -> Result<()> { - let url = match self.build.platform { - Platform::MacOS - | Platform::Windows - | Platform::Linux - | Platform::Server - | Platform::Liveview => { + let url = match self.build.bundle { + BundleFormat::MacOS + | BundleFormat::Windows + | BundleFormat::Linux + | BundleFormat::Server => { let Some(Some(pid)) = self.child.as_mut().map(|f| f.id()) else { tracing::warn!("No process to attach debugger to"); return Ok(()); @@ -1395,7 +1396,7 @@ We checked the folder: {} ) } - Platform::Web => { + BundleFormat::Web => { // code --open-url "vscode://DioxusLabs.dioxus/debugger?uri=http://127.0.0.1:8080" // todo - debugger could open to the *current* page afaik we don't have a way to have that info let address = server.devserver_address(); @@ -1409,7 +1410,7 @@ We checked the folder: {} format!("vscode://DioxusLabs.dioxus/debugger?uri={protocol}://{address}{base_path}") } - Platform::Ios => { + BundleFormat::Ios => { let Some(pid) = self.pid else { tracing::warn!("No process to attach debugger to"); return Ok(()); @@ -1448,7 +1449,7 @@ We checked the folder: {} // (lldb) settings append target.exec-search-paths target/dx/tw6/debug/android/app/app/src/main/jniLibs/arm64-v8a/libdioxusmain.so // (lldb) process handle SIGSEGV --pass true --stop false --notify true (otherwise the java threads cause crash) // - Platform::Android => { + BundleFormat::Android => { // adb push ./sdk/ndk/29.0.13113456/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/20/lib/linux/aarch64/lldb-server /tmp // adb shell "/tmp/lldb-server --server --listen ..." // "vscode://vadimcn.vscode-lldb/launch/config?{{'request':'connect','port': {}}}", @@ -1536,7 +1537,7 @@ We checked the folder: {} } }; - tracing::info!("Opening debugger for [{}]: {url}", self.build.platform); + tracing::info!("Opening debugger for [{}]: {url}", self.build.bundle); _ = tokio::process::Command::new("code") .arg("--open-url") diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 9f019148a3..795f1b5647 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -316,9 +316,9 @@ //! - xbuild: use crate::{ - AndroidTools, BuildContext, ClientRenderer, DioxusConfig, Error, LinkAction, LinkerFlavor, - Platform, PlatformArg, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, - Workspace, DX_RUSTC_WRAPPER_ENV_VAR, + AndroidTools, BuildContext, BundleFormat, DioxusConfig, Error, LinkAction, LinkerFlavor, + Platform, PlatformArg, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, + WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::Context; use cargo_metadata::diagnostic::Diagnostic; @@ -370,8 +370,9 @@ pub(crate) struct BuildRequest { pub(crate) crate_target: krates::cm::Target, pub(crate) profile: String, pub(crate) release: bool, - pub(crate) platform: Platform, - pub(crate) enabled_platforms: Vec, + pub(crate) renderer: Renderer, + pub(crate) bundle: BundleFormat, + pub(crate) enabled_renderers: Vec, pub(crate) triple: Triple, pub(crate) device: bool, pub(crate) package: String, @@ -435,7 +436,7 @@ pub enum BuildMode { /// The patch cache is only populated on fat builds and then used for thin builds (see `BuildMode::Thin`). #[derive(Clone, Debug)] pub struct BuildArtifacts { - pub(crate) platform: Platform, + pub(crate) bundle: BundleFormat, pub(crate) exe: PathBuf, pub(crate) direct_rustc: RustcArgs, pub(crate) time_start: SystemTime, @@ -554,8 +555,8 @@ impl BuildRequest { // Infer the renderer from platform argument if the platform argument is "native" or "desktop" let renderer = args.renderer.or(match args.platform { - Some(PlatformArg::Desktop) => Some(ClientRenderer::Webview), - Some(PlatformArg::Native) => Some(ClientRenderer::Native), + Some(PlatformArg::Desktop) => Some(Renderer::Webview), + Some(PlatformArg::Native) => Some(Renderer::Native), _ => None, }); @@ -569,7 +570,7 @@ impl BuildRequest { // The user passed --platform XYZ but already has `default = ["ABC"]` in their Cargo.toml or dioxus = { features = ["abc"] } // We want to strip out the default platform and use the one they passed, setting no-default-features _ => { - features.extend(Self::platformless_features(main_package)); + features.extend(Self::rendererless_features(main_package)); no_default_features = true; Platform::from(platform_arg) } @@ -623,39 +624,7 @@ impl BuildRequest { // The triple ends up being a source of truth for us later hence all this work to figure it out let triple = match args.target.clone() { Some(target) => target, - None => match platform { - // Generally just use the host's triple for native executables unless specified otherwise - Platform::MacOS - | Platform::Windows - | Platform::Linux - | Platform::Server - | Platform::Liveview => target_lexicon::HOST, - - // We currently assume unknown-unknown for web, but we might want to eventually - // support emscripten - Platform::Web => "wasm32-unknown-unknown".parse().unwrap(), - - // For iOS we should prefer the actual architecture for the simulator, but in lieu of actually - // figuring that out, we'll assume aarch64 on m-series and x86_64 otherwise - Platform::Ios => { - // use the host's architecture and sim if --device is passed - use target_lexicon::{Architecture, HOST}; - match HOST.architecture { - Architecture::Aarch64(_) if device => "aarch64-apple-ios".parse().unwrap(), - Architecture::Aarch64(_) => "aarch64-apple-ios-sim".parse().unwrap(), - _ if device => "x86_64-apple-ios".parse().unwrap(), - _ => "x86_64-apple-ios".parse().unwrap(), - } - } - - // Same idea with android but we figure out the connected device using adb - Platform::Android => { - workspace - .android_tools()? - .autodetect_android_device_triple() - .await - } - }, + None => platform.into_target(device, &workspace).await?, }; // Somethings we override are also present in the user's config. @@ -748,7 +717,7 @@ impl BuildRequest { ); Ok(Self { - platform, + renderer, features, no_default_features, crate_package, @@ -758,7 +727,7 @@ impl BuildRequest { device, workspace, config, - enabled_platforms, + enabled_renderers, target_dir, custom_linker, link_args_file, @@ -1027,7 +996,7 @@ impl BuildRequest { exe: &Path, assets: &mut AssetManifest, ) -> Result<()> { - match self.platform { + match self.bundle { // Run wasm-bindgen on the wasm binary and set its output to be in the bundle folder // Also run wasm-opt on the wasm binary, and sets the index.html since that's also the "executable". // @@ -1051,7 +1020,7 @@ impl BuildRequest { // assets/ // logo.png // ``` - Platform::Web => { + BundleFormat::Web => { self.bundle_web(ctx, exe, assets).await?; } @@ -1068,13 +1037,12 @@ impl BuildRequest { // // These are all super simple, just copy the exe into the folder // eventually, perhaps, maybe strip + encrypt the exe? - Platform::Android - | Platform::MacOS - | Platform::Windows - | Platform::Linux - | Platform::Ios - | Platform::Liveview - | Platform::Server => { + BundleFormat::Android + | BundleFormat::MacOS + | BundleFormat::Windows + | BundleFormat::Linux + | BundleFormat::Ios + | BundleFormat::Server => { std::fs::create_dir_all(self.exe_dir())?; std::fs::copy(exe, self.main_exe())?; } @@ -1136,7 +1104,7 @@ impl BuildRequest { /// Should be the same on all platforms - just copy over the assets from the manifest into the output directory async fn write_assets(&self, ctx: &BuildContext, assets: &AssetManifest) -> Result<()> { // Server doesn't need assets - web will provide them - if self.platform == Platform::Server { + if self.bundle == BundleFormat::Server { return Ok(()); } @@ -1356,7 +1324,10 @@ impl BuildRequest { // Requiring the ASLR offset here is necessary but unfortunately might be flakey in practice. // Android apps can take a long time to open, and a hot patch might've been issued in the interim, // making this hotpatch a failure. - if self.platform != Platform::Web { + if !matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) { let stub_bytes = crate::build::create_undefined_symbol_stub( cache, &object_files, @@ -1884,7 +1855,10 @@ impl BuildRequest { } // We want to go through wasm-ld directly, so we need to remove the -flavor flag - if self.platform == Platform::Web { + if matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) { let flavor_idx = args.iter().position(|arg| *arg == "-flavor").unwrap(); args.remove(flavor_idx + 1); args.remove(flavor_idx); @@ -2069,7 +2043,10 @@ impl BuildRequest { ); cmd.arg(format!("-Clinker={}", Workspace::path_to_dx()?.display())); - if self.platform == Platform::Web { + if matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) { cmd.arg("-Crelocation-model=pic"); } @@ -2190,7 +2167,7 @@ impl BuildRequest { // The bundle splitter needs relocation data to create a call-graph. // This will automatically be erased by wasm-opt during the optimization step. - if self.platform == Platform::Web && self.wasm_split { + if self.bundle == BundleFormat::Web && self.wasm_split { cargo_args.push("-Clink-args=--emit-relocs".to_string()); } @@ -2206,7 +2183,7 @@ impl BuildRequest { // for debuggability, we need to make sure android studio can properly understand our build // https://stackoverflow.com/questions/68481401/debugging-a-prebuilt-shared-library-in-android-studio - if self.platform == Platform::Android { + if self.bundle == BundleFormat::Android { cargo_args.push("-Clink-arg=-Wl,--build-id=sha1".to_string()); } @@ -2290,8 +2267,10 @@ impl BuildRequest { // https://blog.rust-lang.org/2024/09/24/webassembly-targets-change-in-default-target-features/#disabling-on-by-default-webassembly-proposals // // It's fine that these exist in the base module but not in the patch. - if self.platform == Platform::Web - || self.triple.operating_system == OperatingSystem::Wasi + if matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) || self.triple.operating_system == OperatingSystem::Wasi { cargo_args.push("-Ctarget-cpu=mvp".into()); cargo_args.push("-Clink-arg=--no-gc-sections".into()); @@ -2312,7 +2291,7 @@ impl BuildRequest { let mut env_vars = vec![]; // Make sure to set all the crazy android flags. Cross-compiling is hard, man. - if self.platform == Platform::Android { + if self.bundle == BundleFormat::Android { env_vars.extend(self.android_env_vars()?); }; @@ -2330,8 +2309,10 @@ impl BuildRequest { // Disable reference types on wasm when using hotpatching // https://blog.rust-lang.org/2024/09/24/webassembly-targets-change-in-default-target-features/#disabling-on-by-default-webassembly-proposals - if self.platform == Platform::Web - && matches!(ctx.mode, BuildMode::Thin { .. } | BuildMode::Fat) + if matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) && matches!(ctx.mode, BuildMode::Thin { .. } | BuildMode::Fat) { rust_flags.flags.push("-Ctarget-cpu=mvp".to_string()); } @@ -2534,43 +2515,41 @@ impl BuildRequest { pub(crate) fn root_dir(&self) -> PathBuf { let platform_dir = self.platform_dir(); - match self.platform { - Platform::Web => platform_dir.join("public"), - Platform::Server => platform_dir.clone(), // ends up *next* to the public folder + match self.bundle { + BundleFormat::Web => platform_dir.join("public"), + BundleFormat::Server => platform_dir.clone(), // ends up *next* to the public folder // These might not actually need to be called `.app` but it does let us run these with `open` - Platform::MacOS => platform_dir.join(format!("{}.app", self.bundled_app_name())), - Platform::Ios => platform_dir.join(format!("{}.app", self.bundled_app_name())), + BundleFormat::MacOS => platform_dir.join(format!("{}.app", self.bundled_app_name())), + BundleFormat::Ios => platform_dir.join(format!("{}.app", self.bundled_app_name())), // in theory, these all could end up directly in the root dir - Platform::Android => platform_dir.join("app"), // .apk (after bundling) - Platform::Linux => platform_dir.join("app"), // .appimage (after bundling) - Platform::Windows => platform_dir.join("app"), // .exe (after bundling) - Platform::Liveview => platform_dir.join("app"), // .exe (after bundling) + BundleFormat::Android => platform_dir.join("app"), // .apk (after bundling) + BundleFormat::Linux => platform_dir.join("app"), // .appimage (after bundling) + BundleFormat::Windows => platform_dir.join("app"), // .exe (after bundling) } } fn platform_dir(&self) -> PathBuf { - self.build_dir(self.platform, self.release) + self.build_dir(self.bundle, self.release) } fn platform_exe_name(&self) -> String { - match self.platform { - Platform::MacOS => self.executable_name().to_string(), - Platform::Ios => self.executable_name().to_string(), - Platform::Server => self.executable_name().to_string(), - Platform::Liveview => self.executable_name().to_string(), - Platform::Windows => format!("{}.exe", self.executable_name()), + match self.bundle { + BundleFormat::MacOS => self.executable_name().to_string(), + BundleFormat::Ios => self.executable_name().to_string(), + BundleFormat::Server => self.executable_name().to_string(), + BundleFormat::Windows => format!("{}.exe", self.executable_name()), // from the apk spec, the root exe is a shared library // we include the user's rust code as a shared library with a fixed namespace - Platform::Android => "libdioxusmain.so".to_string(), + BundleFormat::Android => "libdioxusmain.so".to_string(), // this will be wrong, I think, but not important? - Platform::Web => format!("{}_bg.wasm", self.executable_name()), + BundleFormat::Web => format!("{}_bg.wasm", self.executable_name()), // todo: maybe this should be called AppRun? - Platform::Linux => self.executable_name().to_string(), + BundleFormat::Linux => self.executable_name().to_string(), } } @@ -2825,22 +2804,22 @@ impl BuildRequest { /// target/dx/build/app/web/ /// target/dx/build/app/web/public/ /// target/dx/build/app/web/server.exe - pub(crate) fn build_dir(&self, platform: Platform, release: bool) -> PathBuf { + pub(crate) fn build_dir(&self, bundle: BundleFormat, release: bool) -> PathBuf { self.internal_out_dir() .join(&self.main_target) .join(if release { "release" } else { "debug" }) - .join(platform.build_folder_name()) + .join(bundle.build_folder_name()) } /// target/dx/bundle/app/ /// target/dx/bundle/app/blah.app /// target/dx/bundle/app/blah.exe /// target/dx/bundle/app/public/ - pub(crate) fn bundle_dir(&self, platform: Platform) -> PathBuf { + pub(crate) fn bundle_dir(&self, bundle: BundleFormat) -> PathBuf { self.internal_out_dir() .join(&self.main_target) .join("bundle") - .join(platform.build_folder_name()) + .join(bundle.build_folder_name()) } /// Get the workspace directory for the crate @@ -2880,11 +2859,11 @@ impl BuildRequest { /// Get the features required to build for the given platform fn feature_for_platform_and_renderer( package: &krates::cm::Package, - platform: Platform, - renderer: Option, + triple: &Triple, + renderer: Renderer, ) -> String { // Try to find the feature that activates the dioxus feature for the given platform - let dioxus_feature = platform.feature_name(renderer); + let dioxus_feature = renderer.feature_name(triple); let res = package.features.iter().find_map(|(key, features)| { // if the feature is just the name of the platform, we use that @@ -2912,7 +2891,7 @@ impl BuildRequest { res.unwrap_or_else(|| { let fallback = format!("dioxus/{}", dioxus_feature) ; tracing::debug!( - "Could not find explicit feature for platform {platform}, passing `fallback` instead" + "Could not find explicit feature for renderer {renderer}, passing `fallback` instead" ); fallback }) @@ -2933,8 +2912,8 @@ impl BuildRequest { pub(crate) fn enabled_cargo_toml_platforms( package: &krates::cm::Package, no_default_features: bool, - ) -> Vec { - let mut platforms = vec![]; + ) -> Vec { + let mut renderers = vec![]; // Attempt to discover the platform directly from the dioxus dependency // @@ -2943,8 +2922,8 @@ impl BuildRequest { // if let Some(dxs) = package.dependencies.iter().find(|dep| dep.name == "dioxus") { for f in dxs.features.iter() { - if let Some(platform) = Platform::autodetect_from_cargo_feature(f) { - platforms.push(platform); + if let Some(renderer) = Renderer::autodetect_from_cargo_feature(f) { + renderers.push(renderer); } } } @@ -2960,11 +2939,11 @@ impl BuildRequest { // default = ["web"] // web = ["dioxus/web"] if no_default_features { - return platforms; + return renderers; } let Some(default) = package.features.get("default") else { - return platforms; + return renderers; }; // we only trace features 1 level deep.. @@ -2973,9 +2952,9 @@ impl BuildRequest { // If the user directly specified a platform we can just use that. if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); - let auto = Platform::autodetect_from_cargo_feature(dx_feature); + let auto = Renderer::autodetect_from_cargo_feature(dx_feature); if let Some(auto) = auto { - platforms.push(auto); + renderers.push(auto); } } @@ -2985,23 +2964,23 @@ impl BuildRequest { for feature in internal_feature { if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); - let auto = Platform::autodetect_from_cargo_feature(dx_feature); + let auto = Renderer::autodetect_from_cargo_feature(dx_feature); if let Some(auto) = auto { - platforms.push(auto); + renderers.push(auto); } } } } } - platforms.sort(); - platforms.dedup(); + renderers.sort(); + renderers.dedup(); - platforms + renderers } /// Gather the features that are enabled for the package - fn platformless_features(package: &krates::cm::Package) -> Vec { + fn rendererless_features(package: &krates::cm::Package) -> Vec { let Some(default) = package.features.get("default") else { return Vec::new(); }; @@ -3014,7 +2993,7 @@ impl BuildRequest { // Don't keep features that point to a platform via dioxus/blah if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); - if Platform::autodetect_from_cargo_feature(dx_feature).is_some() { + if Renderer::autodetect_from_cargo_feature(dx_feature).is_some() { continue 'top; } } @@ -3024,7 +3003,7 @@ impl BuildRequest { for feature in internal_feature { if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); - if Platform::autodetect_from_cargo_feature(dx_feature).is_some() { + if Renderer::autodetect_from_cargo_feature(dx_feature).is_some() { continue 'top; } } @@ -3136,36 +3115,35 @@ impl BuildRequest { /// todo(jon): use handlebars templates instead of these prebaked templates async fn write_metadata(&self) -> Result<()> { // write the Info.plist file - match self.platform { - Platform::MacOS => { + match self.bundle { + BundleFormat::MacOS => { let dest = self.root_dir().join("Contents").join("Info.plist"); - let plist = self.info_plist_contents(self.platform)?; + let plist = self.info_plist_contents(self.bundle)?; std::fs::write(dest, plist)?; } - Platform::Ios => { + BundleFormat::Ios => { let dest = self.root_dir().join("Info.plist"); - let plist = self.info_plist_contents(self.platform)?; + let plist = self.info_plist_contents(self.bundle)?; std::fs::write(dest, plist)?; } // AndroidManifest.xml // er.... maybe even all the kotlin/java/gradle stuff? - Platform::Android => {} + BundleFormat::Android => {} // Probably some custom format or a plist file (haha) // When we do the proper bundle, we'll need to do something with wix templates, I think? - Platform::Windows => {} + BundleFormat::Windows => {} // eventually we'll create the .appimage file, I guess? - Platform::Linux => {} + BundleFormat::Linux => {} // These are served as folders, not appimages, so we don't need to do anything special (I think?) // Eventually maybe write some secrets/.env files for the server? // We could also distribute them as a deb/rpm for linux and msi for windows - Platform::Web => {} - Platform::Server => {} - Platform::Liveview => {} + BundleFormat::Web => {} + BundleFormat::Server => {} } Ok(()) @@ -3173,8 +3151,8 @@ impl BuildRequest { /// Run the optimizers, obfuscators, minimizers, signers, etc async fn optimize(&self, ctx: &BuildContext) -> Result<()> { - match self.platform { - Platform::Web => { + match self.bundle { + BundleFormat::Web => { // Compress the asset dir // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output let pre_compress = self.should_pre_compress_web_assets(self.release); @@ -3189,13 +3167,12 @@ impl BuildRequest { .unwrap()?; } } - Platform::MacOS => {} - Platform::Windows => {} - Platform::Linux => {} - Platform::Ios => {} - Platform::Android => {} - Platform::Server => {} - Platform::Liveview => {} + BundleFormat::MacOS + | BundleFormat::Windows + | BundleFormat::Linux + | BundleFormat::Ios + | BundleFormat::Android + | BundleFormat::Server => {} } Ok(()) @@ -3209,7 +3186,7 @@ impl BuildRequest { /// Check if the wasm output should be bundled to an asset type app. fn should_bundle_to_asset(&self) -> bool { - self.release && !self.wasm_split && self.platform == Platform::Web + self.release && !self.wasm_split && self.bundle == BundleFormat::Web } /// Bundle the web app @@ -3509,7 +3486,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ } } - fn info_plist_contents(&self, platform: Platform) -> Result { + fn info_plist_contents(&self, bundle: BundleFormat) -> Result { #[derive(Serialize)] pub struct InfoPlistData { pub display_name: String, @@ -3520,13 +3497,13 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ // Attempt to use the user's manually specified let _app = &self.config.application; - match platform { - Platform::MacOS => { + match bundle { + BundleFormat::MacOS => { if let Some(macos_info_plist) = _app.macos_info_plist.as_deref() { return Ok(std::fs::read_to_string(macos_info_plist)?); } } - Platform::Ios => { + BundleFormat::Ios => { if let Some(macos_info_plist) = _app.ios_info_plist.as_deref() { return Ok(std::fs::read_to_string(macos_info_plist)?); } @@ -3534,8 +3511,8 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ _ => {} } - match platform { - Platform::MacOS => handlebars::Handlebars::new() + match bundle { + BundleFormat::MacOS => handlebars::Handlebars::new() .render_template( include_str!("../../assets/macos/mac.plist.hbs"), &InfoPlistData { @@ -3546,7 +3523,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ }, ) .map_err(|e| e.into()), - Platform::Ios => handlebars::Handlebars::new() + BundleFormat::Ios => handlebars::Handlebars::new() .render_template( include_str!("../../assets/ios/ios.plist.hbs"), &InfoPlistData { @@ -3565,7 +3542,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ /// /// This might include codesigning, zipping, creating an appimage, etc async fn assemble(&self, ctx: &BuildContext) -> Result<()> { - if let Platform::Android = self.platform { + if self.bundle == BundleFormat::Android { ctx.status_running_gradle(); // When the build mode is set to release and there is an Android signature configuration, use assembleRelease @@ -3665,7 +3642,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ static INITIALIZED: OnceLock> = OnceLock::new(); let success = INITIALIZED.get_or_init(|| { - if self.platform != Platform::Server { + if self.bundle != BundleFormat::Server { _ = remove_dir_all(self.exe_dir()); } @@ -3688,7 +3665,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ // we could download the templates from somewhere (github?) but after having banged my head against // cargo-mobile2 for ages, I give up with that. We're literally just going to hardcode the templates // by writing them here. - if let Platform::Android = self.platform { + if self.bundle == BundleFormat::Android { self.build_android_app_dir()?; } @@ -3703,14 +3680,14 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ } pub(crate) fn asset_dir(&self) -> PathBuf { - match self.platform { - Platform::MacOS => self + match self.bundle { + BundleFormat::MacOS => self .root_dir() .join("Contents") .join("Resources") .join("assets"), - Platform::Android => self + BundleFormat::Android => self .root_dir() .join("app") .join("src") @@ -3718,12 +3695,11 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ .join("assets"), // everyone else is soooo normal, just app/assets :) - Platform::Web - | Platform::Ios - | Platform::Windows - | Platform::Linux - | Platform::Server - | Platform::Liveview => self.root_dir().join("assets"), + BundleFormat::Web + | BundleFormat::Ios + | BundleFormat::Windows + | BundleFormat::Linux + | BundleFormat::Server => self.root_dir().join("assets"), } } @@ -3738,12 +3714,12 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ /// /// todo(jon): investigate if we need to put .wasm in `wasm`. It kinda leaks implementation details, which ideally we don't want to do. fn exe_dir(&self) -> PathBuf { - match self.platform { - Platform::MacOS => self.root_dir().join("Contents").join("MacOS"), - Platform::Web => self.root_dir().join("wasm"), + match self.bundle { + BundleFormat::MacOS => self.root_dir().join("Contents").join("MacOS"), + BundleFormat::Web => self.root_dir().join("wasm"), // Android has a whole build structure to it - Platform::Android => self + BundleFormat::Android => self .root_dir() .join("app") .join("src") @@ -3752,11 +3728,10 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ .join(AndroidTools::android_jnilib(&self.triple)), // these are all the same, I think? - Platform::Windows - | Platform::Linux - | Platform::Ios - | Platform::Server - | Platform::Liveview => self.root_dir(), + BundleFormat::Windows + | BundleFormat::Linux + | BundleFormat::Ios + | BundleFormat::Server => self.root_dir(), } } @@ -3797,12 +3772,12 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ pub(crate) async fn verify_tooling(&self, ctx: &BuildContext) -> Result<()> { ctx.status_installing_tooling(); - match self.platform { - Platform::Web => self.verify_web_tooling().await?, - Platform::Ios => self.verify_ios_tooling().await?, - Platform::Android => self.verify_android_tooling().await?, - Platform::Linux => self.verify_linux_tooling().await?, - Platform::MacOS | Platform::Windows | Platform::Server | Platform::Liveview => {} + match self.bundle { + BundleFormat::Web => self.verify_web_tooling().await?, + BundleFormat::Ios => self.verify_ios_tooling().await?, + BundleFormat::Android => self.verify_android_tooling().await?, + BundleFormat::Linux => self.verify_linux_tooling().await?, + BundleFormat::MacOS | BundleFormat::Windows | BundleFormat::Server => {} } Ok(()) @@ -3945,8 +3920,8 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ } async fn create_patch_cache(&self, exe: &Path) -> Result { - let exe = match self.platform { - Platform::Web => self.wasm_bindgen_wasm_output_file(), + let exe = match self.bundle { + BundleFormat::Web => self.wasm_bindgen_wasm_output_file(), _ => exe.to_path_buf(), }; @@ -4151,7 +4126,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ self.base_path .as_deref() .or(self.config.web.app.base_path.as_deref()) - .filter(|_| matches!(self.platform, Platform::Web | Platform::Server)) + .filter(|_| matches!(self.bundle, BundleFormat::Web | BundleFormat::Server)) } /// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`. @@ -4179,13 +4154,13 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ return Ok(()); } - match self.platform { + match self.bundle { // Boot an iOS simulator if one is not already running. // // We always choose the most recently opened simulator based on the xcrun list. // Note that simulators can be running but the simulator app itself is not open. // Calling `open::that` is always fine, even on running apps, since apps are singletons. - Platform::Ios => { + BundleFormat::Ios => { #[derive(Deserialize, Debug)] struct XcrunListJson { // "com.apple.CoreSimulator.SimRuntime.iOS-18-4": [{}, {}, {}] @@ -4250,7 +4225,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ open::that_detached(path_to_sim)?; } - Platform::Android => { + BundleFormat::Android => { let tools = self.workspace.android_tools()?; tokio::spawn(async move { let emulator = tools.emulator(); @@ -4343,7 +4318,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ // The default dioxus experience is to lightly optimize the web build, both in debug and release // Note that typically in release builds, you would strip debuginfo, but we actually choose to do // that with wasm-opt tooling instead. - if matches!(self.platform, Platform::Web) { + if matches!(self.bundle, BundleFormat::Web) { match self.release { true => args.push(r#"profile.web.opt-level="s""#.to_string()), false => args.push(r#"profile.web.opt-level="1""#.to_string()), diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index c362402f3a..5490f3c50f 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,5 +1,5 @@ +use crate::BuildMode; use crate::{cli::*, AppBuilder, BuildRequest, Workspace}; -use crate::{BuildMode, Platform, PlatformArg}; use super::target::TargetArgs; @@ -48,7 +48,7 @@ impl BuildArgs { // This involves modifying the BuildRequest to add the client features and server features // only if we can properly detect that it's a fullstack build. Careful with this, since // we didn't build BuildRequest to be generally mutable. - let default_server = client.enabled_platforms.contains(&Platform::Server); + let default_server = client.enabled_renderers.contains(&crate::Renderer::Server); // Make sure we set the fullstack platform so we actually build the fullstack variant // Users need to enable "fullstack" in their default feature set. @@ -119,8 +119,8 @@ impl CommandWithPlatformOverrides { // Copy the main target from the client to the server let main_target = client.main_target.clone(); let mut server_args = server_args.clone(); - // The platform in the server build is always set to Server - server_args.platform = Some(PlatformArg::Server); + // The renderer in the server build is always set to Server + server_args.renderer = Some(crate::Renderer::Server); server = Some(BuildRequest::new(&server_args, Some(main_target), workspace.clone()).await?); } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index d6dd6bbaa6..f907e2ff7c 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -1,4 +1,4 @@ -use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, Platform}; +use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, BundleFormat}; use anyhow::{anyhow, Context}; use path_absolutize::Absolutize; use std::collections::HashMap; @@ -55,7 +55,7 @@ impl Bundle { } // If we're building for iOS, we need to bundle the iOS bundle - if client.platform == Platform::Ios && self.package_types.is_none() { + if client.bundle == BundleFormat::Ios && self.package_types.is_none() { self.package_types = Some(vec![crate::PackageType::IosBundle]); } @@ -67,9 +67,9 @@ impl Bundle { } // Create a list of bundles that we might need to copy - match client.platform { + match client.bundle { // By default, mac/win/linux work with tauri bundle - Platform::MacOS | Platform::Linux | Platform::Windows => { + BundleFormat::MacOS | BundleFormat::Linux | BundleFormat::Windows => { tracing::info!("Running desktop bundler..."); for bundle in Self::bundle_desktop(&client, &self.package_types)? { bundles.extend(bundle.bundle_paths); @@ -77,15 +77,14 @@ impl Bundle { } // Web/ios can just use their root_dir - Platform::Web => bundles.push(client.root_dir()), - Platform::Ios => { + BundleFormat::Web => bundles.push(client.root_dir()), + BundleFormat::Ios => { tracing::warn!("iOS bundles are not currently codesigned! You will need to codesign the app before distributing."); bundles.push(client.root_dir()) } - Platform::Server => bundles.push(client.root_dir()), - Platform::Liveview => bundles.push(client.root_dir()), + BundleFormat::Server => bundles.push(client.root_dir()), - Platform::Android => { + BundleFormat::Android => { let aab = client .android_gradle_bundle() .await @@ -145,16 +144,16 @@ impl Bundle { let krate = &build; let exe = build.main_exe(); - _ = std::fs::remove_dir_all(krate.bundle_dir(build.platform)); + _ = std::fs::remove_dir_all(krate.bundle_dir(build.bundle)); let package = krate.package(); let mut name: PathBuf = krate.executable_name().into(); if cfg!(windows) { name.set_extension("exe"); } - std::fs::create_dir_all(krate.bundle_dir(build.platform)) + std::fs::create_dir_all(krate.bundle_dir(build.bundle)) .context("Failed to create bundle directory")?; - std::fs::copy(&exe, krate.bundle_dir(build.platform).join(&name)) + std::fs::copy(&exe, krate.bundle_dir(build.bundle).join(&name)) .with_context(|| "Failed to copy the output executable into the bundle directory")?; let binaries = vec![ @@ -223,7 +222,7 @@ impl Bundle { } let mut settings = SettingsBuilder::new() - .project_out_directory(krate.bundle_dir(build.platform)) + .project_out_directory(krate.bundle_dir(build.bundle)) .package_settings(PackageSettings { product_name: krate.bundled_app_name(), version: package.version.to_string(), diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index 6230a155e4..3fd9424392 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -1,7 +1,7 @@ use super::*; use crate::{ serve::{AppServer, ServeUpdate, WebServer}, - BuilderUpdate, Error, Platform, Result, + BuilderUpdate, BundleFormat, Error, Result, }; use dioxus_dx_wire_format::BuildStage; @@ -40,7 +40,7 @@ impl RunArgs { match msg { ServeUpdate::BuilderUpdate { id, update } => { - let platform = builder.get_build(id).unwrap().build.platform; + let bundle_format = builder.get_build(id).unwrap().build.bundle; // And then update the websocketed clients with the new build status in case they want it devserver.new_build_update(&update).await; @@ -56,7 +56,7 @@ impl RunArgs { .await .inspect_err(|e| tracing::error!("Failed to open app: {}", e)); - if platform == Platform::Web { + if bundle_format == BundleFormat::Web { tracing::info!( "Serving app at http://{}:{}", builder.devserver_bind_ip, @@ -66,7 +66,7 @@ impl RunArgs { } BuilderUpdate::Progress { stage } => match stage { BuildStage::Initializing => { - tracing::info!("[{platform}] Initializing build") + tracing::info!("[{bundle_format}] Initializing build") } BuildStage::Starting { .. } => {} BuildStage::InstallingTooling => {} @@ -76,15 +76,15 @@ impl RunArgs { krate, } => { tracing::debug!( - "[{platform}] ({current}/{total}) Compiling {krate} ", + "[{bundle_format}] ({current}/{total}) Compiling {krate} ", ) } BuildStage::RunningBindgen => { - tracing::info!("[{platform}] Running WASM bindgen") + tracing::info!("[{bundle_format}] Running WASM bindgen") } BuildStage::SplittingBundle => {} BuildStage::OptimizingWasm => { - tracing::info!("[{platform}] Optimizing WASM with `wasm-opt`") + tracing::info!("[{bundle_format}] Optimizing WASM with `wasm-opt`") } BuildStage::Linking => tracing::info!("Linking app"), BuildStage::Hotpatching => {} @@ -93,30 +93,32 @@ impl RunArgs { total, path, } => tracing::info!( - "[{platform}] Copying asset {} ({current}/{total})", + "[{bundle_format}] Copying asset {} ({current}/{total})", path.display(), ), - BuildStage::Bundling => tracing::info!("[{platform}] Bundling app"), + BuildStage::Bundling => { + tracing::info!("[{bundle_format}] Bundling app") + } BuildStage::RunningGradle => { - tracing::info!("[{platform}] Running Gradle") + tracing::info!("[{bundle_format}] Running Gradle") } BuildStage::Success => {} BuildStage::Restarting => {} BuildStage::CompressingAssets => {} BuildStage::ExtractingAssets => {} BuildStage::Prerendering => { - tracing::info!("[{platform}] Prerendering app") + tracing::info!("[{bundle_format}] Prerendering app") } BuildStage::Failed => { - tracing::error!("[{platform}] Build failed"); + tracing::error!("[{bundle_format}] Build failed"); return Err(Error::Cargo(format!( - "Build failed for platform: {platform}" + "Build failed for bundle: {bundle_format}" ))); } BuildStage::Aborted => { - tracing::error!("[{platform}] Build aborted"); + tracing::error!("[{bundle_format}] Build aborted"); return Err(Error::Cargo(format!( - "Build aborted for platform: {platform}" + "Build aborted for bundle: {bundle_format}" ))); } _ => {} @@ -129,18 +131,18 @@ impl RunArgs { return Err(err); } BuilderUpdate::StdoutReceived { msg } => { - tracing::info!("[{platform}] {msg}"); + tracing::info!("[{bundle_format}] {msg}"); } BuilderUpdate::StderrReceived { msg } => { - tracing::error!("[{platform}] {msg}"); + tracing::error!("[{bundle_format}] {msg}"); } BuilderUpdate::ProcessExited { status } => { if !status.success() { tracing::error!( - "Application [{platform}] exited with error: {status}" + "Application [{bundle_format}] exited with error: {status}" ); return Err(Error::Runtime(format!( - "Application [{platform}] exited with error: {status}" + "Application [{bundle_format}] exited with error: {status}" ))); } diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index 4ca048a6d5..ee6777a1c4 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -1,6 +1,6 @@ use crate::cli::*; -use crate::ClientRenderer; use crate::PlatformArg; +use crate::Renderer; use target_lexicon::Triple; const HELP_HEADING: &str = "Target Options"; @@ -14,7 +14,7 @@ pub(crate) struct TargetArgs { /// Build renderer: support Webview and Native [default: "webview"] #[clap(long, value_enum, help_heading = HELP_HEADING)] - pub(crate) renderer: Option, + pub(crate) renderer: Option, /// Build in release mode [default: false] #[clap(long, short, help_heading = HELP_HEADING)] diff --git a/packages/cli/src/logging.rs b/packages/cli/src/logging.rs index f4630db19b..01c9bb2264 100644 --- a/packages/cli/src/logging.rs +++ b/packages/cli/src/logging.rs @@ -14,10 +14,12 @@ //! 3. Build CLI layer for routing tracing logs to the TUI. //! 4. Build fmt layer for non-interactive logging with a custom writer that prevents output during interactive mode. -use crate::{serve::ServeUpdate, Cli, Commands, Platform as TargetPlatform, Verbosity}; +use crate::BundleFormat; +use crate::{serve::ServeUpdate, Cli, Commands, Verbosity}; use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use clap::Parser; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; +use std::str::FromStr; use std::sync::OnceLock; use std::{ collections::HashMap, @@ -391,7 +393,7 @@ impl TraceMsg { #[derive(Clone, PartialEq)] pub enum TraceSrc { - App(TargetPlatform), + App(BundleFormat), Dev, Build, Bundle, @@ -412,12 +414,9 @@ impl From for TraceSrc { "dev" => Self::Dev, "bld" => Self::Build, "cargo" => Self::Cargo, - "app" => Self::App(TargetPlatform::Web), - "windows" => Self::App(TargetPlatform::Windows), - "macos" => Self::App(TargetPlatform::MacOS), - "linux" => Self::App(TargetPlatform::Linux), - "server" => Self::App(TargetPlatform::Server), - _ => Self::Unknown, + other => BundleFormat::from_str(other) + .map(Self::App) + .unwrap_or_else(|_| Self::Unknown), } } } @@ -425,16 +424,7 @@ impl From for TraceSrc { impl Display for TraceSrc { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::App(platform) => match platform { - TargetPlatform::Web => write!(f, "web"), - TargetPlatform::MacOS => write!(f, "macos"), - TargetPlatform::Windows => write!(f, "windows"), - TargetPlatform::Linux => write!(f, "linux"), - TargetPlatform::Server => write!(f, "server"), - TargetPlatform::Ios => write!(f, "ios"), - TargetPlatform::Android => write!(f, "android"), - TargetPlatform::Liveview => write!(f, "liveview"), - }, + Self::App(bundle) => write!(f, "{bundle}"), Self::Dev => write!(f, "dev"), Self::Build => write!(f, "build"), Self::Cargo => write!(f, "cargo"), diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 529bd0f5a0..fca1310a13 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -1,6 +1,10 @@ +use anyhow::Result; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::str::FromStr; +use target_lexicon::{Environment, OperatingSystem, Triple}; + +use crate::Workspace; #[derive( Copy, @@ -18,10 +22,10 @@ use std::str::FromStr; )] #[non_exhaustive] pub(crate) enum PlatformArg { - /// Targeting the web platform using WASM - #[clap(name = "web")] + /// Targeting the WASM architecture + #[clap(name = "wasm")] #[default] - Web, + Wasm, /// Targeting macos desktop #[clap(name = "macos")] @@ -48,21 +52,6 @@ pub(crate) enum PlatformArg { /// Targeting the current platform with the "desktop" renderer #[clap(name = "desktop")] Desktop, - - /// Targeting the current platform with the "native" renderer - #[clap(name = "native")] - Native, - - /// Targeting the server platform using Axum and Dioxus-Fullstack - /// - /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply - /// means you're only building the server variant without the `.wasm` to serve. - #[clap(name = "server")] - Server, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[clap(name = "liveview")] - Liveview, } #[derive( @@ -79,7 +68,7 @@ pub(crate) enum PlatformArg { clap::ValueEnum, )] #[non_exhaustive] -pub(crate) enum ClientRenderer { +pub(crate) enum Renderer { /// Targeting webview renderer #[serde(rename = "webview")] Webview, @@ -87,6 +76,86 @@ pub(crate) enum ClientRenderer { /// Targeting native renderer #[serde(rename = "native")] Native, + + /// Targeting the server platform using Axum and Dioxus-Fullstack + /// + /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply + /// means you're only building the server variant without the `.wasm` to serve. + #[serde(rename = "server")] + Server, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[serde(rename = "liveview")] + Liveview, + + /// Targeting the web renderer + #[serde(rename = "web")] + Web, +} + +impl Renderer { + /// Get the feature name for the platform in the dioxus crate + pub(crate) fn feature_name(&self, target: &Triple) -> &str { + match self { + Renderer::Webview => match (target.environment, target.operating_system) { + (Environment::Android, _) | (_, OperatingSystem::IOS(_)) => "mobile", + _ => "desktop", + }, + Renderer::Native => "native", + Renderer::Server => "server", + Renderer::Liveview => "liveview", + Renderer::Web => "web", + } + } + + pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option { + match feature { + "web" => Some(Self::Web), + "desktop" | "mobile" => Some(Self::Webview), + "native" => Some(Self::Native), + "liveview" => Some(Self::Liveview), + "server" => Some(Self::Server), + _ => None, + } + } +} + +#[derive(Debug)] +pub(crate) struct UnknownRendererError; + +impl std::error::Error for UnknownRendererError {} + +impl std::fmt::Display for UnknownRendererError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown renderer") + } +} + +impl FromStr for Renderer { + type Err = UnknownRendererError; + + fn from_str(s: &str) -> Result { + match s { + "webview" => Ok(Self::Webview), + "native" => Ok(Self::Native), + "server" => Ok(Self::Server), + "liveview" => Ok(Self::Liveview), + "web" => Ok(Self::Web), + _ => Err(UnknownRendererError), + } + } +} + +impl Display for Renderer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Renderer::Webview => "webview", + Renderer::Native => "native", + Renderer::Server => "server", + Renderer::Liveview => "liveview", + Renderer::Web => "web", + }) + } } #[derive( @@ -94,10 +163,10 @@ pub(crate) enum ClientRenderer { )] #[non_exhaustive] pub(crate) enum Platform { - /// Targeting the web platform using WASM - #[serde(rename = "web")] + /// Targeting the WASM architecture + #[serde(rename = "wasm")] #[default] - Web, + Wasm, /// Targeting macos desktop #[serde(rename = "macos")] @@ -120,17 +189,128 @@ pub(crate) enum Platform { /// Targeting the android platform #[serde(rename = "android")] Android, +} - /// Targeting the server platform using Axum and Dioxus-Fullstack - /// - /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply - /// means you're only building the server variant without the `.wasm` to serve. +#[derive( + Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, +)] +#[non_exhaustive] +pub(crate) enum BundleFormat { + /// Targeting the web bundle structure + #[serde(rename = "web")] + #[default] + Web, + + /// Targeting the macos desktop bundle structure + #[serde(rename = "macos")] + MacOS, + + /// Targeting the windows desktop bundle structure + #[serde(rename = "windows")] + Windows, + + /// Targeting the linux desktop bundle structure + #[serde(rename = "linux")] + Linux, + + /// Targeting the server bundle structure (a single binary placed next to the web build) #[serde(rename = "server")] Server, - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[serde(rename = "liveview")] - Liveview, + /// Targeting the ios bundle structure + /// + /// Can't work properly if you're not building from an Apple device. + #[serde(rename = "ios")] + Ios, + + /// Targeting the android bundle structure + #[serde(rename = "android")] + Android, +} + +#[derive(Debug)] +pub(crate) struct UnknownBundleFormatError; + +impl std::error::Error for UnknownBundleFormatError {} + +impl std::fmt::Display for UnknownBundleFormatError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown bundle format") + } +} + +impl FromStr for BundleFormat { + type Err = UnknownBundleFormatError; + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "macos" => Ok(Self::MacOS), + "windows" => Ok(Self::Windows), + "linux" => Ok(Self::Linux), + "server" => Ok(Self::Server), + "ios" => Ok(Self::Ios), + "android" => Ok(Self::Android), + _ => Err(UnknownBundleFormatError), + } + } +} + +impl Display for BundleFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + BundleFormat::Web => "web", + BundleFormat::MacOS => "macos", + BundleFormat::Windows => "windows", + BundleFormat::Linux => "linux", + BundleFormat::Server => "server", + BundleFormat::Ios => "ios", + BundleFormat::Android => "android", + }) + } +} + +impl BundleFormat { + /// Get the name of the folder we need to generate for this platform + /// + /// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own + pub(crate) fn build_folder_name(&self) -> &'static str { + match self { + Self::Web => "web", + Self::Server => "web", + Self::Ios => "ios", + Self::Android => "android", + Self::Windows => "windows", + Self::Linux => "linux", + Self::MacOS => "macos", + } + } + + pub(crate) fn profile_name(&self, release: bool) -> String { + let base_profile = match self { + Self::MacOS | Self::Windows | Self::Linux => "desktop", + Self::Web => "wasm", + Self::Ios => "ios", + Self::Android => "android", + Self::Server => "server", + }; + + let opt_level = if release { "release" } else { "dev" }; + + format!("{}-{}", base_profile, opt_level) + } + + pub(crate) fn expected_name(&self) -> &'static str { + match self { + Self::Web => "Web", + Self::MacOS => "Desktop MacOS", + Self::Windows => "Desktop Windows", + Self::Linux => "Desktop Linux", + Self::Ios => "Mobile iOS", + Self::Android => "Mobile Android", + Self::Server => "Server", + } + } } /// An error that occurs when a platform is not recognized @@ -147,12 +327,10 @@ impl FromStr for Platform { fn from_str(s: &str) -> Result { match s { - "web" => Ok(Self::Web), + "wasm" => Ok(Self::Wasm), "macos" => Ok(Self::MacOS), "windows" => Ok(Self::Windows), "linux" => Ok(Self::Linux), - "liveview" => Ok(Self::Liveview), - "server" => Ok(Self::Server), "ios" => Ok(Self::Ios), "android" => Ok(Self::Android), _ => Err(UnknownPlatformError), @@ -163,14 +341,12 @@ impl FromStr for Platform { impl Display for Platform { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { - Platform::Web => "web", + Platform::Wasm => "wasm", Platform::MacOS => "macos", Platform::Windows => "windows", Platform::Linux => "linux", Platform::Ios => "ios", Platform::Android => "android", - Platform::Server => "server", - Platform::Liveview => "liveview", }) } } @@ -179,17 +355,15 @@ impl From for Platform { fn from(value: PlatformArg) -> Self { match value { // Most values map 1:1 - PlatformArg::Web => Platform::Web, + PlatformArg::Wasm => Platform::Wasm, PlatformArg::MacOS => Platform::MacOS, PlatformArg::Windows => Platform::Windows, PlatformArg::Linux => Platform::Linux, PlatformArg::Ios => Platform::Ios, PlatformArg::Android => Platform::Android, - PlatformArg::Server => Platform::Server, - PlatformArg::Liveview => Platform::Liveview, // The alias arguments - PlatformArg::Desktop | PlatformArg::Native => Platform::TARGET_PLATFORM.unwrap(), + PlatformArg::Desktop => Platform::TARGET_PLATFORM.unwrap(), } } } @@ -204,78 +378,34 @@ impl Platform { #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] pub(crate) const TARGET_PLATFORM: Option = None; - // /// Get the feature name for the platform in the dioxus crate - pub(crate) fn feature_name(&self, renderer: Option) -> &str { + pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result { match self { - Platform::Web => "web", - Platform::MacOS | Platform::Windows | Platform::Linux => match renderer { - None | Some(ClientRenderer::Webview) => "desktop", - Some(ClientRenderer::Native) => "native", - }, - Platform::Ios | Platform::Android => match renderer { - None | Some(ClientRenderer::Webview) => "mobile", - Some(ClientRenderer::Native) => "native", - }, - Platform::Server => "server", - Platform::Liveview => "liveview", - } - } - - /// Get the name of the folder we need to generate for this platform - /// - /// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own - pub(crate) fn build_folder_name(&self) -> &'static str { - match self { - Platform::Web => "web", - Platform::Server => "web", - Platform::Liveview => "liveview", - Platform::Ios => "ios", - Platform::Android => "android", - Platform::Windows => "windows", - Platform::Linux => "linux", - Platform::MacOS => "macos", - } - } - - pub(crate) fn expected_name(&self) -> &'static str { - match self { - Platform::Web => "Web", - Platform::MacOS => "Desktop MacOS", - Platform::Windows => "Desktop Windows", - Platform::Linux => "Desktop Linux", - Platform::Ios => "Mobile iOS", - Platform::Android => "Mobile Android", - Platform::Server => "Server", - Platform::Liveview => "Liveview", - } - } - - pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option { - match feature { - "web" => Some(Platform::Web), - "desktop" | "native" => Platform::TARGET_PLATFORM, - "mobile" => None, - "liveview" => Some(Platform::Liveview), - "server" => Some(Platform::Server), - _ => None, - } - } - - pub(crate) fn profile_name(&self, release: bool) -> String { - let base_profile = match self { - // TODO: add native profile? - Platform::MacOS | Platform::Windows | Platform::Linux => "desktop", - Platform::Web => "web", - Platform::Ios => "ios", - Platform::Android => "android", - Platform::Server => "server", - Platform::Liveview => "liveview", - }; - - if release { - format!("{}-release", base_profile) - } else { - format!("{}-dev", base_profile) + // Generally just use the host's triple for native executables unless specified otherwise + Platform::MacOS | Platform::Windows | Platform::Linux => Ok(Triple::host()), + + // We currently assume unknown-unknown for web, but we might want to eventually + // support emscripten + Platform::Wasm => Ok("wasm32-unknown-unknown".parse()?), + + // For iOS we should prefer the actual architecture for the simulator, but in lieu of actually + // figuring that out, we'll assume aarch64 on m-series and x86_64 otherwise + Platform::Ios => { + // use the host's architecture and sim if --device is passed + use target_lexicon::{Architecture, HOST}; + let triple_str = match HOST.architecture { + Architecture::Aarch64(_) if device => "aarch64-apple-ios", + Architecture::Aarch64(_) => "aarch64-apple-ios-sim", + _ if device => "x86_64-apple-ios", + _ => "x86_64-apple-ios", + }; + Ok(triple_str.parse()?) + } + + // Same idea with android but we figure out the connected device using adb + Platform::Android => Ok(workspace + .android_tools()? + .autodetect_android_device_triple() + .await), } } } diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index ecc22e4ca6..2340fccd65 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,6 +1,6 @@ use crate::{ styles::{GLOW_STYLE, LINK_STYLE}, - AppBuilder, BuildId, BuildMode, BuilderUpdate, Error, Platform, Result, ServeArgs, + AppBuilder, BuildId, BuildMode, BuilderUpdate, BundleFormat, Error, Result, ServeArgs, TraceController, }; @@ -120,15 +120,15 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> // Received a message from the devtools server - currently we only use this for // logging, so we just forward it the tui - ServeUpdate::WsMessage { msg, platform } => { - screen.push_ws_message(platform, &msg); + ServeUpdate::WsMessage { msg, bundle } => { + screen.push_ws_message(bundle, &msg); } // Wait for logs from the build engine // These will cause us to update the screen // We also can check the status of the builds here in case we have multiple ongoing builds ServeUpdate::BuilderUpdate { id, update } => { - let platform = builder.get_build(id).unwrap().build.platform; + let bundle_format = builder.get_build(id).unwrap().build.bundle; // Queue any logs to be printed if need be screen.new_build_update(&update); @@ -146,7 +146,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> } => { if exit_on_error { return Err(Error::Cargo(format!( - "Build failed for platform: {platform}" + "Build failed for platform: {bundle_format}" ))); } } @@ -155,7 +155,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> } => { if exit_on_error { return Err(Error::Cargo(format!( - "Build aborted for platform: {platform}" + "Build aborted for platform: {bundle_format}" ))); } } @@ -201,23 +201,25 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> } }, BuilderUpdate::StdoutReceived { msg } => { - screen.push_stdio(platform, msg, tracing::Level::INFO); + screen.push_stdio(bundle_format, msg, tracing::Level::INFO); } BuilderUpdate::StderrReceived { msg } => { - screen.push_stdio(platform, msg, tracing::Level::ERROR); + screen.push_stdio(bundle_format, msg, tracing::Level::ERROR); } BuilderUpdate::ProcessExited { status } => { if status.success() { tracing::info!( - r#"Application [{platform}] exited gracefully. + r#"Application [{bundle_format}] exited gracefully. • To restart the app, press `r` to rebuild or `o` to open • To exit the server, press `ctrl+c`"# ); } else { - tracing::error!("Application [{platform}] exited with error: {status}"); + tracing::error!( + "Application [{bundle_format}] exited with error: {status}" + ); if exit_on_error { return Err(Error::Runtime(format!( - "Application [{platform}] exited with error: {status}" + "Application [{bundle_format}] exited with error: {status}" ))); } } @@ -238,7 +240,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> } ServeUpdate::OpenApp => match builder.use_hotpatch_engine { - true if !matches!(builder.client.build.platform, Platform::Web) => { + true if !matches!(builder.client.build.bundle, BundleFormat::Web) => { tracing::warn!( "Opening a native app with hotpatching enabled requires a full rebuild..." ); diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index f520026284..8e2e0caf30 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,6 +1,6 @@ use crate::{ serve::{ansi_buffer::AnsiStringLine, ServeUpdate, WebServer}, - BuildId, BuildStage, BuilderUpdate, Platform, TraceContent, TraceMsg, TraceSrc, + BuildId, BuildStage, BuilderUpdate, BundleFormat, TraceContent, TraceMsg, TraceSrc, }; use cargo_metadata::diagnostic::Diagnostic; use crossterm::{ @@ -316,12 +316,12 @@ impl Output { /// Add a message from stderr to the logs /// This will queue the stderr message as a TraceMsg and print it on the next render /// We'll use the `App` TraceSrc for the msg, and whatever level is provided - pub fn push_stdio(&mut self, platform: Platform, msg: String, level: Level) { - self.push_log(TraceMsg::text(TraceSrc::App(platform), level, msg)); + pub fn push_stdio(&mut self, bundle: BundleFormat, msg: String, level: Level) { + self.push_log(TraceMsg::text(TraceSrc::App(bundle), level, msg)); } /// Push a message from the websocket to the logs - pub fn push_ws_message(&mut self, platform: Platform, message: &axum::extract::ws::Message) { + pub fn push_ws_message(&mut self, bundle: BundleFormat, message: &axum::extract::ws::Message) { use dioxus_devtools_types::ClientMsg; // We can only handle text messages from the websocket... @@ -336,7 +336,7 @@ impl Output { let msg = match res { Ok(msg) => msg, Err(err) => { - tracing::error!(dx_src = ?TraceSrc::Dev, "Error parsing message from {}: {} -> {:?}", platform, err, text.as_str()); + tracing::error!(dx_src = ?TraceSrc::Dev, "Error parsing message from {}: {} -> {:?}", bundle, err, text.as_str()); return; } }; @@ -358,7 +358,7 @@ impl Output { }; // We don't care about logging the app's message so we directly push it instead of using tracing. - self.push_log(TraceMsg::text(TraceSrc::App(platform), level, content)); + self.push_log(TraceMsg::text(TraceSrc::App(bundle), level, content)); } /// Change internal state based on the build engine's update @@ -641,7 +641,7 @@ impl Output { frame.render_widget( Paragraph::new(Line::from(vec![ "Platform: ".gray(), - client.build.platform.expected_name().yellow(), + client.build.bundle.expected_name().yellow(), if state.runner.is_fullstack() { " + fullstack".yellow() } else { @@ -672,7 +672,7 @@ impl Output { frame.render_widget_ref( Paragraph::new(Line::from(vec![ - if client.build.platform == Platform::Web { + if client.build.bundle == BundleFormat::Web { "Serving at: ".gray() } else { "ServerFns at: ".gray() diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index a00587d405..9906d473eb 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,7 +1,7 @@ use super::{AppBuilder, ServeUpdate, WebServer}; use crate::{ platform_override::CommandWithPlatformOverrides, BuildArtifacts, BuildId, BuildMode, - BuildTargets, BuilderUpdate, Error, HotpatchModuleCache, Platform, Result, ServeArgs, + BuildTargets, BuilderUpdate, BundleFormat, Error, HotpatchModuleCache, Result, ServeArgs, TailwindCli, TraceSrc, Workspace, }; use anyhow::Context; @@ -150,8 +150,8 @@ impl AppServer { // All servers will end up behind us (the devserver) but on a different port // This is so we can serve a loading screen as well as devtools without anything particularly fancy let fullstack = server.is_some(); - let should_proxy_port = match client.platform { - Platform::Server => true, + let should_proxy_port = match client.bundle { + BundleFormat::Server => true, _ => fullstack && !ssg, }; @@ -506,7 +506,8 @@ impl AppServer { use crate::styles::NOTE_STYLE; tracing::info!(dx_src = ?TraceSrc::Dev, "Hotreloading: {NOTE_STYLE}{}{NOTE_STYLE:#}", file); - if !server.has_hotreload_sockets() && self.client.build.platform != Platform::Web { + if !server.has_hotreload_sockets() && self.client.build.bundle != BundleFormat::Web + { tracing::warn!("No clients to hotreload - try reloading the app!"); } @@ -524,8 +525,8 @@ impl AppServer { devserver: &mut WebServer, ) -> Result<()> { // Make sure to save artifacts regardless of if we're opening the app or not - match artifacts.platform { - Platform::Server => { + match artifacts.bundle { + BundleFormat::Server => { if let Some(server) = self.server.as_mut() { server.artifacts = Some(artifacts.clone()); } @@ -631,7 +632,7 @@ impl AppServer { // If the client is running on Android, we need to remove the port forwarding // todo: use the android tools "adb" - if matches!(self.client.build.platform, Platform::Android) { + if matches!(self.client.build.bundle, BundleFormat::Android) { if let Err(err) = Command::new(&self.workspace.android_tools()?.adb) .arg("reverse") .arg("--remove") @@ -756,7 +757,7 @@ impl AppServer { BuildId::CLIENT => { // multiple tabs on web can cause this to be called incorrectly, and it doesn't // make any sense anyways - if self.client.build.platform != Platform::Web { + if self.client.build.bundle != BundleFormat::Web { if let Some(aslr_reference) = aslr_reference { self.client.aslr_reference = Some(aslr_reference); } @@ -774,7 +775,7 @@ impl AppServer { } // Assign the runtime asset dir to the runner - if self.client.build.platform == Platform::Ios { + if self.client.build.bundle == BundleFormat::Ios { // xcrun simctl get_app_container booted com.dioxuslabs let res = Command::new("xcrun") .arg("simctl") diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index f60eb99c35..a7f2cf72bc 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,5 +1,5 @@ use crate::{ - config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, Platform, + config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, BundleFormat, Result, TraceSrc, }; use anyhow::Context; @@ -62,7 +62,7 @@ pub(crate) struct WebServer { new_build_status_sockets: UnboundedReceiver, build_status: SharedStatus, application_name: String, - platform: Platform, + bundle: BundleFormat, } pub(crate) struct ConnectedWsClient { @@ -128,7 +128,7 @@ impl WebServer { new_hot_reload_sockets: hot_reload_sockets_rx, new_build_status_sockets: build_status_sockets_rx, application_name: runner.app_name().to_string(), - platform: runner.client.build.platform, + bundle: runner.client.build.bundle, }) } @@ -163,7 +163,7 @@ impl WebServer { drop(new_message); // Update the socket with project info and current build status - let project_info = SharedStatus::new(Status::ClientInit { application_name: self.application_name.clone(), platform: self.platform }); + let project_info = SharedStatus::new(Status::ClientInit { application_name: self.application_name.clone(), bundle: self.bundle }); if project_info.send_to(&mut new_socket.socket).await.is_ok() { _ = self.build_status.send_to(&mut new_socket.socket).await; self.build_status_sockets.push(new_socket); @@ -175,7 +175,7 @@ impl WebServer { } Some((idx, message)) = new_message.next() => { match message { - Some(Ok(msg)) => return ServeUpdate::WsMessage { msg, platform: Platform::Web }, + Some(Ok(msg)) => return ServeUpdate::WsMessage { msg, bundle: BundleFormat::Web }, _ => { drop(new_message); _ = self.hot_reload_sockets.remove(idx); @@ -375,8 +375,8 @@ impl WebServer { } pub fn server_address(&self) -> Option { - match self.platform { - Platform::Web | Platform::Server => Some(self.devserver_address()), + match self.bundle { + BundleFormat::Web | BundleFormat::Server => Some(self.devserver_address()), _ => self.proxied_server_address(), } } @@ -731,7 +731,7 @@ struct SharedStatus(Arc>); enum Status { ClientInit { application_name: String, - platform: Platform, + bundle: BundleFormat, }, Building { progress: f64, diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index dfb4b74050..0e8d22d0ec 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -1,4 +1,4 @@ -use crate::{BuildId, BuilderUpdate, Platform, TraceMsg}; +use crate::{BuildId, BuilderUpdate, BundleFormat, TraceMsg}; use axum::extract::ws::Message as WsMessage; use std::path::PathBuf; @@ -13,7 +13,7 @@ pub(crate) enum ServeUpdate { pid: Option, }, WsMessage { - platform: Platform, + bundle: BundleFormat, msg: WsMessage, }, From 59dc4d062fd715563d46d3b8835b34bae3969d6f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 14:01:58 -0500 Subject: [PATCH 07/25] resolve renderer and bundle format --- packages/cli/src/build/request.rs | 94 +++++++++++++++++-------------- packages/cli/src/cli/target.rs | 5 ++ packages/cli/src/platform.rs | 51 ++++++++++++++++- 3 files changed, 106 insertions(+), 44 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 795f1b5647..d6d7a2c0dd 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -317,8 +317,8 @@ use crate::{ AndroidTools, BuildContext, BundleFormat, DioxusConfig, Error, LinkAction, LinkerFlavor, - Platform, PlatformArg, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, - WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR, + Platform, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, + Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::Context; use cargo_metadata::diagnostic::Diagnostic; @@ -370,7 +370,6 @@ pub(crate) struct BuildRequest { pub(crate) crate_target: krates::cm::Target, pub(crate) profile: String, pub(crate) release: bool, - pub(crate) renderer: Renderer, pub(crate) bundle: BundleFormat, pub(crate) enabled_renderers: Vec, pub(crate) triple: Triple, @@ -546,39 +545,32 @@ impl BuildRequest { // The crate might enable multiple platforms or no platforms at // We collect all the platforms it enables first and then select based on the --platform arg - let enabled_platforms = - Self::enabled_cargo_toml_platforms(main_package, args.no_default_features); + let enabled_renderers = + Self::enabled_cargo_toml_renderers(main_package, args.no_default_features); let using_dioxus_explicitly = main_package .dependencies .iter() .any(|dep| dep.name == "dioxus"); - // Infer the renderer from platform argument if the platform argument is "native" or "desktop" - let renderer = args.renderer.or(match args.platform { - Some(PlatformArg::Desktop) => Some(Renderer::Webview), - Some(PlatformArg::Native) => Some(Renderer::Native), - _ => None, - }); - let mut features = args.features.clone(); let mut no_default_features = args.no_default_features; - let platform: Platform = match args.platform { - Some(platform_arg) => match enabled_platforms.len() { - 0 => Platform::from(platform_arg), + let renderer: Option = match args.renderer { + Some(renderer) => match enabled_renderers.len() { + 0 => Some(renderer), // The user passed --platform XYZ but already has `default = ["ABC"]` in their Cargo.toml or dioxus = { features = ["abc"] } // We want to strip out the default platform and use the one they passed, setting no-default-features _ => { features.extend(Self::rendererless_features(main_package)); no_default_features = true; - Platform::from(platform_arg) + Some(renderer) } }, - None if !using_dioxus_explicitly => Platform::TARGET_PLATFORM.unwrap(), - None => match enabled_platforms.len() { - 0 => Platform::TARGET_PLATFORM.unwrap(), - 1 => enabled_platforms[0], + None if !using_dioxus_explicitly => None, + None => match enabled_renderers.as_slice() { + [] => Renderer::Webview.into(), // Default to webview if no platform is specified and no dioxus features are enabled + [renderer] => Some(*renderer), _ => { return Err(anyhow::anyhow!( "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--platform` or set a default platform in Cargo.toml" @@ -588,11 +580,41 @@ impl BuildRequest { }, }; + // We usually use the simulator unless --device is passed *or* a device is detected by probing. + // For now, though, since we don't have probing, it just defaults to false + // Tools like xcrun/adb can detect devices + let device = args.device; + + // Resolve the platform args into a concrete platform + let mut platform: Option = args.platform.map(|p| p.into()); + // If the user didn't pass a platform, but we have a renderer, get the default platform for that renderer + if let (None, Some(renderer)) = (platform, renderer) { + platform = Some(renderer.default_platform()); + }; + + // We want a real triple to build with, so we'll autodetect it if it's not provided + // The triple ends up being a source of truth for us later hence all this work to figure it out + let triple = match (args.target.clone(), platform) { + // If there is an explicit target, use it + (Some(target), _) => target, + // If there is a platform, use it to determine the target triple + (None, Some(platform)) => platform.into_target(device, &workspace).await?, + _ => Triple::host(), + }; + + // Resolve the bundle format based on the combination of the target triple and renderer + let bundle = match args.bundle { + // If there is an explicit bundle format, use it + Some(bundle) => bundle, + // Otherwise guess a bundle format based on the target triple and renderer + None => BundleFormat::from_target(&triple, renderer)?, + }; + // Add any features required to turn on the client - if using_dioxus_explicitly { + if let Some(renderer) = renderer { features.push(Self::feature_for_platform_and_renderer( main_package, - platform, + &triple, renderer, )); } @@ -602,7 +624,7 @@ impl BuildRequest { // We might want to move some of these profiles into dioxus.toml and make them "virtual". let profile = match args.profile.clone() { Some(profile) => profile, - None => platform.profile_name(args.release), + None => bundle.profile_name(args.release), }; // Determining release mode is based on the profile, actually, so we need to check that @@ -615,18 +637,6 @@ impl BuildRequest { .clone() .unwrap_or_else(|| main_package.name.clone()); - // We usually use the simulator unless --device is passed *or* a device is detected by probing. - // For now, though, since we don't have probing, it just defaults to false - // Tools like xcrun/adb can detect devices - let device = args.device; - - // We want a real triple to build with, so we'll autodetect it if it's not provided - // The triple ends up being a source of truth for us later hence all this work to figure it out - let triple = match args.target.clone() { - Some(target) => target, - None => platform.into_target(device, &workspace).await?, - }; - // Somethings we override are also present in the user's config. // If we can't get them by introspecting cargo, then we need to get them from the config // @@ -637,7 +647,7 @@ impl BuildRequest { let mut custom_linker = cargo_config.linker(triple.to_string()).ok().flatten(); let mut rustflags = cargo_config2::Flags::default(); - if matches!(platform, Platform::Android) { + if matches!(bundle, BundleFormat::Android) { rustflags.flags.extend([ "-Clink-arg=-landroid".to_string(), "-Clink-arg=-llog".to_string(), @@ -666,7 +676,7 @@ impl BuildRequest { } // If no custom linker is set, then android falls back to us as the linker - if custom_linker.is_none() && platform == Platform::Android { + if custom_linker.is_none() && bundle == BundleFormat::Android { custom_linker = Some(workspace.android_tools()?.android_cc(&triple)); } @@ -717,8 +727,8 @@ impl BuildRequest { ); Ok(Self { - renderer, features, + bundle, no_default_features, crate_package, crate_target, @@ -810,7 +820,7 @@ impl BuildRequest { ctx.status_starting_build(crate_count); let mut cmd = self.build_command(ctx)?; - tracing::debug!(dx_src = ?TraceSrc::Build, "Executing cargo for {} using {}", self.platform, self.triple); + tracing::debug!(dx_src = ?TraceSrc::Build, "Executing cargo for {} using {}", self.bundle, self.triple); let mut child = cmd .stdout(Stdio::piped()) @@ -950,7 +960,7 @@ impl BuildRequest { let assets = self.collect_assets(&exe, ctx)?; let time_end = SystemTime::now(); let mode = ctx.mode.clone(); - let platform = self.platform; + let bundle = self.bundle; tracing::debug!( "Build completed successfully in {}us: {:?}", @@ -960,7 +970,7 @@ impl BuildRequest { Ok(BuildArtifacts { time_end, - platform, + bundle, exe, direct_rustc, time_start, @@ -2909,7 +2919,7 @@ impl BuildRequest { /// Return the platforms that are enabled for the package /// /// Ideally only one platform is enabled but we need to be able to - pub(crate) fn enabled_cargo_toml_platforms( + pub(crate) fn enabled_cargo_toml_renderers( package: &krates::cm::Package, no_default_features: bool, ) -> Vec { diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index ee6777a1c4..462f3e59d2 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -1,4 +1,5 @@ use crate::cli::*; +use crate::BundleFormat; use crate::PlatformArg; use crate::Renderer; use target_lexicon::Triple; @@ -12,6 +13,10 @@ pub(crate) struct TargetArgs { #[clap(long, value_enum, help_heading = HELP_HEADING)] pub(crate) platform: Option, + /// Build bundle: supports web, macos, windows, linux, ios, android, and server [default: "web"] + #[clap(long, value_enum, help_heading = HELP_HEADING)] + pub(crate) bundle: Option, + /// Build renderer: support Webview and Native [default: "webview"] #[clap(long, value_enum, help_heading = HELP_HEADING)] pub(crate) renderer: Option, diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index fca1310a13..3eb34047cd 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -1,8 +1,8 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::str::FromStr; -use target_lexicon::{Environment, OperatingSystem, Triple}; +use target_lexicon::{Architecture, Environment, OperatingSystem, Triple}; use crate::Workspace; @@ -118,6 +118,15 @@ impl Renderer { _ => None, } } + + pub(crate) fn default_platform(&self) -> Platform { + match self { + Renderer::Webview | Renderer::Native | Renderer::Server | Renderer::Liveview => { + Platform::TARGET_PLATFORM.unwrap() + } + Renderer::Web => Platform::Wasm, + } + } } #[derive(Debug)] @@ -271,6 +280,15 @@ impl Display for BundleFormat { } impl BundleFormat { + #[cfg(target_os = "macos")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::MacOS); + #[cfg(target_os = "windows")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::Windows); + #[cfg(target_os = "linux")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::Linux); + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + pub(crate) const TARGET_PLATFORM: Option = None; + /// Get the name of the folder we need to generate for this platform /// /// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own @@ -311,6 +329,35 @@ impl BundleFormat { Self::Server => "Server", } } + + pub(crate) fn from_target(target: &Triple, renderer: Option) -> Result { + match ( + renderer, + target.architecture, + target.environment, + target.operating_system, + ) { + // The server always uses the server bundle format + (Some(Renderer::Server), _, _, _) => Ok(BundleFormat::Server), + // The web renderer always uses the web bundle format + (Some(Renderer::Web), _, _, _) => Ok(BundleFormat::Web), + // Otherwise, guess it based on the target + // Assume any wasm32 or wasm64 target is a web target + (_, Architecture::Wasm32 | Architecture::Wasm64, _, _) => Ok(BundleFormat::Web), + // For native targets, we need to determine the bundle format based on the OS + (_, _, Environment::Android, _) => Ok(BundleFormat::Android), + (_, _, _, OperatingSystem::IOS(_)) => Ok(BundleFormat::Ios), + (_, _, _, OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_)) => { + Ok(BundleFormat::MacOS) + } + (_, _, _, OperatingSystem::Linux) => Ok(BundleFormat::Linux), + (_, _, _, OperatingSystem::Windows) => Ok(BundleFormat::Windows), + // If we don't recognize the target, default to desktop + _ => BundleFormat::TARGET_PLATFORM.context( + "failed to determine bundle format. Try setting the `--bundle` flag manually", + ), + } + } } /// An error that occurs when a platform is not recognized From 4594dff8ad20551ecbd63a742845c3720a7708ed Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 14:29:32 -0500 Subject: [PATCH 08/25] add shorthand flags --- packages/cli/src/build/request.rs | 13 +- packages/cli/src/cli/build.rs | 2 +- packages/cli/src/cli/target.rs | 10 +- packages/cli/src/platform.rs | 340 +++++++++++++++--------------- 4 files changed, 182 insertions(+), 183 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index d6d7a2c0dd..cfe8cd75ac 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -317,7 +317,7 @@ use crate::{ AndroidTools, BuildContext, BundleFormat, DioxusConfig, Error, LinkAction, LinkerFlavor, - Platform, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, + PlatformArg, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::Context; @@ -555,7 +555,7 @@ impl BuildRequest { let mut features = args.features.clone(); let mut no_default_features = args.no_default_features; - let renderer: Option = match args.renderer { + let renderer: Option = match args.renderer.into() { Some(renderer) => match enabled_renderers.len() { 0 => Some(renderer), @@ -586,10 +586,10 @@ impl BuildRequest { let device = args.device; // Resolve the platform args into a concrete platform - let mut platform: Option = args.platform.map(|p| p.into()); + let mut platform = args.platform; // If the user didn't pass a platform, but we have a renderer, get the default platform for that renderer - if let (None, Some(renderer)) = (platform, renderer) { - platform = Some(renderer.default_platform()); + if let (PlatformArg::Unknown, Some(renderer)) = (platform, renderer) { + platform = renderer.default_platform(); }; // We want a real triple to build with, so we'll autodetect it if it's not provided @@ -598,8 +598,7 @@ impl BuildRequest { // If there is an explicit target, use it (Some(target), _) => target, // If there is a platform, use it to determine the target triple - (None, Some(platform)) => platform.into_target(device, &workspace).await?, - _ => Triple::host(), + (None, platform) => platform.into_target(device, &workspace).await?, }; // Resolve the bundle format based on the combination of the target triple and renderer diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 5490f3c50f..f8cdf2834e 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -120,7 +120,7 @@ impl CommandWithPlatformOverrides { let main_target = client.main_target.clone(); let mut server_args = server_args.clone(); // The renderer in the server build is always set to Server - server_args.renderer = Some(crate::Renderer::Server); + server_args.renderer.renderer = Some(crate::Renderer::Server); server = Some(BuildRequest::new(&server_args, Some(main_target), workspace.clone()).await?); } diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index 462f3e59d2..30211e81a5 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -1,7 +1,7 @@ use crate::cli::*; use crate::BundleFormat; use crate::PlatformArg; -use crate::Renderer; +use crate::RendererArg; use target_lexicon::Triple; const HELP_HEADING: &str = "Target Options"; @@ -10,16 +10,16 @@ const HELP_HEADING: &str = "Target Options"; #[derive(Clone, Debug, Default, Deserialize, Parser)] pub(crate) struct TargetArgs { /// Build platform: support Web & Desktop [default: "default_platform"] - #[clap(long, value_enum, help_heading = HELP_HEADING)] - pub(crate) platform: Option, + #[clap(flatten)] + pub(crate) platform: PlatformArg, /// Build bundle: supports web, macos, windows, linux, ios, android, and server [default: "web"] #[clap(long, value_enum, help_heading = HELP_HEADING)] pub(crate) bundle: Option, /// Build renderer: support Webview and Native [default: "webview"] - #[clap(long, value_enum, help_heading = HELP_HEADING)] - pub(crate) renderer: Option, + #[clap(flatten)] + pub(crate) renderer: RendererArg, /// Build in release mode [default: false] #[clap(long, short, help_heading = HELP_HEADING)] diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 3eb34047cd..afb57d38c7 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use clap::{arg, ArgMatches, Args, FromArgMatches}; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::str::FromStr; @@ -7,89 +8,219 @@ use target_lexicon::{Architecture, Environment, OperatingSystem, Triple}; use crate::Workspace; #[derive( - Copy, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - Debug, - Default, - clap::ValueEnum, + Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, )] #[non_exhaustive] pub(crate) enum PlatformArg { /// Targeting the WASM architecture - #[clap(name = "wasm")] - #[default] Wasm, /// Targeting macos desktop - #[clap(name = "macos")] MacOS, /// Targeting windows desktop - #[clap(name = "windows")] Windows, /// Targeting linux desktop - #[clap(name = "linux")] Linux, /// Targeting the ios platform /// /// Can't work properly if you're not building from an Apple device. - #[clap(name = "ios")] Ios, /// Targeting the android platform - #[clap(name = "android")] Android, /// Targeting the current platform with the "desktop" renderer - #[clap(name = "desktop")] Desktop, + + /// An unknown target platform + #[default] + Unknown, +} + +impl Args for PlatformArg { + fn augment_args(cmd: clap::Command) -> clap::Command { + cmd.arg(arg!(--wasm "The wasm target platform")) + .arg(arg!(--macos "The macos target platform")) + .arg(arg!(--windows "The windows target platform")) + .arg(arg!(--linux "The linux target platform")) + .arg(arg!(--ios "The ios target platform")) + .arg(arg!(--android "The android target platform")) + .arg(arg!(--desktop "The desktop target platform")) + .group( + clap::ArgGroup::new("platform") + .args([ + "wasm", "macos", "windows", "linux", "ios", "android", "desktop", + ]) + .multiple(false) + .required(false), + ) + } + + fn augment_args_for_update(cmd: clap::Command) -> clap::Command { + Self::augment_args(cmd) + } +} + +impl FromArgMatches for PlatformArg { + fn from_arg_matches(matches: &ArgMatches) -> Result { + if let Some(platform) = matches.get_one::("platform") { + match platform.as_str() { + "wasm" => Ok(Self::Wasm), + "macos" => Ok(Self::MacOS), + "windows" => Ok(Self::Windows), + "linux" => Ok(Self::Linux), + "ios" => Ok(Self::Ios), + "android" => Ok(Self::Android), + "desktop" => Ok(Self::Desktop), + _ => Err(clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + format!("Unknown platform: {}", platform), + )), + } + } else { + Ok(Self::Unknown) + } + } + fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), clap::Error> { + *self = Self::from_arg_matches(matches)?; + Ok(()) + } +} + +impl PlatformArg { + #[cfg(target_os = "macos")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::MacOS); + #[cfg(target_os = "windows")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::Windows); + #[cfg(target_os = "linux")] + pub(crate) const TARGET_PLATFORM: Option = Some(Self::Linux); + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + pub(crate) const TARGET_PLATFORM: Option = None; + + pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result { + match self { + // Generally just use the host's triple for native executables unless specified otherwise + Self::MacOS | Self::Windows | Self::Linux | Self::Desktop | Self::Unknown => { + Ok(Triple::host()) + } + + // We currently assume unknown-unknown for web, but we might want to eventually + // support emscripten + Self::Wasm => Ok("wasm32-unknown-unknown".parse()?), + + // For iOS we should prefer the actual architecture for the simulator, but in lieu of actually + // figuring that out, we'll assume aarch64 on m-series and x86_64 otherwise + Self::Ios => { + // use the host's architecture and sim if --device is passed + use target_lexicon::{Architecture, HOST}; + let triple_str = match HOST.architecture { + Architecture::Aarch64(_) if device => "aarch64-apple-ios", + Architecture::Aarch64(_) => "aarch64-apple-ios-sim", + _ if device => "x86_64-apple-ios", + _ => "x86_64-apple-ios", + }; + Ok(triple_str.parse()?) + } + + // Same idea with android but we figure out the connected device using adb + Self::Android => Ok(workspace + .android_tools()? + .autodetect_android_device_triple() + .await), + } + } } #[derive( - Copy, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - Debug, - clap::ValueEnum, + Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, )] +pub(crate) struct RendererArg { + pub(crate) renderer: Option, +} + +impl Args for RendererArg { + fn augment_args(cmd: clap::Command) -> clap::Command { + cmd.arg(arg!(--web "Targeting the web renderer")) + .arg(arg!(--webview "Targeting the webview renderer")) + .arg(arg!(--native "Targeting the native renderer")) + .arg(arg!(--server "Targeting the server renderer")) + .arg(arg!(--liveview "Targeting the liveview renderer")) + .group( + clap::ArgGroup::new("renderer") + .args(["web", "webview", "native", "server", "liveview"]) + .multiple(false) + .required(false), + ) + } + + fn augment_args_for_update(cmd: clap::Command) -> clap::Command { + Self::augment_args(cmd) + } +} + +impl FromArgMatches for RendererArg { + fn from_arg_matches(matches: &ArgMatches) -> Result { + if let Some(renderer) = matches.get_one::("renderer") { + match renderer.as_str() { + "web" => Ok(Self { + renderer: Some(Renderer::Web), + }), + "webview" => Ok(Self { + renderer: Some(Renderer::Webview), + }), + "native" => Ok(Self { + renderer: Some(Renderer::Native), + }), + "server" => Ok(Self { + renderer: Some(Renderer::Server), + }), + "liveview" => Ok(Self { + renderer: Some(Renderer::Liveview), + }), + _ => Err(clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + format!("Unknown platform: {}", renderer), + )), + } + } else { + Ok(Self { renderer: None }) + } + } + + fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), clap::Error> { + *self = Self::from_arg_matches(matches)?; + Ok(()) + } +} + +impl Into> for RendererArg { + fn into(self) -> Option { + self.renderer + } +} + +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)] #[non_exhaustive] pub(crate) enum Renderer { /// Targeting webview renderer - #[serde(rename = "webview")] Webview, /// Targeting native renderer - #[serde(rename = "native")] Native, /// Targeting the server platform using Axum and Dioxus-Fullstack /// /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply /// means you're only building the server variant without the `.wasm` to serve. - #[serde(rename = "server")] Server, /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[serde(rename = "liveview")] Liveview, /// Targeting the web renderer - #[serde(rename = "web")] Web, } @@ -119,12 +250,12 @@ impl Renderer { } } - pub(crate) fn default_platform(&self) -> Platform { + pub(crate) fn default_platform(&self) -> PlatformArg { match self { Renderer::Webview | Renderer::Native | Renderer::Server | Renderer::Liveview => { - Platform::TARGET_PLATFORM.unwrap() + PlatformArg::TARGET_PLATFORM.unwrap() } - Renderer::Web => Platform::Wasm, + Renderer::Web => PlatformArg::Wasm, } } } @@ -166,40 +297,6 @@ impl Display for Renderer { }) } } - -#[derive( - Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, -)] -#[non_exhaustive] -pub(crate) enum Platform { - /// Targeting the WASM architecture - #[serde(rename = "wasm")] - #[default] - Wasm, - - /// Targeting macos desktop - #[serde(rename = "macos")] - MacOS, - - /// Targeting windows desktop - #[serde(rename = "windows")] - Windows, - - /// Targeting linux desktop - #[serde(rename = "linux")] - Linux, - - /// Targeting the ios platform - /// - /// Can't work properly if you're not building from an Apple device. - #[serde(rename = "ios")] - Ios, - - /// Targeting the android platform - #[serde(rename = "android")] - Android, -} - #[derive( Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, )] @@ -359,100 +456,3 @@ impl BundleFormat { } } } - -/// An error that occurs when a platform is not recognized -pub(crate) struct UnknownPlatformError; - -impl std::fmt::Display for UnknownPlatformError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Unknown platform") - } -} - -impl FromStr for Platform { - type Err = UnknownPlatformError; - - fn from_str(s: &str) -> Result { - match s { - "wasm" => Ok(Self::Wasm), - "macos" => Ok(Self::MacOS), - "windows" => Ok(Self::Windows), - "linux" => Ok(Self::Linux), - "ios" => Ok(Self::Ios), - "android" => Ok(Self::Android), - _ => Err(UnknownPlatformError), - } - } -} - -impl Display for Platform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Platform::Wasm => "wasm", - Platform::MacOS => "macos", - Platform::Windows => "windows", - Platform::Linux => "linux", - Platform::Ios => "ios", - Platform::Android => "android", - }) - } -} - -impl From for Platform { - fn from(value: PlatformArg) -> Self { - match value { - // Most values map 1:1 - PlatformArg::Wasm => Platform::Wasm, - PlatformArg::MacOS => Platform::MacOS, - PlatformArg::Windows => Platform::Windows, - PlatformArg::Linux => Platform::Linux, - PlatformArg::Ios => Platform::Ios, - PlatformArg::Android => Platform::Android, - - // The alias arguments - PlatformArg::Desktop => Platform::TARGET_PLATFORM.unwrap(), - } - } -} - -impl Platform { - #[cfg(target_os = "macos")] - pub(crate) const TARGET_PLATFORM: Option = Some(Platform::MacOS); - #[cfg(target_os = "windows")] - pub(crate) const TARGET_PLATFORM: Option = Some(Platform::Windows); - #[cfg(target_os = "linux")] - pub(crate) const TARGET_PLATFORM: Option = Some(Platform::Linux); - #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] - pub(crate) const TARGET_PLATFORM: Option = None; - - pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result { - match self { - // Generally just use the host's triple for native executables unless specified otherwise - Platform::MacOS | Platform::Windows | Platform::Linux => Ok(Triple::host()), - - // We currently assume unknown-unknown for web, but we might want to eventually - // support emscripten - Platform::Wasm => Ok("wasm32-unknown-unknown".parse()?), - - // For iOS we should prefer the actual architecture for the simulator, but in lieu of actually - // figuring that out, we'll assume aarch64 on m-series and x86_64 otherwise - Platform::Ios => { - // use the host's architecture and sim if --device is passed - use target_lexicon::{Architecture, HOST}; - let triple_str = match HOST.architecture { - Architecture::Aarch64(_) if device => "aarch64-apple-ios", - Architecture::Aarch64(_) => "aarch64-apple-ios-sim", - _ if device => "x86_64-apple-ios", - _ => "x86_64-apple-ios", - }; - Ok(triple_str.parse()?) - } - - // Same idea with android but we figure out the connected device using adb - Platform::Android => Ok(workspace - .android_tools()? - .autodetect_android_device_triple() - .await), - } - } -} From 11b1539f1023bc1f075c4bbe267601bf30d21d73 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 14:32:05 -0500 Subject: [PATCH 09/25] add headings to the help for shorthands --- packages/cli/src/platform.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index afb57d38c7..afee36e79e 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -42,13 +42,14 @@ pub(crate) enum PlatformArg { impl Args for PlatformArg { fn augment_args(cmd: clap::Command) -> clap::Command { - cmd.arg(arg!(--wasm "The wasm target platform")) - .arg(arg!(--macos "The macos target platform")) - .arg(arg!(--windows "The windows target platform")) - .arg(arg!(--linux "The linux target platform")) - .arg(arg!(--ios "The ios target platform")) - .arg(arg!(--android "The android target platform")) - .arg(arg!(--desktop "The desktop target platform")) + const HELP_HEADING: &str = "Platform"; + cmd.arg(arg!(--wasm "The wasm target platform").help_heading(HELP_HEADING)) + .arg(arg!(--macos "The macos target platform").help_heading(HELP_HEADING)) + .arg(arg!(--windows "The windows target platform").help_heading(HELP_HEADING)) + .arg(arg!(--linux "The linux target platform").help_heading(HELP_HEADING)) + .arg(arg!(--ios "The ios target platform").help_heading(HELP_HEADING)) + .arg(arg!(--android "The android target platform").help_heading(HELP_HEADING)) + .arg(arg!(--desktop "The desktop target platform").help_heading(HELP_HEADING)) .group( clap::ArgGroup::new("platform") .args([ @@ -143,11 +144,12 @@ pub(crate) struct RendererArg { impl Args for RendererArg { fn augment_args(cmd: clap::Command) -> clap::Command { - cmd.arg(arg!(--web "Targeting the web renderer")) - .arg(arg!(--webview "Targeting the webview renderer")) - .arg(arg!(--native "Targeting the native renderer")) - .arg(arg!(--server "Targeting the server renderer")) - .arg(arg!(--liveview "Targeting the liveview renderer")) + const HELP_HEADING: &str = "Renderer"; + cmd.arg(arg!(--web "Targeting the web renderer").help_heading(HELP_HEADING)) + .arg(arg!(--webview "Targeting the webview renderer").help_heading(HELP_HEADING)) + .arg(arg!(--native "Targeting the native renderer").help_heading(HELP_HEADING)) + .arg(arg!(--server "Targeting the server renderer").help_heading(HELP_HEADING)) + .arg(arg!(--liveview "Targeting the liveview renderer").help_heading(HELP_HEADING)) .group( clap::ArgGroup::new("renderer") .args(["web", "webview", "native", "server", "liveview"]) From f77f3716dfa9ab435f988babded9449214789be0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 14:51:56 -0500 Subject: [PATCH 10/25] resolve the renderer from the target triple if it isn't passed --- packages/cli/src/build/request.rs | 17 +++++++++++++++-- packages/cli/src/platform.rs | 9 +++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 48cc390934..351637baa9 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -555,7 +555,7 @@ impl BuildRequest { let mut features = args.features.clone(); let mut no_default_features = args.no_default_features; - let renderer: Option = match args.renderer.into() { + let mut renderer: Option = match args.renderer.into() { Some(renderer) => match enabled_renderers.len() { 0 => Some(renderer), @@ -569,7 +569,7 @@ impl BuildRequest { }, None if !using_dioxus_explicitly => None, None => match enabled_renderers.as_slice() { - [] => Renderer::Webview.into(), // Default to webview if no platform is specified and no dioxus features are enabled + [] => None, // Wait until we resolve everything else first then we will resolve it from the triple [renderer] => Some(*renderer), _ => { return Err(anyhow::anyhow!( @@ -601,6 +601,12 @@ impl BuildRequest { (None, platform) => platform.into_target(device, &workspace).await?, }; + // If the user didn't pass a renderer, and we didn't find a good default renderer, but they are using dioxus explicitly, + // then we can try to guess the renderer based on the target triple. + if renderer.is_none() && using_dioxus_explicitly { + renderer = Some(Renderer::from_target(&triple)); + } + // Resolve the bundle format based on the combination of the target triple and renderer let bundle = match args.bundle { // If there is an explicit bundle format, use it @@ -708,6 +714,13 @@ impl BuildRequest { let extra_cargo_args = shell_words::split(&args.cargo_args.clone().unwrap_or_default()) .context("Failed to parse cargo args")?; + tracing::debug!( + r#"Target Info: + • features: {features:?} + • triple: {triple:?} + • bundle format: {bundle:?} + "# + ); tracing::debug!( r#"Log Files: • link_args_file: {}, diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index afee36e79e..be3346d48e 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -260,6 +260,15 @@ impl Renderer { Renderer::Web => PlatformArg::Wasm, } } + + pub(crate) fn from_target(triple: &Triple) -> Self { + match triple.architecture { + // Assume any wasm32 or wasm64 target is a web target + Architecture::Wasm32 | Architecture::Wasm64 => Self::Web, + // Otherwise, assume webview for native targets + _ => Self::Webview, + } + } } #[derive(Debug)] From cef67e7ab2f24f6b9e18eda25d87a260be65d017 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 15:26:36 -0500 Subject: [PATCH 11/25] make value after --fullstack optional --- packages/cli/src/build/request.rs | 34 ++++++++++++++++++++++++------- packages/cli/src/cli/build.rs | 6 +++++- packages/cli/src/cli/serve.rs | 6 +++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 351637baa9..4049a4ad76 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -573,7 +573,7 @@ impl BuildRequest { [renderer] => Some(*renderer), _ => { return Err(anyhow::anyhow!( - "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--platform` or set a default platform in Cargo.toml" + "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--web`, `--webview`, or `--native` or set a default platform in Cargo.toml" ) .into()) } @@ -717,7 +717,7 @@ impl BuildRequest { tracing::debug!( r#"Target Info: • features: {features:?} - • triple: {triple:?} + • triple: {triple} • bundle format: {bundle:?} "# ); @@ -2898,28 +2898,42 @@ impl BuildRequest { let res = package.features.iter().find_map(|(key, features)| { // if the feature is just the name of the platform, we use that if key == dioxus_feature { + tracing::debug!("Found feature {key} for renderer {renderer}"); return Some(key.clone()); } - // Otherwise look for the feature that starts with dioxus/ or dioxus?/ and matches the platform + // Otherwise look for the feature that starts with dioxus/ or dioxus?/ and matches just the single platform + // we are looking for. + let mut dioxus_renderers_enabled = Vec::new(); for feature in features { if let Some((_, after_dioxus)) = feature.split_once("dioxus") { if let Some(dioxus_feature_enabled) = after_dioxus.trim_start_matches('?').strip_prefix('/') { - // If that enables the feature we are looking for, return that feature - if dioxus_feature_enabled == dioxus_feature { - return Some(key.clone()); + if let Some(renderer) = + Renderer::autodetect_from_cargo_feature(dioxus_feature_enabled) + { + dioxus_renderers_enabled.push(renderer); } } } } + // If there is exactly one renderer enabled by this feature, we can use it + if let [single_renderer] = dioxus_renderers_enabled.as_slice() { + if *single_renderer == renderer { + tracing::debug!( + "Found feature {key} for renderer {renderer} which enables dioxus/{renderer}" + ); + return Some(key.clone()); + } + } + None }); res.unwrap_or_else(|| { - let fallback = format!("dioxus/{}", dioxus_feature) ; + let fallback = format!("dioxus/{}", dioxus_feature); tracing::debug!( "Could not find explicit feature for renderer {renderer}, passing `fallback` instead" ); @@ -3024,6 +3038,9 @@ impl BuildRequest { if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); if Renderer::autodetect_from_cargo_feature(dx_feature).is_some() { + tracing::debug!( + "Dropping feature {feature} since it points to a platform renderer" + ); continue 'top; } } @@ -3034,6 +3051,9 @@ impl BuildRequest { if feature.starts_with("dioxus/") { let dx_feature = feature.trim_start_matches("dioxus/"); if Renderer::autodetect_from_cargo_feature(dx_feature).is_some() { + tracing::debug!( + "Dropping feature {feature} since it points to a platform renderer transitively" + ); continue 'top; } } diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index f8cdf2834e..bf3278121b 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -12,7 +12,11 @@ pub struct BuildArgs { /// Enable fullstack mode [default: false] /// /// This is automatically detected from `dx serve` if the "fullstack" feature is enabled by default. - #[clap(long)] + #[arg( + long, + default_missing_value = "true", + num_args = 0..=1, + )] pub(crate) fullstack: Option, /// Pre-render all routes returned from the app's `/static_routes` endpoint [default: false] diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index ed392a1417..8c635cd7eb 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -6,15 +6,15 @@ use std::{backtrace::Backtrace, panic::AssertUnwindSafe}; /// Serve the project /// -/// `dx serve` takes cargo args by default, except with a required `--platform` arg: +/// `dx serve` takes cargo args by default with additional renderer args like `--web`, `--webview`, and `--native`: /// /// ```sh -/// dx serve --example blah --target blah --platform android +/// dx serve --example blah --target blah --android /// ``` /// /// A simple serve: /// ```sh -/// dx serve --platform web +/// dx serve --web /// ``` /// /// As of dioxus 0.7, `dx serve` allows independent customization of the client and server builds, From a07357923fa1f7f3eeb643ff49fe239542f7b715 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 16:29:58 -0500 Subject: [PATCH 12/25] convert desktop -> mobile when targeting mobile webview --- packages/cli/src/build/request.rs | 47 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 4049a4ad76..cabf0df7bb 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -343,7 +343,7 @@ use std::{ }, time::{SystemTime, UNIX_EPOCH}, }; -use target_lexicon::{OperatingSystem, Triple}; +use target_lexicon::{Environment, OperatingSystem, Triple}; use tempfile::{NamedTempFile, TempDir}; use tokio::{io::AsyncBufReadExt, process::Command}; use uuid::Uuid; @@ -570,7 +570,27 @@ impl BuildRequest { None if !using_dioxus_explicitly => None, None => match enabled_renderers.as_slice() { [] => None, // Wait until we resolve everything else first then we will resolve it from the triple - [renderer] => Some(*renderer), + [(renderer, feature)] => { + let targeting_mobile = match (&args.target, args.platform) { + (_, PlatformArg::Android | PlatformArg::Ios) => true, + (Some(target), _) => { + matches!( + (target.environment, target.operating_system), + (Environment::Android, OperatingSystem::IOS(_)) + ) + } + _ => false, + }; + // The renderer usually maps directly to a feature flag, but the mobile crate is an exception. + // If we see the `desktop` renderer in the default features, but the user is targeting mobile, + // we should remove the default `desktop` feature, but still target webview. The feature selection + // logic below will handle adding back in the `mobile` feature flag + if targeting_mobile && feature == "desktop" { + features.extend(Self::rendererless_features(main_package)); + no_default_features = true; + } + Some(*renderer) + }, _ => { return Err(anyhow::anyhow!( "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--web`, `--webview`, or `--native` or set a default platform in Cargo.toml" @@ -580,6 +600,12 @@ impl BuildRequest { }, }; + // Just grab the renderers from the enabled renderers and discard the feature names + let enabled_renderers = enabled_renderers + .into_iter() + .map(|(renderer, _)| renderer) + .collect::>(); + // We usually use the simulator unless --device is passed *or* a device is detected by probing. // For now, though, since we don't have probing, it just defaults to false // Tools like xcrun/adb can detect devices @@ -2910,18 +2936,17 @@ impl BuildRequest { if let Some(dioxus_feature_enabled) = after_dioxus.trim_start_matches('?').strip_prefix('/') { - if let Some(renderer) = - Renderer::autodetect_from_cargo_feature(dioxus_feature_enabled) + if Renderer::autodetect_from_cargo_feature(dioxus_feature_enabled).is_some() { - dioxus_renderers_enabled.push(renderer); + dioxus_renderers_enabled.push(dioxus_feature_enabled.to_string()); } } } } // If there is exactly one renderer enabled by this feature, we can use it - if let [single_renderer] = dioxus_renderers_enabled.as_slice() { - if *single_renderer == renderer { + if let [feature_name] = dioxus_renderers_enabled.as_slice() { + if feature_name == dioxus_feature { tracing::debug!( "Found feature {key} for renderer {renderer} which enables dioxus/{renderer}" ); @@ -2956,7 +2981,7 @@ impl BuildRequest { pub(crate) fn enabled_cargo_toml_renderers( package: &krates::cm::Package, no_default_features: bool, - ) -> Vec { + ) -> Vec<(Renderer, String)> { let mut renderers = vec![]; // Attempt to discover the platform directly from the dioxus dependency @@ -2967,7 +2992,7 @@ impl BuildRequest { if let Some(dxs) = package.dependencies.iter().find(|dep| dep.name == "dioxus") { for f in dxs.features.iter() { if let Some(renderer) = Renderer::autodetect_from_cargo_feature(f) { - renderers.push(renderer); + renderers.push((renderer, f.clone())); } } } @@ -2998,7 +3023,7 @@ impl BuildRequest { let dx_feature = feature.trim_start_matches("dioxus/"); let auto = Renderer::autodetect_from_cargo_feature(dx_feature); if let Some(auto) = auto { - renderers.push(auto); + renderers.push((auto, dx_feature.to_string())); } } @@ -3010,7 +3035,7 @@ impl BuildRequest { let dx_feature = feature.trim_start_matches("dioxus/"); let auto = Renderer::autodetect_from_cargo_feature(dx_feature); if let Some(auto) = auto { - renderers.push(auto); + renderers.push((auto, dx_feature.to_string())); } } } From e01dc1dd1e6a8a3fc1c5019b8c93331e1333a14c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 16:42:16 -0500 Subject: [PATCH 13/25] use the new shorthand flags in the playwright tests --- packages/dioxus/README.md | 4 +++- .../barebones-template/README.md | 6 ++--- .../playwright-tests/playwright.config.js | 22 +++++++++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index 1592a999f8..1fe3c67116 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -11,6 +11,7 @@ --- + Dioxus is a framework for building cross-platform apps in Rust. With one codebase, you can build web, desktop, and mobile apps with fullstack server functions. Dioxus is designed to be easy to learn for developers familiar with web technologies like HTML, CSS, and JavaScript.
@@ -31,6 +32,7 @@ Dioxus is crossplatform app framework that empowers developer to build beautiful ## Quick start To get started with Dioxus, you'll want to grab the dioxus-cli tool: `dx`. We distribute `dx` with `cargo-binstall` - if you already have binstall skip this step. + ```shell # skip if you already have cargo-binstall cargo install cargo-binstall @@ -42,7 +44,7 @@ cargo binstall dioxus-cli dx new my-app && cd my-app # and then serve! -dx serve --platform desktop +dx serve --webview ``` ## Your first app diff --git a/packages/playwright-tests/barebones-template/README.md b/packages/playwright-tests/barebones-template/README.md index 1e629b6306..f6c278c96e 100644 --- a/packages/playwright-tests/barebones-template/README.md +++ b/packages/playwright-tests/barebones-template/README.md @@ -18,8 +18,8 @@ Run the following command in the root of your project to start developing with t dx serve ``` -To run for a different platform, use the `--platform platform` flag. E.g. +To run for a different platform, use the `--webview`, `--native`, or `--web` flags. E.g. + ```bash -dx serve --platform desktop +dx serve --webview ``` - diff --git a/packages/playwright-tests/playwright.config.js b/packages/playwright-tests/playwright.config.js index 1c60742612..0078fdca4f 100644 --- a/packages/playwright-tests/playwright.config.js +++ b/packages/playwright-tests/playwright.config.js @@ -87,7 +87,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "web"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 9990', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 9990', port: 9990, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -96,7 +96,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "web-routing"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 2020', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 2020', port: 2020, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -105,7 +105,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "web-hash-routing"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 2021', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 2021', port: 2021, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -114,7 +114,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "fullstack"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 3333', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 3333', port: 3333, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -123,7 +123,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "fullstack-mounted"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 7777', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 7777', port: 7777, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -132,7 +132,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "fullstack-routing"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 8888', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 8888', port: 8888, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -141,7 +141,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "suspense-carousel"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 4040', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 4040', port: 4040, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -150,7 +150,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "nested-suspense"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 5050', + 'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 5050', port: 5050, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -159,7 +159,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "nested-suspense"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --bin nested-suspense-ssg --force-sequential --platform web --ssg --addr "127.0.0.1" --port 6060', + 'cargo run --package dioxus-cli --release -- run --verbose --bin nested-suspense-ssg --force-sequential --web --ssg --addr "127.0.0.1" --port 6060', port: 6060, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -178,7 +178,7 @@ module.exports = defineConfig({ { cwd: path.join(process.cwd(), "wasm-split-harness"), command: - 'cargo run --package dioxus-cli --release -- run --verbose --bin wasm-split-harness --platform web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release', + 'cargo run --package dioxus-cli --release -- run --verbose --bin wasm-split-harness --web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release', port: 8001, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, @@ -204,7 +204,7 @@ module.exports = defineConfig({ }, { command: - 'rm -rf ./web-hot-patch-temp && cp -r ./web-hot-patch ./web-hot-patch-temp && cd web-hot-patch-temp && cargo run --manifest-path ../../cli/Cargo.toml --release -- serve --verbose --force-sequential --platform web --addr "127.0.0.1" --port 9980 --hot-patch --exit-on-error', + 'rm -rf ./web-hot-patch-temp && cp -r ./web-hot-patch ./web-hot-patch-temp && cd web-hot-patch-temp && cargo run --manifest-path ../../cli/Cargo.toml --release -- serve --verbose --force-sequential --web --addr "127.0.0.1" --port 9980 --hot-patch --exit-on-error', port: 9980, timeout: 50 * 60 * 1000, reuseExistingServer: !process.env.CI, From e2a66770641c11c36a1e3f3713eff95893cd86a2 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 8 Jul 2025 16:57:36 -0500 Subject: [PATCH 14/25] fix clippy --- packages/cli/src/build/builder.rs | 15 +++++++-------- packages/cli/src/build/patch.rs | 4 ++-- packages/cli/src/build/request.rs | 8 ++++---- packages/cli/src/cli/autoformat.rs | 23 +++++++++++------------ packages/cli/src/cli/check.rs | 2 +- packages/cli/src/cli/run.rs | 2 +- packages/cli/src/cli/translate.rs | 2 +- packages/cli/src/config/bundle.rs | 2 +- packages/cli/src/logging.rs | 6 +++--- packages/cli/src/platform.rs | 12 ++++++------ packages/cli/src/serve/output.rs | 2 +- packages/cli/src/serve/proxy.rs | 9 ++++----- packages/cli/src/serve/server.rs | 5 ++--- packages/cli/src/tailwind.rs | 2 +- 14 files changed, 45 insertions(+), 49 deletions(-) diff --git a/packages/cli/src/build/builder.rs b/packages/cli/src/build/builder.rs index 9f71d99517..81e676c26d 100644 --- a/packages/cli/src/build/builder.rs +++ b/packages/cli/src/build/builder.rs @@ -179,7 +179,7 @@ impl AppBuilder { match bundle { Ok(Ok(bundle)) => BuilderUpdate::BuildReady { bundle }, Ok(Err(err)) => BuilderUpdate::BuildFailed { err }, - Err(err) => BuilderUpdate::BuildFailed { err: crate::Error::Runtime(format!("Build panicked! {:#?}", err)) }, + Err(err) => BuilderUpdate::BuildFailed { err: crate::Error::Runtime(format!("Build panicked! {err:#?}")) }, } }, Some(Ok(Some(msg))) = OptionFuture::from(self.stdout.as_mut().map(|f| f.next_line())) => { @@ -924,7 +924,7 @@ impl AppBuilder { .await?; if !output.status.success() { - return Err(format!("Failed to install app: {:?}", output).into()); + return Err(format!("Failed to install app: {output:?}").into()); } let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?) @@ -959,7 +959,7 @@ impl AppBuilder { .await?; if !output.status.success() { - return Err(format!("Failed to launch app: {:?}", output).into()); + return Err(format!("Failed to launch app: {output:?}").into()); } let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?) @@ -984,7 +984,7 @@ impl AppBuilder { .await?; if !output.status.success() { - return Err(format!("Failed to resume app: {:?}", output).into()); + return Err(format!("Failed to resume app: {output:?}").into()); } Ok(()) @@ -1196,8 +1196,8 @@ We checked the folder: {} let port = devserver_socket.port(); if let Err(e) = Command::new(&adb) .arg("reverse") - .arg(format!("tcp:{}", port)) - .arg(format!("tcp:{}", port)) + .arg(format!("tcp:{port}")) + .arg(format!("tcp:{port}")) .output() .await { @@ -1391,8 +1391,7 @@ We checked the folder: {} }; format!( - "vscode://vadimcn.vscode-lldb/launch/config?{{'request':'attach','pid':{}}}", - pid + "vscode://vadimcn.vscode-lldb/launch/config?{{'request':'attach','pid':{pid}}}" ) } diff --git a/packages/cli/src/build/patch.rs b/packages/cli/src/build/patch.rs index 6fdc7cc320..ed38220e0a 100644 --- a/packages/cli/src/build/patch.rs +++ b/packages/cli/src/build/patch.rs @@ -775,7 +775,7 @@ pub fn create_undefined_symbol_stub( let mut defined_symbols = HashSet::new(); for path in sorted { - let bytes = std::fs::read(path).with_context(|| format!("failed to read {:?}", path))?; + let bytes = std::fs::read(path).with_context(|| format!("failed to read {path:?}"))?; let file = File::parse(bytes.deref() as &[u8])?; for symbol in file.symbols() { if symbol.is_undefined() { @@ -853,7 +853,7 @@ pub fn create_undefined_symbol_stub( if aslr_reference < aslr_ref_address { return Err(PatchError::InvalidModule( format!( - "ASLR reference is less than the main module's address - is there a `main`?. {:x} < {:x}", aslr_reference, aslr_ref_address ) + "ASLR reference is less than the main module's address - is there a `main`?. {aslr_reference:x} < {aslr_ref_address:x}" ) )); } diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index cabf0df7bb..a036f896b0 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -531,10 +531,10 @@ impl BuildRequest { filtered_packages.join(", ")}; if let Some(example) = &args.example { let examples = target_of_kind(&TargetKind::Example); - format!("Failed to find example {example}. \nAvailable examples are:\n{}", examples) + format!("Failed to find example {example}. \nAvailable examples are:\n{examples}") } else if let Some(bin) = &args.bin { let binaries = target_of_kind(&TargetKind::Bin); - format!("Failed to find binary {bin}. \nAvailable binaries are:\n{}", binaries) + format!("Failed to find binary {bin}. \nAvailable binaries are:\n{binaries}") } else { format!("Failed to find target {target_name}. \nIt looks like you are trying to build dioxus in a library crate. \ You either need to run dx from inside a binary crate or build a specific example with the `--example` flag. \ @@ -2958,7 +2958,7 @@ impl BuildRequest { }); res.unwrap_or_else(|| { - let fallback = format!("dioxus/{}", dioxus_feature); + let fallback = format!("dioxus/{dioxus_feature}"); tracing::debug!( "Could not find explicit feature for renderer {renderer}, passing `fallback` instead" ); @@ -3391,7 +3391,7 @@ impl BuildRequest { .as_ref() .context("generated bindgen module has no name?")?; - let path = bindgen_outdir.join(format!("module_{}_{}.wasm", idx, comp_name)); + let path = bindgen_outdir.join(format!("module_{idx}_{comp_name}.wasm")); wasm_opt::write_wasm(&module.bytes, &path, &wasm_opt_options).await?; let hash_id = module diff --git a/packages/cli/src/cli/autoformat.rs b/packages/cli/src/cli/autoformat.rs index 41d238af93..999df0ceaf 100644 --- a/packages/cli/src/cli/autoformat.rs +++ b/packages/cli/src/cli/autoformat.rs @@ -55,7 +55,7 @@ impl Autoformat { // Format raw text. let indent = indentation_for(".", self.split_line_attributes)?; if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0, indent) { - println!("{}", inner); + println!("{inner}"); } else { return Err("error formatting codeblock".into()); } @@ -79,7 +79,7 @@ impl Autoformat { if let Err(e) = autoformat_project(check, split_line_attributes, format_rust_code, crate_dir) { - return Err(format!("error formatting project: {}", e).into()); + return Err(format!("error formatting project: {e}").into()); } } @@ -111,17 +111,17 @@ fn refactor_file( let Ok(Ok(edits)) = syn::parse_file(&s).map(|file| dioxus_autofmt::try_fmt_file(&s, &file, indent)) else { - return Err(format!("failed to format file: {}", s).into()); + return Err(format!("failed to format file: {s}").into()); }; let out = dioxus_autofmt::apply_formats(&s, edits); if file == "-" { - print!("{}", out); + print!("{out}"); } else if let Err(e) = fs::write(&file, out) { tracing::error!("failed to write formatted content to file: {e}",); } else { - println!("formatted {}", file); + println!("formatted {file}"); } Ok(()) @@ -135,8 +135,8 @@ fn format_file( let mut contents = fs::read_to_string(&path)?; let mut if_write = false; if format_rust_code { - let formatted = format_rust(&contents) - .map_err(|err| Error::Parse(format!("Syntax Error:\n{}", err)))?; + let formatted = + format_rust(&contents).map_err(|err| Error::Parse(format!("Syntax Error:\n{err}")))?; if contents != formatted { if_write = true; contents = formatted; @@ -144,9 +144,9 @@ fn format_file( } let parsed = syn::parse_file(&contents) - .map_err(|err| Error::Parse(format!("Failed to parse file: {}", err)))?; + .map_err(|err| Error::Parse(format!("Failed to parse file: {err}")))?; let edits = dioxus_autofmt::try_fmt_file(&contents, &parsed, indent) - .map_err(|err| Error::Parse(format!("Failed to format file: {}", err)))?; + .map_err(|err| Error::Parse(format!("Failed to format file: {err}")))?; let len = edits.len(); if !edits.is_empty() { @@ -202,7 +202,7 @@ fn autoformat_project( let files_formatted: usize = counts.into_iter().flatten().sum(); if files_formatted > 0 && check { - return Err(format!("{} files needed formatting", files_formatted).into()); + return Err(format!("{files_formatted} files needed formatting").into()); } Ok(()) @@ -268,8 +268,7 @@ fn format_syn_error(err: syn::Error) -> Error { let line = start.line; let column = start.column; Error::Parse(format!( - "Syntax Error in line {} column {}:\n{}", - line, column, err + "Syntax Error in line {line} column {column}:\n{err}" )) } diff --git a/packages/cli/src/cli/check.rs b/packages/cli/src/cli/check.rs index 4a04fc97a7..04c8c369f3 100644 --- a/packages/cli/src/cli/check.rs +++ b/packages/cli/src/cli/check.rs @@ -119,7 +119,7 @@ async fn check_files_and_report(files_to_check: Vec) -> Result<()> { Ok(()) } 1 => Err("1 issue found.".into()), - _ => Err(format!("{} issues found.", total_issues).into()), + _ => Err(format!("{total_issues} issues found.").into()), } } diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index 3fd9424392..b4ba2bae82 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -124,7 +124,7 @@ impl RunArgs { _ => {} }, BuilderUpdate::CompilerMessage { message } => { - print!("{}", message); + print!("{message}"); } BuilderUpdate::BuildFailed { err } => { tracing::error!("Build failed: {}", err); diff --git a/packages/cli/src/cli/translate.rs b/packages/cli/src/cli/translate.rs index c752396a6c..f3a144ff34 100644 --- a/packages/cli/src/cli/translate.rs +++ b/packages/cli/src/cli/translate.rs @@ -40,7 +40,7 @@ impl Translate { // instead we should be printing as json (or maybe even a different format) if we're not interactive match self.output { Some(output) => std::fs::write(output, &html)?, - None => print!("{}", html), + None => print!("{html}"), } Ok(StructuredOutput::HtmlTranslate { html }) diff --git a/packages/cli/src/config/bundle.rs b/packages/cli/src/config/bundle.rs index 15ba2a4709..4bd8e760b1 100644 --- a/packages/cli/src/config/bundle.rs +++ b/packages/cli/src/config/bundle.rs @@ -259,7 +259,7 @@ impl FromStr for PackageType { "appimage" => Ok(PackageType::AppImage), "dmg" => Ok(PackageType::Dmg), "updater" => Ok(PackageType::Updater), - _ => Err(format!("{} is not a valid package type", s)), + _ => Err(format!("{s} is not a valid package type")), } } } diff --git a/packages/cli/src/logging.rs b/packages/cli/src/logging.rs index 01c9bb2264..37c204bddb 100644 --- a/packages/cli/src/logging.rs +++ b/packages/cli/src/logging.rs @@ -317,7 +317,7 @@ impl Visit for CollectVisitor { let name = field.name(); let mut value_string = String::new(); - write!(value_string, "{:?}", value).unwrap(); + write!(value_string, "{value:?}").unwrap(); if name == "message" { self.message = value_string; @@ -337,8 +337,8 @@ impl Visit for CollectVisitor { fn format_field(field_name: &str, value: &dyn Debug) -> String { let mut out = String::new(); match field_name { - "message" => write!(out, "{:?}", value), - _ => write!(out, "{}={:?}", field_name, value), + "message" => write!(out, "{value:?}"), + _ => write!(out, "{field_name}={value:?}"), } .unwrap(); diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index be3346d48e..be7b06328c 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -78,7 +78,7 @@ impl FromArgMatches for PlatformArg { "desktop" => Ok(Self::Desktop), _ => Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, - format!("Unknown platform: {}", platform), + format!("Unknown platform: {platform}"), )), } } else { @@ -184,7 +184,7 @@ impl FromArgMatches for RendererArg { }), _ => Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, - format!("Unknown platform: {}", renderer), + format!("Unknown platform: {renderer}"), )), } } else { @@ -198,9 +198,9 @@ impl FromArgMatches for RendererArg { } } -impl Into> for RendererArg { - fn into(self) -> Option { - self.renderer +impl From for Option { + fn from(val: RendererArg) -> Self { + val.renderer } } @@ -423,7 +423,7 @@ impl BundleFormat { let opt_level = if release { "release" } else { "dev" }; - format!("{}-{}", base_profile, opt_level) + format!("{base_profile}-{opt_level}") } pub(crate) fn expected_name(&self) -> &'static str { diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 8e2e0caf30..93fdd55579 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -755,7 +755,7 @@ impl Output { ); let server_address = match state.server.server_address() { - Some(address) => format!("http://{}", address).yellow(), + Some(address) => format!("http://{address}").yellow(), None => "no address".dark_gray(), }; frame.render_widget( diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index 028174fac2..5582a1e3df 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -163,8 +163,7 @@ pub(crate) fn handle_proxy_error(e: Error) -> axum::http::Response panic!("Unexpected error type: {}", e), + e => panic!("Unexpected error type: {e}"), } } } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index a7f2cf72bc..1d4b20e1fd 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -461,8 +461,7 @@ fn build_devserver_router( Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(Body::from(format!( - "Backend connection failed. The backend is likely still starting up. Please try again in a few seconds. Error: {:#?}", - error + "Backend connection failed. The backend is likely still starting up. Please try again in a few seconds. Error: {error:#?}" ))) .unwrap() }, @@ -577,7 +576,7 @@ fn build_serve_dir(runner: &AppServer) -> axum::routing::MethodRouter { .handle_error(|error: Infallible| async move { ( StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), + format!("Unhandled internal error: {error}"), ) }) } diff --git a/packages/cli/src/tailwind.rs b/packages/cli/src/tailwind.rs index 649815c433..8048701c2f 100644 --- a/packages/cli/src/tailwind.rs +++ b/packages/cli/src/tailwind.rs @@ -219,7 +219,7 @@ impl TailwindCli { _ => return None, }; - Some(format!("tailwindcss-{}-{}", platform, arch)) + Some(format!("tailwindcss-{platform}-{arch}")) } fn install_dir(&self) -> Result { From c5851ed9703f86404da4e9d318894d187bc27e70 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 14 Jul 2025 14:06:28 -0700 Subject: [PATCH 15/25] merge fix --- packages/cli/src/serve/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 2f4570f4bf..63b2e810a7 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -1,4 +1,4 @@ -use crate::{BuildId, BuilderUpdate, BundleFormat, Error, Platform, TraceMsg}; +use crate::{BuildId, BuilderUpdate, BundleFormat, Error, TraceMsg}; use axum::extract::ws::Message as WsMessage; use std::path::PathBuf; From 256153b7022dfe91b2ff4f78bf29a39e10cc8f71 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 14 Jul 2025 14:08:42 -0700 Subject: [PATCH 16/25] cleanup merge conflicts --- packages/cli/src/build/request.rs | 11 ++++---- packages/cli/src/cli/bundle.rs | 2 +- packages/cli/src/cli/run.rs | 2 +- packages/cli/src/serve/mod.rs | 2 +- packages/cli/src/serve/runner.rs | 4 +-- packages/cli/src/workspace.rs | 43 ++++++++++++++++--------------- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index dcfaa0f567..ab3b3d27cc 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -595,10 +595,9 @@ impl BuildRequest { Some(*renderer) } _ => { - return Err(anyhow::anyhow!( + return bail!( "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--web`, `--webview`, or `--native` or set a default platform in Cargo.toml" - ) - .into()); + ); // None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(), // None => match enabled_platforms.len() { // 0 => bail!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`"), @@ -1168,7 +1167,7 @@ impl BuildRequest { // On android, the c++_static flag means we need to copy the libc++_shared.so precompiled // library to the jniLibs folder - if arg.contains("-lc++_static") && self.platform == Platform::Android { + if arg.contains("-lc++_static") && self.bundle == BundleFormat::Android { std::fs::copy( self.workspace.android_tools()?.libcpp_shared(&self.triple), framework_dir.join("libc++_shared.so"), @@ -1186,7 +1185,7 @@ impl BuildRequest { self.root_dir().join("Contents").join("Frameworks") } OperatingSystem::IOS(_) => self.root_dir().join("Frameworks"), - OperatingSystem::Linux if self.platform == Platform::Android => self + OperatingSystem::Linux if self.bundle == BundleFormat::Android => self .root_dir() .join("app") .join("src") @@ -4629,7 +4628,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ } async fn prebuild(&self) -> Result<()> { - if self.platform == Platform::Server { + if self.bundle == BundleFormat::Server { return Ok(()); } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index b83f1cedff..dfcb60cb96 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -1,5 +1,5 @@ use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, BundleFormat}; -use anyhow::{anyhow, Context}; +use anyhow::{bail, Context}; use path_absolutize::Absolutize; use std::collections::HashMap; use tauri_bundler::{BundleBinary, BundleSettings, PackageSettings, SettingsBuilder}; diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index 1f1e5e4e2f..525ce4caf2 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -1,7 +1,7 @@ use super::*; use crate::{ serve::{AppServer, ServeUpdate, WebServer}, - BuilderUpdate, BundleFormat, Error, Result, + BuilderUpdate, BundleFormat, Result, }; use anyhow::bail; use dioxus_dx_wire_format::BuildStage; diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 39cf01a4cd..b318df9f4e 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,6 +1,6 @@ use crate::{ styles::{GLOW_STYLE, LINK_STYLE}, - AppBuilder, BuildId, BuildMode, BuilderUpdate, BundleFormat, Error, Result, ServeArgs, + AppBuilder, BuildId, BuildMode, BuilderUpdate, BundleFormat, Result, ServeArgs, TraceController, }; diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index c48efdd048..faba51d018 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,8 +1,8 @@ use super::{AppBuilder, ServeUpdate, WebServer}; use crate::{ platform_override::CommandWithPlatformOverrides, BuildArtifacts, BuildId, BuildMode, - BuildTargets, BuilderUpdate, BundleFormat, Error, HotpatchModuleCache, Result, ServeArgs, - TailwindCli, TraceSrc, Workspace, + BuildTargets, BuilderUpdate, BundleFormat, HotpatchModuleCache, Result, ServeArgs, TailwindCli, + TraceSrc, Workspace, }; use anyhow::{bail, Context}; use dioxus_core::internal::{ diff --git a/packages/cli/src/workspace.rs b/packages/cli/src/workspace.rs index 659fdbc06a..e9bb893148 100644 --- a/packages/cli/src/workspace.rs +++ b/packages/cli/src/workspace.rs @@ -36,29 +36,30 @@ impl Workspace { return Ok(ws.clone()); } - let krates_future: JoinHandle> = tokio::task::spawn_blocking(|| { - let manifest_options = crate::logging::VERBOSITY.get().unwrap(); - let lock_options = LockOptions { - frozen: manifest_options.frozen, - locked: manifest_options.locked, - offline: manifest_options.offline, - }; - - let mut cmd = Cmd::new(); - cmd.lock_opts(lock_options); - - let mut builder = krates::Builder::new(); - builder.workspace(true); - let res = builder.build(cmd, |_| {})?; - - if !lock_options.offline { - if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") { - std::thread::sleep(Duration::from_secs(res.parse().unwrap_or(5))); + let krates_future: JoinHandle> = + tokio::task::spawn_blocking(|| { + let manifest_options = crate::logging::VERBOSITY.get().unwrap(); + let lock_options = LockOptions { + frozen: manifest_options.frozen, + locked: manifest_options.locked, + offline: manifest_options.offline, + }; + + let mut cmd = Cmd::new(); + cmd.lock_opts(lock_options); + + let mut builder = krates::Builder::new(); + builder.workspace(true); + let res = builder.build(cmd, |_| {})?; + + if !lock_options.offline { + if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") { + std::thread::sleep(Duration::from_secs(res.parse().unwrap_or(5))); + } } - } - Ok(res) as Result - }); + Ok(res) as Result + }); let spin_future = async move { tokio::time::sleep(Duration::from_millis(1000)).await; From bd2844984a42a6d8d94745979dfb6f8cc82cb395 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 14 Jul 2025 17:02:05 -0500 Subject: [PATCH 17/25] fix clippy --- packages/cli/src/build/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index ab3b3d27cc..6c791cc029 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -595,7 +595,7 @@ impl BuildRequest { Some(*renderer) } _ => { - return bail!( + bail!( "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--web`, `--webview`, or `--native` or set a default platform in Cargo.toml" ); // None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(), From 860d3de7a4b798c552afdcf8e0196a4aca739d45 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 14 Jul 2025 19:16:17 -0700 Subject: [PATCH 18/25] remove nit --- packages/cli/src/workspace.rs | 45 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/workspace.rs b/packages/cli/src/workspace.rs index e9bb893148..31aa7a354c 100644 --- a/packages/cli/src/workspace.rs +++ b/packages/cli/src/workspace.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use std::{collections::HashSet, path::Path}; use std::{path::PathBuf, time::Duration}; use target_lexicon::Triple; -use tokio::{process::Command, task::JoinHandle}; +use tokio::process::Command; pub struct Workspace { pub(crate) krates: Krates, @@ -36,30 +36,29 @@ impl Workspace { return Ok(ws.clone()); } - let krates_future: JoinHandle> = - tokio::task::spawn_blocking(|| { - let manifest_options = crate::logging::VERBOSITY.get().unwrap(); - let lock_options = LockOptions { - frozen: manifest_options.frozen, - locked: manifest_options.locked, - offline: manifest_options.offline, - }; - - let mut cmd = Cmd::new(); - cmd.lock_opts(lock_options); - - let mut builder = krates::Builder::new(); - builder.workspace(true); - let res = builder.build(cmd, |_| {})?; - - if !lock_options.offline { - if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") { - std::thread::sleep(Duration::from_secs(res.parse().unwrap_or(5))); - } + let krates_future = tokio::task::spawn_blocking(|| { + let manifest_options = crate::logging::VERBOSITY.get().unwrap(); + let lock_options = LockOptions { + frozen: manifest_options.frozen, + locked: manifest_options.locked, + offline: manifest_options.offline, + }; + + let mut cmd = Cmd::new(); + cmd.lock_opts(lock_options); + + let mut builder = krates::Builder::new(); + builder.workspace(true); + let res = builder.build(cmd, |_| {})?; + + if !lock_options.offline { + if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") { + std::thread::sleep(Duration::from_secs(res.parse().unwrap_or(5))); } + } - Ok(res) as Result - }); + Ok(res) as Result + }); let spin_future = async move { tokio::time::sleep(Duration::from_millis(1000)).await; From 0c7886500d95a3d5583ee0931234365e5af08811 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 21 Jul 2025 15:37:09 -0500 Subject: [PATCH 19/25] restore platform as a legacy argument --- packages/cli/src/build/request.rs | 34 ++++--- packages/cli/src/cli/target.rs | 25 ++++-- packages/cli/src/platform.rs | 142 +++++++++++++++++++++++++----- 3 files changed, 159 insertions(+), 42 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 7017fd8c6b..4491a47b4e 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -317,7 +317,7 @@ use crate::{ AndroidTools, BuildContext, BundleFormat, DioxusConfig, Error, LinkAction, LinkerFlavor, - PlatformArg, Renderer, Result, RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, + Renderer, Result, RustcArgs, TargetAlias, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR, }; use anyhow::{bail, Context}; @@ -558,7 +558,18 @@ impl BuildRequest { let mut features = args.features.clone(); let mut no_default_features = args.no_default_features; - let mut renderer: Option = match args.renderer.into() { + // Apply the platform alias if present + let mut target_alias = args.target_alias; + let mut renderer = args.renderer; + let mut bundle = args.bundle; + if let Some(platform) = args.platform { + let (default_target, default_renderer, default_bundle) = platform.into_triple(); + target_alias = target_alias.or(default_target); + renderer.renderer = Some(renderer.renderer.unwrap_or(default_renderer)); + bundle = Some(bundle.unwrap_or(default_bundle)); + } + + let mut renderer: Option = match renderer.into() { Some(renderer) => match enabled_renderers.len() { 0 => Some(renderer), @@ -574,8 +585,8 @@ impl BuildRequest { None => match enabled_renderers.as_slice() { [] => None, // Wait until we resolve everything else first then we will resolve it from the triple [(renderer, feature)] => { - let targeting_mobile = match (&args.target, args.platform) { - (_, PlatformArg::Android | PlatformArg::Ios) => true, + let targeting_mobile = match (&args.target, target_alias) { + (_, TargetAlias::Android | TargetAlias::Ios) => true, (Some(target), _) => { matches!( (target.environment, target.operating_system), @@ -619,16 +630,15 @@ impl BuildRequest { // Tools like xcrun/adb can detect devices let device = args.device; - // Resolve the platform args into a concrete platform - let mut platform = args.platform; + // Resolve the target alias and renderer into a concrete target alias. // If the user didn't pass a platform, but we have a renderer, get the default platform for that renderer - if let (PlatformArg::Unknown, Some(renderer)) = (platform, renderer) { - platform = renderer.default_platform(); + if let (TargetAlias::Unknown, Some(renderer)) = (target_alias, renderer) { + target_alias = renderer.default_platform(); }; // We want a real triple to build with, so we'll autodetect it if it's not provided // The triple ends up being a source of truth for us later hence all this work to figure it out - let triple = match (args.target.clone(), platform) { + let triple = match (args.target.clone(), target_alias) { // If there is an explicit target, use it (Some(target), _) => target, // If there is a platform, use it to determine the target triple @@ -642,7 +652,7 @@ impl BuildRequest { } // Resolve the bundle format based on the combination of the target triple and renderer - let bundle = match args.bundle { + let bundle = match bundle { // If there is an explicit bundle format, use it Some(bundle) => bundle, // Otherwise guess a bundle format based on the target triple and renderer @@ -1175,7 +1185,7 @@ impl BuildRequest { // On android, the c++_shared flag means we need to copy the libc++_shared.so precompiled // library to the jniLibs folder - if arg.contains("-lc++_shared") && self.platform == Platform::Android { + if arg.contains("-lc++_shared") && self.bundle == BundleFormat::Android { std::fs::copy( self.workspace.android_tools()?.libcpp_shared(&self.triple), framework_dir.join("libc++_shared.so"), @@ -3240,7 +3250,7 @@ impl BuildRequest { /// Return the platforms that are enabled for the package /// /// Ideally only one platform is enabled but we need to be able to - pub(crate) fn enabled_cargo_toml_platforms( + pub(crate) fn enabled_cargo_toml_renderers( package: &krates::cm::Package, no_default_features: bool, ) -> Vec<(Renderer, String)> { diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index 30211e81a5..ac29eb1826 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -1,7 +1,8 @@ use crate::cli::*; use crate::BundleFormat; -use crate::PlatformArg; +use crate::Platform; use crate::RendererArg; +use crate::TargetAlias; use target_lexicon::Triple; const HELP_HEADING: &str = "Target Options"; @@ -9,17 +10,27 @@ const HELP_HEADING: &str = "Target Options"; /// A single target to build for #[derive(Clone, Debug, Default, Deserialize, Parser)] pub(crate) struct TargetArgs { - /// Build platform: support Web & Desktop [default: "default_platform"] + /// The target alias to use for this build. Supports wasm, macos, windows, linux, ios, android, and host [default: "host"] #[clap(flatten)] - pub(crate) platform: PlatformArg, + pub(crate) target_alias: TargetAlias, - /// Build bundle: supports web, macos, windows, linux, ios, android, and server [default: "web"] + /// Build renderer: supports web, webview, native, server, and liveview + #[clap(flatten)] + pub(crate) renderer: RendererArg, + + /// Build bundle: supports web, macos, windows, linux, ios, android, and server #[clap(long, value_enum, help_heading = HELP_HEADING)] pub(crate) bundle: Option, - /// Build renderer: support Webview and Native [default: "webview"] - #[clap(flatten)] - pub(crate) renderer: RendererArg, + /// Build platform: supports Web, MacOS, Windows, Linux, iOS, Android, and Server + /// + /// The platform implies a combination of the target alias, renderer, and bundle format flags. + /// + /// You should generally prefer to use the `--web`, `--webview`, or `--native` flags to set the renderer + /// or the `--wasm`, `--macos`, `--windows`, `--linux`, `--ios`, or `--android` flags to set the target alias + /// instead of this flag. The renderer, target alias, and bundle format will be inferred if you only pass one. + #[clap(long, value_enum, help_heading = HELP_HEADING)] + pub(crate) platform: Option, /// Build in release mode [default: false] #[clap(long, short, help_heading = HELP_HEADING)] diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index be7b06328c..e5e51c7e7f 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -11,7 +11,7 @@ use crate::Workspace; Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, )] #[non_exhaustive] -pub(crate) enum PlatformArg { +pub(crate) enum TargetAlias { /// Targeting the WASM architecture Wasm, @@ -32,28 +32,28 @@ pub(crate) enum PlatformArg { /// Targeting the android platform Android, - /// Targeting the current platform with the "desktop" renderer - Desktop, + /// Targeting the current target + Host, /// An unknown target platform #[default] Unknown, } -impl Args for PlatformArg { +impl Args for TargetAlias { fn augment_args(cmd: clap::Command) -> clap::Command { - const HELP_HEADING: &str = "Platform"; - cmd.arg(arg!(--wasm "The wasm target platform").help_heading(HELP_HEADING)) - .arg(arg!(--macos "The macos target platform").help_heading(HELP_HEADING)) - .arg(arg!(--windows "The windows target platform").help_heading(HELP_HEADING)) - .arg(arg!(--linux "The linux target platform").help_heading(HELP_HEADING)) - .arg(arg!(--ios "The ios target platform").help_heading(HELP_HEADING)) - .arg(arg!(--android "The android target platform").help_heading(HELP_HEADING)) - .arg(arg!(--desktop "The desktop target platform").help_heading(HELP_HEADING)) + const HELP_HEADING: &str = "Target Alias"; + cmd.arg(arg!(--wasm "The wasm target").help_heading(HELP_HEADING)) + .arg(arg!(--macos "The macos target").help_heading(HELP_HEADING)) + .arg(arg!(--windows "The windows target").help_heading(HELP_HEADING)) + .arg(arg!(--linux "The linux target").help_heading(HELP_HEADING)) + .arg(arg!(--ios "The ios target").help_heading(HELP_HEADING)) + .arg(arg!(--android "The android target").help_heading(HELP_HEADING)) + .arg(arg!(--host "The host target").help_heading(HELP_HEADING)) .group( - clap::ArgGroup::new("platform") + clap::ArgGroup::new("target_alias") .args([ - "wasm", "macos", "windows", "linux", "ios", "android", "desktop", + "wasm", "macos", "windows", "linux", "ios", "android", "host", ]) .multiple(false) .required(false), @@ -65,9 +65,9 @@ impl Args for PlatformArg { } } -impl FromArgMatches for PlatformArg { +impl FromArgMatches for TargetAlias { fn from_arg_matches(matches: &ArgMatches) -> Result { - if let Some(platform) = matches.get_one::("platform") { + if let Some(platform) = matches.get_one::("target_alias") { match platform.as_str() { "wasm" => Ok(Self::Wasm), "macos" => Ok(Self::MacOS), @@ -75,10 +75,10 @@ impl FromArgMatches for PlatformArg { "linux" => Ok(Self::Linux), "ios" => Ok(Self::Ios), "android" => Ok(Self::Android), - "desktop" => Ok(Self::Desktop), + "host" => Ok(Self::Host), _ => Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, - format!("Unknown platform: {platform}"), + format!("Unknown target alias: {platform}"), )), } } else { @@ -91,7 +91,7 @@ impl FromArgMatches for PlatformArg { } } -impl PlatformArg { +impl TargetAlias { #[cfg(target_os = "macos")] pub(crate) const TARGET_PLATFORM: Option = Some(Self::MacOS); #[cfg(target_os = "windows")] @@ -104,7 +104,7 @@ impl PlatformArg { pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result { match self { // Generally just use the host's triple for native executables unless specified otherwise - Self::MacOS | Self::Windows | Self::Linux | Self::Desktop | Self::Unknown => { + Self::MacOS | Self::Windows | Self::Linux | Self::Host | Self::Unknown => { Ok(Triple::host()) } @@ -133,6 +133,14 @@ impl PlatformArg { .await), } } + + pub(crate) fn or(self, other: Self) -> Self { + if self == Self::Unknown { + other + } else { + self + } + } } #[derive( @@ -252,12 +260,12 @@ impl Renderer { } } - pub(crate) fn default_platform(&self) -> PlatformArg { + pub(crate) fn default_platform(&self) -> TargetAlias { match self { Renderer::Webview | Renderer::Native | Renderer::Server | Renderer::Liveview => { - PlatformArg::TARGET_PLATFORM.unwrap() + TargetAlias::TARGET_PLATFORM.unwrap() } - Renderer::Web => PlatformArg::Wasm, + Renderer::Web => TargetAlias::Wasm, } } @@ -467,3 +475,91 @@ impl BundleFormat { } } } + +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Debug, + Default, + clap::ValueEnum, +)] +#[non_exhaustive] +pub(crate) enum Platform { + /// Alias for `--wasm --web --bundle-format web` + #[clap(name = "web")] + #[serde(rename = "web")] + #[default] + Web, + + /// Alias for `--macos --webview --bundle-format macos` + #[cfg_attr(target_os = "macos", clap(alias = "desktop"))] + #[clap(name = "macos")] + #[serde(rename = "macos")] + MacOS, + + /// Alias for `--windows --webview --bundle-format windows` + #[cfg_attr(target_os = "windows", clap(alias = "desktop"))] + #[clap(name = "windows")] + #[serde(rename = "windows")] + Windows, + + /// Alias for `--linux --webview --bundle-format linux` + #[cfg_attr(target_os = "linux", clap(alias = "desktop"))] + #[clap(name = "linux")] + #[serde(rename = "linux")] + Linux, + + /// Alias for `--ios --webview --bundle-format ios` + #[clap(name = "ios")] + #[serde(rename = "ios")] + Ios, + + /// Alias for `--android --webview --bundle-format android` + #[clap(name = "android")] + #[serde(rename = "android")] + Android, + + /// Alias for `--host --server --bundle-format server` + #[clap(name = "server")] + #[serde(rename = "server")] + Server, + + /// Alias for `--host --liveview --bundle-format host` + #[clap(name = "liveview")] + #[serde(rename = "liveview")] + Liveview, +} + +impl Platform { + pub(crate) fn into_triple(self) -> (TargetAlias, Renderer, BundleFormat) { + match self { + Platform::Web => (TargetAlias::Wasm, Renderer::Web, BundleFormat::Web), + Platform::MacOS => (TargetAlias::MacOS, Renderer::Webview, BundleFormat::MacOS), + Platform::Windows => ( + TargetAlias::Windows, + Renderer::Webview, + BundleFormat::Windows, + ), + Platform::Linux => (TargetAlias::Linux, Renderer::Webview, BundleFormat::Linux), + Platform::Ios => (TargetAlias::Ios, Renderer::Webview, BundleFormat::Ios), + Platform::Android => ( + TargetAlias::Android, + Renderer::Webview, + BundleFormat::Android, + ), + Platform::Server => (TargetAlias::Host, Renderer::Server, BundleFormat::Server), + Platform::Liveview => ( + TargetAlias::Host, + Renderer::Liveview, + BundleFormat::TARGET_PLATFORM.unwrap(), + ), + } + } +} From 2d6f796c30cf020c9a5b0d0995d93c9bca95feea Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 21 Jul 2025 15:42:48 -0500 Subject: [PATCH 20/25] improve help message --- packages/cli/src/cli/target.rs | 2 +- packages/cli/src/platform.rs | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index ac29eb1826..f20c7faf2c 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -18,7 +18,7 @@ pub(crate) struct TargetArgs { #[clap(flatten)] pub(crate) renderer: RendererArg, - /// Build bundle: supports web, macos, windows, linux, ios, android, and server + /// The bundle format to target for the build: supports web, macos, windows, linux, ios, android, and server #[clap(long, value_enum, help_heading = HELP_HEADING)] pub(crate) bundle: Option, diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index e5e51c7e7f..711f153833 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -43,13 +43,13 @@ pub(crate) enum TargetAlias { impl Args for TargetAlias { fn augment_args(cmd: clap::Command) -> clap::Command { const HELP_HEADING: &str = "Target Alias"; - cmd.arg(arg!(--wasm "The wasm target").help_heading(HELP_HEADING)) - .arg(arg!(--macos "The macos target").help_heading(HELP_HEADING)) - .arg(arg!(--windows "The windows target").help_heading(HELP_HEADING)) - .arg(arg!(--linux "The linux target").help_heading(HELP_HEADING)) - .arg(arg!(--ios "The ios target").help_heading(HELP_HEADING)) - .arg(arg!(--android "The android target").help_heading(HELP_HEADING)) - .arg(arg!(--host "The host target").help_heading(HELP_HEADING)) + cmd.arg(arg!(--wasm "Target the wasm triple").help_heading(HELP_HEADING)) + .arg(arg!(--macos "Target the macos triple").help_heading(HELP_HEADING)) + .arg(arg!(--windows "Target the windows triple").help_heading(HELP_HEADING)) + .arg(arg!(--linux "Target the linux triple").help_heading(HELP_HEADING)) + .arg(arg!(--ios "Target the ios triple").help_heading(HELP_HEADING)) + .arg(arg!(--android "Target the android triple").help_heading(HELP_HEADING)) + .arg(arg!(--host "Target the host triple").help_heading(HELP_HEADING)) .group( clap::ArgGroup::new("target_alias") .args([ @@ -153,11 +153,11 @@ pub(crate) struct RendererArg { impl Args for RendererArg { fn augment_args(cmd: clap::Command) -> clap::Command { const HELP_HEADING: &str = "Renderer"; - cmd.arg(arg!(--web "Targeting the web renderer").help_heading(HELP_HEADING)) - .arg(arg!(--webview "Targeting the webview renderer").help_heading(HELP_HEADING)) - .arg(arg!(--native "Targeting the native renderer").help_heading(HELP_HEADING)) - .arg(arg!(--server "Targeting the server renderer").help_heading(HELP_HEADING)) - .arg(arg!(--liveview "Targeting the liveview renderer").help_heading(HELP_HEADING)) + cmd.arg(arg!(--web "Enable the dioxus web renderer").help_heading(HELP_HEADING)) + .arg(arg!(--webview "Enable the dioxus webview renderer").help_heading(HELP_HEADING)) + .arg(arg!(--native "Enable the dioxus native renderer").help_heading(HELP_HEADING)) + .arg(arg!(--server "Enable the dioxus server renderer").help_heading(HELP_HEADING)) + .arg(arg!(--liveview "Enable the dioxus liveview renderer").help_heading(HELP_HEADING)) .group( clap::ArgGroup::new("renderer") .args(["web", "webview", "native", "server", "liveview"]) From ec0f180258d510e7a407613fae071b14204c4137 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 22 Jul 2025 13:59:13 -0700 Subject: [PATCH 21/25] vendor openssl on native, pin server_fn --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 4 ++-- packages/native/Cargo.toml | 6 +++++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7244aa647a..3b679bb359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5663,6 +5663,7 @@ dependencies = [ "dioxus-native-dom", "futures-util", "keyboard-types", + "openssl", "rustc-hash 2.1.1", "tokio", "tracing", @@ -14254,9 +14255,9 @@ dependencies = [ [[package]] name = "serde_qs" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b417bedc008acbdf6d6b4bc482d29859924114bbe2650b7921fb68a261d0aa6" +checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" dependencies = [ "percent-encoding", "serde", @@ -14351,9 +14352,9 @@ dependencies = [ [[package]] name = "server_fn" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b0f92b9d3a62c73f238ac21f7a09f15bad335a9d1651514d9da80d2eaf8d4c" +checksum = "9c27fbd25ecc066481e383e2ed62ab2480e708aa3fe46cba36e95f58e61dfd04" dependencies = [ "axum 0.8.4", "base64 0.22.1", @@ -14368,7 +14369,6 @@ dependencies = [ "hyper 1.6.0", "inventory", "js-sys", - "once_cell", "pin-project-lite", "reqwest 0.12.22", "rustc_version", @@ -14381,7 +14381,7 @@ dependencies = [ "thiserror 2.0.12", "throw_error", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.27.0", "tower 0.5.2", "tower-layer", "url", @@ -14394,9 +14394,9 @@ dependencies = [ [[package]] name = "server_fn_macro" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341dd1087afe9f3e546c5979a4f0b6d55ac072e1201313f86e7fe364223835ac" +checksum = "b9d530c872590473016016679c94e3bddf47372941fc924f687ffd11a1778a71" dependencies = [ "const_format", "convert_case 0.8.0", @@ -14409,9 +14409,9 @@ dependencies = [ [[package]] name = "server_fn_macro_default" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ab934f581482a66da82f2b57b15390ad67c9ab85bd9a6c54bb65060fb1380" +checksum = "ca7abc92ed696648275ed9ff171131a83d571af11748593dc2e6eb6a4e22a5b9" dependencies = [ "server_fn_macro", "syn 2.0.104", @@ -16650,7 +16650,6 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", - "rustls 0.23.29", "tokio", "tungstenite 0.26.2", ] @@ -16663,6 +16662,7 @@ checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" dependencies = [ "futures-util", "log", + "rustls 0.23.29", "tokio", "tungstenite 0.27.0", ] diff --git a/Cargo.toml b/Cargo.toml index d2ee101327..4117c757a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -240,8 +240,8 @@ lru = "0.16.0" async-trait = "0.1.88" axum = { version = "0.8.4", default-features = false } axum-server = { version = "0.7.2", default-features = false } -server_fn = { version = "0.8.2", default-features = false } -server_fn_macro = { version = "0.8.2" } +server_fn = { version = "=0.8.3", default-features = false } +server_fn_macro = { version = "=0.8.3" } tower = "0.5.2" http = "1.3.1" notify = { version = "8.1.0" } diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 615ccabf05..8a577ac983 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -10,13 +10,14 @@ homepage = "https://dioxuslabs.com/learn/0.6/getting_started" keywords = ["dom", "ui", "gui", "react"] [features] -default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts"] +default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts", "openssl-vendored-android"] svg = ["blitz-dom/svg", "blitz-paint/svg"] net = ["dep:tokio", "dep:blitz-net"] accessibility = ["blitz-shell/accessibility", "blitz-dom/accessibility"] tracing = ["dep:tracing", "blitz-shell/tracing", "blitz-dom/tracing"] hot-reload = ["dep:dioxus-cli-config", "dep:dioxus-devtools"] system-fonts = ["blitz-dom/system_fonts"] +openssl-vendored-android = ["openssl"] autofocus = [] [dependencies] @@ -51,6 +52,9 @@ tracing = { workspace = true, optional = true } rustc-hash = { workspace = true } futures-util = { workspace = true } +[target.'cfg(target_os = "android")'.dependencies] +openssl = { version = "0.10", features = ["vendored"], optional = true } + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] From 0b9eb8c381b80bba40256cb6fefccfbbd4d1e96a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 22 Jul 2025 13:59:27 -0700 Subject: [PATCH 22/25] print build errors as warnings --- packages/cli/src/build/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/build/context.rs b/packages/cli/src/build/context.rs index db99ac81d0..c0ab88edc7 100644 --- a/packages/cli/src/build/context.rs +++ b/packages/cli/src/build/context.rs @@ -105,7 +105,7 @@ impl BuildContext { } pub(crate) fn status_build_error(&self, line: String) { - tracing::debug!(dx_src = ?TraceSrc::Cargo, "{line}"); + tracing::warn!(dx_src = ?TraceSrc::Cargo, "{line}"); } pub(crate) fn status_build_message(&self, line: String) { From 081c1a7b13b8201e7a405bab8ada770ec82332f9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 22 Jul 2025 13:59:34 -0700 Subject: [PATCH 23/25] add --desktop arg compat --- packages/cli/src/platform.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 711f153833..67596e5be7 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -50,10 +50,11 @@ impl Args for TargetAlias { .arg(arg!(--ios "Target the ios triple").help_heading(HELP_HEADING)) .arg(arg!(--android "Target the android triple").help_heading(HELP_HEADING)) .arg(arg!(--host "Target the host triple").help_heading(HELP_HEADING)) + .arg(arg!(--desktop "Target the host triple").help_heading(HELP_HEADING)) .group( clap::ArgGroup::new("target_alias") .args([ - "wasm", "macos", "windows", "linux", "ios", "android", "host", + "wasm", "macos", "windows", "linux", "ios", "android", "host", "host", ]) .multiple(false) .required(false), @@ -327,14 +328,17 @@ pub(crate) enum BundleFormat { Web, /// Targeting the macos desktop bundle structure + #[cfg_attr(target_os = "macos", serde(alias = "desktop"))] #[serde(rename = "macos")] MacOS, /// Targeting the windows desktop bundle structure + #[cfg_attr(target_os = "windows", serde(alias = "desktop"))] #[serde(rename = "windows")] Windows, /// Targeting the linux desktop bundle structure + #[cfg_attr(target_os = "linux", serde(alias = "desktop"))] #[serde(rename = "linux")] Linux, From 84e36851f60e7e4fc6770f5461be4acc41cd92ce Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 22 Jul 2025 14:05:36 -0700 Subject: [PATCH 24/25] adjust cli headings --- packages/cli/src/platform.rs | 10 +++++----- packages/cli/src/serve/output.rs | 2 +- packages/dioxus/README.md | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/platform.rs b/packages/cli/src/platform.rs index 67596e5be7..d9aa092b3b 100644 --- a/packages/cli/src/platform.rs +++ b/packages/cli/src/platform.rs @@ -441,11 +441,11 @@ impl BundleFormat { pub(crate) fn expected_name(&self) -> &'static str { match self { Self::Web => "Web", - Self::MacOS => "Desktop MacOS", - Self::Windows => "Desktop Windows", - Self::Linux => "Desktop Linux", - Self::Ios => "Mobile iOS", - Self::Android => "Mobile Android", + Self::MacOS => "MacOS", + Self::Windows => "Windows", + Self::Linux => "Linux", + Self::Ios => "iOS", + Self::Android => "Android", Self::Server => "Server", } } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 2b04772688..372e6dc1e6 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -770,7 +770,7 @@ impl Output { frame.render_widget( Paragraph::new(Line::from(vec![ "Read the docs: ".gray(), - "https://dioxuslabs.com/0.6/docs".blue(), + "https://dioxuslabs.com/learn/0.7/".blue(), ])), links_list[0], ); diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index 1fe3c67116..8563ad49da 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -32,7 +32,6 @@ Dioxus is crossplatform app framework that empowers developer to build beautiful ## Quick start To get started with Dioxus, you'll want to grab the dioxus-cli tool: `dx`. We distribute `dx` with `cargo-binstall` - if you already have binstall skip this step. - ```shell # skip if you already have cargo-binstall cargo install cargo-binstall @@ -44,7 +43,7 @@ cargo binstall dioxus-cli dx new my-app && cd my-app # and then serve! -dx serve --webview +dx serve --desktop ``` ## Your first app From b4c2af69f9b074867f998f1327088b324e54c5fa Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 22 Jul 2025 14:11:58 -0700 Subject: [PATCH 25/25] prefer self.is_wasm_or_wasi() --- packages/cli/src/build/request.rs | 41 ++++++++++--------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 22b8f0fecb..130b4cda4c 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -613,12 +613,6 @@ impl BuildRequest { bail!( "Multiple platforms enabled in Cargo.toml. Please specify a platform with `--web`, `--webview`, or `--native` or set a default platform in Cargo.toml" ); - // None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(), - // None => match enabled_platforms.len() { - // 0 => bail!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`"), - // 1 => enabled_platforms[0], - // _ => { - // bail!("Multiple platforms enabled in Cargo.toml. Please specify a platform with `--platform` or set a default platform in Cargo.toml") } }, }; @@ -783,8 +777,7 @@ impl BuildRequest { r#"Target Info: • features: {features:?} • triple: {triple} - • bundle format: {bundle:?} - "# + • bundle format: {bundle:?}"# ); tracing::debug!( r#"Log Files: @@ -793,8 +786,7 @@ impl BuildRequest { • rustc_wrapper_args_file: {}, • session_cache_dir: {} • linker: {:?} - • target_dir: {:?} - "#, + • target_dir: {:?}"#, link_args_file.path().display(), link_err_file.path().display(), rustc_wrapper_args_file.path().display(), @@ -1050,7 +1042,6 @@ impl BuildRequest { let time_end = SystemTime::now(); let mode = ctx.mode.clone(); let bundle = self.bundle; - // let platform = self.platform; let depinfo = RustcDepInfo::from_file(&exe.with_extension("d")).unwrap_or_default(); tracing::debug!( @@ -1436,10 +1427,7 @@ impl BuildRequest { // Requiring the ASLR offset here is necessary but unfortunately might be flakey in practice. // Android apps can take a long time to open, and a hot patch might've been issued in the interim, // making this hotpatch a failure. - if !matches!( - self.triple.architecture, - target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 - ) { + if !self.is_wasm_or_wasi() { let stub_bytes = crate::build::create_undefined_symbol_stub( cache, &object_files, @@ -1980,10 +1968,7 @@ impl BuildRequest { } // We want to go through wasm-ld directly, so we need to remove the -flavor flag - if matches!( - self.triple.architecture, - target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 - ) { + if self.is_wasm_or_wasi() { let flavor_idx = args.iter().position(|arg| *arg == "-flavor").unwrap(); args.remove(flavor_idx + 1); args.remove(flavor_idx); @@ -2180,10 +2165,7 @@ impl BuildRequest { ); cmd.arg(format!("-Clinker={}", Workspace::path_to_dx()?.display())); - if matches!( - self.triple.architecture, - target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 - ) { + if self.is_wasm_or_wasi() { cmd.arg("-Crelocation-model=pic"); } @@ -2448,11 +2430,7 @@ impl BuildRequest { // Disable reference types on wasm when using hotpatching // https://blog.rust-lang.org/2024/09/24/webassembly-targets-change-in-default-target-features/#disabling-on-by-default-webassembly-proposals - if matches!( - self.triple.architecture, - target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 - ) && matches!(ctx.mode, BuildMode::Thin { .. } | BuildMode::Fat) - { + if self.is_wasm_or_wasi() && matches!(ctx.mode, BuildMode::Thin { .. } | BuildMode::Fat) { rust_flags.flags.push("-Ctarget-cpu=mvp".to_string()); } @@ -3400,6 +3378,13 @@ impl BuildRequest { self.exe_dir().join(self.platform_exe_name()) } + fn is_wasm_or_wasi(&self) -> bool { + matches!( + self.triple.architecture, + target_lexicon::Architecture::Wasm32 | target_lexicon::Architecture::Wasm64 + ) || self.triple.operating_system == target_lexicon::OperatingSystem::Wasi + } + /// Does the app specify: /// /// - Dioxus with "fullstack" enabled? (access to serverfns, etc)