mirror of
https://github.com/denoland/deno.git
synced 2024-12-29 18:49:07 -05:00
119 lines
3 KiB
Rust
119 lines
3 KiB
Rust
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||
|
|
||
|
use std::fs;
|
||
|
use std::path::Path;
|
||
|
use std::path::PathBuf;
|
||
|
use std::process::Command;
|
||
|
|
||
|
use deno_core::anyhow::bail;
|
||
|
use deno_core::anyhow::Context;
|
||
|
use deno_core::error::AnyError;
|
||
|
|
||
|
fn unzip_with_shell(
|
||
|
archive_path: &Path,
|
||
|
archive_data: &[u8],
|
||
|
dest_path: &Path,
|
||
|
) -> Result<(), AnyError> {
|
||
|
fs::write(archive_path, archive_data)?;
|
||
|
let unpack_status = if cfg!(windows) {
|
||
|
Command::new("tar.exe")
|
||
|
.arg("xf")
|
||
|
.arg(archive_path)
|
||
|
.arg("-C")
|
||
|
.arg(dest_path)
|
||
|
.spawn()
|
||
|
.map_err(|err| {
|
||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||
|
std::io::Error::new(
|
||
|
std::io::ErrorKind::NotFound,
|
||
|
"`tar.exe` was not found in your PATH",
|
||
|
)
|
||
|
} else {
|
||
|
err
|
||
|
}
|
||
|
})?
|
||
|
.wait()?
|
||
|
} else {
|
||
|
Command::new("unzip")
|
||
|
.current_dir(dest_path)
|
||
|
.arg(archive_path)
|
||
|
.spawn()
|
||
|
.map_err(|err| {
|
||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||
|
std::io::Error::new(
|
||
|
std::io::ErrorKind::NotFound,
|
||
|
"`unzip` was not found in your PATH, please install `unzip`",
|
||
|
)
|
||
|
} else {
|
||
|
err
|
||
|
}
|
||
|
})?
|
||
|
.wait()?
|
||
|
};
|
||
|
|
||
|
if !unpack_status.success() {
|
||
|
bail!("Failed to unpack archive.");
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn unzip(
|
||
|
archive_name: &str,
|
||
|
archive_data: &[u8],
|
||
|
dest_path: &Path,
|
||
|
) -> Result<(), AnyError> {
|
||
|
let mut archive = zip::ZipArchive::new(std::io::Cursor::new(archive_data))?;
|
||
|
archive
|
||
|
.extract(dest_path)
|
||
|
.with_context(|| format!("failed to extract archive: {archive_name}"))?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub struct UnpackArgs<'a> {
|
||
|
pub exe_name: &'a str,
|
||
|
pub archive_name: &'a str,
|
||
|
pub archive_data: &'a [u8],
|
||
|
pub is_windows: bool,
|
||
|
pub dest_path: &'a Path,
|
||
|
}
|
||
|
|
||
|
pub fn unpack_into_dir(args: UnpackArgs) -> Result<PathBuf, AnyError> {
|
||
|
let UnpackArgs {
|
||
|
exe_name,
|
||
|
archive_name,
|
||
|
archive_data,
|
||
|
is_windows,
|
||
|
dest_path,
|
||
|
} = args;
|
||
|
let exe_ext = if is_windows { "exe" } else { "" };
|
||
|
let archive_path = dest_path.join(exe_name).with_extension("zip");
|
||
|
let exe_path = dest_path.join(exe_name).with_extension(exe_ext);
|
||
|
assert!(!exe_path.exists());
|
||
|
|
||
|
let archive_ext = Path::new(archive_name)
|
||
|
.extension()
|
||
|
.and_then(|ext| ext.to_str())
|
||
|
.unwrap();
|
||
|
match archive_ext {
|
||
|
"zip" => match unzip(archive_name, archive_data, dest_path) {
|
||
|
Ok(()) if !exe_path.exists() => {
|
||
|
log::warn!("unpacking via the zip crate didn't produce the executable");
|
||
|
// No error but didn't produce exe, fallback to shelling out
|
||
|
unzip_with_shell(&archive_path, archive_data, dest_path)?;
|
||
|
}
|
||
|
Ok(_) => {}
|
||
|
Err(e) => {
|
||
|
log::warn!("unpacking via zip crate failed: {e}");
|
||
|
// Fallback to shelling out
|
||
|
unzip_with_shell(&archive_path, archive_data, dest_path)?;
|
||
|
}
|
||
|
},
|
||
|
ext => bail!("Unsupported archive type: '{ext}'"),
|
||
|
}
|
||
|
|
||
|
assert!(exe_path.exists());
|
||
|
Ok(exe_path)
|
||
|
}
|