From 768dab399fc45006f445435093547484342e161e Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Tue, 28 Jul 2026 17:14:16 +1000 Subject: [PATCH] G-033: proj: package-mode scope prefix - visitor binary resolves dev/src against the cwd's project punk_main.tcl: an optional proj: prefix on the package-mode string (e.g 'proj:internal-src') rebinds the dev/src path blocks to the punk project containing the current directory instead of the executable's own project. Root discovery is ::punkboot::proj_root_find, a lean boot mirror of punk::repo::find_project/is_project_root in Tcl builtins only (git/fossil control markers via 'file exists', unwise- path exclusion, ./src plus src/modules|src/vfs|src/scriptapps| punkproject.toml candidate check; .git accepted as file OR dir for worktree checkouts - recorded divergence). The proc stays defined post-boot like the G-129 mount procs. The prefix sits outside the ordered dash-list, whose block order remains the same-version tie-break dial; an invalid mode remainder is not consumed (whole arg falls through as subcommand/script as today); bare 'proj:' reads as internal and warns. Discovery outcome is always reported on stderr (detected root + effective precedence - never a silent rebind), a failed walk-up warns and leaves dev/src unbound (no exe-relative fallback rebind), and proj: without a dev/src block warns it has no effect. Under proj:, dev resolves /modules{,_tclN} and /lib{,_tclN} only; src feeds the discovered root into the existing src-mode machinery unchanged (tm paths, auto_path, package prefer latest, inline #modpod scanner). Exe-relative src/dev launches are untouched. punk::args::moduledoc::punkexe 0.1.1: the packagemode help text drafted in the goal file is now live as the packagemode leader of (script)::punkexe (@leaders max 2), with proj: wording in the @cmd overview; rendering/parse wiring remains G-032. Verified on a rebaked punk91 kit, standalone copy outside any source tree: proj:internal-src from tomlish (fossil root, builds no executable) loads tomlish 999999.0a1.0 from its src/modules with kit-first ties; proj:src flips the ordering; from punkshell the visited-root modpod scan registers 6 modpods; no-project and no-root-using-block warnings fire and proceed; bin/punk91 src unchanged. Docs: bin/AGENTS.md launch-package-modes proj: contract, src/vfs/AGENTS.md punk_main contract line, ARCHITECTURE.md entry point. punkshell 0.29.0 (minor - new launch invocation) + CHANGELOG. Claude-Session: https://claude.ai/code/session_0156PuejSCGjgeGb7jiABrDU Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- ARCHITECTURE.md | 4 +- CHANGELOG.md | 15 ++ bin/AGENTS.md | 8 +- punkproject.toml | 2 +- .../args/moduledoc/punkexe-999999.0a1.0.tm | 38 ++- .../args/moduledoc/punkexe-buildversion.txt | 2 +- src/vfs/AGENTS.md | 2 +- src/vfs/_config/punk_main.tcl | 235 +++++++++++++----- 8 files changed, 237 insertions(+), 69 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 43e16947..d0374af1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -42,7 +42,7 @@ app package from src/lib/ (app-punkshell, app-punkscript, app-repl, app-shellsp ## Boot and launch chain - **Kit anatomy.** Each `src/vfs/*.vfs` folder is a runtime payload; `src/runtime/mapvfs.config` maps payload folders to platform runtimes under `bin/runtime//`; `src/vfs/_vfscommon.vfs/` is a generated merge of common libraries (never hand-edited); `src/vfs/_config/` holds the entry scripts. Sources: `src/vfs/AGENTS.md`, `src/runtime/AGENTS.md`. -- **Entry point.** `src/vfs/_config/punk_main.tcl`. An optional first argument is a dash-delimited package mode composed of tokens from `dev`, `os`, `src`, `internal` (`internal` is always appended when absent). The next argument is the subcommand: `shell`, `script`, `punk`, `tclsh`, `shellspy`. Any other first argument is treated as a script invocation; no arguments at all means interactive shell. Subcommands map to app packages under `src/lib/` (`app-punkshell`, `app-punkscript`, `app-repl`, `app-shellspy`). +- **Entry point.** `src/vfs/_config/punk_main.tcl`. An optional first argument is a dash-delimited package mode composed of tokens from `dev`, `os`, `src`, `internal` (`internal` is always appended when absent), optionally scoped with the `proj:` prefix (G-033: `dev`/`src` resolve against the project containing the cwd - walk-up to the nearest git/fossil repo root with a punkshell-style src tree - instead of the executable's own project; discovery is always reported, never a silent rebind). The next argument is the subcommand: `shell`, `script`, `punk`, `tclsh`, `shellspy`. Any other first argument is treated as a script invocation; no arguments at all means interactive shell. Subcommands map to app packages under `src/lib/` (`app-punkshell`, `app-punkscript`, `app-repl`, `app-shellspy`). - **Package modes pick module provenance.** `internal` uses module snapshots baked into the kit at build time; `dev` loads build outputs from repo-root `modules/` and `lib/`; `src` loads the unbuilt working tree (`src/modules`, `src/lib`, `src/bootsupport`, `src/vendormodules`) with `package prefer latest` so magic-version dev modules (`999999.0a1.0`) beat stamped snapshots. `src` mode is the standard way to verify working-tree changes without a rebuild. Source: `bin/AGENTS.md` "Launch package modes". - **Payload mount derivation (G-129, achieved).** The boot keys zipfs presence on `tcl::zipfs::mount` and derives where the executable's attached archive actually mounted from the mount table itself (the entry whose archive file is the executable; fallback: the mount containing the boot script) instead of assuming `tcl::zipfs::root` + `//zipfs:/app`. Modern runtimes still answer `//zipfs:/app`; the androwish/undroidwish 8.6 backport has no `root` command, mounts at the executable's own path, and boots the same payload through its `app/main.tcl` in-archive hook (`src/vfs/punk8win.vfs` carries fauxlinks for both conventions). A mounted archive the boot cannot attribute is reported on stderr rather than silently yielding no internal paths. The derivation procs stay defined post-boot (`::punkboot::zipfs_kit_mountbase`). Sources: `src/vfs/AGENTS.md`, `bin/AGENTS.md` "Kit-wrappable runtime requirements", `src/tests/shell/testsuites/punkexe/kitmountpoint.test`. - **Static package capture (G-058, achieved).** Boot probe-loads the runtime's statically linked packages in a throwaway interp and records what each provides; every interp the shell fabricates (boot, codethread, shellthread workers) seeds `package ifneeded {load {} }` from that record, and `punk::packagepreference` resolves static-vs-bundled version-aware. Sources: `src/vfs/AGENTS.md`, `goals/archive/G-058-static-runtime-packages.md`. @@ -106,7 +106,7 @@ Mechanics here are documented as pointers only - detailed prose would be obsolet - Input mode and completion: G-013 (raw-mode default), G-044 (punk::args-driven completion). - Stored configuration and help: G-014 (punk::config toml), G-042 (subshell help topics), G-043 (subshell definition plugins). - Console err channel: G-011. -- Launch surface: G-023 (version-named binaries), G-033 (`proj:` package mode), G-077 (`-e` one-liners), G-089 (scriptlib in kits). +- Launch surface: G-023 (version-named binaries), G-077 (`-e` one-liners), G-089 (scriptlib in kits). - 8.6 buildsuite family: G-101 (kit container) - the core runtime (G-099) and the Tk/tklib/tcllib dependency set (G-100) are achieved 2026-07-26. ## Doc map - which lane holds what diff --git a/CHANGELOG.md b/CHANGELOG.md index d236aa24..aa31070f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.29.0] - 2026-07-28 + +- `proj:` package-mode scope prefix (G-033): an installed or standalone punk shell + binary can visit the punk project containing the current directory - + ` proj:internal-src shell` (canonical, kit copies win same-version ties) + or `proj:src` (project copies win). The boot walks up from the cwd to the nearest + git/fossil repo root with a punkshell-style src tree and feeds it to the existing + src/dev path machinery (src/modules + bootsupport + vendormodules + src/lib, + `package prefer latest`, #modpod registration), reporting the detected root and + effective precedence on stderr - warn-and-proceed (dev/src unbound) when no + project is found, never an exe-relative fallback rebind. Exe-relative plain + `src`/`dev` launches are unchanged. The packagemode contract (blocks, ordering, + proj: examples) is now live punk::args documentation in + punk::args::moduledoc::punkexe 0.1.1 (rendering wiring remains G-032). + ## [0.28.4] - 2026-07-27 - punk::zip 0.3.0 (G-126): optional punkzip extraction accelerator. New diff --git a/bin/AGENTS.md b/bin/AGENTS.md index 669510ec..72872dcd 100644 --- a/bin/AGENTS.md +++ b/bin/AGENTS.md @@ -87,7 +87,7 @@ platform-specific difference left in examples is path separator style ### Launch package modes (built punk shells) -The first argument to a built punk shell may be a dash-delimited package mode composed of tokens from `dev`, `os`, `src`, `internal` (e.g. `punksys src`, `punk902z dev-os`). `internal` is the default and is always appended when absent. A first argument that is not a valid mode list is treated as a subcommand instead. Implementation: `src/vfs/_config/punk_main.tcl` (search `all_package_modes`). +The first argument to a built punk shell may be a dash-delimited package mode composed of tokens from `dev`, `os`, `src`, `internal` (e.g. `punksys src`, `punk902z dev-os`), optionally scoped with the `proj:` prefix (e.g. `punk91 proj:internal-src`). `internal` is the default and is always appended when absent. A first argument that is not a valid mode list is treated as a subcommand instead (a `proj:` prefix on a non-mode string is not consumed either). Implementation: `src/vfs/_config/punk_main.tcl` (search `all_package_modes`). The full packagemode contract (block meanings, ordering, proj: examples) is the punk::args documentation at id `(script)::punkexe` (module `punk::args::moduledoc::punkexe`). `src` mode is the one that matters for verifying working-tree changes: @@ -95,6 +95,12 @@ The first argument to a built punk shell may be a dash-delimited package mode co - Prepends `src/modules`, `src/modules_tcl`, `src/bootsupport/modules{,_tcl}` and `src/vendormodules{,_tcl}` to the module path, sets `package prefer latest` so dev-numbered `999999.0a1.0` source modules beat kit-stamped snapshots on unversioned `package require`, and registers `#modpod` modules from `src/modules` (startup notice: `src mode: registered N #modpod modules from .../src/modules`). - Consequence: `punksys src` / `punk902z src` exercises current working-tree source with no kit rebuild; a plain launch (`punksys`) exercises the module snapshots baked into the kit at build time. `package present ` reporting `999999.0a1.0` (vs a release version like `0.7.1`) tells you which set a session is running - runbooks with version expectations must state the intended launch mode. +`proj:` scope (G-033, achieved - see goals/archive/G-033-proj-mode-cwd-project.md) makes `dev`/`src` resolve against the project containing the CURRENT DIRECTORY instead of the executable's own project - visitor mode for an installed/standalone punk binary exploring a project that builds no shell of its own (the project may be modules-only, e.g. tomlish): + +- Root discovery walks up from the cwd to the nearest git/fossil repo root that looks like a punkshell-style project (has `./src` plus one of `src/modules`, `src/vfs`, `src/scriptapps`, or `punkproject.toml` - a lean boot mirror of `punk::repo::is_project_root`). +- The launch always reports the detected root and effective mode precedence on stderr; when no project is found it warns and proceeds with dev/src unbound (never a silent or false rebind, no exe-relative fallback). `proj:` with no dev/src block warns that it has no effect. +- Canonical visitor invocation: ` proj:internal-src shell` - kit copies win same-version ties, protecting the visiting shell's own infrastructure. `proj:src` is the faithful-vintage variant where the visited project's copies win ties. The project's genuinely-own code (dev-versioned or absent from the kit) loads from the project in either order. + ### Kit-wrappable runtime requirements (G-129) What a runtime must provide for `make.tcl bake` to wrap it as a punk ZIP kit and for that diff --git a/punkproject.toml b/punkproject.toml index 420e5c48..688971bc 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.28.4" +version = "0.29.0" license = "BSD-2-Clause" diff --git a/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm b/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm index 35d7f482..6e04faa5 100644 --- a/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm +++ b/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm @@ -91,9 +91,13 @@ tcl::namespace::eval punk::args::moduledoc::punkexe { An optional FIRST argument selects package modes: one or more of the tokens dev, os, src, internal joined with '-' when combined - (e.g 'dev', 'src', 'dev-src'). Modes decide which module sources - the shell trusts and their precedence; 'internal' (kit-bundled - modules) is always appended when absent. Details: bin/AGENTS.md + (e.g 'dev', 'src', 'dev-src'), optionally scoped with the + 'proj:' prefix (e.g 'proj:internal-src') so dev/src resolve + against the project containing the current directory instead of + the executable's own (G-033 visitor mode). Modes decide which + module sources the shell trusts and their precedence; 'internal' + (kit-bundled modules) is always appended when absent. Full + contract: the packagemode leader below and bin/AGENTS.md 'launch package modes' in the punkshell source tree. The next argument selects a subcommand from the choices below. @@ -102,7 +106,33 @@ tcl::namespace::eval punk::args::moduledoc::punkexe { ('shell'); any other first argument is treated as a script invocation (handled as for the 'script' subcommand)." @form -synopsis " ?packagemode? ?subcommand? ?arg ...?" - @leaders -min 0 -max 1 + @leaders -min 0 -max 2 + packagemode -type string -optional 1 -default internal -help\ + "Package-source mode: an ordered dash-separated list of path + blocks, optionally scoped with the 'proj:' prefix. + Blocks (each adds a group of module/library paths): + internal - paths inside the executable's kit (always included; + appended last unless listed explicitly) + dev - the project's built output (/modules ...) + src - the project's unbuilt source (src/modules, + src/bootsupport/modules, src/vendormodules, src/lib) + os - ad-hoc .tm paths from the current directory/environment + Without 'proj:', dev/src resolve against the executable's own + project (executable in /bin). With 'proj:', they + resolve against the nearest project root at or above the current + directory (a git or fossil repository root) - for an installed + punkshell binary visiting a project that builds no shell of its + own. + ORDER MATTERS: earlier blocks win when the same version of a + module exists in more than one location. + Examples: + src work on the executable's own project source + proj:internal-src explore the cwd project; the executable's own + copies win version ties (robust visitor default) + proj:src explore the cwd project; ITS copies win version + ties (faithful to the project's snapshot vintage) + The launch reports the detected project root and effective + precedence." subcommand -type string -optional 1 -choicerestricted 0 -choices {tclsh script shell punk shellspy} -choicelabels { tclsh " Run as a (near) stock tclsh - no punk modules loaded." diff --git a/src/modules/punk/args/moduledoc/punkexe-buildversion.txt b/src/modules/punk/args/moduledoc/punkexe-buildversion.txt index f47d01c8..781c895b 100644 --- a/src/modules/punk/args/moduledoc/punkexe-buildversion.txt +++ b/src/modules/punk/args/moduledoc/punkexe-buildversion.txt @@ -1,3 +1,3 @@ -0.1.0 +0.1.1 #First line must be a semantic version number #all other lines are ignored. diff --git a/src/vfs/AGENTS.md b/src/vfs/AGENTS.md index 6c0fc603..70b05017 100644 --- a/src/vfs/AGENTS.md +++ b/src/vfs/AGENTS.md @@ -16,7 +16,7 @@ VFS (Virtual File System) folders define the runtime payloads that get wrapped i - A `*.vfs` folder built against a runtime is expected to carry a root-level startup script: an actual `main.tcl`, or a root fauxlink resolving to the name `main.tcl` whose target exists and is a `.tcl` file. `make.tcl` warns when neither is present (column-0 `BUILD-WARNING:` token, ANSI-highlighted, recapped at end of run like provenance warnings) but still builds - a kit without a startup script is legal. Overlay-only folders with no runtime mapping (e.g `_vfscommon.vfs`) are not checked. - VFS content must be compatible with the target platform runtime. - `_vfscommon.vfs/` is an auto-generated merge of common libraries; manually editing it will be overwritten. -- `_config/punk_main.tcl` accepts an optional package_mode argument before the subcommand. Valid modes are `dev`, `os`, `internal`, and `src`, dash-delimited in priority order (e.g `src-internal`). `internal` is always appended if omitted. +- `_config/punk_main.tcl` accepts an optional package_mode argument before the subcommand. Valid modes are `dev`, `os`, `internal`, and `src`, dash-delimited in priority order (e.g `src-internal`). `internal` is always appended if omitted. An optional `proj:` prefix (G-033, e.g `proj:internal-src`) makes `dev`/`src` resolve against the project containing the cwd (walk-up to the nearest git/fossil repo root with a punkshell-style src tree, via `::punkboot::proj_root_find`) instead of the executable's own project; discovery outcome is always reported on stderr and a failed discovery leaves dev/src unbound rather than falling back exe-relative. - `_config/punk_main.tcl` captures the runtime's static/builtin packages at boot (G-058): empty-filename `info loaded` prefixes are probe-loaded in a throwaway interp (denylist `::punkboot::static_probe_denylist`, default `tk* vfs mk4tcl vlerq tdbc*` - side-effecting or composite inits) and each package a probe-load actually PROVIDES is recorded in `::punkboot::static_packages` {pkgname {version prefix

}} with `::punkboot::static_prefixes` alongside. The boot interp and every punkshell-fabricated interp/thread (punk::lib::interp_sync_package_paths / snapshot_package_paths, repl codethread, shellthread workers) seed `package ifneeded {load {} }` from it, and punk::packagepreference resolves static-vs-bundled version-aware. Composite statics whose script layer lived in the runtime's own (replaced) zipfs provide nothing under the probe and are correctly left to bundled copies. - `src` mode loads unbuilt modules and libraries from the project's `src/` tree (`src/modules`, `src/lib`, `src/bootsupport`, `src/vendormodules`) instead of built output. It also runs an inline `#modpod` package-ifneeded scanner (using Tcl builtins only, since no punk modules are loaded at boot time) and sets `package prefer latest` so `999999.0a1.0` dev modules are preferred over stable bootsupport/vendored copies. - `_config/punk_main.tcl` and `_config/project_main.tcl` derive the kit's payload location from the runtime's own zipfs mount table (G-129): zipfs presence keys on `tcl::zipfs::mount` (not `tcl::zipfs::root`, which the androwish/undroidwish 8.6 backport lacks), the mount base is the table entry whose archive file is `[info nameofexecutable]` (fallback: the mount containing the boot script), `//zipfs:/app` stays the expected modern answer rather than a compiled-in assumption, and a mounted archive that cannot be attributed to the executable is reported on stderr. The derivation lives in `::punkboot` procs (`zipfs_kit_mountbase`, `zipfs_mount_pairs`, `path_within`) that remain defined after boot for probing and tests. Both files carry identical copies of this logic (G-031 is the planned consolidation). Characterization: `src/tests/shell/testsuites/punkexe/kitmountpoint.test`. diff --git a/src/vfs/_config/punk_main.tcl b/src/vfs/_config/punk_main.tcl index 816a4352..21b02b48 100644 --- a/src/vfs/_config/punk_main.tcl +++ b/src/vfs/_config/punk_main.tcl @@ -366,6 +366,45 @@ apply { args { } return "" } + proc proj_root_find {startdir} { + #G-033: lean boot mirror of punk::repo::find_project (scanup on + #is_project_root) using only Tcl builtins - punk::repo is not loadable at + #the package-mode boot phase. Walks up from $startdir to the nearest VCS + #repo root (git or fossil control markers; 'file exists' sees + #windows-hidden items) that also passes a lean punk::repo::is_candidate_root + #check: not an unwise location, has ./src, and one of ./src/modules, + #./src/vfs, ./src/scriptapps or ./punkproject.toml. Keep the semantics + #aligned with punk::repo so a project findable at boot is findable by the + #runtime layer and vice versa (known divergence: .git is accepted as file + #OR dir - git worktree/submodule checkouts are legitimate visit targets). + #Returns the project root, or "" when no candidate is found. + set unwise_paths [list "/" "/dev" "/bin" "/root" "/etc" "/opt" "/usr" "/usr/local" "/usr/local/bin" "/usr/local/lib" "c:/windows"] + set dir [file normalize $startdir] + while 1 { + set is_repo_root 0 + foreach marker [list .git _FOSSIL_ .fslckout .fos] { + if {[file exists [file join $dir $marker]]} { + set is_repo_root 1 + break + } + } + if {$is_repo_root && [string tolower $dir] ni $unwise_paths} { + if {[file isdirectory [file join $dir src]]} { + if {[file isdirectory [file join $dir src modules]]\ + || [file isdirectory [file join $dir src vfs]]\ + || [file isdirectory [file join $dir src scriptapps]]\ + || [file exists [file join $dir punkproject.toml]]} { + return $dir + } + } + } + set parent [file dirname $dir] + if {$parent eq $dir} { + return "" + } + set dir $parent + } + } } #G-129: key zipfs presence on tcl::zipfs::mount - present in every supported zipfs @@ -613,14 +652,24 @@ apply { args { #Token-by-token validation: instead of exhaustively listing all permutations, #split the first arg on dash and validate each token against the known mode set. #This scales to any number of modes without enumerating every permutation. + #G-033: an optional 'proj:' prefix on the mode string (e.g proj:internal-src) + #scopes WHICH project the dev/src blocks resolve against - the project containing + #the cwd instead of the executable's own. The prefix sits outside the ordered + #dash-list, whose block order remains the same-version tie-break dial. + set proj_scope 0 + set test_mode_string $test_package_mode + if {[string match proj:* $test_package_mode]} { + set proj_scope 1 + set test_mode_string [string range $test_package_mode 5 end] + } set package_modes "" set arglist "" - if {$test_package_mode eq ""} { - #empty first arg consumed as equivalent of 'internal' + if {$test_mode_string eq ""} { + #empty first arg (or bare 'proj:') consumed as equivalent of 'internal' set package_modes internal set arglist [lrange $args 1 end] } else { - set tokens [split $test_package_mode -] + set tokens [split $test_mode_string -] set valid 1 foreach t $tokens { if {$t ni $all_package_modes} { @@ -636,6 +685,9 @@ apply { args { set arglist [lrange $args 1 end] } else { #not a package_mode - treat as subcommand + #(a 'proj:' prefix on a non-mode string is not consumed either - the + #whole arg passes through, e.g as a script name) + set proj_scope 0 set package_modes internal set arglist $args } @@ -644,6 +696,28 @@ apply { args { set ::argv $arglist set ::argc [llength $arglist] #assert: package_modes is now a list of at least length 1 (in which case the only possible value is: internal) + + #-------------------------------------------------------- + #G-033 proj: scope - resolve dev/src against the project containing the cwd + #(walk-up to the nearest VCS repo root, the punk::repo::is_project_root marker) + #instead of the executable's own project. Explicit prefix = opt-in; discovery and + #its outcome are always reported on stderr (a rebind must never be silent), and a + #failed discovery never falls back to exe-relative roots (no false rebind). + #-------------------------------------------------------- + set proj_root "" + if {$proj_scope} { + if {!("dev" in $package_modes || "src" in $package_modes)} { + puts stderr "proj: WARNING - 'proj:' has no effect: package mode list '[join $package_modes -]' contains no project-root-using block (dev or src)" + } else { + set proj_root [::punkboot::proj_root_find [pwd]] + if {$proj_root eq ""} { + puts stderr "proj: WARNING - no project root found walking up from [pwd] (nearest git/fossil repo root with a punkshell-style src tree) - dev/src blocks are not bound to any project (proceeding; internal/os paths unaffected)" + } else { + puts stderr "proj: project root detected (walk-up from [pwd]): $proj_root" + puts stderr "proj: effective package mode precedence: [join $package_modes -] (earlier blocks win same-version ties)" + } + } + } #Note regarding the use of package forget and binary packages #If the package has loaded a binary component - then a package forget and a subsequent package require can result in both binaries being present, as seen in 'info loaded' result - potentially resulting in anomalous behaviour #In general package forget after a package has already been required may need special handling and should be avoided where possible. @@ -722,24 +796,36 @@ apply { args { #case differences could represent different paths on unix-like platforms. #It's perhaps a little unwise to configure matching paths with only case differences for a cross-platform tool .. but we should support it for those who use it and have no interest in windows - todo! review if {"dev" in $package_modes} { - set normexe_dir [file dirname $normexe] - if {[file tail $normexe_dir] eq "bin"} { - #underlying exe in a bin dir - backtrack 1 - lappend exe_module_folders [file dirname $normexe_dir]/modules - lappend exe_module_folders [file dirname $normexe_dir]/modules_tcl$tclmajorv + set exe_module_folders [list] + if {$proj_scope} { + #G-033: dev module paths resolve against the cwd-walk-up project root + #only - no exe-relative fallback (a proj: launch must never silently + #rebind to the executable's own project; a failed discovery was + #already warned above and the dev block then contributes nothing). + if {$proj_root ne ""} { + lappend exe_module_folders $proj_root/modules + lappend exe_module_folders $proj_root/modules_tcl$tclmajorv + } } else { - lappend exe_module_folders $normexe_dir/modules - lappend exe_module_folders $normexe_dir/modules_tcl$tclmajorv - } - set nameexe_dir [file dirname [file normalize [info nameofexecutable]]] ;#must be normalized for capitalisation consistency + set normexe_dir [file dirname $normexe] + if {[file tail $normexe_dir] eq "bin"} { + #underlying exe in a bin dir - backtrack 1 + lappend exe_module_folders [file dirname $normexe_dir]/modules + lappend exe_module_folders [file dirname $normexe_dir]/modules_tcl$tclmajorv + } else { + lappend exe_module_folders $normexe_dir/modules + lappend exe_module_folders $normexe_dir/modules_tcl$tclmajorv + } + set nameexe_dir [file dirname [file normalize [info nameofexecutable]]] ;#must be normalized for capitalisation consistency - #possible symlink (may resolve to same path as above - we check below to not add in twice) - if {[file tail $nameexe_dir] eq "bin"} { - lappend exe_module_folders [file dirname $nameexe_dir]/modules - lappend exe_module_folders [file dirname $nameexe_dir]/modules_tcl$tclmajorv - } else { - lappend exe_module_folders $nameexe_dir/modules - lappend exe_module_folders $nameexe_dir/modules_tcl$tclmajorv + #possible symlink (may resolve to same path as above - we check below to not add in twice) + if {[file tail $nameexe_dir] eq "bin"} { + lappend exe_module_folders [file dirname $nameexe_dir]/modules + lappend exe_module_folders [file dirname $nameexe_dir]/modules_tcl$tclmajorv + } else { + lappend exe_module_folders $nameexe_dir/modules + lappend exe_module_folders $nameexe_dir/modules_tcl$tclmajorv + } } #foreach modulefolder $exe_module_folders { @@ -766,7 +852,10 @@ apply { args { # } #} if {![llength $exe_module_folders]} { - puts stderr "Warning - no 'modules' or 'modules_tcl$tclmajorv' folders found relative to executable (or it's symlink if any)" + #proj_scope with no discovered root was already warned at proj: discovery + if {!$proj_scope} { + puts stderr "Warning - no 'modules' or 'modules_tcl$tclmajorv' folders found relative to executable (or it's symlink if any)" + } } else { set tm_additions_dev $exe_module_folders } @@ -776,21 +865,28 @@ apply { args { #Unlike dev mode (which points at built output in /modules), #src mode points at the unbuilt source in /src/modules etc. if {"src" in $package_modes} { - #reuse the dev-mode project root discovery (exe in bin/ -> backtrack 1) set src_project_root "" - set normexe_dir_for_src [file dirname $normexe] - if {[file tail $normexe_dir_for_src] eq "bin"} { - set src_project_root [file dirname $normexe_dir_for_src] + if {$proj_scope} { + #G-033: src resolves against the cwd-walk-up project root; empty + #(warned at discovery) means the src block contributes nothing - + #never an exe-relative fallback rebind. + set src_project_root $proj_root } else { - set src_project_root $normexe_dir_for_src - } - #also check symlink target - if {$src_project_root eq ""} { - set nameexe_dir_for_src [file dirname [file normalize [info nameofexecutable]]] - if {[file tail $nameexe_dir_for_src] eq "bin"} { - set src_project_root [file dirname $nameexe_dir_for_src] + #reuse the dev-mode project root discovery (exe in bin/ -> backtrack 1) + set normexe_dir_for_src [file dirname $normexe] + if {[file tail $normexe_dir_for_src] eq "bin"} { + set src_project_root [file dirname $normexe_dir_for_src] } else { - set src_project_root $nameexe_dir_for_src + set src_project_root $normexe_dir_for_src + } + #also check symlink target + if {$src_project_root eq ""} { + set nameexe_dir_for_src [file dirname [file normalize [info nameofexecutable]]] + if {[file tail $nameexe_dir_for_src] eq "bin"} { + set src_project_root [file dirname $nameexe_dir_for_src] + } else { + set src_project_root $nameexe_dir_for_src + } } } @@ -816,7 +912,14 @@ apply { args { } } } else { - puts stderr "Warning - src mode: no src/ directory found relative to executable ($normexe_dir_for_src)" + if {$proj_scope} { + #no discovered root was already warned at proj: discovery + if {$src_project_root ne ""} { + puts stderr "Warning - src mode (proj:): no src/ directory under detected project root ($src_project_root)" + } + } else { + puts stderr "Warning - src mode: no src/ directory found relative to executable ($normexe_dir_for_src)" + } } } @@ -947,36 +1050,50 @@ apply { args { #on other platforms, case differences could represent different paths #review set process_folders [list] - foreach libsub [list lib_tcl$tclmajorv lib] { - if {[file tail $nameexe_dir] eq "bin"} { - set libfolder [file dirname $nameexe_dir]/$libsub - } else { - set libfolder $nameexe_dir/$libsub - } - if {[file isdirectory $libfolder]} { - #lappend auto_path_additions_dev $libfolder - lappend process_folders $libfolder - } - - # ------------- - if {[file tail $normexe_dir] eq "bin"} { - set libfolder [file dirname $normexe_dir]/$libsub - } else { - set libfolder $normexe_dir/$libsub - } - if {[file isdirectory $libfolder]} { - #lappend auto_path_additions_dev $libfolder - if {$libfolder ni $process_folders} { - lappend process_folders $libfolder + if {$proj_scope} { + #G-033: dev lib paths resolve against the cwd-walk-up project root + #only (no exe-relative or cwd-relative candidates - see the dev + #module-path block above for rationale). + if {$proj_root ne ""} { + foreach libsub [list lib_tcl$tclmajorv lib] { + set libfolder $proj_root/$libsub + if {[file isdirectory $libfolder] && $libfolder ni $process_folders} { + lappend process_folders $libfolder + } } } - # ------------- - set libfolder [pwd]/$libsub - if {[file isdirectory $libfolder]} { - #lappend auto_path_additions_dev $libfolder - if {$libfolder ni $process_folders} { + } else { + foreach libsub [list lib_tcl$tclmajorv lib] { + if {[file tail $nameexe_dir] eq "bin"} { + set libfolder [file dirname $nameexe_dir]/$libsub + } else { + set libfolder $nameexe_dir/$libsub + } + if {[file isdirectory $libfolder]} { + #lappend auto_path_additions_dev $libfolder lappend process_folders $libfolder } + + # ------------- + if {[file tail $normexe_dir] eq "bin"} { + set libfolder [file dirname $normexe_dir]/$libsub + } else { + set libfolder $normexe_dir/$libsub + } + if {[file isdirectory $libfolder]} { + #lappend auto_path_additions_dev $libfolder + if {$libfolder ni $process_folders} { + lappend process_folders $libfolder + } + } + # ------------- + set libfolder [pwd]/$libsub + if {[file isdirectory $libfolder]} { + #lappend auto_path_additions_dev $libfolder + if {$libfolder ni $process_folders} { + lappend process_folders $libfolder + } + } } } foreach f $process_folders {