Skip to content

Commit

Permalink
Rework binary detection (#169)
Browse files Browse the repository at this point in the history
# Objective

Rework the detection of binaries again, aligning it closer to how Cargo
works and enabling several new features / fixing several bugs.

- Support multiple binaries in a single package
- Support running examples (closes #69)
- Fix detection of `target` folder location (closes #145)

With this PR, we can now just run `bevy run --example breakout web` in
the Bevy repo and it works without any additional configuration!

# Solution

The code previously made the wrong assumption that we want to run
packages with a binary target. Instead, we want to run the binary
targets itself. A package can even have multiple of them.
Additionally, examples need to be considered as binary targets. The
`--example` argument now needs to be factored into the detection
algorithm and the different path for examples must be considered in the
bundling step.

## Note for Reviewers

The crux of the changes is contained in `run/mod.rs` in the
`select_run_binary` function.
This implements the new algorithm to select the correct binary to run.

In `serve.rs`, the `index.html` file now needs to be generated
dynamically, in order to pick the path to the correct binary. Because
the `index.html` needs to be a _static_ string, it needs to be leaked to
create a static reference.
  • Loading branch information
TimJentzsch authored Dec 3, 2024
1 parent dbfb6fc commit 4439e83
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 125 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ serde_json = "1.0.128"
reqwest = { features = ["blocking", "json"], version = "0.12.7" }
regex = "1.10.6"

# Understanding Cargo.toml
toml_edit = { version = "0.22.21", default-features = false, features = [
"parse",
] }

# Understanding package versions
semver = { version = "1.0.23", features = ["serde"] }

Expand Down
5 changes: 5 additions & 0 deletions src/build/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ impl BuildArgs {
self.cargo_args.compilation_args.profile()
}

/// The targeted platform.
pub(crate) fn target(&self) -> Option<String> {
self.cargo_args.compilation_args.target(self.is_web())
}

/// Generate arguments to forward to `cargo build`.
pub(crate) fn cargo_args_builder(&self) -> ArgBuilder {
self.cargo_args.args_builder(self.is_web())
Expand Down
14 changes: 12 additions & 2 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
external_cli::{cargo, rustup, wasm_bindgen, CommandHelpers},
manifest::package_name,
run::select_run_binary,
};

pub use self::args::BuildArgs;
Expand All @@ -13,11 +13,21 @@ pub fn build(args: &BuildArgs) -> anyhow::Result<()> {
if args.is_web() {
ensure_web_setup()?;

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...");
wasm_bindgen::bundle(&package_name()?, args.profile())?;
let bin_target = select_run_binary(
&metadata,
args.cargo_args.package_args.package.as_deref(),
args.cargo_args.target_args.bin.as_deref(),
args.cargo_args.target_args.example.as_deref(),
args.target().as_deref(),
args.profile(),
)?;
wasm_bindgen::bundle(&bin_target)?;
} else {
cargo::build::command().args(cargo_args).ensure_status()?;
}
Expand Down
14 changes: 14 additions & 0 deletions src/external_cli/cargo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ impl Package {
.any(|target_kind| *target_kind == TargetKind::Bin)
})
}

/// An iterator over all binary targets contained in this package.
pub fn bin_targets(&self) -> impl Iterator<Item = &Target> {
self.targets
.iter()
.filter(|target| target.kind.iter().any(|kind| *kind == TargetKind::Bin))
}

/// An iterator over all example targets contained in this package.
pub fn example_targets(&self) -> impl Iterator<Item = &Target> {
self.targets
.iter()
.filter(|target| target.kind.iter().any(|kind| *kind == TargetKind::Example))
}
}

#[derive(Debug, Deserialize)]
Expand Down
8 changes: 8 additions & 0 deletions src/external_cli/cargo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ impl CargoCompilationArgs {
}
}

pub(crate) fn target(&self, is_web: bool) -> Option<String> {
if is_web {
Some("wasm32-unknown-unknown".to_string())
} else {
self.target.clone()
}
}

pub(crate) fn args_builder(&self, is_web: bool) -> ArgBuilder {
// web takes precedence over --target <TRIPLE>
let target = if is_web {
Expand Down
37 changes: 13 additions & 24 deletions src/external_cli/wasm_bindgen.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
use std::{path::Path, process::Command};
use std::process::Command;

use crate::{external_cli::CommandHelpers, run::BinTarget};

use super::arg_builder::ArgBuilder;

pub(crate) const PACKAGE: &str = "wasm-bindgen-cli";
pub(crate) const PROGRAM: &str = "wasm-bindgen";

/// Determine the path to the folder where the Wasm build artifacts are stored.
pub(crate) fn get_target_folder(profile: &str) -> String {
format!("target/wasm32-unknown-unknown/{profile}")
}

/// Bundle the Wasm build for the web.
pub(crate) fn bundle(package_name: &str, profile: &str) -> anyhow::Result<()> {
let target_folder = get_target_folder(profile);
pub(crate) fn bundle(bin_target: &BinTarget) -> anyhow::Result<()> {
let original_wasm = bin_target
.artifact_directory
.clone()
.join(format!("{}.wasm", bin_target.bin_name));

let status = Command::new(PROGRAM)
Command::new(PROGRAM)
.args(
ArgBuilder::new()
.arg("--no-typescript")
.add_with_value("--out-name", "bevy_app")
.add_with_value("--out-dir", &target_folder)
.add_with_value("--out-name", &bin_target.bin_name)
.add_with_value("--out-dir", bin_target.artifact_directory.to_string_lossy())
.add_with_value("--target", "web")
.arg(format!("{target_folder}/{package_name}.wasm")),
.arg(original_wasm.to_string_lossy()),
)
.status()?;
.ensure_status()?;

anyhow::ensure!(status.success(), "Failed to bundle project for the web.");
Ok(())
}

/// Determine if a file path in the target folder is an artifact generated by wasm-bindgen.
pub(crate) fn is_bindgen_artifact(path: &Path) -> bool {
// The JS interface wrapping the WASM binary
let js_path = Path::new("bevy_app.js");
// The WASM bindgen
let wasm_path = Path::new("bevy_app_bg.wasm");

path == js_path || path == wasm_path
}
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
pub mod build;
pub mod external_cli;
pub mod lint;
pub mod manifest;
pub mod run;
pub mod template;
24 changes: 0 additions & 24 deletions src/manifest.rs

This file was deleted.

5 changes: 5 additions & 0 deletions src/run/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ impl RunArgs {
self.cargo_args.compilation_args.profile()
}

/// The targeted platform.
pub(crate) fn target(&self) -> Option<String> {
self.cargo_args.compilation_args.target(self.is_web())
}

/// Generate arguments for `cargo`.
pub(crate) fn cargo_args_builder(&self) -> ArgBuilder {
self.cargo_args.args_builder(self.is_web())
Expand Down
Loading

0 comments on commit 4439e83

Please sign in to comment.