use tar;
use ureq;
use zstd;
use crate::config;
static CMAKE_NAME: &str = "cmake";
static CMAKE_VERSION: &str = "3.31.5";
static CMAKE_TAG: &str = "cmake3.31.5";
static CMAKE_URL: &str = "https://github.com/ascpkg/asc-downloads/releases/download";
static CMAKE_ZST_SHA1: [(&str, &str); 5] = [
(
"cmake-3.31.5-windows-x86_64.tar.zst",
"27858c730d087790a73ee24e0558c910dc6fe7cf",
),
(
"cmake-3.31.5-windows-arm64.tar.zst",
"e101eb3330ac1400a127dcdcb259df98c5a23909",
),
(
"cmake-3.31.5-macos-universal.tar.zst",
"7f9cfe68d318340e6134bf94c699e0320623b366",
),
(
"cmake-3.31.5-linux-x86_64.tar.zst",
"7d48c9fbf085fbbbfce8bfb255c32466601d47e1",
),
(
"cmake-3.31.5-linux-aarch64.tar.zst",
"c859bf689e074bcf403b80b882c03cdd800f4bf8",
),
];
pub fn download_cmake_if_not_exists() -> String {
let name = CMAKE_NAME;
let version = CMAKE_VERSION;
let tag = CMAKE_TAG;
let url_prefix = CMAKE_URL;
let bin_dir = config::system_paths::DataPath::bin_cmake_dir();
let (zst_name, url, tar_path, bin_path) = if cfg!(target_os = "windows") {
let arch = match std::env::consts::ARCH {
"x86_64" => "x86_64",
"aarch64" => "arm64",
name => {
panic!("unsupported arch {name}");
}
};
let file_name = format!("{name}-{version}-windows-{arch}");
(
format!("{file_name}.tar.zst"),
format!("{url_prefix}/{tag}/{file_name}.tar.zst"),
format!("{bin_dir}/{file_name}.tar"),
format!("{bin_dir}/{file_name}/bin/{CMAKE_NAME}.exe"),
)
} else if cfg!(target_os = "macos") {
let arch = match std::env::consts::ARCH {
"x86_64" => "universal",
"aarch64" => "universal",
name => {
panic!("unsupported arch {name}");
}
};
let file_name = format!("{name}-{version}-macos-{arch}");
(
format!("{file_name}.tar.zst"),
format!("{url_prefix}/{tag}/{file_name}.tar.zst"),
format!("{bin_dir}/{file_name}.tar"),
format!("{bin_dir}/{file_name}/bin/{CMAKE_NAME}.app/Contents/bin/cmake"),
)
} else {
let arch = match std::env::consts::ARCH {
"x86_64" => "x86_64",
"aarch64" => "aarch64",
name => {
panic!("unsupported arch {name}");
}
};
let file_name = format!("{name}-{version}-linux-{arch}");
(
format!("{file_name}.tar.zst"),
format!("{url_prefix}/{tag}/{file_name}.tar.zst"),
format!("{bin_dir}/{file_name}.tar"),
format!("{bin_dir}/{file_name}/bin/{CMAKE_NAME}"),
)
};
let zst_path = format!("{tar_path}.tar.zst");
let info = format!("url: '{url}', tar_path: '{tar_path}'");
let zst_sha1 = std::collections::HashMap::from(CMAKE_ZST_SHA1);
let meta = std::fs::metadata(&zst_path);
if meta.is_err()
|| !meta.as_ref().unwrap().is_file()
|| &crate::util::hash::file_sha1_sum(&zst_path).as_str()
!= zst_sha1.get(zst_name.as_str()).unwrap_or(&"")
{
for _ in 0..3 {
tracing::info!(message = "downloading", url = url);
if meta.as_ref().is_ok() {
let _ = std::fs::remove_file(&zst_path);
}
let agent = ureq::AgentBuilder::new()
.try_proxy_from_env(true)
.timeout_read(std::time::Duration::from_secs(15))
.timeout_write(std::time::Duration::from_secs(5))
.build();
let response = agent
.get(&url)
.call()
.expect(&format!("ureq::get error, {info}"));
let mut zst_file = std::fs::File::create(&zst_path)
.expect(&format!("std::fs::File::create error, {info}"));
std::io::copy(&mut response.into_reader(), &mut zst_file)
.expect(&format!("std::io::copy error, {info}"));
let calculated_sha1 = crate::util::hash::file_sha1_sum(&zst_path);
if let Some(expected_sha1) = zst_sha1.get(zst_name.as_str()) {
if &calculated_sha1.as_str() != expected_sha1 {
tracing::error!(
message = "sha1 mismatch",
expected = expected_sha1,
calculated = calculated_sha1
);
continue;
}
}
break;
}
}
let meta = std::fs::metadata(&zst_path);
if meta.is_ok() && meta.unwrap().is_file() {
let calculated_sha1 = crate::util::hash::file_sha1_sum(&zst_path);
if let Some(expected_sha1) = zst_sha1.get(zst_name.as_str()) {
if &calculated_sha1.as_str() != expected_sha1 {
tracing::error!(
message = "sha1 mismatch",
expected = expected_sha1,
calculated = calculated_sha1
);
}
}
}
let meta = std::fs::metadata(&tar_path);
if meta.is_err() || !meta.unwrap().is_file() {
tracing::info!(message = "extracting", zst = zst_path);
let zst_file =
std::fs::File::open(zst_path).expect(&format!("std::fs::File::open error, {info}"));
let output_file = std::fs::File::create(&tar_path).expect(&format!(
"std::fs::File::create({:#?}) error, {info}",
tar_path
));
zstd::stream::copy_decode(zst_file, output_file)
.expect(&format!("zstd::stream::copy_decode error, {info}"));
}
let meta = std::fs::metadata(&bin_path);
if meta.is_err() || !meta.unwrap().is_file() {
tracing::info!(message = "extracting", tar = tar_path);
let mut tar = tar::Archive::new(std::fs::File::open(&tar_path).unwrap());
tar.unpack(&bin_dir)
.expect(&format!("tar::Archive::unpack error, {info}"));
}
return bin_path;
}