wasm_pack/build/
mod.rs

1//! Building a Rust crate into a `.wasm` binary.
2
3use crate::child;
4use crate::command::build::BuildProfile;
5use crate::emoji;
6use crate::manifest::Crate;
7use crate::PBAR;
8use anyhow::{anyhow, bail, Context, Result};
9use std::path::Path;
10use std::process::Command;
11use std::str;
12
13pub mod wasm_target;
14
15/// Used when comparing the currently installed
16/// wasm-pack version with the latest on crates.io.
17pub struct WasmPackVersion {
18    /// The currently installed wasm-pack version.
19    pub local: String,
20    /// The latest version of wasm-pack that's released at
21    /// crates.io.
22    pub latest: String,
23}
24
25/// Ensure that `rustc` is present and that it is >= 1.30.0
26pub fn check_rustc_version() -> Result<String> {
27    let local_minor_version = rustc_minor_version();
28    match local_minor_version {
29        Some(mv) => {
30            if mv < 30 {
31                bail!(
32                    "Your version of Rust, '1.{}', is not supported. Please install Rust version 1.30.0 or higher.",
33                    mv.to_string()
34                )
35            } else {
36                Ok(mv.to_string())
37            }
38        }
39        None => bail!("We can't figure out what your Rust version is- which means you might not have Rust installed. Please install Rust version 1.30.0 or higher."),
40    }
41}
42
43// from https://github.com/alexcrichton/proc-macro2/blob/79e40a113b51836f33214c6d00228934b41bd4ad/build.rs#L44-L61
44fn rustc_minor_version() -> Option<u32> {
45    macro_rules! otry {
46        ($e:expr) => {
47            match $e {
48                Some(e) => e,
49                None => return None,
50            }
51        };
52    }
53    let output = otry!(Command::new("rustc").arg("--version").output().ok());
54    let version = otry!(str::from_utf8(&output.stdout).ok());
55    let mut pieces = version.split('.');
56    if pieces.next() != Some("rustc 1") {
57        return None;
58    }
59    otry!(pieces.next()).parse().ok()
60}
61
62/// Checks and returns local and latest versions of wasm-pack
63pub fn check_wasm_pack_versions() -> Result<WasmPackVersion> {
64    match wasm_pack_local_version() {
65        Some(local) => Ok(WasmPackVersion {local, latest: Crate::return_wasm_pack_latest_version()?.unwrap_or_else(|| "".to_string())}),
66        None => bail!("We can't figure out what your wasm-pack version is, make sure the installation path is correct.")
67    }
68}
69
70fn wasm_pack_local_version() -> Option<String> {
71    let output = env!("CARGO_PKG_VERSION");
72    Some(output.to_string())
73}
74
75/// Run `cargo build` targetting `wasm32-unknown-unknown`.
76pub fn cargo_build_wasm(
77    path: &Path,
78    profile: BuildProfile,
79    extra_options: &[String],
80) -> Result<()> {
81    let msg = format!("{}Compiling to Wasm...", emoji::CYCLONE);
82    PBAR.info(&msg);
83
84    let mut cmd = Command::new("cargo");
85    cmd.current_dir(path).arg("build").arg("--lib");
86
87    if PBAR.quiet() {
88        cmd.arg("--quiet");
89    }
90
91    match profile {
92        BuildProfile::Profiling => {
93            // Once there are DWARF debug info consumers, force enable debug
94            // info, because builds that use the release cargo profile disables
95            // debug info.
96            //
97            // cmd.env("RUSTFLAGS", "-g");
98            cmd.arg("--release");
99        }
100        BuildProfile::Release => {
101            cmd.arg("--release");
102        }
103        BuildProfile::Dev => {
104            // Plain cargo builds use the dev cargo profile, which includes
105            // debug info by default.
106        }
107    }
108
109    cmd.arg("--target").arg("wasm32-unknown-unknown");
110
111    // The `cargo` command is executed inside the directory at `path`, so relative paths set via extra options won't work.
112    // To remedy the situation, all detected paths are converted to absolute paths.
113    let mut handle_path = false;
114    let extra_options_with_absolute_paths = extra_options
115        .iter()
116        .map(|option| -> Result<String> {
117            let value = if handle_path && Path::new(option).is_relative() {
118                std::env::current_dir()?
119                    .join(option)
120                    .to_str()
121                    .ok_or_else(|| anyhow!("path contains non-UTF-8 characters"))?
122                    .to_string()
123            } else {
124                option.to_string()
125            };
126            handle_path = matches!(&**option, "--target-dir" | "--out-dir" | "--manifest-path");
127            Ok(value)
128        })
129        .collect::<Result<Vec<_>>>()?;
130    cmd.args(extra_options_with_absolute_paths);
131
132    child::run(cmd, "cargo build").context("Compiling your crate to WebAssembly failed")?;
133    Ok(())
134}
135
136/// Runs `cargo build --tests` targeting `wasm32-unknown-unknown`.
137///
138/// This generates the `Cargo.lock` file that we use in order to know which version of
139/// wasm-bindgen-cli to use when running tests.
140///
141/// Note that the command to build tests and the command to run tests must use the same parameters, i.e. features to be
142/// disabled / enabled must be consistent for both `cargo build` and `cargo test`.
143///
144/// # Parameters
145///
146/// * `path`: Path to the crate directory to build tests.
147/// * `debug`: Whether to build tests in `debug` mode.
148/// * `extra_options`: Additional parameters to pass to `cargo` when building tests.
149pub fn cargo_build_wasm_tests(path: &Path, debug: bool, extra_options: &[String]) -> Result<()> {
150    let mut cmd = Command::new("cargo");
151
152    cmd.current_dir(path).arg("build").arg("--tests");
153
154    if PBAR.quiet() {
155        cmd.arg("--quiet");
156    }
157
158    if !debug {
159        cmd.arg("--release");
160    }
161
162    cmd.arg("--target").arg("wasm32-unknown-unknown");
163
164    cmd.args(extra_options);
165
166    child::run(cmd, "cargo build").context("Compilation of your program failed")?;
167    Ok(())
168}