parent
4976003e07
commit
d6baf439b7
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,50 @@ |
|||||||
|
# wasm-pack new |
||||||
|
|
||||||
|
The `wasm-pack new` command creates a new RustWasm project for you, |
||||||
|
using [`cargo-generate`] under the hood. |
||||||
|
|
||||||
|
It takes 3 parameters, name, template, and mode: |
||||||
|
|
||||||
|
``` |
||||||
|
wasm-pack new <name> --template <template> --mode <normal|noinstall|force> |
||||||
|
``` |
||||||
|
|
||||||
|
The template will default to the `rustwasm/wasm-pack-template`. |
||||||
|
|
||||||
|
## Name |
||||||
|
|
||||||
|
The `wasm-pack new` command must be given a name argument, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
wasm-pack new myproject |
||||||
|
``` |
||||||
|
|
||||||
|
## Template |
||||||
|
|
||||||
|
The `wasm-pack new` command can be given an optional template argument, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
wasm-pack new myproject --template https://github.com/rustwasm/wasm-pack-template |
||||||
|
``` |
||||||
|
|
||||||
|
The template can be an address to a git repo that contains a [`cargo-generate`] |
||||||
|
template. |
||||||
|
|
||||||
|
[`cargo-generate`]: https://github.com/ashleygwilliams/cargo-generate |
||||||
|
|
||||||
|
## Mode |
||||||
|
|
||||||
|
The `wasm-pack new` command can be given an optional mode argument, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
wasm-pack new myproject --mode noinstall |
||||||
|
``` |
||||||
|
|
||||||
|
The mode pass can be either "normal", "noinstall", or "force". "normal is passed by |
||||||
|
degault. |
||||||
|
|
||||||
|
`noinstall` means that wasm-pack should not attempt to install any underlying tools. |
||||||
|
If a necessary tool cannot be found, the command will error. |
||||||
|
|
||||||
|
`force` means that wasm-pack should not check the local Rust version. If a local Rust |
||||||
|
is an unacceptable Rust version, the command will error. |
@ -0,0 +1,13 @@ |
|||||||
|
# Quickstart |
||||||
|
|
||||||
|
1. Install `rust` using [`rustup`]. |
||||||
|
1. [Install this tool.] |
||||||
|
1. Run `wasm-pack new hello-wasm`. |
||||||
|
1. `cd hello-wasm` |
||||||
|
1. Run `wasm-pack build`. |
||||||
|
1. This tool generates files in a `pkg` dir |
||||||
|
1. To publish to npm, run `wasm-pack publish`. You may need to login to the |
||||||
|
registry you want to publish to. You can login using `wasm-pack login`. |
||||||
|
|
||||||
|
[`rustup`]: https://rustup.rs/ |
||||||
|
[Install this tool.]: https://rustwasm.github.io/wasm-pack/installer/ |
@ -0,0 +1,28 @@ |
|||||||
|
use cache; |
||||||
|
use failure::Error; |
||||||
|
use generate; |
||||||
|
use install::{self, Tool}; |
||||||
|
use log::info; |
||||||
|
use std::result; |
||||||
|
use PBAR; |
||||||
|
|
||||||
|
/// Executes the 'cargo-generate' command in the current directory
|
||||||
|
/// which generates a new rustwasm project from a template.
|
||||||
|
pub fn generate( |
||||||
|
template: String, |
||||||
|
name: String, |
||||||
|
install_permitted: bool, |
||||||
|
) -> result::Result<(), Error> { |
||||||
|
info!("Generating a new rustwasm project..."); |
||||||
|
let download = install::download_prebuilt_or_cargo_install( |
||||||
|
Tool::CargoGenerate, |
||||||
|
&cache::get_wasm_pack_cache()?, |
||||||
|
"latest", |
||||||
|
install_permitted, |
||||||
|
)?; |
||||||
|
generate::generate(&template, &name, &download)?; |
||||||
|
|
||||||
|
let msg = format!("🐑 Generated new project at /{}", name); |
||||||
|
PBAR.info(&msg); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
//! Functionality related to running `cargo-generate`.
|
||||||
|
|
||||||
|
use binary_install::Download; |
||||||
|
use child; |
||||||
|
use emoji; |
||||||
|
use failure::{self, ResultExt}; |
||||||
|
use std::process::Command; |
||||||
|
|
||||||
|
/// Run `cargo generate` in the current directory to create a new
|
||||||
|
/// project from a template
|
||||||
|
pub fn generate(template: &str, name: &str, download: &Download) -> Result<(), failure::Error> { |
||||||
|
let bin_path = download.binary("cargo-generate")?; |
||||||
|
let mut cmd = Command::new(&bin_path); |
||||||
|
cmd.arg("generate"); |
||||||
|
cmd.arg("--git").arg(&template); |
||||||
|
cmd.arg("--name").arg(&name); |
||||||
|
|
||||||
|
println!( |
||||||
|
"{} Generating a new rustwasm project with name '{}'...", |
||||||
|
emoji::SHEEP, |
||||||
|
name |
||||||
|
); |
||||||
|
child::run(cmd, "cargo-generate").context("Running cargo-generate")?; |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
use install::Tool; |
||||||
|
use serde::Deserialize; |
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)] |
||||||
|
pub struct Krate { |
||||||
|
pub max_version: String, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)] |
||||||
|
pub struct KrateResponse { |
||||||
|
#[serde(rename = "crate")] |
||||||
|
pub krate: Krate, |
||||||
|
} |
||||||
|
|
||||||
|
impl Krate { |
||||||
|
pub fn new(name: &Tool) -> Result<Krate, failure::Error> { |
||||||
|
let krate_address = format!("https://crates.io/api/v1/crates/{}", name); |
||||||
|
let client = reqwest::Client::new(); |
||||||
|
let mut res = client.get(&krate_address).send()?; |
||||||
|
|
||||||
|
let kr: KrateResponse = serde_json::from_str(&res.text()?)?; |
||||||
|
Ok(kr.krate) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,245 @@ |
|||||||
|
//! Functionality related to installing prebuilt binaries and/or running cargo install.
|
||||||
|
|
||||||
|
use self::krate::Krate; |
||||||
|
use binary_install::{Cache, Download}; |
||||||
|
use child; |
||||||
|
use emoji; |
||||||
|
use failure::{self, ResultExt}; |
||||||
|
use log::debug; |
||||||
|
use log::{info, warn}; |
||||||
|
use std::env; |
||||||
|
use std::fs; |
||||||
|
use std::path::PathBuf; |
||||||
|
use std::process::Command; |
||||||
|
use target; |
||||||
|
use which::which; |
||||||
|
use PBAR; |
||||||
|
|
||||||
|
mod krate; |
||||||
|
mod mode; |
||||||
|
mod tool; |
||||||
|
pub use self::mode::InstallMode; |
||||||
|
pub use self::tool::Tool; |
||||||
|
|
||||||
|
/// Install a cargo CLI tool
|
||||||
|
///
|
||||||
|
/// Prefers an existing local install, if any exists. Then checks if there is a
|
||||||
|
/// global install on `$PATH` that fits the bill. Then attempts to download a
|
||||||
|
/// tarball from the GitHub releases page, if this target has prebuilt
|
||||||
|
/// binaries. Finally, falls back to `cargo install`.
|
||||||
|
pub fn download_prebuilt_or_cargo_install( |
||||||
|
tool: Tool, |
||||||
|
cache: &Cache, |
||||||
|
version: &str, |
||||||
|
install_permitted: bool, |
||||||
|
) -> Result<Download, failure::Error> { |
||||||
|
// If the tool is installed globally and it has the right version, use
|
||||||
|
// that. Assume that other tools are installed next to it.
|
||||||
|
//
|
||||||
|
// This situation can arise if the tool is already installed via
|
||||||
|
// `cargo install`, for example.
|
||||||
|
if let Ok(path) = which(tool.to_string()) { |
||||||
|
debug!("found global {} binary at: {}", tool, path.display()); |
||||||
|
if check_version(&tool, &path, version)? { |
||||||
|
return Ok(Download::at(path.parent().unwrap())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let msg = format!("{}Installing {}...", emoji::DOWN_ARROW, tool); |
||||||
|
PBAR.info(&msg); |
||||||
|
|
||||||
|
let dl = download_prebuilt(&tool, &cache, version, install_permitted); |
||||||
|
match dl { |
||||||
|
Ok(dl) => return Ok(dl), |
||||||
|
Err(e) => { |
||||||
|
warn!( |
||||||
|
"could not download pre-built `{}`: {}. Falling back to `cargo install`.", |
||||||
|
tool, e |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cargo_install(tool, &cache, version, install_permitted) |
||||||
|
} |
||||||
|
|
||||||
|
/// Check if the tool dependency is locally satisfied.
|
||||||
|
fn check_version( |
||||||
|
tool: &Tool, |
||||||
|
path: &PathBuf, |
||||||
|
expected_version: &str, |
||||||
|
) -> Result<bool, failure::Error> { |
||||||
|
let expected_version = if expected_version == "latest" { |
||||||
|
let krate = Krate::new(tool)?; |
||||||
|
krate.max_version |
||||||
|
} else { |
||||||
|
expected_version.to_string() |
||||||
|
}; |
||||||
|
|
||||||
|
let mut cmd = Command::new(path); |
||||||
|
cmd.arg("--version"); |
||||||
|
child::run_capture_stdout(cmd, tool) |
||||||
|
.map(|stdout| { |
||||||
|
stdout |
||||||
|
.trim() |
||||||
|
.split_whitespace() |
||||||
|
.nth(1) |
||||||
|
.map(|v| { |
||||||
|
info!( |
||||||
|
"Checking installed `{}` version == expected version: {} == {}", |
||||||
|
tool, v, &expected_version |
||||||
|
); |
||||||
|
Ok(v == expected_version) |
||||||
|
}) |
||||||
|
.unwrap_or(Ok(false)) |
||||||
|
}) |
||||||
|
.unwrap_or(Ok(false)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Downloads a precompiled copy of the tool, if available.
|
||||||
|
pub fn download_prebuilt( |
||||||
|
tool: &Tool, |
||||||
|
cache: &Cache, |
||||||
|
version: &str, |
||||||
|
install_permitted: bool, |
||||||
|
) -> Result<Download, failure::Error> { |
||||||
|
let url = match prebuilt_url(tool, version) { |
||||||
|
Ok(url) => url, |
||||||
|
Err(e) => bail!( |
||||||
|
"no prebuilt {} binaries are available for this platform: {}", |
||||||
|
tool, |
||||||
|
e, |
||||||
|
), |
||||||
|
}; |
||||||
|
match tool { |
||||||
|
Tool::WasmBindgen => { |
||||||
|
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), |
||||||
|
} |
||||||
|
} |
||||||
|
Tool::CargoGenerate => { |
||||||
|
let binaries = &["cargo-generate"]; |
||||||
|
match cache.download(install_permitted, "cargo-generate", binaries, &url)? { |
||||||
|
Some(download) => Ok(download), |
||||||
|
None => bail!("cargo-generate v{} is not installed!", version), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the URL of a precompiled version of wasm-bindgen, if we have one
|
||||||
|
/// available for our host platform.
|
||||||
|
fn prebuilt_url(tool: &Tool, version: &str) -> Result<String, failure::Error> { |
||||||
|
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 { |
||||||
|
bail!("Unrecognized target!") |
||||||
|
}; |
||||||
|
|
||||||
|
match tool { |
||||||
|
Tool::WasmBindgen => { |
||||||
|
Ok(format!( |
||||||
|
"https://github.com/rustwasm/wasm-bindgen/releases/download/{0}/wasm-bindgen-{0}-{1}.tar.gz", |
||||||
|
version, |
||||||
|
target |
||||||
|
)) |
||||||
|
}, |
||||||
|
Tool::CargoGenerate => { |
||||||
|
Ok(format!( |
||||||
|
"https://github.com/ashleygwilliams/cargo-generate/releases/download/v{0}/cargo-generate-v{0}-{1}.tar.gz", |
||||||
|
Krate::new(&Tool::CargoGenerate)?.max_version, |
||||||
|
target |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Use `cargo install` to install the tool locally into the given
|
||||||
|
/// crate.
|
||||||
|
pub fn cargo_install( |
||||||
|
tool: Tool, |
||||||
|
cache: &Cache, |
||||||
|
version: &str, |
||||||
|
install_permitted: bool, |
||||||
|
) -> Result<Download, failure::Error> { |
||||||
|
debug!( |
||||||
|
"Attempting to use a `cargo install`ed version of `{}={}`", |
||||||
|
tool, version, |
||||||
|
); |
||||||
|
|
||||||
|
let dirname = format!("{}-cargo-install-{}", tool, version); |
||||||
|
let destination = cache.join(dirname.as_ref()); |
||||||
|
if destination.exists() { |
||||||
|
debug!( |
||||||
|
"`cargo install`ed `{}={}` already exists at {}", |
||||||
|
tool, |
||||||
|
version, |
||||||
|
destination.display() |
||||||
|
); |
||||||
|
return Ok(Download::at(&destination)); |
||||||
|
} |
||||||
|
|
||||||
|
if !install_permitted { |
||||||
|
bail!("{} v{} is not installed!", tool, version) |
||||||
|
} |
||||||
|
|
||||||
|
// Run `cargo install` to a temporary location to handle ctrl-c gracefully
|
||||||
|
// and ensure we don't accidentally use stale files in the future
|
||||||
|
let tmp = cache.join(format!(".{}", dirname).as_ref()); |
||||||
|
drop(fs::remove_dir_all(&tmp)); |
||||||
|
debug!("cargo installing {} to tempdir: {}", tool, tmp.display(),); |
||||||
|
|
||||||
|
let context = format!("failed to create temp dir for `cargo install {}`", tool); |
||||||
|
fs::create_dir_all(&tmp).context(context)?; |
||||||
|
|
||||||
|
let crate_name = match tool { |
||||||
|
Tool::WasmBindgen => "wasm-bindgen-cli".to_string(), |
||||||
|
_ => tool.to_string(), |
||||||
|
}; |
||||||
|
let mut cmd = Command::new("cargo"); |
||||||
|
cmd.arg("install") |
||||||
|
.arg("--force") |
||||||
|
.arg(crate_name) |
||||||
|
.arg("--version") |
||||||
|
.arg(version) |
||||||
|
.arg("--root") |
||||||
|
.arg(&tmp); |
||||||
|
|
||||||
|
let context = format!("Installing {} with cargo", tool); |
||||||
|
child::run(cmd, "cargo install").context(context)?; |
||||||
|
|
||||||
|
// `cargo install` will put the installed binaries in `$root/bin/*`, but we
|
||||||
|
// just want them in `$root/*` directly (which matches how the tarballs are
|
||||||
|
// laid out, and where the rest of our code expects them to be). So we do a
|
||||||
|
// little renaming here.
|
||||||
|
let binaries = match tool { |
||||||
|
Tool::WasmBindgen => vec!["wasm-bindgen", "wasm-bindgen-test-runner"], |
||||||
|
Tool::CargoGenerate => vec!["cargo-genrate"], |
||||||
|
}; |
||||||
|
|
||||||
|
for b in binaries.iter().cloned() { |
||||||
|
let from = tmp |
||||||
|
.join("bin") |
||||||
|
.join(b) |
||||||
|
.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 `{}`", |
||||||
|
from.display(), |
||||||
|
to.display(), |
||||||
|
b |
||||||
|
) |
||||||
|
})?; |
||||||
|
} |
||||||
|
|
||||||
|
// Finally, move the `tmp` directory into our binary cache.
|
||||||
|
fs::rename(&tmp, &destination)?; |
||||||
|
|
||||||
|
Ok(Download::at(&destination)) |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
/// The `InstallMode` determines which mode of initialization we are running, and
|
||||||
|
/// what install steps we perform.
|
||||||
|
#[derive(Clone, Copy, Debug)] |
||||||
|
pub enum InstallMode { |
||||||
|
/// Perform all the install steps.
|
||||||
|
Normal, |
||||||
|
/// Don't install tools like `wasm-bindgen`, just use the global
|
||||||
|
/// environment's existing versions to do builds.
|
||||||
|
Noinstall, |
||||||
|
/// Skip the rustc version check
|
||||||
|
Force, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for InstallMode { |
||||||
|
fn default() -> InstallMode { |
||||||
|
InstallMode::Normal |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for InstallMode { |
||||||
|
type Err = failure::Error; |
||||||
|
fn from_str(s: &str) -> Result<Self, failure::Error> { |
||||||
|
match s { |
||||||
|
"no-install" => Ok(InstallMode::Noinstall), |
||||||
|
"normal" => Ok(InstallMode::Normal), |
||||||
|
"force" => Ok(InstallMode::Force), |
||||||
|
_ => bail!("Unknown build mode: {}", s), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl InstallMode { |
||||||
|
/// Determines if installation is permitted during a function call based on --mode flag
|
||||||
|
pub fn install_permitted(self) -> bool { |
||||||
|
match self { |
||||||
|
InstallMode::Normal => true, |
||||||
|
InstallMode::Force => true, |
||||||
|
InstallMode::Noinstall => false, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
use std::fmt; |
||||||
|
|
||||||
|
/// Represents the set of CLI tools wasm-pack uses
|
||||||
|
pub enum Tool { |
||||||
|
/// cargo-generate CLI tool
|
||||||
|
CargoGenerate, |
||||||
|
/// wasm-bindgen CLI tools
|
||||||
|
WasmBindgen, |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Tool { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match self { |
||||||
|
Tool::CargoGenerate => write!(f, "cargo-generate"), |
||||||
|
Tool::WasmBindgen => write!(f, "wasm-bindgen"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
use assert_cmd::prelude::*; |
||||||
|
use utils; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn new_with_no_name_errors() { |
||||||
|
let fixture = utils::fixture::not_a_crate(); |
||||||
|
fixture.install_local_cargo_generate(); |
||||||
|
fixture.wasm_pack().arg("new").assert().failure(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn new_with_name_succeeds() { |
||||||
|
let fixture = utils::fixture::not_a_crate(); |
||||||
|
fixture.install_local_cargo_generate(); |
||||||
|
fixture |
||||||
|
.wasm_pack() |
||||||
|
.arg("new") |
||||||
|
.arg("hello") |
||||||
|
.assert() |
||||||
|
.success(); |
||||||
|
} |
Loading…
Reference in new issue