From d6034dd4d1840ea80d85ddc9f854a569012639e2 Mon Sep 17 00:00:00 2001 From: DAA <42379074+DaAlbrecht@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:42:12 +0100 Subject: [PATCH] Install the exact `wasm-bindgen-cli` version matching the `wasm-bindgen` version (#208) Closes #202 --- src/build/mod.rs | 12 ++++++ src/external_cli/cargo/install.rs | 68 +++++++++++++++++++++++-------- src/external_cli/wasm_bindgen.rs | 19 ++++++++- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/build/mod.rs b/src/build/mod.rs index 3213a2c..83e93ba 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -52,12 +52,24 @@ pub fn build(args: &BuildArgs) -> anyhow::Result<()> { } pub(crate) fn ensure_web_setup(skip_prompts: bool) -> anyhow::Result<()> { + // The resolved dependency graph is needed to ensure the `wasm-bindgen-cli` version matches + // exactly the `wasm-bindgen` version + let metadata = cargo::metadata::metadata()?; + + let wasm_bindgen_version = metadata + .packages + .iter() + .find(|package| package.name == "wasm-bindgen") + .map(|package| package.version.to_string()) + .ok_or_else(|| anyhow::anyhow!("Failed to find wasm-bindgen"))?; + // `wasm32-unknown-unknown` compilation target rustup::install_target_if_needed("wasm32-unknown-unknown", skip_prompts)?; // `wasm-bindgen-cli` for bundling cargo::install::if_needed( wasm_bindgen::PROGRAM, wasm_bindgen::PACKAGE, + Some(&wasm_bindgen_version), skip_prompts, false, )?; diff --git a/src/external_cli/cargo/install.rs b/src/external_cli/cargo/install.rs index 4edea6b..b0849f2 100644 --- a/src/external_cli/cargo/install.rs +++ b/src/external_cli/cargo/install.rs @@ -1,18 +1,24 @@ -use std::process::{exit, Command}; +use std::{ + process::{exit, Command}, + str::FromStr, +}; use dialoguer::Confirm; +use semver::Version; + +use crate::external_cli::wasm_bindgen; + +use self::wasm_bindgen::wasm_bindgen_cli_version; /// Check if the given program is installed on the system. /// /// This assumes that the program offers a `--version` flag. -fn is_installed(program: &str) -> bool { - let output = Command::new(program).arg("--version").output(); - - if let Ok(output) = output { - output.status.success() - } else { - false - } +fn is_installed(program: &str) -> Option> { + Command::new(program) + .arg("--version") + .output() + .map(|output| output.stdout) + .ok() } /// Checks if the program is installed and installs it if it isn't. @@ -21,19 +27,43 @@ fn is_installed(program: &str) -> bool { pub(crate) fn if_needed( program: &str, package: &str, + package_version: Option<&str>, skip_prompts: bool, hidden: bool, ) -> anyhow::Result { - if is_installed(program) { - return Ok(false); + let mut prompt: Option = None; + + if let Some(stdout) = is_installed(program) { + let Some(package_version) = package_version else { + // If no `package_version` is specified and the program is installed, + // there is nothing to do. + return Ok(false); + }; + + // Its important that the `wasm-bindgen-cli` and the `wasm-bindgen` version match exactly, + // therefore compare the desired `package_version` with the installed + // `wasm-bindgen-cli` version + if package == wasm_bindgen::PACKAGE { + let version = wasm_bindgen_cli_version(&stdout)?; + let desired_version = Version::from_str(package_version)?; + if version == desired_version { + return Ok(false); + } + prompt = Some(format!( + "`{program}:{version}` is installed, but \ + version `{desired_version}` is required. Install and replace?" + )); + } } // Abort if the user doesn't want to install it if !skip_prompts && !Confirm::new() - .with_prompt(format!( - "`{program}` is missing, should I install it for you?" - )) + .with_prompt( + prompt.unwrap_or_else(|| { + format!("`{program}` is missing, should I install it for you?") + }), + ) .interact()? { exit(1); @@ -42,15 +72,19 @@ pub(crate) fn if_needed( let mut cmd = Command::new(super::program()); cmd.arg("install").arg(package); + if let Some(version) = package_version { + cmd.arg("--version").arg(version); + } + let status = if hidden { cmd.output()?.status } else { cmd.status()? }; - if !status.success() { - Err(anyhow::anyhow!("Failed to install `{program}`.")) - } else { + if status.success() { Ok(true) + } else { + Err(anyhow::anyhow!("Failed to install `{program}`.")) } } diff --git a/src/external_cli/wasm_bindgen.rs b/src/external_cli/wasm_bindgen.rs index 088bbbd..2c99c24 100644 --- a/src/external_cli/wasm_bindgen.rs +++ b/src/external_cli/wasm_bindgen.rs @@ -1,4 +1,5 @@ -use std::process::Command; +use semver::Version; +use std::{process::Command, str::FromStr}; use crate::{external_cli::CommandHelpers, run::BinTarget}; @@ -27,3 +28,19 @@ pub(crate) fn bundle(bin_target: &BinTarget) -> anyhow::Result<()> { Ok(()) } + +/// Transforms the output from `wasm-bindgen --version` into a [Version]. +pub(crate) fn wasm_bindgen_cli_version(stdout: &[u8]) -> anyhow::Result { + let stdout = String::from_utf8_lossy(stdout); + // Example stdout from `wasm-bindgen --version`: wasm-bindgen 0.2.99 + stdout + .split_whitespace() + .nth(1) + .ok_or_else(|| { + anyhow::anyhow!( + "unexpected output format: {}, expected format to be: `wasm-bindgen `", + stdout + ) + }) + .and_then(|version| Version::from_str(version).map_err(|e| anyhow::anyhow!(e))) +}