From bb92a538b4282e065b2de323f17100dde78877cf Mon Sep 17 00:00:00 2001 From: tomoaki Date: Sun, 28 Dec 2025 13:57:05 +0900 Subject: [PATCH] runner: resolve repo root without NYASH_ROOT --- src/runner/modes/common_util/resolve/mod.rs | 1 + src/runner/modes/common_util/resolve/root.rs | 54 +++++++++++++++++++ .../common_util/resolve/strip/prelude.rs | 8 +-- 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/runner/modes/common_util/resolve/root.rs diff --git a/src/runner/modes/common_util/resolve/mod.rs b/src/runner/modes/common_util/resolve/mod.rs index 2e909aed..c8042cdb 100644 --- a/src/runner/modes/common_util/resolve/mod.rs +++ b/src/runner/modes/common_util/resolve/mod.rs @@ -21,6 +21,7 @@ pub mod context; pub mod path_util; +pub mod root; pub mod prelude_manager; pub mod seam; pub mod selfhost_pipeline; diff --git a/src/runner/modes/common_util/resolve/root.rs b/src/runner/modes/common_util/resolve/root.rs new file mode 100644 index 00000000..1078727c --- /dev/null +++ b/src/runner/modes/common_util/resolve/root.rs @@ -0,0 +1,54 @@ +//! root — repository root resolution helpers (SSOT) +//! +//! `NYASH_ROOT` is allowed as an override for tools, but runtime semantics must +//! not depend on whether it is set. When we need to locate repo-relative assets +//! (e.g., operator preludes), prefer resolving a stable root via: +//! 1) `NYASH_ROOT` when set +//! 2) walking up from an on-disk hint file (e.g., the main source file) +//! 3) current working directory +//! 4) current executable path + +use std::path::{Path, PathBuf}; + +pub fn resolve_repo_root(hint_file: Option<&str>) -> Option { + if let Ok(root) = std::env::var("NYASH_ROOT") { + let p = PathBuf::from(root); + if p.exists() { + return Some(p); + } + } + + if let Some(hint) = hint_file { + if let Some(root) = walk_up_to_repo_root(Path::new(hint).parent()?) { + return Some(root); + } + } + + if let Ok(cwd) = std::env::current_dir() { + if let Some(root) = walk_up_to_repo_root(&cwd) { + return Some(root); + } + } + + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + if let Some(root) = walk_up_to_repo_root(dir) { + return Some(root); + } + } + } + + None +} + +fn walk_up_to_repo_root(start: &Path) -> Option { + let mut cur = start; + for _ in 0..16 { + if cur.join("Cargo.toml").exists() { + return Some(cur.to_path_buf()); + } + cur = cur.parent()?; + } + None +} + diff --git a/src/runner/modes/common_util/resolve/strip/prelude.rs b/src/runner/modes/common_util/resolve/strip/prelude.rs index 70b5df98..25b89c58 100644 --- a/src/runner/modes/common_util/resolve/strip/prelude.rs +++ b/src/runner/modes/common_util/resolve/strip/prelude.rs @@ -31,14 +31,14 @@ pub fn resolve_prelude_paths_profiled( let opbox_all = crate::config::env::env_bool("NYASH_OPERATOR_BOX_ALL") || crate::config::env::env_bool("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL"); - if let Ok(root) = std::env::var("NYASH_ROOT") { + if let Some(root) = crate::runner::modes::common_util::resolve::root::resolve_repo_root(Some(filename)) { let must_have = [ "apps/lib/std/operators/stringify.hako", "apps/lib/std/operators/compare.hako", "apps/lib/std/operators/add.hako", ]; for rel in must_have.iter() { - let p = std::path::Path::new(&root).join(rel); + let p = root.join(rel); if p.exists() { let path = p.to_string_lossy().to_string(); if !out.iter().any(|x| x == &path) { @@ -49,7 +49,7 @@ pub fn resolve_prelude_paths_profiled( } // Inject remaining arithmetic/bitwise/unary operator modules when ALL is requested if opbox_all { - if let Ok(root) = std::env::var("NYASH_ROOT") { + if let Some(root) = crate::runner::modes::common_util::resolve::root::resolve_repo_root(Some(filename)) { let rels = vec![ "apps/lib/std/operators/sub.hako", "apps/lib/std/operators/mul.hako", @@ -66,7 +66,7 @@ pub fn resolve_prelude_paths_profiled( "apps/lib/std/operators/bitnot.hako", ]; for rel in rels { - let p = std::path::Path::new(&root).join(rel); + let p = root.join(rel); if p.exists() { let path = p.to_string_lossy().to_string(); if !out.iter().any(|x| x == &path) {