From 6a230870ebeae12fd90e0f5ac5d8033db0bb08ca Mon Sep 17 00:00:00 2001 From: "Derek T. Jones" Date: Thu, 6 Aug 2026 09:15:21 -0700 Subject: [PATCH] Let the environment say where the library lives ARCHETYPE_INCLUDE is a colon-separated search path in the shape PATH taught everyone to expect. It is consulted after --include, so an explicit flag still wins, and after the source file's own directory, which primarySource has always pushed to the front -- so nothing changes for a game sitting beside the library it includes. The motivating case is a packaged interpreter. The snap knows perfectly well where standard.arch ended up inside its own squashfs; without this the player has to know too, and type it. A package can now set the variable once and have `archetype --source=mygame.arch` simply work. Set but empty counts as unset, the way it usually does. An empty entry *within* a list still means the current directory, the way it does in PATH, so --include=a: is unchanged. Splitting the view in place rather than through an istringstream means the only strings allocated are the ones actually kept, which addSearchPath takes by value and moves. The SHOW(path) trace went with the block it lived in. It was guarded by `#if NDEBUG` and so was only ever meant for a debug build, but the build this project documents sets no CMAKE_BUILD_TYPE, CMake therefore passes no -DNDEBUG, and the trace printed on every compile anyone has ever run. With that call gone the macro had no users left, so it is gone as well. Verified that a game compiled by way of the variable is byte-for-byte the game compiled by way of the flag. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019g4fqvhoV5MtLJJXM7uFLZ --- src/main.cc | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/main.cc b/src/main.cc index a488762..e477bd7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -35,13 +36,6 @@ #include "inspect_universe.hh" -#if NDEBUG -# define SHOW(expr) -#else -# define SHOW(expr) std::cerr << #expr << " == " << (expr) << std::endl -#endif - - namespace archetype { static constexpr std::string_view VersionString = "3.0"; @@ -104,6 +98,10 @@ void usage() { << " --inspect In combination with --update, append post-turn world state (RDF/Turtle)." << endl << " --inspect=file.acx Load a saved binary file and dump its world state as RDF/Turtle." << endl << " --full Add method signatures and parser vocabulary to the RDF output." << endl + << endl + << "Environment:" << endl + << " ARCHETYPE_INCLUDE Colon-separated paths to search for source, after any" << endl + << " given by --include." << endl ; } @@ -136,6 +134,21 @@ static void checkpoint_at_exit() { } } +// A colon-separated list of directories, in the shape PATH taught everyone to +// expect, searched in the order written. Splitting the view in place rather +// than through an istringstream means the only strings allocated are the ones +// actually kept, which addSearchPath takes by value and moves. +static void add_search_paths(string_view list) { + while (not list.empty()) { + auto colon = list.find(':'); + Wellspring::instance().addSearchPath(string{list.substr(0, colon)}); + if (colon == string_view::npos) { + break; + } + list.remove_prefix(colon + 1); + } +} + static void from_source(map &opts, const AutosaveOptions& autosave_opts) { auto it_source = opts.find("source"); @@ -146,13 +159,18 @@ static void from_source(map &opts, throw invalid_argument(format("Cannot open \"{}\"", source_path)); } if (auto it_include = opts.find("include"); it_include != opts.end()) { - string includes = it_include->second; - opts.erase(it_include); - istringstream in(includes); - string path; while (getline(in, path, ':')) { - SHOW(path); - Wellspring::instance().addSearchPath(path); - } + add_search_paths(it_include->second); + opts.erase(it_include); + } + // The environment comes second, so an explicit --include still wins. This + // is what lets a packaged interpreter point at the library it ships with -- + // the snap knows where standard.arch ended up, and the player should not + // have to. Set but empty counts as unset, the way it usually does; an + // empty entry *within* a list still means the current directory, the way it + // does in PATH. + if (const char* from_env = getenv("ARCHETYPE_INCLUDE"); + from_env != nullptr and *from_env != '\0') { + add_search_paths(from_env); } TokenStream tokens(source); if (not Universe::instance().make(tokens)) {