Skip to content

Add a wasm-pack test subcommand #271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
install:
- ps: Install-Product node 10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not entirely sure how this works, but we might want to use a node version manager for this as well? there are windows ones, last time i tried (admittedly a while ago), i like nodist (which now that i link to it looks like it needs a maintainer...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd also like to not hardcode a version but specify LTS, not sure how Install Product works, but if we switched to a version manager i'm sure we could

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, Install-Product cannot do LTS.

As for using a node version manager, I'd like to punt on that until if/when it becomes an issue, unless you feel strongly about it. Tinkering with CI for a different OS is not fun...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing! i think that makes sense

- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- set RUST_BACKTRACE=1
- rustup target add wasm32-unknown-unknown --toolchain nightly
- rustc -V
- cargo -V

build: false

test_script:
- cargo test --locked -- --test-threads 1
- cargo test --locked

before_deploy:
- ps: |
Expand Down
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
target/
**/target
**/*.rs.bk
**/pkg
tests/fixtures/**/Cargo.lock
tests/.crates.toml
tests/bin
wasm-pack.log
13 changes: 12 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
language: rust
sudo: false

INSTALL_NODE_VIA_NVM: &INSTALL_NODE_VIA_NVM
|
rustup target add wasm32-unknown-unknown
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
source ~/.nvm/nvm.sh
nvm install lts/carbon

# Cache `cargo install`ed tools, but don't cache the project's `target`
# directory (which ends up over-caching and filling all disk space!)
cache:
Expand Down Expand Up @@ -32,9 +39,13 @@ matrix:
include:

# tests pass

- env: JOB=test RUST_BACKTRACE=1
rust: nightly
addons:
firefox: latest
chrome: stable
install:
- *INSTALL_NODE_VIA_NVM
script:
- cargo test --locked
- rustup component add rustfmt-preview
Expand Down
130 changes: 112 additions & 18 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ structopt = "0.2"
tar = "0.4.16"
toml = "0.4"
which = "2.0.0"
zip = "0.4.2"

[dev-dependencies]
copy_dir = "0.1.2"
Expand Down
243 changes: 243 additions & 0 deletions src/binaries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//! Utilities for finding and installing binaries that we depend on.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole file might be better as it's own crate.... not urgent, but curious what you think

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could. I don't have strong opinions either way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's land this but open an isue to potentially move it out. i could see it being useful for other CLI projects potentially (might even want to add it to cargo at somepoint?)


use curl;
use error::Error;
use failure;
use flate2;
use slog::Logger;
use std::collections::HashSet;
use std::ffi;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use tar;
use target;
use which::which;
use zip;

/// Get the path for a crate's directory of locally-installed binaries.
///
/// This does not check whether or ensure that the directory exists.
pub fn local_bin_dir(crate_path: &Path) -> PathBuf {
crate_path.join("bin")
}

/// Ensure that the crate's directory for locally-installed binaries exists.
pub fn ensure_local_bin_dir(crate_path: &Path) -> io::Result<()> {
fs::create_dir_all(local_bin_dir(crate_path))
}

/// Get the path for where `bin` would be if we have a crate-local install for
/// it.
///
/// This does *not* check whether there is a file at that path or not.
///
/// This will automatically add the `.exe` extension for windows.
pub fn local_bin_path(crate_path: &Path, bin: &str) -> PathBuf {
let mut p = local_bin_dir(crate_path).join(bin);
if target::WINDOWS {
p.set_extension("exe");
}
p
}

/// Get the local (at `$CRATE/bin/$BIN`; preferred) or global (on `$PATH`) path
/// for the given binary.
///
/// If this function returns `Some(path)`, then a file at that path exists (or
/// at least existed when we checked! In general, we aren't really worried about
/// racing with an uninstall of a tool that we rely on.)
pub fn bin_path(log: &Logger, crate_path: &Path, bin: &str) -> Option<PathBuf> {
assert!(!bin.ends_with(".exe"));
debug!(log, "Searching for {} binary...", bin);

// Return the path to the local binary, if it exists.
let local_path = |crate_path: &Path| -> Option<PathBuf> {
let p = local_bin_path(crate_path, bin);
debug!(log, "Checking for local {} binary at {}", bin, p.display());
if p.is_file() {
Some(p)
} else {
None
}
};

// Return the path to the global binary, if it exists.
let global_path = || -> Option<PathBuf> {
debug!(log, "Looking for global {} binary on $PATH", bin);
if let Ok(p) = which(bin) {
Some(p)
} else {
None
}
};

local_path(crate_path)
.or_else(global_path)
.map(|p| {
let p = p.canonicalize().unwrap_or(p);
debug!(log, "Using {} binary at {}", bin, p.display());
p
}).or_else(|| {
debug!(log, "Could not find {} binary.", bin);
None
})
}

fn with_url_context<T, E>(url: &str, r: Result<T, E>) -> Result<T, impl failure::Fail>
where
Result<T, E>: failure::ResultExt<T, E>,
{
use failure::ResultExt;
r.with_context(|_| format!("when requesting {}", url))
}

fn transfer(
url: &str,
easy: &mut curl::easy::Easy,
data: &mut Vec<u8>,
) -> Result<(), failure::Error> {
let mut transfer = easy.transfer();
with_url_context(
url,
transfer.write_function(|part| {
data.extend_from_slice(part);
Ok(part.len())
}),
)?;
with_url_context(url, transfer.perform())?;
Ok(())
}

fn curl(url: &str) -> Result<Vec<u8>, failure::Error> {
let mut data = Vec::new();

let mut easy = curl::easy::Easy::new();
with_url_context(url, easy.follow_location(true))?;
with_url_context(url, easy.url(url))?;
transfer(url, &mut easy, &mut data)?;

let status_code = with_url_context(url, easy.response_code())?;
if 200 <= status_code && status_code < 300 {
Ok(data)
} else {
Err(Error::http(&format!(
"received a bad HTTP status code ({}) when requesting {}",
status_code, url
)).into())
}
}

/// Download the `.tar.gz` file at the given URL and unpack the given binaries
/// from it into the given crate.
///
/// Upon success, every `$BIN` in `binaries` will be at `$CRATE/bin/$BIN`.
pub fn install_binaries_from_targz_at_url<'a, I>(
crate_path: &Path,
url: &str,
binaries: I,
) -> Result<(), Error>
where
I: IntoIterator<Item = &'a str>,
{
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();

let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..]));

ensure_local_bin_dir(crate_path)?;
let bin = local_bin_dir(crate_path);

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);
bin.join(entry.path()?.file_name().unwrap())
}
_ => continue,
};

entry.unpack(dest)?;
}

if binaries.is_empty() {
Ok(())
} else {
Err(Error::archive(&format!(
"the tarball at {} was missing expected executables: {}",
url,
binaries
.into_iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", "),
)))
}
}

/// Install binaries from within the given zip at the given URL.
///
/// Upon success, the binaries will be at the `$CRATE/bin/$BIN` path.
pub fn install_binaries_from_zip_at_url<'a, I>(
crate_path: &Path,
url: &str,
binaries: I,
) -> Result<(), Error>
where
I: IntoIterator<Item = &'a str>,
{
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();

let data = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
let data = io::Cursor::new(data);
let mut zip = zip::ZipArchive::new(data)?;

ensure_local_bin_dir(crate_path)?;
let bin = local_bin_dir(crate_path);

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(bin.join(entry_path.file_name().unwrap()))?;
io::copy(&mut entry, &mut dest)?;
}
_ => continue,
};
}

if binaries.is_empty() {
Ok(())
} else {
Err(Error::archive(&format!(
"the zip at {} was missing expected executables: {}",
url,
binaries
.into_iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", "),
)))
}
}

#[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()
}
Loading