use sha2::{Digest, Sha256};
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
use yieldskill_runtime_darwin_amd64 as runtime;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
use yieldskill_runtime_darwin_arm64 as runtime;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use yieldskill_runtime_linux_amd64 as runtime;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
use yieldskill_runtime_linux_arm64 as runtime;
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
use yieldskill_runtime_windows_amd64 as runtime;
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
use yieldskill_runtime_windows_arm64 as runtime;
fn runtime_path() -> Result<PathBuf, String> {
let root = std::env::var_os("YIELD_RUNTIME_CACHE")
.map(PathBuf::from)
.unwrap_or_else(|| std::env::temp_dir().join("yieldskill"));
let name = if cfg!(windows) {
"yskill.exe"
} else {
"yskill"
};
let directory = root
.join(env!("CARGO_PKG_VERSION"))
.join(std::env::consts::ARCH);
let path = directory.join(name);
if verified(&path) {
return Ok(path);
}
fs::create_dir_all(&directory)
.map_err(|error| format!("could not create runtime cache: {error}"))?;
let temporary = directory.join(format!(".{name}.{}.tmp", std::process::id()));
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&temporary)
.map_err(|error| format!("could not stage packaged runtime: {error}"))?;
file.write_all(runtime::BYTES)
.and_then(|_| file.sync_all())
.map_err(|error| format!("could not write packaged runtime: {error}"))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&temporary, fs::Permissions::from_mode(0o755))
.map_err(|error| format!("could not mark packaged runtime executable: {error}"))?;
}
if let Err(error) = fs::rename(&temporary, &path) {
let _ = fs::remove_file(&temporary);
if !verified(&path) {
return Err(format!("could not install packaged runtime: {error}"));
}
}
if !verified(&path) {
return Err("packaged runtime checksum mismatch".to_string());
}
Ok(path)
}
fn verified(path: &Path) -> bool {
fs::read(path)
.map(|bytes| hex::encode(Sha256::digest(bytes)) == runtime::SHA256)
.unwrap_or(false)
}
fn main() {
let path = match runtime_path() {
Ok(path) => path,
Err(error) => {
eprintln!("yskill: {error}");
std::process::exit(1);
}
};
let args: Vec<_> = std::env::args_os().skip(1).collect();
let mut command = Command::new(path);
command.args(args);
if std::env::var_os("YIELD_LANGUAGE").is_none() {
command.env("YIELD_LANGUAGE", "rust");
}
if let Ok(launcher) = std::env::current_exe() {
command.env("YIELD_LAUNCHER_PATH", launcher);
}
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
let error = command.exec();
eprintln!("yskill: could not start packaged runtime: {error}");
std::process::exit(1)
}
#[cfg(windows)]
{
match command.status() {
Ok(status) => std::process::exit(status.code().unwrap_or(1)),
Err(error) => {
eprintln!("yskill: could not start packaged runtime: {error}");
std::process::exit(1);
}
}
}
}