use std::path::{Path, PathBuf};
use std::process::Command;
use yog_cli::manifest::{MANIFEST, project_root};
use super::build::{ARTIFACTS_DIR, build};
use super::prepare::write_file;
pub(super) fn list_run_targets() -> Result<(), String> {
let home = yog_home::Home::locate()?;
let resolver = yog_home::targets::Resolver::new(&home)?;
for t in &resolver.targets()? {
println!("{:<20} {}", t.id, t.label);
}
Ok(())
}
pub(super) fn run_target(id: &str, debugging_symbols: bool, dev: bool) -> Result<(), String> {
let root = project_root()?;
let resolver = yog_home::targets::Resolver::new(&yog_home::Home::locate()?)?;
let target = resolver.target(id)?;
let plan = resolver.plan(&target);
let (meta, artifact) = build(debugging_symbols, dev)?;
if let Some(dir) = &plan.export_dir {
std::fs::create_dir_all(dir).map_err(|e| format!("creating {}: {e}", dir.display()))?;
let dest = dir.join(format!("{}.yog", meta.id));
std::fs::copy(&artifact, &dest)
.map_err(|e| format!("copying to {}: {e}", dest.display()))?;
write_file(
&source_marker(dir, &meta.id),
root.display().to_string().as_bytes(),
)?;
eprintln!("==> exported {} -> {}", meta.id, dest.display());
refresh_exported(dir, &meta.id, debugging_symbols);
}
let Some(command) = &plan.command else {
eprintln!("==> {id} has no command — export only, nothing to launch.");
return Ok(());
};
eprintln!(
"==> launching {}: {command} {}",
target.label,
plan.args.join(" ")
);
let mut proc = Command::new(command);
proc.args(&plan.args);
if let Some(cwd) = &plan.cwd {
proc.current_dir(cwd);
}
for (k, v) in &plan.env {
proc.env(k, v);
}
let mut child = proc
.spawn()
.map_err(|e| format!("failed to launch `{command}`: {e}"))?;
eprintln!("==> launched pid {}", child.id());
let status = child
.wait()
.map_err(|e| format!("failed waiting on `{command}`: {e}"))?;
if !status.success() {
return Err(format!("`{command}` exited with {status}"));
}
Ok(())
}
pub(super) fn source_marker(dir: &Path, id: &str) -> PathBuf {
dir.join(format!("{id}.source"))
}
pub(super) fn refresh_exported(dir: &Path, current: &str, debugging_symbols: bool) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
let mut others: Vec<(String, PathBuf)> = entries
.flatten()
.filter_map(|e| {
let path = e.path();
let id = path
.file_name()?
.to_str()?
.strip_suffix(".source")?
.to_string();
if id == current || !dir.join(format!("{id}.yog")).is_file() {
return None;
}
let source = std::fs::read_to_string(&path).ok()?;
Some((id, PathBuf::from(source.trim())))
})
.collect();
others.sort();
let Ok(exe) = std::env::current_exe() else {
return;
};
for (id, source) in others {
if !source.join(MANIFEST).is_file() {
eprintln!(
"warning: {id}: {} is no longer a mod project — keeping the exported {id}.yog",
source.display()
);
continue;
}
eprintln!("==> refreshing {id} ({})", source.display());
let mut cmd = Command::new(&exe);
cmd.arg("build").current_dir(&source);
if debugging_symbols {
cmd.arg("debug");
}
let built = cmd.status().is_ok_and(|s| s.success());
let artifact = source
.join(ARTIFACTS_DIR)
.join(yog_ids::Format::Mod.file(&id));
if !built || !artifact.is_file() {
eprintln!("warning: {id} didn't build — keeping its previous {id}.yog");
continue;
}
if let Err(e) = std::fs::copy(&artifact, dir.join(format!("{id}.yog"))) {
eprintln!("warning: {id}: copying {}: {e}", artifact.display());
}
}
}