refactor: Move binary installation to it's own crate

master
Jesper Håkansson 7 years ago
parent ceb4779723
commit a4b29a9293
  1. 14
      Cargo.lock
  2. 4
      Cargo.toml
  3. 14
      binary-install/Cargo.toml
  4. 44
      binary-install/src/error.rs
  5. 47
      binary-install/src/fetch.rs
  6. 143
      binary-install/src/lib.rs
  7. 76
      binary-install/src/path.rs
  8. 8
      binary-install/src/target.rs
  9. 247
      src/binaries.rs
  10. 7
      src/bindgen.rs
  11. 2
      src/lib.rs
  12. 9
      src/test/webdriver.rs
  13. 1
      tests/all/main.rs
  14. 4
      tests/all/test.rs
  15. 27
      tests/all/utils/fixture.rs

14
Cargo.lock generated

@ -50,6 +50,19 @@ dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "binary-install"
version = "0.1.0"
dependencies = [
"curl 0.4.18 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tar 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"which 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.4"
@ -970,6 +983,7 @@ name = "wasm-pack"
version = "0.5.1"
dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"binary-install 0.1.0",
"cargo_metadata 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"console 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"curl 0.4.18 (registry+https://github.com/rust-lang/crates.io-index)",

@ -9,6 +9,9 @@ readme = "README.md"
categories = ["wasm"]
documentation = "https://rustwasm.github.io/wasm-pack/"
[workspace]
members = [".", "binary-install"]
[dependencies]
atty = "0.2.11"
cargo_metadata = "0.6.0"
@ -32,6 +35,7 @@ tar = "0.4.16"
toml = "0.4"
which = "2.0.0"
zip = "0.4.2"
binary-install = { version = "0.1.0", path = "./binary-install" }
[dev-dependencies]
tempfile = "3"

@ -0,0 +1,14 @@
[package]
name = "binary-install"
version = "0.1.0"
authors = ["Jesper Håkansson <jesper@jesperh.se>"]
[dependencies]
curl = "0.4.13"
failure = "0.1.2"
flate2 = "1.0.2"
slog = "2.3"
tar = "0.4.16"
which = "2.0.0"
zip = "0.4.2"

@ -0,0 +1,44 @@
//! Code related to error handling for binary-install
/// Errors that can potentially occur in `binary-install`.
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "{}", message)]
/// An error related to an archive that we downloaded.
Archive {
/// Error message.
message: String,
},
#[fail(display = "{}", message)]
/// Error related to some HTTP request.
Http {
/// Error message.
message: String,
},
}
impl Error {
/// Construct an archive error.
pub fn archive(message: &str) -> Self {
Error::Archive {
message: message.to_string(),
}
}
/// Construct an http error.
pub fn http(message: &str) -> Self {
Error::Http {
message: message.to_string(),
}
}
/// Get a string description of this error's type.
pub fn error_type(&self) -> String {
match self {
Error::Archive { .. } => "There was an error related to an archive file. Details:\n\n",
Error::Http { .. } => "There wasn an HTTP error. Details:\n\n",
}
.to_string()
}
}

@ -0,0 +1,47 @@
use error::Error;
use failure;
pub 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())
}
}
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(())
}

@ -0,0 +1,143 @@
//! Utilities for finding and installing binaries that we depend on.
extern crate curl;
#[macro_use]
extern crate failure;
extern crate flate2;
#[macro_use]
extern crate slog;
extern crate tar;
extern crate which;
extern crate zip;
pub mod error;
pub mod path;
mod fetch;
mod target;
use error::Error;
use fetch::curl;
use path::{ensure_local_bin_dir, local_bin_dir};
use std::collections::HashSet;
use std::ffi;
use std::fs;
use std::io;
use std::path::Path;
/// 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<(), failure::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(", "),
))
.into())
}
}
/// 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<(), failure::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(", "),
))
.into())
}
}
#[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()
}

@ -0,0 +1,76 @@
use slog::Logger;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use target;
use which::which;
/// 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
})
}

@ -0,0 +1,8 @@
//! Information about the target wasm-pack is currently being compiled for.
//!
//! That is, whether we are building wasm-pack for windows vs linux, and x86 vs
//! x86-64, etc.
#![allow(missing_docs)]
pub const WINDOWS: bool = cfg!(target_os = "windows");

@ -1,247 +0,0 @@
//! Utilities for finding and installing binaries that we depend on.
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<(), failure::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(", "),
))
.into())
}
}
/// 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<(), failure::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(", "),
))
.into())
}
}
#[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()
}

@ -1,6 +1,9 @@
//! Functionality related to installing and running `wasm-bindgen`.
use binaries::{self, bin_path, install_binaries_from_targz_at_url};
use binary_install::{
install_binaries_from_targz_at_url,
path::{bin_path, local_bin_path},
};
use child;
use emoji;
use error::Error;
@ -102,7 +105,7 @@ pub fn cargo_install_wasm_bindgen(
.arg(crate_path);
child::run(logger, cmd, "cargo install").context("Installing wasm-bindgen with cargo")?;
assert!(binaries::local_bin_path(crate_path, "wasm-bindgen").is_file());
assert!(local_bin_path(crate_path, "wasm-bindgen").is_file());
Ok(())
}

@ -19,6 +19,7 @@ extern crate serde_json;
extern crate structopt;
#[macro_use]
extern crate slog;
extern crate binary_install;
extern crate slog_async;
extern crate slog_term;
extern crate tar;
@ -26,7 +27,6 @@ extern crate toml;
extern crate which;
extern crate zip;
pub mod binaries;
pub mod bindgen;
pub mod build;
pub mod child;

@ -1,7 +1,8 @@
//! Getting WebDriver client binaries.
use binaries::{
self, bin_path, install_binaries_from_targz_at_url, install_binaries_from_zip_at_url,
use binary_install::{
install_binaries_from_targz_at_url, install_binaries_from_zip_at_url,
path::{bin_path, local_bin_path},
};
use command::build::BuildMode;
use error::Error;
@ -31,7 +32,7 @@ pub fn get_or_install_chromedriver(
}
fn get_local_chromedriver_path(crate_path: &Path) -> PathBuf {
binaries::local_bin_path(crate_path, "chromedriver")
local_bin_path(crate_path, "chromedriver")
}
fn get_chromedriver_url() -> Result<String, Error> {
@ -106,7 +107,7 @@ fn get_geckodriver_url() -> Result<String, Error> {
}
fn get_local_geckodriver_path(crate_path: &Path) -> PathBuf {
binaries::local_bin_path(crate_path, "geckodriver")
local_bin_path(crate_path, "geckodriver")
}
/// Download and install a pre-built `geckodriver` binary.

@ -4,6 +4,7 @@ extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate slog;
extern crate binary_install;
extern crate structopt;
extern crate tempfile;
extern crate wasm_pack;

@ -1,8 +1,8 @@
use binary_install;
use std::env;
use std::fs;
use tempfile;
use utils::fixture;
use wasm_pack::binaries;
use wasm_pack::command::{self, build, test, Command};
use wasm_pack::logger;
@ -116,7 +116,7 @@ fn it_can_find_a_webdriver_on_path() {
fixture.install_local_geckodriver();
let geckodriver_dir = tempfile::TempDir::new().unwrap();
let local_geckodriver = binaries::local_bin_path(&fixture.path, "geckodriver");
let local_geckodriver = binary_install::path::local_bin_path(&fixture.path, "geckodriver");
fs::copy(
&local_geckodriver,
geckodriver_dir

@ -1,4 +1,5 @@
use super::logger::null_logger;
use binary_install;
use std::env;
use std::fs;
use std::io;
@ -138,9 +139,9 @@ impl Fixture {
static INSTALL_WASM_BINDGEN: Once = ONCE_INIT;
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
let shared_wasm_bindgen = wasm_pack::binaries::local_bin_path(&tests, "wasm-bindgen");
let shared_wasm_bindgen = binary_install::path::local_bin_path(&tests, "wasm-bindgen");
let shared_wasm_bindgen_test_runner =
wasm_pack::binaries::local_bin_path(&tests, "wasm-bindgen-test-runner");
binary_install::path::local_bin_path(&tests, "wasm-bindgen-test-runner");
INSTALL_WASM_BINDGEN.call_once(|| {
if shared_wasm_bindgen.is_file() {
@ -163,17 +164,17 @@ impl Fixture {
assert!(shared_wasm_bindgen.is_file());
assert!(shared_wasm_bindgen_test_runner.is_file());
wasm_pack::binaries::ensure_local_bin_dir(&self.path).unwrap();
binary_install::path::ensure_local_bin_dir(&self.path).unwrap();
hard_link_or_copy(
&shared_wasm_bindgen,
wasm_pack::binaries::local_bin_path(&self.path, "wasm-bindgen"),
binary_install::path::local_bin_path(&self.path, "wasm-bindgen"),
)
.expect("could not copy `wasm-bindgen` to fixture directory");
hard_link_or_copy(
&shared_wasm_bindgen_test_runner,
wasm_pack::binaries::local_bin_path(&self.path, "wasm-bindgen-test-runner"),
binary_install::path::local_bin_path(&self.path, "wasm-bindgen-test-runner"),
)
.expect("could not copy `wasm-bindgen-test` to fixture directory");
@ -189,10 +190,10 @@ impl Fixture {
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
wasm_pack::binaries::ensure_local_bin_dir(&tests)
binary_install::path::ensure_local_bin_dir(&tests)
.expect("could not create fixture's `bin` directory");
let geckodriver = wasm_pack::binaries::local_bin_path(&tests, "geckodriver");
let geckodriver = binary_install::path::local_bin_path(&tests, "geckodriver");
FETCH_GECKODRIVER.call_once(|| {
if geckodriver.is_file() {
@ -203,12 +204,12 @@ impl Fixture {
assert!(geckodriver.is_file());
});
wasm_pack::binaries::ensure_local_bin_dir(&self.path)
binary_install::path::ensure_local_bin_dir(&self.path)
.expect("could not create fixture's `bin` directory");
hard_link_or_copy(
&geckodriver,
wasm_pack::binaries::local_bin_path(&self.path, "geckodriver"),
binary_install::path::local_bin_path(&self.path, "geckodriver"),
)
.expect("could not copy `geckodriver` to fixture directory");
@ -224,10 +225,10 @@ impl Fixture {
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
wasm_pack::binaries::ensure_local_bin_dir(&tests)
binary_install::path::ensure_local_bin_dir(&tests)
.expect("could not create fixture's `bin` directory");
let chromedriver = wasm_pack::binaries::local_bin_path(&tests, "chromedriver");
let chromedriver = binary_install::path::local_bin_path(&tests, "chromedriver");
FETCH_CHROMEDRIVER.call_once(|| {
if chromedriver.is_file() {
@ -238,12 +239,12 @@ impl Fixture {
assert!(chromedriver.is_file());
});
wasm_pack::binaries::ensure_local_bin_dir(&self.path)
binary_install::path::ensure_local_bin_dir(&self.path)
.expect("could not create fixture's `bin` directory");
hard_link_or_copy(
&chromedriver,
wasm_pack::binaries::local_bin_path(&self.path, "chromedriver"),
binary_install::path::local_bin_path(&self.path, "chromedriver"),
)
.expect("could not copy `chromedriver` to fixture directory");

Loading…
Cancel
Save