Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New default compilation profiles for web commands #199

Merged
merged 18 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,16 @@ actix-web = "4.9.0"
# Opening the app in the browser
webbrowser = "1.0.2"

# Parsing the Cargo manifest
toml_edit = "0.22.22"

# Copying directories
fs_extra = "1.3.0"

# Optimizing Wasm binaries
wasm-opt = { version = "0.116.1", optional = true }

[build-dependencies]
# We don't use `cc` directly, but our dependency `wasm-opt-sys` fails to compile on Windows when using a newer version.
# This can be removed when https://github.com/rust-lang/cc-rs/issues/1324 is fixed.
cc = "=1.2.2"
2 changes: 1 addition & 1 deletion src/build/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl BuildArgs {

/// The profile used to compile the app.
pub(crate) fn profile(&self) -> &str {
self.cargo_args.compilation_args.profile()
self.cargo_args.compilation_args.profile(self.is_web())
}

/// The targeted platform.
Expand Down
19 changes: 12 additions & 7 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,23 @@ use args::BuildSubcommands;
use crate::{
external_cli::{cargo, rustup, wasm_bindgen, CommandHelpers},
run::select_run_binary,
web::bundle::{create_web_bundle, PackedBundle, WebBundle},
web::{
bundle::{create_web_bundle, PackedBundle, WebBundle},
profiles::configure_default_web_profiles,
},
};

pub use self::args::BuildArgs;

mod args;

pub fn build(args: &BuildArgs) -> anyhow::Result<()> {
let cargo_args = args.cargo_args_builder();
let mut cargo_args = args.cargo_args_builder();

if let Some(BuildSubcommands::Web(web_args)) = &args.subcommand {
ensure_web_setup(args.skip_prompts)?;

let metadata = cargo::metadata::metadata_with_args(["--no-deps"])?;

println!("Compiling to WebAssembly...");
cargo::build::command().args(cargo_args).ensure_status()?;

println!("Bundling JavaScript bindings...");
let bin_target = select_run_binary(
&metadata,
args.cargo_args.package_args.package.as_deref(),
Expand All @@ -30,6 +28,13 @@ pub fn build(args: &BuildArgs) -> anyhow::Result<()> {
args.target().as_deref(),
args.profile(),
)?;

cargo_args = cargo_args.append(configure_default_web_profiles(&metadata)?);

println!("Compiling to WebAssembly...");
cargo::build::command().args(cargo_args).ensure_status()?;

println!("Bundling JavaScript bindings...");
wasm_bindgen::bundle(&bin_target)?;

#[cfg(feature = "wasm-opt")]
Expand Down
7 changes: 4 additions & 3 deletions src/external_cli/cargo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ pub struct Metadata {
/// List of members of the workspace.
///
/// Each entry is the Package ID for the package.
pub workspace_members: Option<Vec<String>>,
pub workspace_members: Vec<String>,
/// List of default members of the workspace.
///
/// Each entry is the Package ID for the package.
pub workspace_default_members: Option<Vec<String>>,
pub workspace_default_members: Vec<String>,
/// The absolute path to the build directory where Cargo places its output.
pub target_directory: PathBuf,
/// The absolute path to the root of the workspace.
pub workspace_root: Option<PathBuf>,
/// This will be the root of the package if no workspace is used.
pub workspace_root: PathBuf,
}

#[derive(Debug, Deserialize)]
Expand Down
17 changes: 11 additions & 6 deletions src/external_cli/cargo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,17 @@ impl CargoCompilationArgs {
/// The profile used to compile the app.
///
/// This is determined by the `--release` and `--profile` arguments.
pub(crate) fn profile(&self) -> &str {
if self.is_release {
"release"
} else if let Some(profile) = &self.profile {
pub(crate) fn profile(&self, is_web: bool) -> &str {
if let Some(profile) = &self.profile {
profile
} else if is_web {
if self.is_release {
"web-release"
} else {
"web"
}
} else if self.is_release {
"release"
} else {
"debug"
}
Expand All @@ -97,8 +103,7 @@ impl CargoCompilationArgs {
};

ArgBuilder::new()
.add_flag_if("--release", self.is_release)
.add_opt_value("--profile", &self.profile)
.add_with_value("--profile", self.profile(is_web))
.add_opt_value("--jobs", &self.jobs.map(|jobs| jobs.to_string()))
.add_flag_if("--keep-going", self.is_keep_going)
.add_opt_value("--target", &target)
Expand Down
2 changes: 1 addition & 1 deletion src/run/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl RunArgs {

/// The profile used to compile the app.
pub(crate) fn profile(&self) -> &str {
self.cargo_args.compilation_args.profile()
self.cargo_args.compilation_args.profile(self.is_web())
}

/// The targeted platform.
Expand Down
21 changes: 13 additions & 8 deletions src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
cargo::{self, metadata::Metadata},
wasm_bindgen, CommandHelpers,
},
web::bundle::{create_web_bundle, PackedBundle, WebBundle},
web::{
bundle::{create_web_bundle, PackedBundle, WebBundle},
profiles::configure_default_web_profiles,
},
};

pub use self::args::RunArgs;
Expand All @@ -18,18 +21,12 @@ mod args;
mod serve;

pub fn run(args: &RunArgs) -> anyhow::Result<()> {
let cargo_args = args.cargo_args_builder();
let mut cargo_args = args.cargo_args_builder();

if let Some(RunSubcommands::Web(web_args)) = &args.subcommand {
ensure_web_setup(args.skip_prompts)?;

let metadata = cargo::metadata::metadata_with_args(["--no-deps"])?;

// If targeting the web, run a web server with the WASM build
println!("Compiling to WebAssembly...");
cargo::build::command().args(cargo_args).ensure_status()?;

println!("Bundling JavaScript bindings...");
let bin_target = select_run_binary(
&metadata,
args.cargo_args.package_args.package.as_deref(),
Expand All @@ -38,6 +35,14 @@ pub fn run(args: &RunArgs) -> anyhow::Result<()> {
args.target().as_deref(),
args.profile(),
)?;

cargo_args = cargo_args.append(configure_default_web_profiles(&metadata)?);

// If targeting the web, run a web server with the WASM build
println!("Compiling to WebAssembly...");
cargo::build::command().args(cargo_args).ensure_status()?;

println!("Bundling JavaScript bindings...");
wasm_bindgen::bundle(&bin_target)?;

#[cfg(feature = "wasm-opt")]
Expand Down
1 change: 1 addition & 0 deletions src/web/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Utilities for building and running the app in the browser.

pub(crate) mod bundle;
pub(crate) mod profiles;
#[cfg(feature = "wasm-opt")]
pub(crate) mod wasm_opt;
104 changes: 104 additions & 0 deletions src/web/profiles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::{collections::HashMap, fs};

use anyhow::Context as _;
use toml_edit::DocumentMut;

use crate::external_cli::{arg_builder::ArgBuilder, cargo::metadata::Metadata};

/// Create `--config` args to configure the default profiles to use when compiling for the web.
pub(crate) fn configure_default_web_profiles(metadata: &Metadata) -> anyhow::Result<ArgBuilder> {
let manifest = fs::read_to_string(metadata.workspace_root.join("Cargo.toml"))
.context("failed to read workspace manifest")?
.parse::<DocumentMut>()
.context("failed to parse workspace manifest")?;

let mut args = ArgBuilder::new();

if !is_profile_defined_in_manifest(&manifest, "web") {
args = args.append(configure_web_profile());
}

if !is_profile_defined_in_manifest(&manifest, "web-release") {
args = args.append(configure_web_release_profile());
}

Ok(args)
}

fn is_profile_defined_in_manifest(manifest: &DocumentMut, profile: &str) -> bool {
manifest
.get("profile")
.is_some_and(|profiles| profiles.get(profile).is_some())
}

/// Configure the default profile for web debug builds.
///
/// It is optimized for fast iteration speeds.
fn configure_web_profile() -> ArgBuilder {
configure_profile("web", "dev", HashMap::new())
}

/// Configure the default profile for web release builds.
///
/// It is optimized both for run time performance and loading times.
fn configure_web_release_profile() -> ArgBuilder {
let config = HashMap::from_iter([
// Optimize for size, greatly reducing loading times
("opt-level", "s"),
// Remove debug information, reducing file size further
("strip", "debuginfo"),
]);
configure_profile("web-release", "release", config)
}
BD103 marked this conversation as resolved.
Show resolved Hide resolved

/// Create `--config` args for `cargo` to configure a new compilation profile.
///
/// Equivalent to a `Cargo.toml` like this:
///
/// ```toml
/// [profile.{profile}]
/// inherits = "{inherits}"
/// # config
/// key = "value"
/// ```
fn configure_profile(profile: &str, inherits: &str, config: HashMap<&str, &str>) -> ArgBuilder {
let mut args = ArgBuilder::new().add_with_value(
"--config",
format!(r#"profile.{profile}.inherits="{inherits}""#),
);

for (key, value) in config {
args = args.add_with_value("--config", format!(r#"profile.{profile}.{key}="{value}""#));
}

args
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn should_detect_defined_profile() {
let manifest = r#"
[profile.web]
inherits = "dev"
"#
.parse()
.unwrap();

assert!(is_profile_defined_in_manifest(&manifest, "web"));
}

#[test]
fn should_detect_missing_profile() {
let manifest = r#"
[profile.foo]
inherits = "dev"
"#
.parse()
.unwrap();

assert!(!is_profile_defined_in_manifest(&manifest, "web"));
}
}
Loading