use binary_install::{Cache, Download};
use child;
use command::build::{BuildProfile, Target};
use emoji;
use failure::{self, ResultExt};
use log::debug;
use log::{info, warn};
use manifest::CrateData;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use target;
use which::which;
use PBAR;
pub fn install_wasm_bindgen(
cache: &Cache,
version: &str,
install_permitted: bool,
) -> Result<Download, failure::Error> {
if let Ok(path) = which("wasm-bindgen") {
debug!("found global wasm-bindgen binary at: {}", path.display());
if wasm_bindgen_version_check(&path, version) {
return Ok(Download::at(path.parent().unwrap()));
}
}
let msg = format!("{}Installing wasm-bindgen...", emoji::DOWN_ARROW);
PBAR.info(&msg);
let dl = download_prebuilt_wasm_bindgen(&cache, version, install_permitted);
match dl {
Ok(dl) => return Ok(dl),
Err(e) => {
warn!(
"could not download pre-built `wasm-bindgen`: {}. Falling back to `cargo install`.",
e
);
}
}
cargo_install_wasm_bindgen(&cache, version, install_permitted)
}
pub fn download_prebuilt_wasm_bindgen(
cache: &Cache,
version: &str,
install_permitted: bool,
) -> Result<Download, failure::Error> {
let url = match prebuilt_url(version) {
Some(url) => url,
None => bail!("no prebuilt wasm-bindgen binaries are available for this platform"),
};
let binaries = &["wasm-bindgen", "wasm-bindgen-test-runner"];
match cache.download(install_permitted, "wasm-bindgen", binaries, &url)? {
Some(download) => Ok(download),
None => bail!("wasm-bindgen v{} is not installed!", version),
}
}
fn prebuilt_url(version: &str) -> Option<String> {
let target = if target::LINUX && target::x86_64 {
"x86_64-unknown-linux-musl"
} else if target::MACOS && target::x86_64 {
"x86_64-apple-darwin"
} else if target::WINDOWS && target::x86_64 {
"x86_64-pc-windows-msvc"
} else {
return None;
};
Some(format!(
"https://github.com/rustwasm/wasm-bindgen/releases/download/{0}/wasm-bindgen-{0}-{1}.tar.gz",
version,
target
))
}
pub fn cargo_install_wasm_bindgen(
cache: &Cache,
version: &str,
install_permitted: bool,
) -> Result<Download, failure::Error> {
debug!(
"Attempting to use a `cargo install`ed version of `wasm-bindgen={}`",
version
);
let dirname = format!("wasm-bindgen-cargo-install-{}", version);
let destination = cache.join(dirname.as_ref());
if destination.exists() {
debug!(
"`cargo install`ed `wasm-bindgen={}` already exists at {}",
version,
destination.display()
);
return Ok(Download::at(&destination));
}
if !install_permitted {
bail!("wasm-bindgen v{} is not installed!", version)
}
let tmp = cache.join(format!(".{}", dirname).as_ref());
drop(fs::remove_dir_all(&tmp));
debug!(
"cargo installing wasm-bindgen to tempdir: {}",
tmp.display()
);
fs::create_dir_all(&tmp)
.context("failed to create temp dir for `cargo install wasm-bindgen`")?;
let mut cmd = Command::new("cargo");
cmd.arg("install")
.arg("--force")
.arg("wasm-bindgen-cli")
.arg("--version")
.arg(version)
.arg("--root")
.arg(&tmp);
child::run(cmd, "cargo install").context("Installing wasm-bindgen with cargo")?;
for f in ["wasm-bindgen", "wasm-bindgen-test-runner"].iter().cloned() {
let from = tmp
.join("bin")
.join(f)
.with_extension(env::consts::EXE_EXTENSION);
let to = tmp.join(from.file_name().unwrap());
fs::rename(&from, &to).with_context(|_| {
format!(
"failed to move {} to {} for `cargo install`ed `wasm-bindgen`",
from.display(),
to.display()
)
})?;
}
fs::rename(&tmp, &destination)?;
Ok(Download::at(&destination))
}
pub fn wasm_bindgen_build(
data: &CrateData,
bindgen: &Download,
out_dir: &Path,
out_name: &Option<String>,
disable_dts: bool,
target: &Target,
profile: BuildProfile,
) -> Result<(), failure::Error> {
let release_or_debug = match profile {
BuildProfile::Release | BuildProfile::Profiling => "release",
BuildProfile::Dev => "debug",
};
let out_dir = out_dir.to_str().unwrap();
let wasm_path = data
.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 {
"--typescript"
};
let target_arg = match target {
Target::Nodejs => "--nodejs",
Target::NoModules => "--no-modules",
Target::Web => "--web",
Target::Bundler => "--browser",
};
let bindgen_path = bindgen.binary("wasm-bindgen")?;
let mut cmd = Command::new(bindgen_path);
cmd.arg(&wasm_path)
.arg("--out-dir")
.arg(out_dir)
.arg(dts_arg)
.arg(target_arg);
if let Some(value) = out_name {
cmd.arg("--out-name").arg(value);
}
let profile = data.configured_profile(profile);
if profile.wasm_bindgen_debug_js_glue() {
cmd.arg("--debug");
}
if !profile.wasm_bindgen_demangle_name_section() {
cmd.arg("--no-demangle");
}
if profile.wasm_bindgen_dwarf_debug_info() {
cmd.arg("--keep-debug");
}
child::run(cmd, "wasm-bindgen").context("Running the wasm-bindgen CLI")?;
Ok(())
}
fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str) -> bool {
let mut cmd = Command::new(bindgen_path);
cmd.arg("--version");
child::run_capture_stdout(cmd, "wasm-bindgen")
.map(|stdout| {
stdout
.trim()
.split_whitespace()
.nth(1)
.map(|v| {
info!(
"Checking installed `wasm-bindgen` version == expected version: {} == {}",
v, dep_version
);
v == dep_version
})
.unwrap_or(false)
})
.unwrap_or(false)
}