use std::path::{Path, PathBuf};
pub const ENTITLEMENTS_PLIST: &str =
include_str!("../entitlements.plist");
#[derive(Debug, Clone)]
pub struct AssetPaths {
pub kernel: Option<PathBuf>,
pub init_oci_bin: Option<PathBuf>,
pub init_oci_src: Option<PathBuf>,
pub supermachine_agent: Option<PathBuf>,
}
impl AssetPaths {
pub fn discover() -> Self {
if let Some(dir) = std::env::var_os("SUPERMACHINE_ASSETS_DIR") {
let dir = PathBuf::from(dir);
return Self::from_dir(&dir);
}
let exe = std::env::current_exe().ok();
if let Some(exe) = exe.as_deref() {
if let Some(macos_dir) = exe.parent() {
if macos_dir.file_name().and_then(|s| s.to_str()) == Some("MacOS") {
if let Some(contents) = macos_dir.parent() {
let res = contents.join("Resources");
let probe = Self::from_dir(&res);
if probe.kernel.is_some() {
return probe;
}
}
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let share = ancestor.join("share/supermachine");
if share.join("kernel").is_file() {
return Self::from_dir(&share);
}
}
}
if let Some(exe) = exe.as_deref() {
for ancestor in exe.ancestors() {
let kernel_crate = ancestor.join("crates/supermachine-kernel");
let main_crate = ancestor.join("crates/supermachine");
let agent_crate = ancestor.join("crates/supermachine-guest-agent");
if kernel_crate.join("kernel").is_file() {
return Self {
kernel: Some(kernel_crate.join("kernel")),
init_oci_bin: Some(main_crate.join("oci/init-oci"))
.filter(|p| p.is_file()),
init_oci_src: Some(main_crate.join("oci/init-oci.c"))
.filter(|p| p.is_file()),
supermachine_agent: Some(agent_crate.join(
"target/aarch64-unknown-linux-musl/release/supermachine-agent",
))
.filter(|p| p.is_file()),
};
}
}
}
if let Some(dir) = ensure_bundled_extracted() {
let probe = Self::from_dir(&dir);
if probe.kernel.is_some() {
return probe;
}
}
Self {
kernel: None,
init_oci_bin: None,
init_oci_src: None,
supermachine_agent: None,
}
}
pub fn from_dir(dir: &Path) -> Self {
Self {
kernel: Some(dir.join("kernel")).filter(|p| p.is_file()),
init_oci_bin: Some(dir.join("init-oci")).filter(|p| p.is_file()),
init_oci_src: Some(dir.join("init-oci.c")).filter(|p| p.is_file()),
supermachine_agent: Some(dir.join("supermachine-agent"))
.filter(|p| p.is_file()),
}
}
pub fn from_app_bundle(app: &Path) -> Self {
Self::from_dir(&app.join("Contents/Resources"))
}
}
fn ensure_bundled_extracted() -> Option<PathBuf> {
let dir = bundled_assets_dir()?;
let kernel_dst = dir.join("kernel");
let init_dst = dir.join("init-oci");
let agent_dst = dir.join("supermachine-agent");
if kernel_dst.is_file() && init_dst.is_file() && agent_dst.is_file() {
return Some(dir);
}
if std::fs::create_dir_all(&dir).is_err() {
return None;
}
if !kernel_dst.is_file() {
let tmp = dir.join("kernel.partial");
if std::fs::write(&tmp, supermachine_kernel::KERNEL_BYTES).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
if std::fs::rename(&tmp, &kernel_dst).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
}
if !init_dst.is_file() {
let tmp = dir.join("init-oci.partial");
if supermachine_kernel::extract_init_oci_to(&tmp).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
if std::fs::rename(&tmp, &init_dst).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
}
if !agent_dst.is_file() {
let tmp = dir.join("supermachine-agent.partial");
if supermachine_kernel::extract_supermachine_agent_to(&tmp).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
if std::fs::rename(&tmp, &agent_dst).is_err() {
let _ = std::fs::remove_file(&tmp);
return None;
}
}
Some(dir)
}
fn bundled_assets_dir() -> Option<PathBuf> {
let base = if let Some(d) = std::env::var_os("XDG_DATA_HOME") {
PathBuf::from(d)
} else if let Some(h) = std::env::var_os("HOME") {
PathBuf::from(h).join(".local/share")
} else {
return None;
};
Some(
base.join("supermachine")
.join(format!("v{}", env!("CARGO_PKG_VERSION"))),
)
}