docs: add example extension + extensions guide

This commit is contained in:
2026-08-28 21:54:59 -04:00
parent 2e8553116b
commit 219ea3fb4f
2 changed files with 262 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
-- 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.