|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidance to AI coding assistants when working with code |
| 4 | +in this repository. Codex and Mistral Vibe read it directly. Claude Code and |
| 5 | +Gemini CLI only look for their own `CLAUDE.md`/`GEMINI.md` filename, so both |
| 6 | +are generated as stubs that just import this file (see the `CLAUDE.md:` and |
| 7 | +`GEMINI.md:` rules in `Makefile`). |
| 8 | + |
| 9 | +## What this is |
| 10 | + |
| 11 | +Modules (`envmodules/modules`) is the `module` command: a Tcl-based tool for |
| 12 | +dynamic modification of a user's shell environment via modulefiles. The |
| 13 | +runtime engine (`modulecmd.tcl`) is written in Tcl and must stay compatible |
| 14 | +with Tcl 8.5+. |
| 15 | + |
| 16 | +## Build |
| 17 | + |
| 18 | +```sh |
| 19 | +./configure |
| 20 | +make # builds modulecmd.tcl and other install artifacts |
| 21 | +make install |
| 22 | +``` |
| 23 | + |
| 24 | +`configure`/`Makefile` are hand-written (no autoconf/automake despite |
| 25 | +`lib/aclocal.m4`, `lib/configure.ac` existing for the optional C helper |
| 26 | +library in `lib/`). |
| 27 | + |
| 28 | +### The core script is generated, not hand-edited directly for the final artifact |
| 29 | + |
| 30 | +`modulecmd.tcl` (the actual interpreter installed to `libexecdir`) is built by |
| 31 | +`make` by concatenating the files in `tcl/` in a fixed order (see the |
| 32 | +`modulecmd.tcl:` rule in `Makefile`): |
| 33 | + |
| 34 | +``` |
| 35 | +init -> util -> envmngt -> report -> interp -> mfcmd -> modscan -> |
| 36 | +modfind -> modeval -> modspec -> cache -> coll -> main -> subcmd |
| 37 | +``` |
| 38 | + |
| 39 | +Always edit the individual source file in `tcl/` (e.g. `tcl/main.tcl.in`, |
| 40 | +`tcl/subcmd.tcl.in`); never edit a built `modulecmd.tcl` output directly. |
| 41 | +Files with a `.in` suffix contain `@markname@` substitutions filled in by |
| 42 | +`configure`/`Makefile`; files without `.in` (`mfcmd.tcl`, `modeval.tcl`, |
| 43 | +`modscan.tcl`, `modspec.tcl`, `syntaxdb.tcl`, `util.tcl`) have no |
| 44 | +substitutions. |
| 45 | + |
| 46 | +## Tests |
| 47 | + |
| 48 | +```sh |
| 49 | +make test # full suite via DejaGnu (~17,000 cases, ~12 min) |
| 50 | +make test QUICKTEST=y # quick mode, most essential tests, ~1 min |
| 51 | +make testinstall # test an already-`make install`ed tree |
| 52 | +``` |
| 53 | + |
| 54 | +Run a subset of tests directly with `script/mt` (reports an expected/actual |
| 55 | +diff on failure): |
| 56 | + |
| 57 | +```sh |
| 58 | +script/mt 50/470 # runs testsuite/modules.50-cmds/470-*.exp |
| 59 | +script/mt --help # full usage/selection syntax |
| 60 | +``` |
| 61 | + |
| 62 | +Coverage (uses Nagelfar): |
| 63 | + |
| 64 | +```sh |
| 65 | +make test COVERAGE=y # then inspect tcl/*.tcl_m for ';# Not covered' lines |
| 66 | +script/mt cov 70/{280,290} # coverage run on selected testfiles |
| 67 | +``` |
| 68 | + |
| 69 | +Lint (Nagelfar for Tcl, ShellCheck for sh/bash/ksh): |
| 70 | + |
| 71 | +```sh |
| 72 | +make testlint |
| 73 | +script/mt lint 00/030 |
| 74 | +``` |
| 75 | + |
| 76 | +Performance comparison across released versions and current branch |
| 77 | +(`script/mb`, stashes local changes, builds `modulecmd.tcl` for each |
| 78 | +version): |
| 79 | + |
| 80 | +```sh |
| 81 | +script/mb |
| 82 | +script/mb profile |
| 83 | +``` |
| 84 | + |
| 85 | +## Docs |
| 86 | + |
| 87 | +```sh |
| 88 | +cd doc && make html # requires Sphinx >= 1.0; output in doc/_build/html |
| 89 | +``` |
| 90 | + |
| 91 | +## Repository layout |
| 92 | + |
| 93 | +- `tcl/` — Tcl source, split by concern; concatenated into `modulecmd.tcl` |
| 94 | + (see Build section). Key files: |
| 95 | + - `init.tcl.in` — configuration option definitions (`g_config_defs`) and |
| 96 | + state handling |
| 97 | + - `main.tcl.in` — top-level `module` procedure, sub-command dispatch/parsing |
| 98 | + - `subcmd.tcl.in` — one `cmdModule<Subcmdname>` procedure per sub-command |
| 99 | + - `mfcmd.tcl` — modulefile commands (procedures usable *inside* a |
| 100 | + modulefile, e.g. `setenv`, `prepend-path`) |
| 101 | + - `modeval.tcl` — modulefile evaluation engine |
| 102 | + - `modfind.tcl.in` — locating available/loaded modules |
| 103 | + - `modscan.tcl` — modulefile scan / extra-match search |
| 104 | + - `modspec.tcl` — module specification (name/version/variant) parsing |
| 105 | + - `cache.tcl.in`, `coll.tcl.in` — module cache and collection management |
| 106 | + - `envmngt.tcl.in` — environment variable manipulation |
| 107 | + - `interp.tcl.in` — sub-interpreter management (modulefile sandboxing) |
| 108 | + - `report.tcl.in` — all output/error/debug rendering |
| 109 | + - `util.tcl` — generic helpers |
| 110 | + - `syntaxdb.tcl` — generated Nagelfar syntax DB for linting (do not hand-edit) |
| 111 | +- `init/` — per-shell init/wrapper scripts (`bash.in`, `csh.in`, `fish.in`, |
| 112 | + `tcsh.in`, `zsh.in`, `pwsh.ps1.in`, `perl.pm.in`, `r.R.in`, `ruby.rb.in`, |
| 113 | + `cmake.in`, ...) and shell completion scripts |
| 114 | + (`bash_completion.in`, `fish_completion`, `tcsh_completion.in`, |
| 115 | + `zsh-functions/_module.in`) |
| 116 | +- `script/` — developer/maintainer tooling: `mt` (test runner), `mb` |
| 117 | + (benchmark), `mlprof` (profiling), `mrel`/`mpub` (release), `pre-commit`/ |
| 118 | + `commit-msg` git hooks, `add.modules.in`, `modulecmd.in` wrapper |
| 119 | +- `lib/` — optional C helper library (`envmodules.c`) used for things Tcl |
| 120 | + can't do portably (e.g. group lookups); has its own `configure.ac` |
| 121 | +- `testsuite/` — DejaGnu test suite; `.exp` files grouped by numbered |
| 122 | + directory reflecting test category, e.g. `modules.00-init`, |
| 123 | + `modules.50-cmds`, `modules.70-maint`, `modules.90-avail`, |
| 124 | + `modules.92-spider`, `lint.00-init`, `install.00-init`. Each `.exp` file is |
| 125 | + numbered/prefixed (e.g. `470-variant.exp`); `script/mt 50/470` targets it. |
| 126 | + `modulefiles*/` subdirectories hold fixture modulefiles used by the tests. |
| 127 | +- `share/` — Nagelfar syntax databases, RPM spec, logo assets |
| 128 | +- `doc/source/` — Sphinx documentation source |
| 129 | + - `design/` — design notes for individual features (one `.rst` per feature) |
| 130 | + - `devel/` — maintainer/developer howtos, notably |
| 131 | + `add-new-sub-command.rst` and `add-new-config-option.rst`, which give |
| 132 | + the exact list of files to touch (core code, init/completion scripts, |
| 133 | + Nagelfar syntax db, man pages, testsuite) when adding a sub-command or |
| 134 | + config option — follow these when doing either task |
| 135 | + - `changes.rst` — in-depth behavior/feature changes per major version |
| 136 | + - `*.rst` at top level — man page sources (`module.rst`, `modulefile.rst`, |
| 137 | + `modulecmd.rst`, `ml.rst`, `envml.rst`) |
| 138 | + |
| 139 | +## Coding conventions (Tcl) |
| 140 | + |
| 141 | +- Max line length: 78 characters |
| 142 | +- Indent with 3 spaces, no tabs |
| 143 | +- Tcl minimal escaping style |
| 144 | +- Procedure names: `lowerCamelCase`; variable names: `no_case_at_all` |
| 145 | +- Brace/bracket placement: |
| 146 | + ```tcl |
| 147 | + if {![isStateDefined already_report]} { |
| 148 | + setState already_report 1 |
| 149 | + } |
| 150 | + ``` |
| 151 | +- No trailing whitespace, no misspellings (enforced by the `pre-commit`/ |
| 152 | + `commit-msg` hooks in `script/`, backed by `codespell` and `hunspell`) |
| 153 | +- Code must remain compatible with Tcl 8.5+ |
| 154 | + |
| 155 | +## Commit conventions |
| 156 | + |
| 157 | +- Every commit needs a DCO `Signed-off-by:` trailer matching the committer's |
| 158 | + git `user.name`/`user.email` — use `git commit -s`. |
| 159 | +- Patches (bug fix or feature) should include tests, and the test should be |
| 160 | + shown to fail without the patch. |
| 161 | +- The project follows the Linux kernel's policy on AI coding assistants (see |
| 162 | + "AI coding assistants" in `CONTRIBUTING.rst`): never add a |
| 163 | + `Signed-off-by:` trailer on behalf of the AI — only the human committer |
| 164 | + signs off — and disclose AI involvement with an `Assisted-by:` trailer, |
| 165 | + e.g. `Assisted-by: Claude:claude-sonnet-5`. |
| 166 | + |
| 167 | +## Adding a new `module` sub-command or config option |
| 168 | + |
| 169 | +Don't improvise the file list — `doc/source/devel/add-new-sub-command.rst` |
| 170 | +and `doc/source/devel/add-new-config-option.rst` enumerate, in order, every |
| 171 | +file that needs a change: core Tcl (`main.tcl.in`/`subcmd.tcl.in` or |
| 172 | +`init.tcl.in`), shell completion scripts, Nagelfar syntax db, man pages |
| 173 | +(`doc/source/module.rst`, `modulefile.rst`), `doc/source/changes.rst`, and |
| 174 | +the specific testsuite files/directories to extend. Read the relevant one |
| 175 | +before starting either task. |
0 commit comments