use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
pub const LOADER_ENV: &str = "RUNNING_PROCESS_APE_LOADER";
pub const CACHE_DIR_ENV: &str = "RUNNING_PROCESS_APE_CACHE_DIR";
pub fn runs_natively() -> bool {
!running_process::ape::NEEDS_LOADER
}
pub fn is_ape_header(header: &[u8]) -> bool {
running_process::ape::is_ape_header(header)
}
pub fn is_ape_file(path: &Path) -> bool {
running_process::ape::is_ape_file(path)
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ApeOptions {
pub path: Option<OsString>,
pub loader: Option<OsString>,
pub cache_dirs: Vec<PathBuf>,
}
impl ApeOptions {
pub fn inherited() -> Self {
Self::from_backend(running_process::ape::ApeOptions::inherited())
}
pub fn with_overrides<'a>(
clear: bool,
overrides: impl IntoIterator<Item = (&'a OsStr, Option<&'a OsStr>)>,
) -> Self {
Self::from_backend(running_process::ape::ApeOptions::with_overrides(
clear, overrides,
))
}
fn from_backend(options: running_process::ape::ApeOptions) -> Self {
Self {
path: options.path,
loader: options.loader,
cache_dirs: options.cache_dirs,
}
}
fn to_backend(&self) -> running_process::ape::ApeOptions {
running_process::ape::ApeOptions {
path: self.path.clone(),
loader: self.loader.clone(),
cache_dirs: self.cache_dirs.clone(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ApeLoaderKind {
Explicit,
Embedded,
Installed,
Shell,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ApeLaunch {
kind: ApeLoaderKind,
loader: PathBuf,
image: PathBuf,
ape_path_dir: Option<PathBuf>,
}
impl ApeLaunch {
pub fn kind(&self) -> ApeLoaderKind {
self.kind
}
pub fn loader(&self) -> &Path {
&self.loader
}
pub fn image(&self) -> &Path {
&self.image
}
pub fn ape_path_dir(&self) -> Option<&Path> {
self.ape_path_dir.as_deref()
}
pub fn child_path(&self, inherited: Option<&OsStr>) -> Option<OsString> {
let dir = self.ape_path_dir.as_ref()?;
let rest = inherited.filter(|path| !path.is_empty());
std::env::join_paths(
std::iter::once(dir.clone()).chain(rest.into_iter().flat_map(std::env::split_paths)),
)
.ok()
}
pub fn args<I, S>(&self, args: I) -> Vec<OsString>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
std::iter::once(self.image.clone().into_os_string())
.chain(args.into_iter().map(|arg| arg.as_ref().to_os_string()))
.collect()
}
pub fn spawn_spec<I, S>(&self, args: I) -> crate::SpawnSpec
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let spec = crate::SpawnSpec::new(&self.loader).args(self.args(args));
match self.child_path(ApeOptions::inherited().path.as_deref()) {
Some(path) => spec.env("PATH", path),
None => spec,
}
}
}
pub fn plan_launch(
program: &OsStr,
current_dir: Option<&Path>,
options: &ApeOptions,
) -> Option<ApeLaunch> {
let launch =
running_process::ape::plan_launch(program, current_dir, &options.to_backend())?;
let kind = match launch.kind {
running_process::ape::LoaderKind::Explicit => ApeLoaderKind::Explicit,
running_process::ape::LoaderKind::Embedded => ApeLoaderKind::Embedded,
running_process::ape::LoaderKind::System => ApeLoaderKind::Installed,
running_process::ape::LoaderKind::Shell => ApeLoaderKind::Shell,
};
Some(ApeLaunch {
kind,
loader: launch.loader,
image: launch.image,
ape_path_dir: launch.ape_path_dir,
})
}
pub fn command(program: impl AsRef<OsStr>) -> std::process::Command {
running_process::ape::command(program)
}
pub struct ForkGuard {
_guard: std::sync::RwLockReadGuard<'static, ()>,
}
pub fn fork_guard() -> ForkGuard {
ForkGuard {
_guard: running_process::ape::fork_guard(),
}
}
#[allow(
dead_code,
reason = "only the Unix sync spawn layer builds commands a host can refuse as APE"
)]
pub(crate) fn spawn_std<T>(
command: &mut std::process::Command,
spawn: impl FnMut(&mut std::process::Command) -> std::io::Result<T>,
) -> std::io::Result<T> {
running_process::ape::spawn_std(command, spawn)
}
#[cfg(test)]
mod tests {
#[test]
fn environment_names_match_the_substrate() {
assert_eq!(super::LOADER_ENV, running_process::ape::LOADER_ENV);
assert_eq!(super::CACHE_DIR_ENV, running_process::ape::CACHE_DIR_ENV);
}
}