瀏覽代碼

use the target tripple and bundle format as the source of truth for the build

Evan Almloff 6 小時之前
父節點
當前提交
51b259bfbd

+ 34 - 33
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")

+ 144 - 169
packages/cli/src/build/request.rs

@@ -316,9 +316,9 @@
 //! - xbuild: <https://github.com/rust-mobile/xbuild/blob/master/xbuild/src/command/build.rs>
 
 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<Platform>,
+    pub(crate) renderer: Renderer,
+    pub(crate) bundle: BundleFormat,
+    pub(crate) enabled_renderers: Vec<Renderer>,
     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<ClientRenderer>,
+        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<Platform> {
-        let mut platforms = vec![];
+    ) -> Vec<Renderer> {
+        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<String> {
+    fn rendererless_features(package: &krates::cm::Package) -> Vec<String> {
         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<String> {
+    fn info_plist_contents(&self, bundle: BundleFormat) -> Result<String> {
         #[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<Result<()>> = 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<HotpatchModuleCache> {
-        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()),

+ 4 - 4
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<BuildArgs> {
             // 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?);
         }

+ 12 - 13
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(),

+ 21 - 19
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}"
                                 )));
                             }
 

+ 2 - 2
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<ClientRenderer>,
+    pub(crate) renderer: Option<Renderer>,
 
     /// Build in release mode [default: false]
     #[clap(long, short, help_heading = HELP_HEADING)]

+ 8 - 18
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<String> 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<String> 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"),

+ 240 - 110
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<Self> {
+        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<Self, Self::Err> {
+        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<Self, Self::Err> {
+        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<Self, Self::Err> {
         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<PlatformArg> 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<Self> = None;
 
-    // /// Get the feature name for the platform in the dioxus crate
-    pub(crate) fn feature_name(&self, renderer: Option<ClientRenderer>) -> &str {
+    pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result<Triple> {
         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<Self> {
-        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),
         }
     }
 }

+ 14 - 12
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..."
                     );

+ 8 - 8
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()

+ 10 - 9
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")

+ 8 - 8
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<ConnectedWsClient>,
     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<SocketAddr> {
-        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<RwLock<Status>>);
 enum Status {
     ClientInit {
         application_name: String,
-        platform: Platform,
+        bundle: BundleFormat,
     },
     Building {
         progress: f64,

+ 2 - 2
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<u32>,
     },
     WsMessage {
-        platform: Platform,
+        bundle: BundleFormat,
         msg: WsMessage,
     },