-- ext_fortran.lua -- an example third-party language extension. -- -- WHAT THIS MODULE DOES -- -- Fortran is NOT built into stupidtools: the builtin modules are only the -- C/C++ language pair (src/ext/lang_c.c / src/ext/lang_cpp.c). This file -- shows how a third party adds another language -- and a language-specific -- compiler-flag check -- without touching core or recompiling the binary. -- -- HOW DISCOVERY LOADS IT -- -- Any regular file whose name ends in ".lua" is discovered (src/ext/ -- discovery.h), from any of these sources, in this order: -- -- 1. each --ext-dir DIR command-line flag (repeatable); -- 2. each directory in the colon-separated STUPIDTOOLS_EXT env var; -- 3. the per-user directory: $XDG_DATA_HOME/stupidtools/ext, falling -- back to $HOME/.local/share/stupidtools/ext; -- 4. the builtin directory ("builtin-ext", compile-time configured). -- -- Each module is read into memory and run as ONE sandboxed Lua chunk with -- the full file path as its chunkname (so diagnostics name file AND line). -- Load order within a directory is bytewise-lexicographic, and a module -- that fails to load aborts discovery (fail-fast). Try it: -- -- cp examples/ext_fortran.lua /tmp/extdemo/ -- ./src/stupidtools --ext-dir /tmp/extdemo -- -- or, equivalently: -- -- STUPIDTOOLS_EXT=/tmp/extdemo ./src/stupidtools -- -- Discovery prints one line per loaded module, built from the ACTUAL -- registrations the module performed: -- -- loaded extension: /tmp/extdemo/ext_fortran.lua - checks: 1 -- (fortran_compiler_flag) languages: 1 (fortran) -- -- THE SANDBOX CONTRACT (what a module CANNOT do) -- -- The runtime is deliberately stripped (src/ext/lua.h). Extension code -- runs with: -- * os.execute / os.exit / os.remove / os.rename PRESENT but raising -- a "sandbox blocked:" error when called; -- * dofile / loadfile / require / package / io / debug / coroutine -- REMOVED (the globals are nil; their C libraries are not even -- linked); -- * only the base, table, string, a curated libm-free `math`, and the -- hand-built `os` tables available, plus the `st` registration table. -- -- So a module can declare capabilities but cannot execute programs, touch -- the filesystem, or load other code. Violating the sandbox fails -- discovery with a clear error. Note the probe SOURCE strings below are -- inert text compiled by the host C toolchain, not executed here. -- -- THE EXTENSION POINTS (the only API) -- -- st.register_language(name [, spec-table]) -- Registers a language. v1 registers the NAME only; the optional -- table is accepted (and must BE a table) for forward compatibility -- -- no language detection or toolchain is wired to it yet (see -- "v1 limitations" below). -- -- st.register_check(name, { kind=..., source=..., link={...} }) -- Registers a custom check. `kind` is the probe MODE -- ("compile" | "link" | "run"), `source` is the C snippet the probe -- compiles, and `link` (optional) is a list of extra argv elements -- for the link step. v1 custom checks are C snippets compiled with -- the detected C toolchain (see "v1 limitations" below). -- -- Both names must be non-empty strings free of NUL bytes and must not -- collide with an already-registered name (builtin or earlier module). -- --------------------------------------------------------------------------- -- 1. Register the language itself. "fortran" is arbitrary from core's -- point of view -- any name works. The table argument is validated -- (must be a table) but its contents are NOT consumed in v1. -- --------------------------------------------------------------------------- st.register_language("fortran", { kind = "compile" }) -- --------------------------------------------------------------------------- -- 2. Register a language-specific check. The name becomes a new check kind -- a build file can reference (like the builtin "header" kind is). The -- probe spec says: compile `source` with the C toolchain and require -- the compile (and, with `link`, the link) to succeed. -- -- The source below is deliberately trivially-true C and libm-free: it -- links nothing, calls nothing, includes nothing -- a probe that must -- pass even on the smallest toolchain. A realistic flag probe would -- compile a C snippet with the flag in question and FAIL when the -- compiler rejects the flag. The `-lfortran` link argument is inert -- while the probe compiles (not links) but shows how extra link argv -- is carried through for "link"/"run" mode probes. -- --------------------------------------------------------------------------- st.register_check("fortran_compiler_flag", { kind = "compile", source = "int main(void) { return 0; }", link = { "-lfortran" }, }) -- That is the whole module. Everything else -- detection, probing, the -- generated ./configure -- is core's job; an extension only declares -- what languages and checks exist.