ソースを参照

Use different release profiles for desktop and server builds, adhoc profiles (#3693)

* Use different release profiles for desktop and server builds

* fix clippy

* change the global cargo config instead of the local one

* fix merge

* fix release profile logic

* use adhoc profile generation

* fix compilation of dioxus-lib in fullstack

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
Evan Almloff 1 週間 前
コミット
7d2a3aa751

+ 67 - 64
packages/cli/src/build/request.rs

@@ -344,7 +344,6 @@ use std::{
 use target_lexicon::{OperatingSystem, Triple};
 use tempfile::{NamedTempFile, TempDir};
 use tokio::{io::AsyncBufReadExt, process::Command};
-use toml_edit::Item;
 use uuid::Uuid;
 
 use super::HotpatchModuleCache;
@@ -442,10 +441,6 @@ pub struct BuildArtifacts {
     pub(crate) patch_cache: Option<Arc<HotpatchModuleCache>>,
 }
 
-pub(crate) static PROFILE_WASM: &str = "wasm-dev";
-pub(crate) static PROFILE_ANDROID: &str = "android-dev";
-pub(crate) static PROFILE_SERVER: &str = "server-dev";
-
 impl BuildRequest {
     /// Create a new build request.
     ///
@@ -591,13 +586,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 if args.release => "release".to_string(),
-            None => match platform {
-                Platform::Android => PROFILE_ANDROID.to_string(),
-                Platform::Web => PROFILE_WASM.to_string(),
-                Platform::Server => PROFILE_SERVER.to_string(),
-                _ => "dev".to_string(),
-            },
+            None => platform.profile_name(args.release),
         };
 
         // Determining release mode is based on the profile, actually, so we need to check that
@@ -2087,6 +2076,9 @@ impl BuildRequest {
     fn cargo_build_arguments(&self, ctx: &BuildContext) -> Vec<String> {
         let mut cargo_args = Vec::with_capacity(4);
 
+        // Set the `--config profile.{profile}.{key}={value}` flags for the profile, filling in adhoc profile
+        cargo_args.extend(self.profile_args());
+
         // Add required profile flags. --release overrides any custom profiles.
         cargo_args.push("--profile".to_string());
         cargo_args.push(self.profile.to_string());
@@ -2834,54 +2826,6 @@ impl BuildRequest {
         })
     }
 
-    // The `opt-level=1` increases build times, but can noticeably decrease time
-    // between saving changes and being able to interact with an app (for wasm/web). The "overall"
-    // time difference (between having and not having the optimization) can be
-    // almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends
-    // on setup (hardware, OS, browser, idle load).
-    //
-    // Find or create the client and server profiles in the top-level Cargo.toml file
-    // todo(jon): we should/could make these optional by placing some defaults somewhere
-    pub(crate) fn initialize_profiles(&self) -> crate::Result<()> {
-        let config_path = self.workspace_dir().join("Cargo.toml");
-        let mut config = match std::fs::read_to_string(&config_path) {
-            Ok(config) => config.parse::<toml_edit::DocumentMut>().map_err(|e| {
-                crate::Error::Other(anyhow::anyhow!("Failed to parse Cargo.toml: {}", e))
-            })?,
-            Err(_) => Default::default(),
-        };
-
-        if let Item::Table(table) = config
-            .as_table_mut()
-            .entry("profile")
-            .or_insert(Item::Table(Default::default()))
-        {
-            if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_WASM) {
-                let mut client = toml_edit::Table::new();
-                client.insert("inherits", Item::Value("dev".into()));
-                client.insert("opt-level", Item::Value(1.into()));
-                entry.insert(Item::Table(client));
-            }
-
-            if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_SERVER) {
-                let mut server = toml_edit::Table::new();
-                server.insert("inherits", Item::Value("dev".into()));
-                entry.insert(Item::Table(server));
-            }
-
-            if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_ANDROID) {
-                let mut android = toml_edit::Table::new();
-                android.insert("inherits", Item::Value("dev".into()));
-                entry.insert(Item::Table(android));
-            }
-        }
-
-        std::fs::write(config_path, config.to_string())
-            .context("Failed to write profiles to Cargo.toml")?;
-
-        Ok(())
-    }
-
     /// Return the version of the wasm-bindgen crate if it exists
     fn wasm_bindgen_version(&self) -> Option<String> {
         self.workspace
@@ -3689,10 +3633,6 @@ impl BuildRequest {
     pub(crate) async fn verify_tooling(&self, ctx: &BuildContext) -> Result<()> {
         ctx.status_installing_tooling();
 
-        self
-            .initialize_profiles()
-            .context("Failed to initialize profiles - dioxus can't build without them. You might need to initialize them yourself.")?;
-
         match self.platform {
             Platform::Web => self.verify_web_tooling().await?,
             Platform::Ios => self.verify_ios_tooling().await?,
@@ -4193,4 +4133,67 @@ r#" <script>
             .or_else(|_| which::which("ranlib"))
             .ok()
     }
+
+    /// Assemble a series of `--config key=value` arguments for the build command.
+    ///
+    /// This adds adhoc profiles that dx uses to isolate builds from each other. Normally if you ran
+    /// `cargo build --feature desktop` and `cargo build --feature server`, then both binaries get
+    /// the same name and overwrite each other, causing thrashing and locking issues.
+    ///
+    /// By creating adhoc profiles, we can ensure that each build is isolated and doesn't interfere with each other.
+    ///
+    /// The user can also define custom profiles in their `Cargo.toml` file, which will be used instead
+    /// of the adhoc profiles.
+    ///
+    /// The names of the profiles are:
+    /// - web-dev
+    /// - web-release
+    /// - desktop-dev
+    /// - desktop-release
+    /// - server-dev
+    /// - server-release
+    /// - ios-dev
+    /// - ios-release
+    /// - android-dev
+    /// - android-release
+    /// - liveview-dev
+    /// - liveview-release
+    ///
+    /// Note how every platform gets its own profile, and each platform has a dev and release profile.
+    fn profile_args(&self) -> Vec<String> {
+        // If the user defined the profile in the Cargo.toml, we don't need to add it to our adhoc list
+        if self
+            .workspace
+            .cargo_toml
+            .profile
+            .custom
+            .contains_key(&self.profile)
+        {
+            return vec![];
+        }
+
+        // Otherwise, we need to add the profile arguments to make it adhoc
+        let mut args = Vec::new();
+
+        let profile = self.profile.as_str();
+        let inherits = if self.release { "release" } else { "dev" };
+
+        // Add the profile definition first.
+        args.push(format!(r#"profile.{profile}.inherits="{inherits}""#));
+
+        // 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) {
+            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()),
+            }
+        }
+
+        // Prepend --config to each argument
+        args.into_iter()
+            .flat_map(|arg| ["--config".to_string(), arg])
+            .collect()
+    }
 }

+ 17 - 0
packages/cli/src/platform.rs

@@ -185,4 +185,21 @@ impl Platform {
             _ => None,
         }
     }
+
+    pub(crate) fn profile_name(&self, release: bool) -> String {
+        let base_profile = match self {
+            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)
+        }
+    }
 }

+ 3 - 1
packages/cli/src/workspace.rs

@@ -107,7 +107,9 @@ impl Workspace {
     }
 
     pub fn is_release_profile(&self, profile: &str) -> bool {
-        if profile == "release" {
+        // If the profile is "release" or ends with "-release" like the default platform release profiles,
+        // always put it in the release category.
+        if profile == "release" || profile.ends_with("-release") {
             return true;
         }
 

+ 1 - 1
packages/fullstack/Cargo.toml

@@ -19,7 +19,7 @@ dioxus_server_macro = { workspace = true }
 axum = { workspace = true, optional = true }
 tower-http = { workspace = true, optional = true, features = ["fs"] }
 
-dioxus-lib = { workspace = true }
+dioxus-lib = { workspace = true, features = ["html"] }
 generational-box = { workspace = true }
 
 # Dioxus + SSR