diff --git a/src/bindgen.rs b/src/bindgen.rs index acf369fe..b8c4634c 100644 --- a/src/bindgen.rs +++ b/src/bindgen.rs @@ -21,30 +21,10 @@ pub fn wasm_bindgen_build( reference_types: bool, target: Target, profile: BuildProfile, - extra_options: &Vec, + wasm_path: &Path, ) -> Result<()> { - let release_or_debug = match profile { - BuildProfile::Release | BuildProfile::Profiling => "release", - BuildProfile::Dev => "debug", - }; - let out_dir = out_dir.to_str().unwrap(); - let target_directory = { - let mut has_target_dir_iter = extra_options.iter(); - has_target_dir_iter - .find(|&it| it == "--target-dir") - .and_then(|_| has_target_dir_iter.next()) - .map(Path::new) - .unwrap_or(data.target_directory()) - }; - - let wasm_path = target_directory - .join("wasm32-unknown-unknown") - .join(release_or_debug) - .join(data.crate_name()) - .with_extension("wasm"); - let dts_arg = if disable_dts { "--no-typescript" } else { diff --git a/src/build/mod.rs b/src/build/mod.rs index d657071e..22773944 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -6,8 +6,10 @@ use crate::emoji; use crate::manifest::Crate; use crate::PBAR; use anyhow::{anyhow, bail, Context, Result}; -use std::path::Path; -use std::process::Command; +use cargo_metadata::Message; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; use std::str; pub mod wasm_target; @@ -77,12 +79,16 @@ pub fn cargo_build_wasm( path: &Path, profile: BuildProfile, extra_options: &[String], -) -> Result<()> { +) -> Result { let msg = format!("{}Compiling to Wasm...", emoji::CYCLONE); PBAR.info(&msg); let mut cmd = Command::new("cargo"); - cmd.current_dir(path).arg("build").arg("--lib"); + cmd.current_dir(path) + .stdout(Stdio::piped()) + .arg("build") + .arg("--lib") + .arg("--message-format=json-render-diagnostics"); if PBAR.quiet() { cmd.arg("--quiet"); @@ -129,8 +135,36 @@ pub fn cargo_build_wasm( .collect::>>()?; cmd.args(extra_options_with_absolute_paths); - child::run(cmd, "cargo build").context("Compiling your crate to WebAssembly failed")?; - Ok(()) + let mut last_artifact = None; + + child::run_with_handler(cmd, "cargo build", |child| { + let stdout = child.stdout.take().unwrap(); + let reader = BufReader::new(stdout); + + for message in cargo_metadata::Message::parse_stream(reader) { + match message? { + Message::CompilerArtifact(artifact) => last_artifact = Some(artifact), + Message::CompilerMessage(msg) => { + if let Some(rendered) = msg.message.rendered { + println!("{}", rendered); + } + } + _ => (), + } + } + + Ok(()) + }) + .context("Compiling your crate to WebAssembly failed")?; + + let last_artifact = last_artifact + .ok_or_else(|| anyhow!("No artifacts were generated by cargo build"))? + .filenames + .into_iter() + .next() + .ok_or_else(|| anyhow!("No artifact filenames were generated by cargo build"))?; + + Ok(PathBuf::from(last_artifact)) } /// Runs `cargo build --tests` targeting `wasm32-unknown-unknown`. diff --git a/src/child.rs b/src/child.rs index 52249ac3..b6f29a1e 100644 --- a/src/child.rs +++ b/src/child.rs @@ -6,7 +6,7 @@ use crate::install::Tool; use anyhow::{bail, Result}; use log::info; -use std::process::{Command, Stdio}; +use std::process::{Child, Command, Stdio}; /// Return a new Command object pub fn new_command(program: &str) -> Command { @@ -62,3 +62,29 @@ pub fn run_capture_stdout(mut command: Command, command_name: &Tool) -> Result( + mut command: Command, + command_name: &str, + handle: impl FnOnce(&mut Child) -> Result, +) -> Result { + info!("Running {:?}", command); + + let mut child = command.spawn()?; + + let ret = handle(&mut child)?; + + let status = child.wait()?; + + if status.success() { + Ok(ret) + } else { + bail!( + "failed to execute `{}`: exited with {}\n full command: {:?}", + command_name, + status, + command, + ) + } +} diff --git a/src/command/build.rs b/src/command/build.rs index 9960c96c..d5cf259e 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -203,7 +203,13 @@ impl Default for BuildOptions { } } -type BuildStep = fn(&mut Build) -> Result<()>; +type BuildStep = fn(&mut Build, _state: &mut State) -> Result<()>; + +#[derive(Default)] +struct State { + // step state + cargo_artifact: Option, +} impl Build { /// Construct a build command from the given options. @@ -260,9 +266,10 @@ impl Build { let process_steps = Build::get_process_steps(self.mode, self.no_pack, self.no_opt); let started = Instant::now(); + let mut state = State::default(); for (_, process_step) in process_steps { - process_step(self)?; + process_step(self, &mut state)?; } let duration = crate::command::utils::elapsed(started.elapsed()); @@ -331,7 +338,7 @@ impl Build { steps } - fn step_check_rustc_version(&mut self) -> Result<()> { + fn step_check_rustc_version(&mut self, _state: &mut State) -> Result<()> { info!("Checking rustc version..."); let version = build::check_rustc_version()?; let msg = format!("rustc version is {}.", version); @@ -339,43 +346,40 @@ impl Build { Ok(()) } - fn step_check_crate_config(&mut self) -> Result<()> { + fn step_check_crate_config(&mut self, _state: &mut State) -> Result<()> { info!("Checking crate configuration..."); self.crate_data.check_crate_config()?; info!("Crate is correctly configured."); Ok(()) } - fn step_check_for_wasm_target(&mut self) -> Result<()> { + fn step_check_for_wasm_target(&mut self, _state: &mut State) -> Result<()> { info!("Checking for wasm-target..."); build::wasm_target::check_for_wasm32_target()?; info!("Checking for wasm-target was successful."); Ok(()) } - fn step_build_wasm(&mut self) -> Result<()> { + fn step_build_wasm(&mut self, state: &mut State) -> Result<()> { info!("Building wasm..."); - build::cargo_build_wasm(&self.crate_path, self.profile, &self.extra_options)?; + let cargo_artifact = + build::cargo_build_wasm(&self.crate_path, self.profile, &self.extra_options)?; + + info!("wasm built at {:#?}.", cargo_artifact); + + state.cargo_artifact = Some(cargo_artifact); - info!( - "wasm built at {:#?}.", - &self - .crate_path - .join("target") - .join("wasm32-unknown-unknown") - .join("release") - ); Ok(()) } - fn step_create_dir(&mut self) -> Result<()> { + fn step_create_dir(&mut self, _state: &mut State) -> Result<()> { info!("Creating a pkg directory..."); create_pkg_dir(&self.out_dir)?; info!("Created a pkg directory at {:#?}.", &self.crate_path); Ok(()) } - fn step_create_json(&mut self) -> Result<()> { + fn step_create_json(&mut self, _state: &mut State) -> Result<()> { self.crate_data.write_package_json( &self.out_dir, &self.scope, @@ -389,21 +393,21 @@ impl Build { Ok(()) } - fn step_copy_readme(&mut self) -> Result<()> { + fn step_copy_readme(&mut self, _state: &mut State) -> Result<()> { info!("Copying readme from crate..."); readme::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?; info!("Copied readme from crate to {:#?}.", &self.out_dir); Ok(()) } - fn step_copy_license(&mut self) -> Result<()> { + fn step_copy_license(&mut self, _state: &mut State) -> Result<()> { info!("Copying license from crate..."); license::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?; info!("Copied license from crate to {:#?}.", &self.out_dir); Ok(()) } - fn step_install_wasm_bindgen(&mut self) -> Result<()> { + fn step_install_wasm_bindgen(&mut self, _state: &mut State) -> Result<()> { info!("Identifying wasm-bindgen dependency..."); let lockfile = Lockfile::new(&self.crate_data)?; let bindgen_version = lockfile.require_wasm_bindgen()?; @@ -419,7 +423,7 @@ impl Build { Ok(()) } - fn step_run_wasm_bindgen(&mut self) -> Result<()> { + fn step_run_wasm_bindgen(&mut self, state: &mut State) -> Result<()> { info!("Building the wasm bindings..."); bindgen::wasm_bindgen_build( &self.crate_data, @@ -431,13 +435,16 @@ impl Build { self.reference_types, self.target, self.profile, - &self.extra_options, + state + .cargo_artifact + .as_ref() + .expect("bindgen ran before cargo build"), )?; info!("wasm bindings were built at {:#?}.", &self.out_dir); Ok(()) } - fn step_run_wasm_opt(&mut self) -> Result<()> { + fn step_run_wasm_opt(&mut self, _state: &mut State) -> Result<()> { let mut args = match self .crate_data .configured_profile(self.profile)