parent
ceb4779723
commit
a4b29a9293
@ -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() |
|
||||||
} |
|
Loading…
Reference in new issue