diff --git a/.appveyor.yml b/.appveyor.yml index 91ac60e1..58bb395e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,9 +17,6 @@ build: false test_script: - cargo test --release --tests --locked - cargo test --release --doc - - cd binary-install - - cargo test - - cd .. before_deploy: - ps: | diff --git a/Cargo.lock b/Cargo.lock index 60218e2f..c154c3fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,7 @@ dependencies = [ [[package]] name = "binary-install" version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1293,7 +1294,7 @@ version = "0.7.0" dependencies = [ "assert_cmd 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "binary-install 0.0.2", + "binary-install 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "console 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1416,6 +1417,7 @@ dependencies = [ "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum binary-install 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b5bc5f8c50dd6a80d0b303ddab79f42ddcb52fd43d68107ecf622c551fd4cd4" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" diff --git a/Cargo.toml b/Cargo.toml index 372b5475..084579fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ siphasher = "0.2.3" structopt = "0.2" toml = "0.4" which = "2.0.0" -binary-install = { version = "0.0.2", path = "./binary-install" } +binary-install = "0.0.2" walkdir = "2" chrono = "0.4.6" diff --git a/binary-install/Cargo.toml b/binary-install/Cargo.toml deleted file mode 100644 index 10c82e56..00000000 --- a/binary-install/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "binary-install" -description = "install a binary from a path to a global cache" -authors = ["The wasm-pack team"] -repository = "https://github.com/rustwasm/wasm-pack/tree/master/binary-install" -license = "MIT/Apache-2.0" -version = "0.0.2" -documentation = "https://docs.rs/binary-install" -readme = "./README.md" - -[dependencies] -curl = "0.4.13" -dirs = "1.0.4" -failure = "0.1.2" -flate2 = "1.0.2" -hex = "0.3" -is_executable = "0.1.2" -siphasher = "0.2.3" -tar = "0.4.16" -zip = "0.5.0" - -[dev-dependencies] -tempfile = "3.0.5" diff --git a/binary-install/README.md b/binary-install/README.md deleted file mode 100644 index a79d5df5..00000000 --- a/binary-install/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# `binary-install` -> install a binary from a path to a global cache diff --git a/binary-install/src/lib.rs b/binary-install/src/lib.rs deleted file mode 100644 index 37d66148..00000000 --- a/binary-install/src/lib.rs +++ /dev/null @@ -1,346 +0,0 @@ -//! Utilities for finding and installing binaries that we depend on. - -extern crate curl; -#[macro_use] -extern crate failure; -extern crate dirs; -extern crate flate2; -extern crate hex; -extern crate is_executable; -extern crate siphasher; -extern crate tar; -extern crate zip; - -use failure::{Error, ResultExt}; -use siphasher::sip::SipHasher13; -use std::collections::HashSet; -use std::env; -use std::ffi; -use std::fs; -use std::hash::{Hash, Hasher}; -use std::io; -use std::path::{Path, PathBuf}; - -/// Global cache for wasm-pack, currently containing binaries downloaded from -/// urls like wasm-bindgen and such. -#[derive(Debug)] -pub struct Cache { - destination: PathBuf, -} - -/// Representation of a downloaded tarball/zip -#[derive(Debug)] -pub struct Download { - root: PathBuf, -} - -impl Cache { - /// Returns the global cache directory, as inferred from env vars and such. - /// - /// This function may return an error if a cache directory cannot be - /// determined. - pub fn new(name: &str) -> Result { - let cache_name = format!(".{}", name); - let destination = dirs::cache_dir() - .map(|p| p.join(&cache_name)) - .or_else(|| { - let home = dirs::home_dir()?; - Some(home.join(&cache_name)) - }) - .ok_or_else(|| format_err!("couldn't find your home directory, is $HOME not set?"))?; - Ok(Cache::at(&destination)) - } - - /// Creates a new cache specifically at a particular directory, useful in - /// testing and such. - pub fn at(path: &Path) -> Cache { - Cache { - destination: path.to_path_buf(), - } - } - - /// Joins a path to the destination of this cache, returning the result - pub fn join(&self, path: &Path) -> PathBuf { - self.destination.join(path) - } - - /// Downloads a tarball or zip file from the specified url, extracting it - /// locally and returning the directory that the contents were extracted - /// into. - /// - /// Note that this function requries that the contents of `url` never change - /// as the contents of the url are globally cached on the system and never - /// invalidated. - /// - /// The `name` is a human-readable name used to go into the folder name of - /// the destination, and `binaries` is a list of binaries expected to be at - /// the url. If the URL's extraction doesn't contain all the binaries this - /// function will return an error. - pub fn download( - &self, - install_permitted: bool, - name: &str, - binaries: &[&str], - url: &str, - ) -> Result, Error> { - let dirname = hashed_dirname(url, name); - - let destination = self.destination.join(&dirname); - - if destination.exists() { - return Ok(Some(Download { root: destination })); - } - - if !install_permitted { - return Ok(None); - } - - let data = curl(&url).with_context(|_| format!("failed to download from {}", url))?; - - // Extract everything in a temporary directory in case we're ctrl-c'd. - // Don't want to leave around corrupted data! - let temp = self.destination.join(&format!(".{}", dirname)); - drop(fs::remove_dir_all(&temp)); - fs::create_dir_all(&temp)?; - - if url.ends_with(".tar.gz") { - self.extract_tarball(&data, &temp, binaries) - .with_context(|_| format!("failed to extract tarball from {}", url))?; - } else if url.ends_with(".zip") { - self.extract_zip(&data, &temp, binaries) - .with_context(|_| format!("failed to extract zip from {}", url))?; - } else { - // panic instead of runtime error as it's a static violation to - // download a different kind of url, all urls should be encoded into - // the binary anyway - panic!("don't know how to extract {}", url) - } - - // Now that everything is ready move this over to our destination and - // we're good to go. - fs::rename(&temp, &destination)?; - Ok(Some(Download { root: destination })) - } - - fn extract_tarball(&self, tarball: &[u8], dst: &Path, binaries: &[&str]) -> Result<(), Error> { - let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect(); - let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(tarball)); - - for entry in archive.entries()? { - let mut entry = entry?; - - let dest = match entry.path()?.file_stem() { - Some(f) if binaries.contains(f) => { - binaries.remove(f); - dst.join(entry.path()?.file_name().unwrap()) - } - _ => continue, - }; - - entry.unpack(dest)?; - } - - if !binaries.is_empty() { - bail!( - "the tarball was missing expected executables: {}", - binaries - .into_iter() - .map(|s| s.to_string_lossy()) - .collect::>() - .join(", "), - ) - } - - Ok(()) - } - - fn extract_zip(&self, zip: &[u8], dst: &Path, binaries: &[&str]) -> Result<(), Error> { - let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect(); - - let data = io::Cursor::new(zip); - let mut zip = zip::ZipArchive::new(data)?; - - for i in 0..zip.len() { - let mut entry = zip.by_index(i).unwrap(); - let entry_path = entry.sanitized_name(); - match entry_path.file_stem() { - Some(f) if binaries.contains(f) => { - binaries.remove(f); - let mut dest = bin_open_options() - .write(true) - .create_new(true) - .open(dst.join(entry_path.file_name().unwrap()))?; - io::copy(&mut entry, &mut dest)?; - } - _ => continue, - }; - } - - if !binaries.is_empty() { - bail!( - "the zip was missing expected executables: {}", - binaries - .into_iter() - .map(|s| s.to_string_lossy()) - .collect::>() - .join(", "), - ) - } - - return Ok(()); - - #[cfg(unix)] - fn bin_open_options() -> fs::OpenOptions { - use std::os::unix::fs::OpenOptionsExt; - - let mut opts = fs::OpenOptions::new(); - opts.mode(0o755); - opts - } - - #[cfg(not(unix))] - fn bin_open_options() -> fs::OpenOptions { - fs::OpenOptions::new() - } - } -} - -impl Download { - /// Manually constructs a download at the specified path - pub fn at(path: &Path) -> Download { - Download { - root: path.to_path_buf(), - } - } - - /// Returns the path to the binary `name` within this download - pub fn binary(&self, name: &str) -> Result { - use is_executable::IsExecutable; - - let ret = self - .root - .join(name) - .with_extension(env::consts::EXE_EXTENSION); - - if !ret.is_file() { - bail!("{} binary does not exist", ret.display()); - } - if !ret.is_executable() { - bail!("{} is not executable", ret.display()); - } - - Ok(ret) - } -} - -fn curl(url: &str) -> Result, Error> { - let mut data = Vec::new(); - - let mut easy = curl::easy::Easy::new(); - easy.follow_location(true)?; - easy.url(url)?; - easy.get(true)?; - { - let mut transfer = easy.transfer(); - transfer.write_function(|part| { - data.extend_from_slice(part); - Ok(part.len()) - })?; - transfer.perform()?; - } - - let status_code = easy.response_code()?; - if 200 <= status_code && status_code < 300 { - Ok(data) - } else { - bail!( - "received a bad HTTP status code ({}) when requesting {}", - status_code, - url - ) - } -} - -fn hashed_dirname(url: &str, name: &str) -> String { - let mut hasher = SipHasher13::new(); - url.hash(&mut hasher); - let result = hasher.finish(); - let hex = hex::encode(&[ - (result >> 0) as u8, - (result >> 8) as u8, - (result >> 16) as u8, - (result >> 24) as u8, - (result >> 32) as u8, - (result >> 40) as u8, - (result >> 48) as u8, - (result >> 56) as u8, - ]); - format!("{}-{}", name, hex) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_returns_same_hash_for_same_name_and_url() { - let name = "wasm-pack"; - let url = "http://localhost:7878/wasm-pack-v0.6.0.tar.gz"; - - let first = hashed_dirname(url, name); - let second = hashed_dirname(url, name); - - assert!(!first.is_empty()); - assert!(!second.is_empty()); - assert_eq!(first, second); - } - - #[test] - fn it_returns_different_hashes_for_different_urls() { - let name = "wasm-pack"; - let url = "http://localhost:7878/wasm-pack-v0.5.1.tar.gz"; - let second_url = "http://localhost:7878/wasm-pack-v0.6.0.tar.gz"; - - let first = hashed_dirname(url, name); - let second = hashed_dirname(second_url, name); - - assert_ne!(first, second); - } - - #[test] - fn it_returns_cache_dir() { - let name = "wasm-pack"; - let cache = Cache::new(name); - - let expected = dirs::cache_dir() - .unwrap() - .join(PathBuf::from(".".to_owned() + name)); - - assert!(cache.is_ok()); - assert_eq!(cache.unwrap().destination, expected); - } - - #[test] - fn it_returns_destination_if_binary_already_exists() { - use std::fs; - - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - let url = &format!("{}/{}.tar.gz", "http://localhost:7878", binary_name); - - let dirname = hashed_dirname(&url, &binary_name); - let full_path = dir.path().join(dirname); - - // Create temporary directory and binary to simulate that - // a cached binary already exists. - fs::create_dir_all(full_path).unwrap(); - - let dl = cache.download(true, binary_name, &binaries, url); - - assert!(dl.is_ok()); - assert!(dl.unwrap().is_some()) - } -} diff --git a/binary-install/tests/all/cache.rs b/binary-install/tests/all/cache.rs deleted file mode 100644 index 6fa07ba9..00000000 --- a/binary-install/tests/all/cache.rs +++ /dev/null @@ -1,142 +0,0 @@ -use binary_install::Cache; -use std::path::Path; -use utils; - -#[test] -fn it_returns_none_if_install_is_not_permitted() { - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - let dl = cache.download( - false, - binary_name, - &binaries, - &format!("{}/{}.tar.gz", "", binary_name), - ); - - assert!(dl.is_ok()); - assert!(dl.unwrap().is_none()) -} - -#[test] -fn it_downloads_tarball() { - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - // Create a temporary tarball. - let tarball = utils::create_tarball(binary_name).ok(); - - // Spin up a local TcpListener. - let server_port = utils::start_server(tarball, None).recv().unwrap(); - - let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - let dl = cache.download( - true, - binary_name, - &binaries, - &format!("{}/{}.tar.gz", &url, binary_name), - ); - - assert!(dl.is_ok()); - assert!(dl.unwrap().is_some()) -} - -#[test] -fn it_returns_error_when_it_failed_to_download() { - let server_port = 7881; - let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - let full_url = &format!("{}/{}.tar.gz", &url, binary_name); - - let dl = cache.download(true, binary_name, &binaries, full_url); - - assert!(dl.is_err()); - assert_eq!( - &format!("failed to download from {}", full_url), - &format!("{}", dl.unwrap_err()) - ); -} - -#[test] -fn it_returns_error_when_it_failed_to_extract_tarball() { - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - // Spin up a local TcpListener. - let server_port = utils::start_server(None, None).recv().unwrap(); - - let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); - let full_url = &format!("{}/{}.tar.gz", &url, binary_name); - - let dl = cache.download(true, binary_name, &binaries, full_url); - - assert!(dl.is_err()); - assert_eq!( - &format!("failed to extract tarball from {}", full_url), - &format!("{}", dl.unwrap_err()) - ); -} - -#[test] -fn it_returns_error_when_it_failed_to_extract_zip() { - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - // Spin up a local TcpListener. - let server_port = utils::start_server(None, None).recv().unwrap(); - - let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); - let full_url = &format!("{}/{}.zip", &url, binary_name); - - let dl = cache.download(true, binary_name, &binaries, full_url); - - assert!(dl.is_err()); - assert_eq!( - &format!("failed to extract zip from {}", full_url), - &format!("{}", dl.unwrap_err()) - ); -} - -#[test] -#[should_panic(expected = "don't know how to extract http://localhost:7884/wasm-pack.bin")] -fn it_panics_if_not_tarball_or_zip() { - let server_port = 7884; - let binary_name = "wasm-pack"; - let binaries = vec![binary_name]; - - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - // Spin up a local TcpListener. - utils::start_server(None, Some(server_port)).recv().unwrap(); - - let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); - let full_url = &format!("{}/{}.bin", &url, binary_name); - - let _ = cache.download(true, binary_name, &binaries, full_url); -} - -#[test] -fn it_joins_path_with_destination() { - let dir = tempfile::TempDir::new().unwrap(); - let cache = Cache::at(dir.path()); - - assert_eq!(dir.path().join("hello"), cache.join(Path::new("hello"))); -} diff --git a/binary-install/tests/all/download.rs b/binary-install/tests/all/download.rs deleted file mode 100644 index d65bfb80..00000000 --- a/binary-install/tests/all/download.rs +++ /dev/null @@ -1,125 +0,0 @@ -use binary_install::Download; -use std::fs::OpenOptions; - -#[test] -#[cfg(unix)] -fn it_returns_binary_name_for_unix() { - use std::os::unix::fs::OpenOptionsExt; - - let binary_name = "wasm-pack"; - - let dir = tempfile::TempDir::new().unwrap(); - let download = Download::at(dir.path()); - - let full_path = dir.path().join(binary_name); - - let mut options = OpenOptions::new(); - options.create(true); - options.write(true); - - // Make the "binary" an executable. - options.mode(0o755); - - options.open(&full_path).unwrap(); - - let binary = download.binary(binary_name); - - assert!(binary.is_ok()); - assert_eq!(full_path, binary.unwrap()); -} - -#[test] -#[cfg(not(windows))] -fn it_bails_if_not_file_for_unix() { - let binary_name = "wasm-pack"; - - let dir = tempfile::TempDir::new().unwrap(); - let download = Download::at(dir.path()); - - let full_path = dir.path().join(binary_name); - - let mut options = OpenOptions::new(); - options.create(true); - options.write(true); - - let binary = download.binary(binary_name); - - assert!(binary.is_err()); - assert_eq!( - format!("{} binary does not exist", full_path.to_str().unwrap()), - binary.unwrap_err().to_string() - ); -} - -#[test] -#[cfg(windows)] -fn it_bails_if_not_file_for_windows() { - let binary_name = "wasm-pack.exe"; - - let dir = tempfile::TempDir::new().unwrap(); - let download = Download::at(dir.path()); - - let full_path = dir.path().join(binary_name); - - let mut options = OpenOptions::new(); - options.create(true); - options.write(true); - - let binary = download.binary(binary_name); - - assert!(binary.is_err()); - assert_eq!( - format!("{} binary does not exist", full_path.to_str().unwrap()), - binary.unwrap_err().to_string() - ); -} - -#[test] -#[cfg(not(windows))] -fn it_bails_if_not_executable_for_unix() { - let binary_name = "wasm-pack"; - - let dir = tempfile::TempDir::new().unwrap(); - let download = Download::at(dir.path()); - - let full_path = dir.path().join(binary_name); - - let mut options = OpenOptions::new(); - options.create(true); - options.write(true); - - options.open(&full_path).unwrap(); - - let binary = download.binary(binary_name); - - assert!(binary.is_err()); - assert_eq!( - format!("{} is not executable", full_path.to_str().unwrap()), - binary.unwrap_err().to_string() - ); -} - -#[test] -#[cfg(windows)] -fn it_bails_if_not_executable_for_windows() { - let binary_name = "wasm-pack.exe"; - - let dir = tempfile::TempDir::new().unwrap(); - let download = Download::at(dir.path()); - - let full_path = dir.path().join(binary_name); - - let mut options = OpenOptions::new(); - options.create(true); - options.write(true); - - options.open(&full_path).unwrap(); - - let binary = download.binary(binary_name); - - assert!(binary.is_err()); - assert_eq!( - format!("{} is not executable", full_path.to_str().unwrap()), - binary.unwrap_err().to_string() - ); -} diff --git a/binary-install/tests/all/main.rs b/binary-install/tests/all/main.rs deleted file mode 100644 index de1ad721..00000000 --- a/binary-install/tests/all/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate binary_install; -extern crate flate2; -extern crate tar; - -mod cache; -mod download; -mod utils; diff --git a/binary-install/tests/all/utils/mod.rs b/binary-install/tests/all/utils/mod.rs deleted file mode 100644 index 65b13260..00000000 --- a/binary-install/tests/all/utils/mod.rs +++ /dev/null @@ -1,79 +0,0 @@ -use flate2::write::GzEncoder; -use flate2::Compression; -use std::fs::{File, OpenOptions}; -use std::io::{self, Read, Write}; -use std::net::TcpListener; -use std::sync::mpsc::{channel, Receiver}; -use std::thread; - -pub const TEST_SERVER_HOST: &'static str = "localhost"; - -pub fn start_server(tarball: Option>, server_port: Option) -> Receiver { - let (sender, receiver) = channel(); - - thread::spawn(move || { - TcpListener::bind(format!( - "{}:{}", - TEST_SERVER_HOST, - server_port.unwrap_or_else(|| 0) - )) - .map(|listener| { - sender.send(listener.local_addr().unwrap().port()).unwrap(); - - for stream in listener.incoming() { - let mut stream = stream.unwrap(); - - let mut buffer = [0; 512]; - - stream.read(&mut buffer).unwrap(); - - let response = "HTTP/1.1 200 OK\r\n\r\n"; - - stream.write(response.as_bytes()).unwrap(); - - match tarball.to_owned() { - Some(tar) => { - stream.write(tar.as_ref()).unwrap(); - } - None => {} - } - - stream.flush().unwrap(); - } - }) - .unwrap(); - }); - - receiver -} - -pub fn create_tarball(binary_name: &str) -> Result, io::Error> { - let temp_dir = tempfile::TempDir::new().unwrap(); - let full_path = temp_dir.path().join(binary_name.to_owned() + ".tar.gz"); - - let tar = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(&full_path)?; - - let mut file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(temp_dir.path().join(binary_name))?; - - let mut encoder = GzEncoder::new(tar, Compression::default()); - { - let mut archive = tar::Builder::new(&mut encoder); - archive.append_file(binary_name, &mut file)?; - } - - let mut contents = vec![]; - - encoder.finish()?; - - File::open(temp_dir.path().join(&full_path))?.read_to_end(&mut contents)?; - - Ok(contents) -} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 0f931dc2..a59ee16d 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -3,25 +3,25 @@ - [Prerequisites](./prerequisites/index.md) - [npm (optional)](./prerequisites/npm.md) - [Commands](./commands/index.md) - - [`init` (DEPRECATED)](./commands/init.md) - [`build`](./commands/build.md) - [`test`](./commands/test.md) - [`pack` and `publish`](./commands/pack-and-publish.md) + - [`init` (DEPRECATED)](./commands/init.md) - [Tutorials](./tutorials/index.md) - [Hybrid applications with Webpack](./tutorials/hybrid-applications-with-webpack/index.md) - [Getting started](./tutorials/hybrid-applications-with-webpack/getting-started.md) - [Using your library](./tutorials/hybrid-applications-with-webpack/using-your-library.md) - [npm browser packages](./tutorials/npm-browser-packages/index.md) - [Getting started](./tutorials/npm-browser-packages/getting-started.md) - - [Project setup](./tutorials/npm-browser-packages/project-setup/index.md) - - [Using a Template](./tutorials/npm-browser-packages/project-setup/using-a-template.md) - - [Manual Setup](./tutorials/npm-browser-packages/project-setup/manual-setup.md) + - [Manual Setup](./tutorials/npm-browser-packages/getting-started/manual-setup.md) - [Template deep dive](./tutorials/npm-browser-packages/template-deep-dive/index.md) - [`Cargo.toml`](./tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md) - [`src/lib.rs`](./tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md) - [`src/utils.rs`](./tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md) - [`wee_alloc`](./tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md) - - [Building your project](./tutorials/npm-browser-packages/template-deep-dive/building-your-project.md) + - [`tests/web.rs`](./tutorials/npm-browser-packages/template-deep-dive/tests-web-rs.md) + - [Building your project](./tutorials/npm-browser-packages/building-your-project.md) + - [Testing your project](./tutorials/npm-browser-packages/testing-your-project.md) - [Packaging and publishing](./tutorials/npm-browser-packages/packaging-and-publishing.md) - [Using your library](./tutorials/npm-browser-packages/using-your-library.md) - [`Cargo.toml` Configuration](./cargo-toml-configuration.md) diff --git a/docs/src/commands/build.md b/docs/src/commands/build.md index cd9ca192..00ab2e9a 100644 --- a/docs/src/commands/build.md +++ b/docs/src/commands/build.md @@ -20,6 +20,18 @@ wasm-pack build examples/js-hello-world This path should point to a directory that contains a `Cargo.toml` file. If no path is given, the `build` command will run in the current directory. +## Output Directory + +By default, `wasm-pack` will generate a directory for it's build output called `pkg`. +If you'd like to customize this you can use the `--out-dir` flag. + +``` +wasm-pack build --out-dir out +``` + +The above command will put your build artifacts in a directory called `out`, instead +of the default `pkg`. + ## Profile The `build` command accepts an optional profile argument: one of `--dev`, diff --git a/docs/src/prerequisites/index.md b/docs/src/prerequisites/index.md index b42e2b73..c537f70c 100644 --- a/docs/src/prerequisites/index.md +++ b/docs/src/prerequisites/index.md @@ -10,10 +10,10 @@ Next, since `wasm-pack` is a build tool, you'll want to make sure you have [rust]: https://www.rust-lang.org/tools/install -Finally, if you're using `wasm-pack` to install to publish to NPM, you'll want +Finally, if you're using `wasm-pack` to publish to NPM, you'll want to [install and configure `npm`][npm]. In the future, we intend to rewrite the npm registry client bits so that the need for a Node runtime is eliminated. If you're excited about that work- you should reach out to the maintainers and get involved! -[npm]: npm.html +[npm]: prerequisites/npm.html diff --git a/docs/src/tutorials/npm-browser-packages/building-your-package.md b/docs/src/tutorials/npm-browser-packages/building-your-package.md deleted file mode 100644 index 7961a04c..00000000 --- a/docs/src/tutorials/npm-browser-packages/building-your-package.md +++ /dev/null @@ -1,24 +0,0 @@ -# Building your package - -We've written our code so now we need to package it all up. - -We are writing a package that should be used in the browser, so we run this in our terminal: - -```bash -$ wasm-pack build --scope MYSCOPE -``` - -If you were writing a package that should be used in Node.js (with CommonJS modules, e.g. `require`), -you would run this in your terminal: - -```bash -$ wasm-pack build --scope MYSCOPE --target nodejs -``` - -where `MYSCOPE` is your npm username. Normally you could just type `wasm-pack init` but since -other people are doing this tutorial as well we don't want conflicts with the `wasm-add` package -name! This command when run does a few things: - -1. It'll compile your code to wasm if you haven't already -2. It'll generate a pkg folder with the wasm file, a JS wrapper file around the wasm, your README, - and a `package.json` file. diff --git a/docs/src/tutorials/npm-browser-packages/building-your-project.md b/docs/src/tutorials/npm-browser-packages/building-your-project.md new file mode 100644 index 00000000..00ce5e83 --- /dev/null +++ b/docs/src/tutorials/npm-browser-packages/building-your-project.md @@ -0,0 +1,23 @@ +# Building your project + +We've written our code so now we need to build it. + +We are writing a crate that should be used in the browser, so we run this in +our terminal: + +```bash +$ wasm-pack build +``` + +If you were writing a package that should be used in Node.js (with CommonJS +modules, e.g. `require`), you would run this in your terminal: + +```bash +$ wasm-pack build --target nodejs +``` + +This command when run does a few things: + +1. It'll compile your code to wasm if you haven't already +2. It'll generate a `pkg` folder with the wasm file, a JS wrapper file around + the wasm, your README, and a `package.json` file. diff --git a/docs/src/tutorials/npm-browser-packages/getting-started.md b/docs/src/tutorials/npm-browser-packages/getting-started.md index a011e996..4f0f5320 100644 --- a/docs/src/tutorials/npm-browser-packages/getting-started.md +++ b/docs/src/tutorials/npm-browser-packages/getting-started.md @@ -25,4 +25,4 @@ further in this guide. ⚠️ If you'd rather not use a template, or are having trouble with the template, you can do a manual setup by following [these instructions]. -[these instructions]: ../project-setup/manual-setup.html +[these instructions]: ./getting-started/manual-setup.html diff --git a/docs/src/tutorials/npm-browser-packages/project-setup/manual-setup.md b/docs/src/tutorials/npm-browser-packages/getting-started/manual-setup.md similarity index 94% rename from docs/src/tutorials/npm-browser-packages/project-setup/manual-setup.md rename to docs/src/tutorials/npm-browser-packages/getting-started/manual-setup.md index ed07849a..5c7167cc 100644 --- a/docs/src/tutorials/npm-browser-packages/project-setup/manual-setup.md +++ b/docs/src/tutorials/npm-browser-packages/getting-started/manual-setup.md @@ -1,9 +1,9 @@ # Manual Setup ⚠️ This is not the recommended way to start a `wasm-pack` project! If you ended up -here by mistake, go check out our recommended project start, [Using A Template]. +here by mistake, go check out our [recommended project start][template]. -[Using A Template]: using-a-template.md +[template]: ../getting-started.html ### Step 1: Create a New Rust Library Project @@ -25,7 +25,7 @@ section. `wasm-bindgen` is a tool that facilitates interoperability between wasm modules and JavaScript. ⚠️ If you are coming from JavaScript, you might note that when we add the dependency -there is no `^` or `~` symbol- it looks like we're locking to the `0.2` version. +there is no `^` or `~` symbol- it looks like we're locking to the `0.2` version. However, that's not the case! In Rust, the `^` is implied. #### Add `crate-type` diff --git a/docs/src/tutorials/npm-browser-packages/project-setup/index.md b/docs/src/tutorials/npm-browser-packages/project-setup/index.md deleted file mode 100644 index 48e0012c..00000000 --- a/docs/src/tutorials/npm-browser-packages/project-setup/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# Project Setup - -In this section, how to setup a `wasm-pack` project. - -There are a few things you need to do to setup a project for `wasm-pack`. -We strongly recommending [using a template], but you can also set the project -up [manually]. - -[using a template]: ./using-a-template.html -[manually]: ./manual-setup.html diff --git a/docs/src/tutorials/npm-browser-packages/project-setup/using-a-template.md b/docs/src/tutorials/npm-browser-packages/project-setup/using-a-template.md deleted file mode 100644 index a23e6507..00000000 --- a/docs/src/tutorials/npm-browser-packages/project-setup/using-a-template.md +++ /dev/null @@ -1,21 +0,0 @@ -# Using a Template - -You can create a new Rust-WebAssembly project by using the [rustwasm wasm-pack-template]. - -To so do, you'll need the `cargo-generate` tool. To install `cargo-generate`: - -``` -cargo install cargo-generate -``` - -Then run: - -``` -cargo generate --git https://github.com/rustwasm/wasm-pack-template -``` - -You will be prompted to give your project a name. Once you do, you will have a directory -with a new project, ready to go. We'll talk about what's been included in this template -further in this guide. - -[rustwasm wasm-pack-template]: https://github.com/rustwasm/wasm-pack-template diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive.md deleted file mode 100644 index 61843928..00000000 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive.md +++ /dev/null @@ -1 +0,0 @@ -# Template Deep Dive diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md index f6c40155..d7e6f172 100644 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md @@ -23,20 +23,21 @@ crate-type = ["cdylib", "rlib"] A Rust-`wasm` crate is a bit different from a normal crate, and as a result, we need to note this in our `Cargo.toml`. -When `cargo` is told to build a project, or compilation is otherwise done on a Rust project, -the Rust compiler will need to link crates together, using a particular method, either -staticly or dynamically. The two types of crate that you are likely most familiar with are -`#[crate_type = "bin"]` and `#[crate_type = "lib"]`, which are the crate types that largely -represent the difference between Rust application projects and Rust libraries. - -`#[crate_type = "cdylib"]` signifies that you'd like the compiler to create a dynamic system -library. This type of library is suited for situations where you'd like to compile Rust code -as a dynamic library to be loaded from another language. In our case, we'll be compiling to a -`.wasm` file, but this output type will create `*.so` files on Linux, `*.dylib` files on -macOS, and `*.dll` files on Windows in non-`wasm` circumstances. - -`#[crate_type = "rlib"]` signifies that an intermediate "Rust library" file will be produced. -This allows tests to use the main crate. +This `[lib]` annotation is typically not needed in Cargo projects, and if you're +familiar with other Rust crates you'll remember that the most common crate types +are `rlib` (the default) or `bin` for binaries (which don't need a `crate-type` +annotation). + +Here though `crate-type = ["cdylib"]` typically signifies that you'd like the +compiler to create a dynamic system library, but for WebAssembly target it +simply means "create a `*.wasm` file without a `start` function". On other +platforms this output type will create `*.so` file on Linux, `*.dylib` on +macOS, and `*.dll` Windows. + +We also specify `crate-type = ["rlib"]` to ensure that our library can be unit +tested with `wasm-pack test` (which we'll see later). Without this we wouldn't +be able to test our library because the `cdylib` crate type is incompatible with +`wasm-pack`'s style of unit tests. You can read more about linking and crate types, [here](https://doc.rust-lang.org/reference/linkage.html). @@ -56,7 +57,7 @@ wasm-bindgen = "0.2" We'll see more about how to use this library when we discuss what has been generated in `lib.rs`. ⚠️ If you are coming from JavaScript, you might note that when we add the dependency -there is no `^` or `~` symbol- it looks like we're locking to the `0.2` version. +there is no `^` or `~` symbol- it looks like we're locking to the `0.2` version. However, that's not the case! In Rust, the `^` is implied. You can read more about this in the [cargo documentation on specifying dependencies]. @@ -64,13 +65,14 @@ However, that's not the case! In Rust, the `^` is implied. You can read more abo ## 3. `[features]` and [`wee_alloc`], [`console_error_panic_hook`] dependencies -[`wee_alloc`]: https://github.com/rustwasm/wee_alloc -[`console_error_panic_hook`]: https://github.com/rustwasm/console_error_panic_hook +[`wee_alloc`]: https://crates.io/crates/wee_alloc +[`console_error_panic_hook`]: https://crates.io/crates/console_error_panic_hook +[`cfg-if`]: https://crates.io/crates/cfg-if As part of our effort to design a template that helps people discover useful crates for their particular use case, this template includes two dependencies that can be -very useful for folks developing Rust-`wasm` crates: `console-error-panic-hook` and -`wee-alloc`. +very useful for folks developing Rust-`wasm` crates:[ `console_error_panic_hook`] and +[`wee_alloc`]. Because these dependencies are useful primarily in a specific portion of the Rust-`wasm` crate development workflow, we've also set up a bit of glue code that allows us to include @@ -99,13 +101,13 @@ wee_alloc = { version = "0.4.2", optional = true } ``` [`cfg-if`] allows us to check if certain features are enabled on a Rust crate. We'll -use this crate later to optionally enable `console_error_panic_hook` or +use this crate later to optionally enable [`console_error_panic_hook` or `wee_alloc`. By default, only `console_error_panic_hook` is enabled. To disable either feature, we can remove its name from the `default` vector. -To learn more about these features, we discuss them in-depth in the [`src/lib.rs`] and +To learn more about these features, we discuss them in-depth in the [`src/lib.rs`] and [`src/utils.rs`] sections. [`src/lib.rs`]: src-lib-rs.html diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/index.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/index.md index 5cb3392c..17ee68b6 100644 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive/index.md +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/index.md @@ -9,8 +9,10 @@ may look slightly different than what is described here. ### What the Template Gave Us -Let's start by taking a look at what the template generated for us. +Let's start by taking a look at what the template generated for us. -- [`Cargo.toml`](./cargo-toml.html) -- [`src/lib.rs`](./src-lib-rs.html) -- [`src/utils.rs`](./src-utils-rs.html) +- [`Cargo.toml` - the Cargo manifest](./cargo-toml.html) +- [`src/lib.rs` - main library module](./src-lib-rs.html) +- [`src/utils.rs` - a utility module](./src-utils-rs.html) +- [`wee_alloc` - a tiny memory allocator](./wee_alloc.html) +- [`tests/web.rs` - running headless browser tests](./tests-web-rs.html) diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md index 2a5a2fce..df36e4b9 100644 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md @@ -46,7 +46,7 @@ This is all you need to know to interface with JavaScript, at least to start! Yo If you are curious about the rest, read on. -## 2. Crate imports +## 2. Crate Organization ```rust mod utils; @@ -74,7 +74,6 @@ With this in mind, this `use` allows us to call the macro `cfg_if!` inside the c ```rust cfg_if! { if #[cfg(feature = "wee_alloc")] { - extern crate wee_alloc; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; } @@ -83,7 +82,11 @@ cfg_if! { We immediately notice that `cfg_if!` is a macro because it ends in `!`, similarly to other Rust macros such as `println!` and `vec!`. A macro is directly replaced by other code during compile time. -During compile time, `cfg_if!` evaluates the `if` statement. This tests whether the feature `wee_alloc` is present in the `[features]` section of `Cargo.toml` (among other possible ways to set it). +At compile time this will test if the `wee_alloc` feature is enabled for this +compilation. If it's enabled we'll configure a global allocator (according to +[`wee_alloc`'s docs][wee-alloc-docs]), otherwise it'll compile to nothing. + +[wee-alloc-docs]: https://docs.rs/wee_alloc/0.4.3/wee_alloc/ As we saw earlier, the `default` vector in `[features]` only contains `"console_error_panic_hook"` and not `"wee_alloc"`. So, in this case, the `cfg_if!` block will be replaced by no code at all, and hence the default memory allocator will be used instead of `wee_alloc`. @@ -91,7 +94,10 @@ As we saw earlier, the `default` vector in `[features]` only contains `"console_ use wasm_bindgen::prelude::*; ``` -Many modules contain a prelude, a list of things that should be automatically imported. This allows common features of the module to be conveniently accessed without a lengthy prefix. For example, in this file we can use `#[wasm_bindgen]` only because it is brought into scope by the prelude. +Many crates contain a prelude, a list of things that are convenient to import +all at once. This allows common features of the module to be conveniently +accessed without a lengthy prefix. For example, in this file we can use +`#[wasm_bindgen]` only because it is brought into scope by the prelude. The asterisk at the end of this `use` indicates that everything inside the module `wasm_bindgen::prelude` (i.e. the module `prelude` inside the crate `wasm_bindgen`) can be referred to without prefixing it with `wasm_bindgen::prelude`. diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md index 0ee271be..92b05442 100644 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md @@ -22,7 +22,6 @@ This allows us to write `cfg_if!` instead of `cfg_if::cfg_if!`, identically to t ```rust cfg_if! { if #[cfg(feature = "console_error_panic_hook")] { - extern crate console_error_panic_hook; pub use self::console_error_panic_hook::set_once as set_panic_hook; } else { #[inline] @@ -31,36 +30,52 @@ cfg_if! { } ``` -As described in the preceding section, the macro `cfg_if!` evaluates the `if` statement during compile time. This is possible because it is essentially testing whether `"console_error_panic_hook"` is defined in the `[features]` section of `Cargo.toml`, which is available during compile time. - -The entire macro block will either be replaced with the statements in the `if` block or with those in the `else` block. These two cases are now described in turn: +As described in the preceding section, this invocation of the `cfg_if!` +tests whether the `console_error_panic_hook` feature is enabled at compile time, +replacing with the statements in the `if` block or with those in the `else` +block. These two cases are: ```rust -extern crate console_error_panic_hook; pub use self::console_error_panic_hook::set_once as set_panic_hook; ``` -Due to the `use` statement, the function `self::console_error_panic_hook::set_once` can now be accessed more conveniently as `set_panic_hook`. Due to `pub`, this function will be publicly accessible outside of the `utils` module as `utils::set_panic_hook`. +This `use` statement means the function +`self::console_error_panic_hook::set_once` can now be accessed more conveniently +as `set_panic_hook`. With `pub`, this function will be accessible +outside of the `utils` module as `utils::set_panic_hook`. ```rust #[inline] pub fn set_panic_hook() {} ``` -An inline function replaces the function call with the contents of the function during compile time. Here, `set_panic_hook` is defined to be an empty inline function. This allows the use of `set_panic_hook` without any run-time or code-size performance penalty if the feature is not enabled. +Here, `set_panic_hook` is defined to be an empty inline function. The inline +annotation here means that whenever the function is called the function call is +replaced with the body of the function, which is for `set_panic_hook` nothing! +This allows the use of `set_panic_hook` without any run-time or code-size +performance penalty if the feature is not enabled. ## 2. What is `console_error_panic_hook`? -The crate `console_error_panic_hook` enhances error messages in the web browser. This allows you to easily debug WebAssembly code. +The [crate `console_error_panic_hook`][ceph] allows debugging Rust panic +messages in a web browser, making it much easier to debug WebAssembly code. -Let's compare error messages before and after enabling the feature: +Let's compare what happens when Rust code panics before and after enabling the +feature: **Before:** `"RuntimeError: Unreachable executed"` **After:** `"panicked at 'index out of bounds: the len is 3 but the index is 4', libcore/slice/mod.rs:2046:10"` -To do this, a panic hook for WebAssembly is provided that logs panics to the developer console via the JavaScript `console.error` function. +To do this, a [panic hook] is configured that logs panics to the +developer console via the JavaScript `console.error` function. + +Note though that `console_error_panic_hook` is not entirely automatic, so you'll +need to make sure that `utils::set_panic_hook()` is called before any of our +code runs (and it's safe to run `set_panic_hook` many times). -Note that although the template sets up the function, your error messages will not automatically be enhanced. To enable the enhanced errors, call the function `utils::set_panic_hook()` in `lib.rs` when your code first runs. The function may be called multiple times if needed. +For more details, see the [`console_error_panic_hook` +repository](https://github.com/rustwasm/console_error_panic_hook). -For more details, see the [`console_error_panic_hook` repository](https://github.com/rustwasm/console_error_panic_hook). +[ceph]: https://crates.io/crates/console_error_panic_hook +[panic hook]: https://doc.rust-lang.org/std/panic/fn.set_hook.html diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/tests-web-rs.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/tests-web-rs.md new file mode 100644 index 00000000..aa36ce63 --- /dev/null +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/tests-web-rs.md @@ -0,0 +1,74 @@ +# tests/web.rs + +`web.rs` is an integration test [defined with Cargo][cargo-tests] that is +intended to be run in a headless web browser via the `wasm-pack test` command. + +[cargo-tests]: https://doc.rust-lang.org/cargo/guide/tests.html + +It contains three key parts: + +1. [`#[wasm_bindgen_test] functions`](#a1-wasm_bindgen_test-functions) +2. [Crate Configuration](#a2-crate-configuration) +3. [`#![cfg]` directives](#a3-cfg-directives) + +--- + +## 1. `#[wasm_bindgen_test]` functions + +The `#[wasm_bindgen_test]` is like the [normal Rust `#[test]` +attribute][rust-test], except it defines a test accessible to WebAssembly and +headless web browser testing. + +> **Note**: Eventually `#[test]` will work with WebAssembly as well! Currently +> though [custom test frameworks][ctf] are not stable. + +[rust-test]: https://doc.rust-lang.org/book/ch11-01-writing-tests.html +[ctf]: https://github.com/rust-lang/rust/issues/50297 + +```rust +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1 + 1, 2); +} +``` + +Here the `pass` function is a unit test which asserts that arithmetic works in +WebAssembly like we'd expect everywhere else. If the test panics (such as the +`assert_eq!` being false) then the test will fail, otherwise the test will +succeed. + +The [reference documentation for `#[wasm_bindgen_test]`][wbg-test] should have +more information about defining these tests. + +[wbg-test]: https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html + +## 2. Crate Configuration + +Other than the test in this module, we'll also see: + +```rust +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); +``` + +Like we saw earlier in `src/lib.rs` the `*` import pulls in everything from +`wasm_bindgen_test`, notably the `wasm_bindgen_test_configure` macro and the +`wasm_bindgen_test` attribute. + +The `wasm_bindgen_test_configure` macro (denoted by ending in `!`) is used to +indicate that the test is intended to execute in a web browser as opposed to +Node.js, which is the default. + +## 3. `#![cfg]` directives + +The last part we'll notice about this crate is this statement at the top: + +```rust +#![cfg(target_arch = "wasm32")] +``` + +This statement means that the test is only intended for the `wasm32` +architecture, or the `wasm32-unknown-unknown` target. This enables `cargo test` +to work in your project if the library is also being developed for other +platforms by ensuring that these tests only execute in a web browser. diff --git a/docs/src/tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md b/docs/src/tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md index a3571a6e..8dd741d4 100644 --- a/docs/src/tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md +++ b/docs/src/tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md @@ -6,17 +6,22 @@ ## What is `wee_alloc`? -Reducing the size of compiled WebAssembly code is important, since it is often transmitted over the Internet or placed on embedded devices. - -*Want to learn more about code sizein the rustwasm toolchain? Check out this [documentation](https://rustwasm.github.io/docs/book/reference/code-size.html). +WebAssembly code is frequently transmitted over the wire to users, so compiled +code size is often important to ensure an application loads quickly and is +responsive. > `wee_alloc` is a tiny allocator designed for WebAssembly that has a (pre-compression) code-size footprint of only a single kilobyte. [An analysis](http://fitzgeraldnick.com/2018/02/09/wee-alloc.html) suggests that over half of the bare minimum WebAssembly memory footprint is required by Rust's default memory allocator. Yet, WebAssembly code often does not require a sophisticated allocator, since it often just requests a couple of large initial allocations. -`wee_alloc` trades off size for speed. Although it has a tiny code-size footprint, it is relatively slow if additional allocations are needed. +`wee_alloc` trades off size for speed. It has a tiny code-size +footprint, but it is is not competitive in terms of performance with the +default global allocator, for example. -For even more details, see the [`wee_alloc` repository](https://github.com/rustwasm/wee_alloc). +For even more details, see the [`wee_alloc` +repository](https://github.com/rustwasm/wee_alloc), or +[general documentation](https://rustwasm.github.io/docs/book/reference/code-size.html) about +shrinking code size of WebAssembly binaries. ## Enabling `wee_alloc` @@ -25,32 +30,23 @@ In `lib.rs`, we have the configuration for `wee_alloc` inside a `cfg_if!` macro: ```rust cfg_if! { if #[cfg(feature = "wee_alloc")] { - extern crate wee_alloc; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; } } ``` -This code block is intended to initialize `wee_alloc` as the global memory allocator, but only if the `wee_alloc` feature is enabled in `Cargo.toml`. - -To do so we need to append `"wee_alloc"` to the `default` vector in `Cargo.toml`. Then, the `cfg_if!` block is replaced with the contents of the `if` block, shown above. +This code block is intended to initialize `wee_alloc` as the global memory +allocator, but only if the `wee_alloc` feature is enabled at compile time. The +feature can be enabled by passing extra options while building: -```toml -[features] -default = ["console_error_panic_hook", "wee_alloc"] ``` - -## Rust nightly - -`wee_alloc` currently relies on features only available in Rust nightly. As such it requires you to use the nightly toolchain for compilation. If you have [Rustup](https://rustup.rs/) set up, you can install the nightly toolchain as follows: - -``` -rustup toolchain add nightly +$ wasm-pack build -- --features wee_alloc ``` -To use `wasm-pack` with Rust nightly run: +or alternatively you could turn it on by default in `Cargo.toml`: -``` -rustup run nightly wasm-pack build +```toml +[features] +default = ["console_error_panic_hook", "wee_alloc"] ``` diff --git a/docs/src/tutorials/npm-browser-packages/testing-your-project.md b/docs/src/tutorials/npm-browser-packages/testing-your-project.md new file mode 100644 index 00000000..2d1e2a56 --- /dev/null +++ b/docs/src/tutorials/npm-browser-packages/testing-your-project.md @@ -0,0 +1,45 @@ +# Testing your project + +Now after writing and building code, let's actually execute it! You can execute +tests with: + +```bash +$ wasm-pack test --firefox +[INFO]: Checking for the Wasm target... + Finished dev [unoptimized + debuginfo] target(s) in 0.02s + Running target/wasm32-unknown-unknown/debug/deps/web-9e7d380f8600b08e.wasm +Interactive browsers tests are now available at http://127.0.0.1:8000 + +Note that interactive mode is enabled because `NO_HEADLESS` +is specified in the environment of this process. Once you're +done with testing you'll need to kill this server with +Ctrl-C. +``` + +The console won't finish just yet, but as indicated you can visit +http://127.0.0.1:8000 in your web browser to see the test output: + +``` +running 1 test + +test web::pass ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored +``` + +and we've now executed our first tests in a web browser! + +If you'd like to execute tests in a headless web browser (you don't need to +manually visit a page) you can do: + +```bash +$ wasm-pack test --headless --firefox +``` + +and similarly if you're developing a project for Node.js you can also execute +`wasm-pack test --nodejs` to run tests in Node. + +Be sure to see the [testing reference documentation][testing-reference] for +other supported features as well! + +[testing-reference]: https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html diff --git a/src/command/build.rs b/src/command/build.rs index 16ee9e75..455bb27f 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -265,38 +265,27 @@ impl Build { }; ($($name:ident,)*) => (steps![$($name),*]) } + let mut steps = Vec::new(); match &mode { - BuildMode::Normal => steps![ - step_check_rustc_version, - step_check_crate_config, - step_check_for_wasm_target, - step_build_wasm, - step_create_dir, - step_copy_readme, - step_copy_license, - step_install_wasm_bindgen, - step_run_wasm_bindgen, - step_create_json, - ], - BuildMode::Noinstall => steps![ - step_check_rustc_version, - step_check_crate_config, - step_build_wasm, - step_create_dir, - step_copy_readme, - step_copy_license, - step_run_wasm_bindgen, - step_create_json, - ], - BuildMode::Force => steps![ - step_build_wasm, - step_create_dir, - step_copy_readme, - step_copy_license, - step_run_wasm_bindgen, - step_create_json, - ], + BuildMode::Force => {} + _ => { + steps.extend(steps![ + step_check_rustc_version, + step_check_crate_config, + step_check_for_wasm_target, + ]); + } } + steps.extend(steps![ + step_build_wasm, + step_create_dir, + step_copy_readme, + step_copy_license, + step_install_wasm_bindgen, + step_run_wasm_bindgen, + step_create_json, + ]); + steps } fn step_check_rustc_version(&mut self) -> Result<(), Error> { diff --git a/tests/all/build.rs b/tests/all/build.rs index a0361ae7..9bcbcb0a 100644 --- a/tests/all/build.rs +++ b/tests/all/build.rs @@ -212,3 +212,29 @@ fn build_with_arbitrary_cargo_options() { .assert() .success(); } + +#[test] +fn build_no_install() { + let fixture = utils::fixture::js_hello_world(); + fixture.install_local_wasm_bindgen(); + fixture + .wasm_pack() + .arg("build") + .arg("--mode") + .arg("no-install") + .assert() + .success(); +} + +#[test] +fn build_force() { + let fixture = utils::fixture::js_hello_world(); + fixture.install_local_wasm_bindgen(); + fixture + .wasm_pack() + .arg("build") + .arg("--mode") + .arg("force") + .assert() + .success(); +}