kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit ed999ee8e1b053dc5cf1e7fba06cd1ea4d9fe9f2
parent 6d73c1e5ebb79e9c76814b37342f5bdae0b96626
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 15 Jul 2026 14:40:32 -0700

docs: complete phase 1 release audit

Diffstat:
MREADME.md | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Adoc/RELEASE_AUDIT_2026_6_0.md | 1247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/cmd/addr2line.c | 24++++++++++++++++++++++--
Mdriver/cmd/ar.c | 11++++++++++-
Mdriver/cmd/as.c | 25+++++++++++++++++++------
Mdriver/cmd/build.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mdriver/cmd/build_coord.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdriver/cmd/cas.c | 45+++++++++++++++++++++++++++++++++++++++++++--
Mdriver/cmd/cc.c | 204++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mdriver/cmd/cmp.c | 17+++++++++++++++--
Mdriver/cmd/compress.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mdriver/cmd/cpio.c | 23+++++++++++++++++++++++
Mdriver/cmd/cpp.c | 23+++++++++++++++++++++--
Mdriver/cmd/dbg.c | 27+++++++++++++++++++++++++--
Mdriver/cmd/disas.c | 19+++++++++++++++++--
Mdriver/cmd/gram.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/cmd/hash.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdriver/cmd/image.c | 32++++++++++++++++++++++++++++++++
Mdriver/cmd/install.c | 19+++++++++++++++++++
Mdriver/cmd/ld.c | 40++++++++++++++++++++++++++++++++++------
Mdriver/cmd/mc.c | 19++++++++++++++++++-
Mdriver/cmd/nm.c | 22++++++++++++++++++++--
Mdriver/cmd/objcopy.c | 23++++++++++++++++++++++-
Mdriver/cmd/objdump.c | 7+++++++
Mdriver/cmd/pkg.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdriver/cmd/ranlib.c | 9+++++----
Mdriver/cmd/run.c | 20++++++++++++++++++--
Mdriver/cmd/size.c | 20++++++++++++++++++--
Mdriver/cmd/strings.c | 9+++++++++
Mdriver/cmd/strip.c | 17++++++++++++++---
Mdriver/cmd/symbolize.c | 36++++++++++++++++++++++++++----------
Mdriver/cmd/update.c | 35++++++++++++++++++++++++++++++++---
Mdriver/cmd/xxd.c | 9+++++++++
Mdriver/driver.h | 7+++++++
Mdriver/main.c | 37+++++++++++++++++++++++++------------
Minclude/kit/build_coord.h | 7++++++-
Minclude/kit/compile.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/kit/config.h | 3++-
Minclude/kit/core.h | 3++-
Minclude/kit/gram_parse.h | 8++++++--
Minclude/kit/package.h | 11++++++++---
Minclude/kit/support/gram_lex_tables.h | 11++++++++---
Minclude/kit/wasm.h | 5+++--
Mrt/include/kit/backtrace.h | 9++++++---
Mrt/include/kit/baremetal.h | 7++++---
Mrt/include/kit/coro.h | 5+++--
Mrt/include/setjmp.h | 3++-
Mrt/lib/README.md | 7++++---
Atest/audit/release/README.md | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/api_compile_session.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/api_context.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/api_context_arena.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/api_diagnostic.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/api_frontend.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/fixtures/archive_main.c | 3+++
Atest/audit/release/fixtures/archive_value.c | 1+
Atest/audit/release/fixtures/build/BUILD.kit | 3+++
Atest/audit/release/fixtures/build/input.txt | 1+
Atest/audit/release/fixtures/build/recipes/copy.sh | 5+++++
Atest/audit/release/fixtures/cas_map.txt | 1+
Atest/audit/release/fixtures/gram_main.c | 13+++++++++++++
Atest/audit/release/fixtures/gram_simple.ebnf | 1+
Atest/audit/release/fixtures/group_left.c | 5+++++
Atest/audit/release/fixtures/group_main.c | 4++++
Atest/audit/release/fixtures/group_right.c | 5+++++
Atest/audit/release/fixtures/native_sdk_malloc.c | 8++++++++
Atest/audit/release/fixtures/poly_helper.c | 1+
Atest/audit/release/fixtures/poly_main.c | 3+++
Atest/audit/release/fixtures/readme_answer.c | 1+
Atest/audit/release/fixtures/readme_main.c | 3+++
Atest/audit/release/fixtures/select_main.c | 3+++
Atest/audit/release/fixtures/sentinel-tool.sh | 12++++++++++++
Atest/audit/release/fixtures/strong_value.c | 1+
Atest/audit/release/fixtures/uthash_probe.c | 21+++++++++++++++++++++
Atest/audit/release/fixtures/weak_value.c | 1+
Atest/audit/release/lib/harness.sh | 396+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/modules/api.sh | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/modules/core.sh | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/modules/help.sh | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/modules/standalone.sh | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/audit/release/run.sh | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
81 files changed, 4734 insertions(+), 210 deletions(-)

diff --git a/README.md b/README.md @@ -36,7 +36,7 @@ It features: - A single multi-call binary, with an `install` command that drops per-tool symlinks (hard links on Windows) into a directory for drop-in toolchain use - Debug info generation and consumption (DWARF) -- An interactive JIT debugger on the hard-green `test-dbg` host lanes +- An interactive JIT debugger on Darwin/Linux aarch64 hosts - Header dependency generation - Reproducible builds - Signed, content-addressed code distribution (`.kpkg`) @@ -55,9 +55,80 @@ And kit-specific extensions: ## Documentation -Start with [`doc/DESIGN.md`](doc/DESIGN.md) — the entrypoint that covers kit's -design principles, layered architecture, and primary data flows, and indexes a -design doc for every major subsystem (frontends, codegen, IR, optimizer, arch -backends, object formats, linker, JIT, debug info, debugger, runtime, driver, -packaging, …). Forward-looking roadmaps live in [`doc/plan/`](doc/plan/). -Code-interface detail lives in the public headers under `include/kit/`. +The release distribution is self-documenting through: + +- `bin/kit --help` for the tool inventory and general conventions; +- `bin/kit COMMAND --help` (or `bin/kit help COMMAND`) for each command; +- this `README.txt`; and +- the public API comments under `include/kit/`. + +The 2026.6.0 release currently requires explicit resource paths when used from +an extracted distribution. The examples below state those paths instead of +depending on a source-tree working directory. Keep every path quoted if the +distribution may be installed below a directory whose name contains spaces. + +### C developer quick start + +On macOS, run this from the extracted distribution directory. Kit discovers +the native SDK with `-print-sysroot`, but this release requires feeding that +path back with `-isysroot`. The explicit `--support-dir` is likewise required +when the current working directory is outside the Kit source tree. + +```sh +ROOT="$PWD" +K="$ROOT/bin/kit" +SDK="$("$K" cc -print-sysroot)" + +printf 'int answer(void) { return 42; }\n' > answer.c +printf 'int answer(void); int main(void) { return answer() != 42; }\n' > main.c + +"$K" cc --support-dir "$ROOT/support" -isysroot "$SDK" -g -c answer.c -o answer.o +"$K" ar rcs libanswer.a answer.o +"$K" ranlib libanswer.a +"$K" cc --support-dir "$ROOT/support" -isysroot "$SDK" main.c libanswer.a -o hello +./hello +"$K" nm libanswer.a +"$K" objdump -h -t hello +``` + +For hosted cross-compilation, replace `-isysroot "$SDK"` with the target's +canonical `-target TRIPLE --sysroot /path/to/sysroot` pair. For freestanding +work, use a freestanding triple, provide startup code and a linker script, and +select the entry with `-T SCRIPT -e SYMBOL`. The complete current target list +and its required sysroots are not yet exposed by the release binary. + +### Language developer quick start + +The public library does not provide a hosted context adapter. An embedder owns +and initializes a `KitHeap`, `KitDiagSink`, and optional `KitFileIO`, then keeps +them alive through this lifecycle: + +1. Initialize `KitContext` with those host callbacks. +2. Parse a triple with `kit_target_from_triple`, then call `kit_target_new`. +3. Create a `KitCompiler` with `kit_compiler_new` or `kit_compiler_new_ex`. +4. Compile source with `KitCompileSession`, or emit a language directly through + `KitCg` into a `KitObjBuilder`. +5. Emit the relocatable object, or add it to a `KitLinkSession`, resolve, and + emit the linked image through a caller-owned `KitWriter`. +6. Commit a staged compile only after publishing its output; otherwise abort + it. Free sessions/writers/builders before the compiler, then free the target. + +`include/kit/compile.h` contains a concrete source-session/link lifecycle; +`include/kit/cg.h`, `include/kit/object.h`, and `include/kit/link.h` document the +lower-level language-frontend path and ownership rules. A hosted out-of-tree +program using the static library is built with only the distributed toolkit as +follows on macOS: + +```sh +ROOT="/path/to/extracted/kit" # replace with the extraction directory +K="$ROOT/bin/kit" +SDK="$("$K" cc -print-sysroot)" +"$K" cc --support-dir "$ROOT/support" -isysroot "$SDK" \ + -I "$ROOT/include" embed.c "$ROOT/lib/libkit.a" -o embed +./embed +``` + +No extra user-specified library is required when the native sysroot is +explicit. The embedder's heap callbacks must honor the requested alignment; +diagnostics are reported through the supplied sink, whose `errors` and +`warnings` counters are maintained by libkit. diff --git a/doc/RELEASE_AUDIT_2026_6_0.md b/doc/RELEASE_AUDIT_2026_6_0.md @@ -0,0 +1,1247 @@ +# Kit 2026.6.0 black-box release audit + +Phase 1 baseline report, 2026-07-15. This report covers the immutable release +distribution preserved at `build/audit/release-phase1/baseline/dist/kit`. It +does not approve or begin Phase 2 functional remediation. + +Throughout this report, `evidence/...` abbreviates +`build/audit/release-phase1/evidence/...`. + +## 1. Executive release-readiness assessment + +**Release decision: NO-GO.** The baseline is not ready to ship under the stated +product contract. The audit records 37 findings: **4 P0, 17 P1, 13 P2, and 3 +P3**. All four P0s are release blockers: + +1. The compiler-driver family and executable linker do not resolve the shipped + support tree relative to the distribution. Central workflows fail from an + unrelated working directory and can accidentally consume repository `./rt` + files when run in the source tree. +2. Native macOS `cc hello.c` does not automatically apply the valid SDK that + `cc -print-sysroot` itself discovers. +3. `cc --emit=c -O1` terminates by signal instead of succeeding or returning a + normal diagnostic. +4. The linker silently accepts mixed x86-64/aarch64 Mach-O inputs and emits an + arm64-labeled executable that traps at run time. + +There is meaningful healthy depth behind those blockers. With explicit +support/sysroot workarounds, native workflows, all six Linux cells, all seven +FreeBSD/Windows/macOS cells, most freestanding cells, Lua, SQLite, the public +API integration, Gram generation, CAS, packaging, JIT/interpreter execution, +and most binary utilities pass. Direct `kit ld` plus the configured runners +also executes correct freestanding programs on all five architectures. Those +workaround results demonstrate backend capability; they do not satisfy the +self-contained, relocatable release contract. + +No target or advertised command was silently skipped. Android execution is +explicitly NOT APPLICABLE because the configured support is compile/link-only. +No matrix check was blocked by missing provisioning on this host. A successful +live update-channel fetch remains BLOCKED because a production index URL and +initial channel workflow are not discoverable from the distribution. + +The Phase 1 help/documentation candidate does not change that decision or any +functional baseline result. It fixes nine findings entirely within the allowed +documentation surface and clarifies three more. Its help harness passes 168 of +172 cases; the only four failures are the deliberately unimplemented +`kit targets` entry points reserved for Phase 2. All 41 installed command names +produce routed, byte-identical help, and the candidate README, public API +examples, runtime-header documentation, and complex-tool examples execute as +documented. The four functional candidate failures are unchanged baseline +defects, not candidate regressions. + +## 2. Product contract and audit boundaries + +The audited product serves C developers using the command-line toolkit and +language developers using shipped public headers plus `lib/libkit.a`. The +acceptance standard is a self-contained, relocatable compiler/linker/tool suite +that may use ordinary OS facilities and `curl`, but not a host compiler, +assembler, linker, archive tool, binutils, or SDK-discovery helper. Native SDK, +explicit hosted sysroot, freestanding, common GCC/Clang/binutils-style, and +self-describing usage are in scope. Every command advertised by `kit --help` is +treated as supported. + +Phase 1 was black-box and had two discovery stages: + +- The cold stage used only the preserved distribution, its reachable help, + `README.txt`, `VERSION`, public headers, and shipped libraries. +- Only after the cold findings were recorded were `scripts/hosted.sh`, + `doc/PORT.md`, and related provisioning/execution infrastructure consulted + to recover and run the target matrix. Information recoverable only there + remains a product discoverability failure. + +The initial audit did not inspect implementation source or internal tests to +infer intended behavior. It made no functional implementation or existing-test +changes and made no commits. The only allowed Phase 1 tracked surfaces are this +report, the reusable black-box harness and fixtures, and later literal +help/shipped-documentation remediation. Baseline results remain tied to the +immutable copy even if a candidate help binary is subsequently built. + +## 3. Exact baseline identity and environment + +| Item | Recorded value | Evidence | +|---|---|---| +| Audit time | `2026-07-15T20:03:43Z` / `2026-07-15T13:03:43-0700` | `evidence/meta/host.txt` | +| Host | macOS 26.3 (25D125), Darwin 25.3.0, arm64 | `evidence/meta/host.txt` | +| Initial branch | `main...origin/main [ahead 1]` | `evidence/meta/initial-git-status-branch.txt` | +| Initial worktree | clean | `evidence/meta/initial-git-status.txt` | +| Original distribution | `/Users/ryan/code/kit/build/dist/kit` | `evidence/meta/baseline-paths.txt` | +| Preserved distribution | `/Users/ryan/code/kit/build/audit/release-phase1/baseline/dist/kit` | same | +| Exact binary | preserved distribution `bin/kit` | same | +| Binary version | `kit 2026.6.0 (6d73c1e, aarch64-macos)` | `evidence/meta/kit-version.stdout` | +| Repository/distribution VERSION | both `2026.6.0` | `evidence/meta/version-files.txt` | +| SHA-256 | `e16940b54338f4851a3932e9ea2e8af995935b81eb86f15c655234f146fde00a` | `evidence/meta/{baseline,preserved}-kit.sha256` | +| Binary type | Mach-O 64-bit executable arm64 | `evidence/meta/baseline-kit.file` | +| Dynamic dependencies | only `/usr/lib/libSystem.B.dylib` | `evidence/meta/baseline-kit.dependencies` | + +The full initial environment, including `HOME`, `PATH`, and `TMPDIR`, is in +`evidence/meta/environment.txt`. Stateful workflows used isolated HOME, XDG, +cache, config, data, temporary, installation, and output directories rather +than the recorded user locations. + +The shipped layout contains `README.txt`, `VERSION`, one `bin/kit`, 37 +top-level public headers, five `kit/support` headers, `lib/libkit.a`, and the +`support/rt` header/source tree. The complete inventory is +`evidence/meta/distribution-layout.txt`. + +## 4. Distribution, dependency, and relocation assessment + +| Check | Result | Evidence / qualification | +|---|---|---| +| Direct invocation from copied tree | PASS with explicit support and SDK | `evidence/standalone/` | +| Invocation solely through copied `bin` on PATH | PASS with explicit paths | `evidence/standalone/` | +| Distribution path containing spaces | PASS with explicit paths | `evidence/standalone/`, `evidence/cold-distutils/33-relocation-results.txt` | +| Sanitized HOME/XDG/TMP | PASS | `evidence/standalone/`, stateful distutility cases | +| Read-only input/source tree | PASS | `evidence/standalone/` | +| Host-development-tool sentinels | PASS for corrected workflows; no sentinel hit | `evidence/standalone/`, `evidence/cold-distutils/` | +| Native dynamic dependencies | PASS: ordinary `libSystem` only | `evidence/meta/baseline-kit.dependencies` | +| Explicit cross sysroots | PASS for six Linux cells and depth runs on seven platform cells | target evidence roots | +| Automatic shipped support discovery | **FAIL** outside repository cwd | `evidence/standalone/default-relocated-isolated.*` and target/platform evidence | +| Absence of repository-relative dependencies | **FAIL**: defaults report/consume `./rt` | `evidence/cold-api/search-dirs/`, platform isolated-cwd evidence | +| Native SDK use without manual feedback | **FAIL** | `evidence/targets-platforms/28-macos-auto-summary.txt` | +| `kit install --all` aliases | PASS before moving distribution | `evidence/cold-toolchain/workflow/install/`, `evidence/cold-distutils/32-relocate-setup-summary.txt` | +| Move distribution after installation | **FAIL**: absolute symlinks dangle, exit 127 | `evidence/standalone/installed-after-move.*` | +| Update external helper | PASS: failed live fetch invoked permitted `curl` only | `evidence/cold-distutils/update-fetch-corrected.log` | + +The authoritative relocation result comes from an isolated cwd. A run from the +repository root is invalid as standalone evidence because Kit reports `./rt` +and can consume source-tree material. Likewise, an early sentinel failure and +an early move attempt described in the notes were audit setup errors; only the +corrected records named above are counted. + +## 5. Complete command and alias coverage matrix + +All 41 advertised command names received no-argument, `-h`, `--help`, and +`kit help NAME` probes. `kit install --all` created all 41 multicall names in an +isolated prefix; every installed name executed its help, and representative +aliases executed real workflows. “PASS*” means the implementation workflow +passes but a material documentation/compatibility caveat remains. “FAIL” is a +release-surface failure, even if an explicit workaround succeeds. + +| Command | Result | Minimal/nontrivial black-box coverage | Installed name | +|---|---|---|---| +| `cc` | **FAIL** | preprocess, deps, check, object/asm/IR/C/LTO/debug, multi-source, link/run; support/SDK defects and C-output crash | help PASS; compile reproduces support failure | +| `check` | **FAIL** | basic and macro/include checks pass with support; ordinary invocation fails support discovery | help PASS | +| `build-exe` | **FAIL** | multi-source, O/debug/LTO, archive link/run pass with support | help PASS | +| `build-lib` | **FAIL** | static archive and link/run pass with support; shared restriction tested | help PASS | +| `build-obj` | **FAIL** | object, multi-source combine, asm, C, IR, stdout pass with support | help PASS | +| `build` | **FAIL** | cold user blocked; recovered hidden syntax then first build/cache hit/rebuild pass | help PASS | +| `install` | **FAIL** | default/all/verbose/dry-run/specific/space path pass; installed links fail after move | help and installed products PASS before move | +| `cpp` | PASS* | file/stdout/output/stdin/include/define | help PASS | +| `as` | PASS* | Kit-generated assembly reassembled; bad mnemonic diagnostic; stdin `-` gap | help PASS | +| `ld` | **FAIL** | relocatable pass; executable/map/archive/run pass with support; default support failure | help PASS | +| `ar` | **FAIL** | create/replace/list/print/extract/index/link; dashed `-rcs` rejected | help PASS | +| `cpio` | **FAIL** | newc/gzip/LZ4 create/list/extract and symlink; corruption/conflict exit 0 | help PASS | +| `ranlib` | PASS* | refresh, indexed archive link/run; documented `ar s` equivalent is false | help PASS | +| `strip` | **FAIL** | object/archive/debug/keep-symbol pass; executable silently damaged | help PASS | +| `objcopy` | **FAIL** | copy/add/remove/rename/validate pass; relocatable `-O binary` rejected | help PASS | +| `image` | **FAIL** | flat/stdout/section/metadata/IHEX pass with bias; high-address/SREC limits hidden | help PASS | +| `objdump` | PASS | headers/symbols/relocs/disassembly/content/DWARF/archive/private/dynamic | help PASS (`-h` is an operation) | +| `run` | PASS | JIT/interpreter/multi-source/argv/stdin/program exit propagation | help PASS | +| `dbg` | **FAIL** | scripted session and invalid command pass; missing script exits 0 | help PASS | +| `nm` | PASS* | object/archive/sorted/global/prefixed executable | help PASS | +| `size` | PASS* | Berkeley/SysV/hex/multiple/total | help PASS | +| `addr2line` | PASS* | argv/stdin, function/pretty/basename source resolution | help PASS | +| `symbolize` | PASS* | backtrace annotation and basename | help PASS | +| `strings` | **FAIL** | file/stdin/multiple/min length/offset/prefix; common `--` rejected | help PASS | +| `cas` | PASS* | blob/tree add, inspect, verify, materialize, corrupted store rejection | help and representative alias PASS | +| `pkg` | PASS* | keygen/create/inspect/verify/trust/TOFU/unpack/corruption; detached verify undiscoverable | help and representative alias PASS | +| `xxd` | PASS* | table/plain/C array/stdin/reverse/round trip/space/dash path | workflow alias PASS | +| `cmp` | PASS* | equal/different/quiet/verbose/skips/stdin; missing-file status and GNU gaps | help PASS | +| `hash` | PASS | three algorithms/stdin/multiple/space/dash; SHA independent match | workflow alias PASS | +| `sha256sum` | PASS* | fixed alias digest; no `-c` | workflow alias PASS | +| `b2sum` | PASS* | documented BLAKE2b-256 alias; no verification mode | installed/help PASS | +| `crc32` | PASS* | fixed CRC-32 alias | installed/help PASS | +| `compress` | PASS* | gzip/LZ4 file/stdin/auto-detect/corruption | help PASS | +| `gzip` | PASS* | common flags, stdin/stdout, independent `gzip -t` | workflow alias PASS | +| `gunzip` | PASS* | stdin/stdout round trip and empty-input failure | workflow alias PASS | +| `lz4` | PASS* | Kit round trip; independent lz4 oracle unavailable | installed/help PASS | +| `lz4c` | PASS* | fast-mode Kit round trip | installed/help PASS | +| `disas` | PASS* | x64/aa64/rv64 inline/file/stdin/base/mc round trip | workflow alias PASS | +| `mc` | PASS | x64/aa64/rv64, stdin/plain/relocations/disas round trip | workflow alias PASS | +| `gram` | PASS* | parse/dump/generate, generated C compile/link/run; grammar docs absent | help PASS | +| `update` | **BLOCKED/FAIL** | isolated local package install/list/version and failed curl pass; live channel unavailable; `--check` status wrong | isolated representative alias PASS | + +Detailed command evidence is indexed by +`evidence/cold-toolchain/all-cases.tsv` and the summary files in +`evidence/cold-distutils/`. The generated Gram executable produced +`tokens=2 rules=1 control=34 values=18 scratch=5`; see +`evidence/gram-deeper/`. + +## 6. Help and documentation coverage matrix + +For every command except the documented `objdump -h` operation, `-h`, +`--help`, and `kit help COMMAND` are byte-identical on stdout with status 0. +The top-level statement that no arguments equal `--help` is false for 21 +commands. Formal examples exist on only 3 of 41 pages. The complete saved +matrix is `evidence/cold-help/help-matrix.tsv`. + +Legend: H0 = full help/status 0; U2 = usage/status 2; S0/S1 = empty-stdin +success/failure; R0 = REPL then EOF/status 0; O1 = operational failure. + +| Command | No args | Example | Exit convention | Baseline help finding | +|---|---:|---:|---:|---| +| `cc` | H0 | no | missing | says “see source”; incomplete flags/targets | +| `check` | H0 | no | missing | delegates to incomplete cc options | +| `build-exe` | H0 | no | missing | selected options only; no workflow | +| `build-lib` | H0 | no | missing | no static/shared workflow | +| `build-obj` | H0 | no | missing | no languages/emit workflow | +| `build` | U2 | no | missing | no definition/recipe grammar | +| `install` | U2 | no | present | no install/use/relocate workflow | +| `cpp` | H0 | no | present | circular target reference | +| `as` | H0 | no | present | circular target reference; no stdin example | +| `ld` | H0 | no | present | no complete/platform link workflow | +| `ar` | H0 | yes | present | later contradicted by ranlib help | +| `cpio` | U2 | no | present | no create/list/extract workflow | +| `ranlib` | H0 | no | present | claims rejected bare `ar s` equivalent | +| `strip` | H0 | no | present | help flags/examples absent | +| `objcopy` | H0 | no | present | defaults/restrictions absent; copied “strip” text | +| `image` | H0 | no | present | no freestanding example/address limits | +| `objdump` | H0 | yes | present | `-h` exception documented | +| `run` | H0 | yes | present | circular target reference | +| `dbg` | R0 | no | present | repository-test vocabulary; no script workflow | +| `nm` | H0 | no | missing | formats/archive/examples absent | +| `size` | H0 | no | missing | formats/archive/examples absent | +| `addr2line` | H0 | no | missing | address syntax/examples absent | +| `symbolize` | H0 | no | missing | PIE/load-bias and exits absent | +| `strings` | S0 | no | present | no examples/leading-dash convention | +| `cas` | H0 | no | missing | IDs/map/defaults/errors absent | +| `pkg` | U2/full stdout | no | missing | subcommand details/trust/examples absent | +| `xxd` | S0 | no | present | inline pipeline only | +| `cmp` | U2 | no | present | no examples | +| `hash` | S0 | no | present | no examples/check mode | +| `sha256sum` | S0 | no | present | generic hash title/usage | +| `b2sum` | S0 | no | present | generic hash title/usage | +| `crc32` | S0 | no | present | generic hash title/usage | +| `compress` | S0 | no | present | no round-trip/interoperation example | +| `gzip` | S0 | no | present | generic compress title/usage | +| `gunzip` | S1 | no | present | generic compression-first usage | +| `lz4` | S0 | no | present | generic compress title/usage | +| `lz4c` | S0 | no | present | generic compress title/usage | +| `disas` | U2/full stdout | no | present | circular target reference | +| `mc` | U2/full stdout | no | present | no target discovery/round trip | +| `gram` | U2 | no | present | grammar syntax/directives absent | +| `update` | O1 | no | present | default channel/setup absent | + +The shipped README's only documentation entry points, `doc/DESIGN.md` and +`doc/plan/`, are absent. Public headers also refer to absent +`doc/BUILD_COORDINATOR.md`, `doc/CBACKEND.md`, `doc/DISTRIBUTE.md`, and plan +documents. No complete public-API composition example is shipped. The binary +accepts unadvertised `kit --version` and `kit version`, while +`kit help version` rejects the latter spelling. + +The required absence of `kit targets` was reproduced before any remediation: +it prints top help to stdout, `kit: no such tool: targets` to stderr, and exits +2. It must not be added to ordinary help until implemented in Phase 2. + +### Phase 1 help/documentation candidate + +The candidate is separate from the immutable baseline at +`build/audit/release-phase1/candidate/dist/kit`. Its binary is +`kit 2026.6.0 (6d73c1e-dirty, aarch64-macos)`, SHA-256 +`2e2114e8d61bf5e86ab5741e67c763c1a80ff76a8146c8b4267420d30b9d54b6`, +and depends only on `/usr/lib/libSystem.B.dylib`. The dirty marker records the +uncommitted help-only source changes; this candidate is not a new baseline. + +| Candidate validation | Result | Evidence | +|---|---|---| +| Help entry points and required content | 168 PASS, 4 expected FAIL (`targets` no-arg/short/long/routed) | `evidence/candidate-harness/help-final/summary.tsv` | +| Installed aliases in a path containing spaces | 41/41 exit 0, byte-identical routed stdout, empty stderr | `evidence/candidate-alias-help/summary.tsv` | +| Functional/docs harness | 8 PASS, 4 unchanged functional FAIL | `evidence/candidate-harness/functional-docs-final/summary.tsv` | +| README C quickstart under sanitized state/tool sentinels | PASS through compile/archive/index/link/run/inspect; no sentinel hits | `evidence/readme-candidate/` | +| Help-derived `build`, Gram, CAS, and package workflows | PASS | `evidence/candidate-examples/` | +| Public and runtime header self-sufficiency | 42/42 public and 27/27 runtime headers PASS | functional/docs harness and `evidence/candidate-runtime-headers/results.tsv` | +| Dead-reference/source-direction scan of shipped user docs | PASS | `evidence/candidate-doc-checks/forbidden-phrases-final.exit` | + +Every page now contains an `EXAMPLES` section and an `EXIT CODES` section. +Complex pages contain complete, tested workflows; alias-specific pages show the +invoked name and defaults. Help documents observed restrictions and failures +without presenting Phase 2 behavior as implemented. It does not advertise +`kit targets`. + +## 7. Successful workflow results + +| Workflow | Baseline result | Evidence / caveat | +|---|---|---| +| Preprocess and stdin preprocessing | PASS | cold-toolchain `workflow/cpp`, cc preprocessing | +| Dependency generation | PASS single input; **FAIL** common multi-input `-MM` | cold-toolchain and real-project Lua depend | +| Syntax checking | PASS with explicit support | `workflow/check` | +| Object and assembly output | PASS with explicit support | cc/build-obj evidence | +| Portable C output | PASS O0; **P0 FAIL** O1 traps | `workflow/cc-explicit-support/portable-c` | +| IR output | PASS | cc/build-obj evidence | +| Optimized output and LTO | PASS | cc/build-* evidence | +| Debug information | PASS; independently consumed by Kit utilities | objdump/nm/size/addr2line/symbolize evidence | +| Native system SDK | **FAIL default**; PASS with support plus explicit `-isysroot` | platform and standalone evidence | +| Explicit hosted sysroot | PASS across Linux and platform depth cells | target evidence roots | +| Freestanding compile/final link | compile/direct ld/run PASS; **driver baseline FAIL** | freestanding matrix | +| Multiple translation units | PASS | cc/build-exe/group evidence | +| Grouped/polyglot inputs | PASS and run | `evidence/grouped-polyglot/` | +| Weak/strong symbols | PASS expected strong selection | `evidence/symbol-link-order/` | +| Archive ordering | PASS after-reference; expected undefined before-reference | same | +| Startup objects/relocations | PASS in freestanding direct-link matrix | freestanding per-cell artifacts | +| Static libraries | PASS | ar/ranlib/build-lib/Lua | +| Shared libraries | PASS on supported Linux/Windows/Android shapes | target evidence; Android real fixture fails preprocessing | +| `build-exe/lib/obj` | PASS with explicit support | cold-toolchain workflow directories | +| Content-addressed `build` | PASS after repository-only grammar recovery | `evidence/build-coordinator/`; cold discoverability FAIL | +| Assembly/raw machine-code round trip | PASS | as/mc/disas evidence | +| Archive create/replace/list/extract/index/link | PASS | ar/ranlib evidence; dashed form caveat | +| Object inspection/transformation | PASS for documented inputs | objdump/nm/size/objcopy; binary restriction | +| Debug-data line/symbol flow | PASS | addr2line/symbolize workflow | +| Strip/validate | PASS objects/archives; **FAIL executable** | strip evidence | +| Flat image | PASS binary/IHEX with appropriate bias; SREC/high-address gaps | image evidence | +| JIT and interpreter execution | PASS | run workflow | +| Scripted debugger | PASS valid script; **FAIL missing script status** | dbg workflow/failure | +| CAS add/inspect/verify/materialize | PASS including corruption rejection | cold-distutils CAS summaries | +| Package create/signature/verify/unpack | PASS native package and corruption/wrong-key cases; detached-sign verification undiscoverable | cold-distutils pkg summaries | +| CPIO create/list/extract/compression | PASS happy path; **FAIL corruption/conflict status** | cold-toolchain cpio evidence | +| xxd/cmp/hash/compression aliases | PASS round trips; documented compatibility findings | cold-distutils summaries | +| Gram generate/compile/execute | PASS with explicit support/SDK/include/libkit | `evidence/gram-deeper/`; language syntax undiscoverable | +| Install/use multicall names | PASS before moving extraction; **FAIL after move** | standalone evidence | +| Update | PASS isolated local signed install; failed curl returns 1; live success BLOCKED | cold-distutils corrected update evidence | + +The most complete cross-tool journey compiled debug objects, archived/indexed +them, linked and ran them, inspected symbols/sections/DWARF/disassembly, resolved +source locations, transformed/stripped applicable objects, hashed and compressed +artifacts, stored and materialized CAS content, packaged/verified/unpacked it, +and compared retained bytes. Individual stages remain separated in the evidence +so a downstream failure does not mask its producer. + +## 8. Target matrix results + +“Baseline” uses the immutable distribution without an undisclosed support +override. “Depth” records explicit support/sysroot recovery and is not counted +as release success. Every one of the 19 requested cells has an explicit result. + +| Selector | Canonical triple | Baseline | Depth and execution | +|---|---|---|---| +| `linux-glibc-aa64` | `aarch64-linux-gnu` | PASS | dynamic exit 42; hello PASS; ELF independently validated | +| `linux-glibc-x64` | `x86_64-linux-gnu` | PASS | dynamic exit 42; hello PASS; ELF validated | +| `linux-glibc-rv64` | `riscv64-linux-gnu` | PASS | dynamic exit 42; hello PASS; ELF ABI validated | +| `linux-musl-aa64` | `aarch64-linux-musl` | PASS | static+dynamic exit 42 and hello PASS | +| `linux-musl-x64` | `x86_64-linux-musl` | PASS | static+dynamic exit 42 and hello PASS | +| `linux-musl-rv64` | `riscv64-linux-musl` | PASS | static+dynamic exit 42 and hello PASS | +| `freebsd-aa64` | `aarch64-freebsd` | **FAIL: support dir** | explicit support: static link, VM exit 42/hello PASS | +| `freebsd-x64` | `x86_64-freebsd` | **FAIL: support dir** | explicit support: static link, VM exit 42/hello PASS | +| `freebsd-rv64` | `riscv64-freebsd` | **FAIL: support dir** | explicit support: static link, VM exit 42/hello PASS | +| `windows-aa64` | `aarch64-windows` | **FAIL: support dir** | explicit support: PE console link, VM exit 42/hello PASS | +| `windows-x64` | `x86_64-windows` | **FAIL: support dir** | explicit support: PE console link, VM exit 42/hello PASS | +| `android-aa64` | `aarch64-linux-android21` | **FAIL: NDK preprocessing** | minimal PIC/shared/`-landroid` PASS; NativeActivity FAIL; execution N/A | +| `macos-aa64` | `aarch64-apple-darwin` | **FAIL: support and SDK defaults** | explicit support/SDK: native exit 42/hello PASS | +| `macos-x64` | `x86_64-apple-darwin` | **FAIL: support and SDK defaults** | explicit support/SDK: Rosetta exit 42/hello PASS | +| `freestanding-aa64` | `aarch64-none-elf` | **FAIL: driver support dir** | direct ld and QEMU smoke/broad PASS; support workaround driver PASS | +| `freestanding-x64` | `x86_64-none-elf` | **FAIL: driver support dir** | direct ld and QEMU smoke/broad PASS; support workaround driver PASS | +| `freestanding-rv64` | `riscv64-none-elf` | **FAIL: driver support dir** | direct ld and QEMU smoke/broad PASS; support workaround driver PASS | +| `freestanding-rv32` | `riscv32-none-elf` | **FAIL: support/runtime/ABI flags** | direct ld/QEMU PASS with repo runtime; shipped driver runtime unavailable; output flags wrong | +| `freestanding-arm32` | `arm-none-eabi` | **FAIL: driver support dir** | direct ld and QEMU smoke/broad PASS; support workaround driver PASS | + +All requested sysroots and configured FreeBSD/Windows/Linux/macOS/QEMU runners +were present. Android is intentionally compile/link-only in the configured +matrix. Independent validation used `/usr/bin/file` and LLVM `readelf` where +practical; Kit's own tools were also used, avoiding circular validation. + +Evidence roots are `evidence/targets-linux-android/`, +`evidence/targets-platforms/`, and `evidence/targets-freestanding/`. The exact +selector/triple/sysroot/runner mapping came from repository-only provisioning +infrastructure, itself evidence for the missing target-discovery finding. + +## 9. Real-world project results + +Clean copies were made below `build/audit/release-phase1/work/real-projects/`; +the originals in `tmp/projects/` were not modified. + +| Project | Result | Distinct coverage | +|---|---|---| +| Lua 5.4.7 | PASS with explicit support/SDK | generic Makefile, O2, installed `ar rcu`/`ranlib`, `liblua.a`, lua/luac; output `Lua 5.4 42` | +| SQLite 3.50.2 | PASS with explicit support/SDK | large amalgamation at O1, shell link/run; query `3.50.2|42` | +| cJSON 1.7.18 | ordinary Makefile FAIL; flag workaround PASS | common probe chooses unsupported `-fstack-protector`; overriding `R_CFLAGS` builds archives/tests | +| uthash | ordinary header FAIL; upstream workaround PASS | advertised GNU identity selects unsupported `__typeof`; `NO_DECLTYPE` builds/runs | +| Lua `depend` | FAIL | common `cc -MM l*.c` rejected as multiple inputs | + +Commands, statuses, stdout/stderr, artifacts, and outputs are under +`evidence/real-projects/`. + +## 10. Language-developer API and distribution results + +- PASS: all 37 top-level public headers and all five public support headers are + self-sufficient as the first include when the distribution include/support + roots are explicit. +- PASS: an out-of-tree context/target/compiler lifecycle program builds and + runs against only the distributed `kit`, headers, and `libkit.a` after + supplying support and native sysroot paths. +- PASS: a minimal language/frontend integration creates a target/compiler, + emits a function returning 42 through the public CG API, serializes a + relocatable object, links a freestanding ELF through `KitLinkSession`, and + cleans up all handles. +- PASS: an intentional `wasm64` failure invokes the supplied diagnostic sink + and increments the public error count. +- No extra user library was required once the native sysroot was explicit. + `-fno-stack-protector` was used for deliberately large stack-backed arena + probes. +- FAIL for self-description: shipped material has no complete context, host + adapter, compilation, emission, link, diagnostics, and cleanup example. Cold + users cannot discover the composition or required host link flags, and the + README/public headers point to unshipped documents. + +The Phase 1 candidate fixes the documentation failure without changing the API. +The shipped README and headers now give a context/compiler/compile-session/ +object/link/diagnostic/cleanup lifecycle and the exact distributed link form. +The added source-session example, direct-CG example, diagnostic example, and +all 42 public-header probes pass against the candidate distribution. All 27 +runtime headers are also self-sufficient, and dead references in the shipped +runtime README and header comments were replaced with self-contained guidance. +See `evidence/candidate-harness/functional-docs-final/`, +`evidence/candidate-runtime-headers/`, and +`evidence/baseline-runtime-docs/dead-references.log`. + +Fixtures are the `api_context.c`, `api_compile_session.c`, `api_frontend.c`, and +`api_diagnostic.c` files under `test/audit/release/fixtures/`; evidence is +`evidence/language-api/` and the initial cold failures are under +`evidence/cold-api/`. + +## 11. Failure, diagnostic, and exit-code results + +The requested convention is 0 success, 1 operational/tool-reported failure, +and 2 bad command-line usage. Most parsers follow it and reject unsupported +options specifically rather than silently ignoring them. Important exceptions +are explicit below. + +| Failure class | Result | +|---|---| +| Unknown tool | status 2; direct dispatch also prints top help; no suggestion | +| Unknown flags | generally 2 with specific rejection; no close-spelling suggestion | +| Unknown target/format/language | 2 with value named; no discovery pointer/suggestion | +| Missing option argument/input | generally 2; several no-arg tools instead help/0 or stdin/REPL | +| Conflicting flags | **cpio `-o -t` warns and returns 0** | +| Missing/unwritable file | generally 1; **cmp missing file returns 2**; ld can misreport missing entry | +| Wrong architecture | **mixed Mach-O link returns 0 and output traps** | +| Wrong/malformed format | generally 1 with recognized-object diagnostic | +| Duplicate symbol | 1 with symbol named | +| Undefined symbol/archive order | 1; traditional archive ordering observed | +| Malformed/truncated archive | generally 1; **cpio corruption returns 0** | +| Corrupt compressed data | 1 | +| Corrupt CAS data | verify/materialize 1 | +| Corrupt/truncated package or wrong key | 1 | +| Bad detached signature | verification workflow not discoverable | +| Failed curl fetch | 1 in isolated update copy | +| Update check with no index | **diagnostic but status 0** | +| Invalid debugger command | 1; **unreadable script returns 0** | +| Invalid grammar | 1 with location; bad CLI only generic usage/2 | +| Unsupported optimized portable-C path | **diagnostic followed by signal, status 133** | + +Specificity gaps include missing `ld` input becoming an entry-symbol error, +`strip missing.o` saying only “not a recognized object,” and `size` printing a +normal table header to stdout before total failure. Misspelled commands, flags, +targets, languages, and formats do not suggest close valid values. + +## 12. Findings ledger + +All IDs below are frozen baseline IDs. Reproduction records contain the exact +argv, cwd, stdout, stderr, status, and retained artifacts. For concise commands, +let: + +```sh +R=/Users/ryan/code/kit +D=$R/build/audit/release-phase1/baseline/dist/kit +K=$D/bin/kit +E=$R/build/audit/release-phase1/evidence +SDK_PRINTED=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk +SDK_MATRIX=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk +``` + +Each reproduction below is a shell command (or sequence) using these variables. +The cited `command.txt`/`.command` file additionally preserves the fully +expanded cwd, environment, and argv used for the recorded result. + +### KIT-P0-001 — shipped support tree is not resolved relative to Kit + +- **Severity/blocking/status:** P0; release blocking; open. +- **Affected:** `cc`, `check`, `build-exe`, `build-lib`, `build-obj`, executable + `ld`, Gram final link, all non-Linux matrix baseline cells, direct/PATH/install + invocation; both personas. +- **Personas/targets:** C developers and language developers; native macOS, + FreeBSD, Windows, and all freestanding targets (Linux explicit-sysroot cells + did not reproduce this symptom). +- **Reproduce/evidence:** from an empty cwd run + `"$K" cc -c "$R/test/audit/release/fixtures/group_left.c" -o x.o`; + `evidence/standalone/default-relocated-isolated.*`, cold-toolchain + `cases/discovery/*/no-support/`, and platform standalone summaries. +- **Expected/actual:** Kit finds sibling `support/`; instead exits 1, + `cc: support dir not found`, while print paths are cwd-relative `./rt`. +- **Impact/remediation:** central compile/link is neither standalone nor + relocatable and may consume hidden repository files. Resolve defaults from + the executable/distribution, retaining explicit override precedence. +- **Acceptance/regression:** direct, PATH, installed, moved, isolated-cwd, and + path-with-spaces runs pass without overrides across every target family; + search/resource output names absolute shipped paths. Cover POSIX/Windows + install forms and all affected drivers. + +### KIT-P0-002 — native macOS does not automatically apply its discovered SDK + +- **Severity/blocking/status:** P0; release blocking; open. +- **Affected:** native and x64 macOS `cc`; C developer and API host builds. +- **Personas/targets:** C developers and language developers building host + integrations; `aarch64-apple-darwin` and `x86_64-apple-darwin` only. +- **Reproduce/evidence:** after supplying support, run + `"$K" cc --support-dir "$D/support" "$R/test/audit/release/fixtures/native_sdk_malloc.c" -o /tmp/kit-native-sdk` without + `-isysroot`; `evidence/targets-platforms/{28-macos-auto-summary,36-workaround-summary,38-printed-sdk-summary}.txt`. +- **Expected/actual:** native `cc hello.c` uses the system SDK without host + discovery tools; Kit prints a valid SDK via `-print-sysroot` but fails to + apply it, leaving libc declarations unavailable. Explicit `-isysroot` passes. +- **Impact/remediation:** ordinary native hosted compilation is unusable. + Apply the same internal SDK result to default header/link search while + preserving explicit override precedence. +- **Acceptance/regression:** sanitized-PATH aa64 and x64 hello build/run without + `-isysroot`, sentinel stays empty; cover explicit and missing SDKs and spaces. + +### KIT-P0-003 — optimized portable-C emission terminates by signal + +- **Severity/blocking/status:** P0; release blocking crash; open. +- **Affected:** `cc --emit=c -O1/-O2`; C and language developers. +- **Personas/targets:** both personas; portable-C output on the native host + (target-independent frontend/backend composition; cross variants require + regression coverage). +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" cc --support-dir "$D/support" --emit=c -O1 "$E/cold-toolchain/work/src/check.c" -o check-portable.c`; + exact expanded record in + `evidence/cold-toolchain/cases/workflow/cc-explicit-support/portable-c/command.txt`; + the adjacent exit is 133 and stderr precedes `Trace/BPT trap: 5`. +- **Expected/actual:** emit valid C or return a normal precise 1/2 diagnostic; + instead the process signal-terminates after reporting native-target limits. +- **Impact/remediation:** unsafe automation and crash. Convert the unsupported + path to normal error propagation or support the optimization. +- **Acceptance/regression:** O0/O1/O2 for C backend and native/non-native + targets never signal; output/restriction and exit are documented. + +### KIT-P0-004 — mixed-architecture Mach-O inputs silently create a crashing image + +- **Severity/blocking/status:** P0; release blocking wrong artifact; open. +- **Affected:** compiler driver/linker/API, objects and archives; both personas. +- **Personas/targets:** C developers and language developers; observed for + x86_64 input in an aarch64 macOS final link, with all other architectures and + ELF/COFF explicitly in regression scope. +- **Reproduce/evidence:** run + `mkdir -p /tmp/kit-mixed-arch && "$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" -target x86_64-apple-darwin -c "$R/test/audit/release/fixtures/strong_value.c" -o /tmp/kit-mixed-arch/x64.o && "$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" -c "$R/test/audit/release/fixtures/select_main.c" -o /tmp/kit-mixed-arch/aa64.o && "$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" /tmp/kit-mixed-arch/aa64.o /tmp/kit-mixed-arch/x64.o -o /tmp/kit-mixed-arch/mixed`; + recorded results + `evidence/link-failures/{x64-strong-compile,wrong-arch-corrected,wrong-arch-file,wrong-arch-run}.*`. +- **Expected/actual:** status 1 naming file and expected/actual architecture; + actual link is 0 with no diagnostic, arm64-labeled output traps SIGILL/132. +- **Impact/remediation:** silent wrong-code artifact. Target-check every direct, + archive-member, DSO, and LTO input before layout. +- **Acceptance/regression:** mismatches fail without output for representative + architecture pairs in Mach-O, ELF, and COFF; cover objects/archives/DSOs/LTO. + +### KIT-P1-001 — required target discovery command is absent + +- **Severity/blocking/status:** P1; release blocking; open, Phase 2 required. +- **Affected:** `kit targets`, cc/cpp/as/run/disas and all 19 cells; both personas. +- **Personas/targets:** both personas; every hosted and freestanding target in + the 19-cell matrix. +- **Reproduce/evidence:** `"$K" targets`; `evidence/cold-help/targets_noargs.*`. +- **Expected/actual:** human/script output lists canonical triples, aliases, + formats, hosted/freestanding/sysroot and execution capabilities; actual is + top help plus `no such tool`, status 2. +- **Impact/remediation:** cross use requires hidden repository scripts. + Implement the required surface in Phase 2; do not advertise it beforehand. +- **Acceptance/regression:** stable human and machine formats cover every cell, + aliases and unknown-target guidance. + +### KIT-P1-002 — Android NativeActivity cannot preprocess NDK headers + +- **Severity/blocking/status:** P1; release blocking for Android; open. +- **Affected:** `aarch64-linux-android21`, NDK r27d, C developers. +- **Personas/targets:** C developers; Android aa64 only (language developers + encounter the same preprocessor if embedding the frontend). +- **Reproduce/evidence:** run + `"$K" cc -target aarch64-linux-android21 --sysroot /Users/ryan/.cache/kit/android-ndk/r27d/android-ndk-r27d/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -O1 -fPIC -c "$E/targets-linux-android/infra/native_activity.c" -o /tmp/native_activity.o`; + exact expanded record at + `evidence/targets-linux-android/cases/baseline/android-aa64/compile-native-activity/command.txt`. +- **Expected/actual:** NativeActivity object and `-landroid` shared link pass; + actual status 1 at `android/api-level.h:193`, unexpected token in `#if`. +- **Impact/remediation:** real advertised Android shape unusable despite minimal + shared-link success. Support the NDK expression/predefines. +- **Acceptance/regression:** exact fixture links to AArch64 DYN with + `NEEDED libandroid.so`; preprocess API 21 and a higher API. + +### KIT-P1-003 — shipped rv32 support cannot supply its compiler runtime + +- **Severity/blocking/status:** P1; release blocking for rv32; open. +- **Affected:** `riscv32-none-elf` nontrivial i64/fp programs. +- **Personas/targets:** C developers and language developers producing final + freestanding images; rv32 only. +- **Reproduce/evidence:** run + `cd "$E/targets-freestanding/freestanding-rv32" && "$K" cc -target riscv32-none-elf -march=rv32imafc_zicsr_zifencei -mabi=ilp32f -ffreestanding --support-dir "$D/support" -T "$PWD/artifacts/runner-inputs/link.ld" -e _start "$PWD/artifacts/runner-inputs/start.o" "$PWD/artifacts/runner-inputs/wrap.o" "$PWD/artifacts/frontback.O1.o" -o "$PWD/artifacts/frontback-driver-support-workaround.elf"`; + expanded record in `direct-driver-link-support-workaround.command`. +- **Expected/actual:** shipped support supplies/builds runtime; actual status 1, + `compiler runtime is not available`, while infrastructure passes only with + unshipped repository `build/rt/.../libkit_rt.a`. +- **Impact/remediation:** distribution cannot build its broader rv32 program. + Ship/build the hard-float runtime and document ABI selection. +- **Acceptance/regression:** distribution-only broad image runs 0 under rv32 + QEMU; cover i64/fp helper selection and archive injection. + +### KIT-P1-004 — rv32 final ELF loses the single-float ABI flag + +- **Severity/blocking/status:** P1; release blocking ABI metadata defect; open. +- **Affected:** `kit ld`, rv32 ELF consumers. +- **Personas/targets:** both personas and downstream binary consumers; rv32 + hard-float ELF only in the observed case, rv64 in regression scope. +- **Reproduce/evidence:** run + `llvm-readelf --file-header "$E/targets-freestanding/freestanding-rv32/artifacts/runner-inputs/start.o" "$E/targets-freestanding/freestanding-rv32/artifacts/exit.elf"` and compare the flags retained in + `evidence/targets-freestanding/freestanding-rv32/{abi-inputs,inspect,coarse-inspect}.stdout`. +- **Expected/actual:** `0x3` input flags remain `0x3`; final executables are + `0x1`, falsely declaring soft float. +- **Impact/remediation:** downstream compatibility metadata is wrong. Merge + RISC-V input flags and reject incompatible float ABI mixtures. +- **Acceptance/regression:** hard/soft outputs retain correct flags; mixed + inputs fail; cover rv32/rv64 relocatable and final links with readelf oracle. + +### KIT-P1-005 — CPIO reports corruption and conflicting modes as success + +- **Severity/blocking/status:** P1; release blocking automation/integrity; open. +- **Affected:** `cpio` create/list/extract users. +- **Personas/targets:** C developers using binary utilities; all host/target + contexts because CPIO is format-neutral. +- **Reproduce/evidence:** run + `"$K" cpio -t -F "$E/cold-toolchain/work/corrupt.cpio"` and + `cd "$E/cold-toolchain/work/cpio-input" && "$K" cpio -o -t alpha.txt`; exact cases + `evidence/cold-toolchain/cases/failure/cpio/{corrupt-input,conflicting-modes}/`. +- **Expected/actual:** corruption status 1 and `-o -t` usage status 2; both warn + and return 0. +- **Impact/remediation:** automation accepts invalid archives/commands. Propagate + parser/trailer and mutually-exclusive-mode errors. +- **Acceptance/regression:** bad magic/truncation/no trailer and all mode pairs + return documented nonzero classes. + +### KIT-P1-006 — debugger missing script exits success + +- **Severity/blocking/status:** P1; release blocking for scripted automation; open. +- **Affected:** `dbg --batch --script`. +- **Personas/targets:** C developers debugging; observed native Darwin/aarch64 + debugger host, with other supported debugger hosts in regression scope. +- **Reproduce/evidence:** `"$K" dbg --batch --script /missing/path`; + `evidence/cold-toolchain/cases/failure/dbg/missing-script/`. +- **Expected/actual:** diagnostic plus status 1; actual diagnostic plus 0. +- **Impact/remediation:** CI cannot trust debugger status. Propagate script-read + failure. +- **Acceptance/regression:** missing, unreadable, and malformed scripts return + 1; valid scripts remain 0 or program-specific as documented. + +### KIT-P1-007 — strip silently damages linked executables + +- **Severity/blocking/status:** P1; release blocking common binutils use; open. +- **Affected:** `strip` on Mach-O/ELF/PE linked images. +- **Personas/targets:** C developers using drop-in binary utilities; observed + native Mach-O/aarch64, with ELF/PE and other architectures in regression scope. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" strip -o debug-exe-stripped debug-exe`; exact case + `evidence/cold-toolchain/cases/failure/strip/linked-executable/` plus + `strip-executable-stat.stdout` and run evidence. +- **Expected/actual:** runnable stripped image or explicit unsupported status 1; + actual status 0, 33122-byte input becomes 505-byte mode-0644 output. +- **Impact/remediation:** misleading success and destroyed usability. Preserve + linked images or reject before creating output. +- **Acceptance/regression:** linked executable/shared cases across formats plus + existing object/archive behavior. + +### KIT-P1-008 — build coordinator cannot be authored from shipped material + +- **Severity/blocking/status:** P1; release blocking baseline defect; **fixed in + Phase 1 as documentation only**. +- **Affected:** `build`, build users. +- **Personas/targets:** C and language developers using the build coordinator; + target-neutral protocol/discoverability. +- **Reproduce/evidence:** run + `"$K" build --store "$E/cold-toolchain/work/cache/build-store" --root "$E/cold-toolchain/work/build-empty" absent`; cold cases under + `evidence/cold-toolchain/cases/failure/build/`; recovered passing fixture and + cache sequence under `evidence/build-coordinator/`. +- **Expected/actual:** shipped grammar, recipe protocol, variables/defaults and + complete example; actual cold user cannot create `BUILD.kit` and README points + outside dist. Hidden-doc recovery makes first/hit/rebuild pass. +- **Impact/remediation:** supported tool is unusable without repository access. + Ship layered reference and the complete fixture workflow. +- **Acceptance/regression:** cold user reproduces fixture, deep hit, and source + invalidation solely from distribution docs. +- **Phase 1 result:** candidate help defines the file/recipe protocol, variables, + defaults, artifacts, and a complete workflow. The copied example completes a + first build and repeat cache hit; `evidence/candidate-examples/build/`. + +### KIT-P1-009 — shipped README/public API documentation is a dead end + +- **Severity/blocking/status:** P1; release blocking baseline defect; **fixed in + Phase 1 as documentation only**. +- **Affected:** README, public and runtime headers, runtime README, `libkit.a`, + API users. +- **Personas/targets:** primarily language developers, secondarily C developers; + all targets because public lifecycle/ownership documentation is target-neutral. +- **Reproduce/evidence:** run + `cd "$D" && for p in doc/DESIGN.md doc/plan doc/BUILD_COORDINATOR.md doc/CBACKEND.md doc/DISTRIBUTE.md; do test -e "$p" || printf 'missing %s\n' "$p"; done` and follow README/header references; + `evidence/cold-api/{README.txt.log,discoverability-search.log}` and + `evidence/baseline-runtime-docs/dead-references.log`. +- **Expected/actual:** all links ship and an end-to-end context/host adapter/ + compile/emit/link/diagnostic/cleanup example exists; referenced docs are absent + and composition/link flags are unexplained. +- **Impact/remediation:** declarations are usable only by inference. Replace + broken references and ship a complete example including ownership and flags. +- **Acceptance/regression:** the three API fixtures build/run from a relocated + dist by following only shipped text; distribution-link audit finds no dead links. +- **Phase 1 result:** README/public-header lifecycle and exact link guidance, + runtime-header guidance, and shipped documentation mapping are now + self-contained. Four API integrations, 42 public-header probes, 27 runtime- + header probes, and the dead-reference scan pass; see the candidate evidence + cited in section 10. + +### KIT-P1-010 — CLI help is materially incomplete for supported workflows + +- **Severity/blocking/status:** P1; release blocking baseline defect; **fixed in + Phase 1 as help/documentation only**. +- **Affected:** all 41, especially cc/ld/build*/cas/pkg/dbg/gram/update/cross. +- **Personas/targets:** both personas; all targets, with cross/freestanding + users most affected by the missing target/workflow material. +- **Reproduce/evidence:** run + `for c in cc ld build build-exe build-lib build-obj cas pkg dbg gram update; do "$K" "$c" --help; done` and inspect + `evidence/cold-help/{help-coverage.tsv,all-help.txt}`. +- **Expected/actual:** full reachable options/defaults/I/O/env/restrictions and + one example each; only 3 formal examples, cc says “see source,” target links + are circular, and complex formats/workflows are absent. +- **Impact/remediation:** cold users cannot derive passing audited journeys. + Add truthful layered help/examples based on observed behavior; do not claim + unimplemented `targets`. +- **Acceptance/regression:** every page has a copy-pasteable example and the + help harness validates flags, exits, prerequisites, streams, env and aliases. +- **Phase 1 result:** all 41 pages now have examples and exit conventions; the + candidate passes all 168 applicable help cases and all installed-alias help + comparisons. The four deliberate failures are solely the absent Phase 2 + `targets` command. + +### KIT-P1-011 — detached `pkg sign` has no discoverable verification journey + +- **Severity/blocking/status:** P1; release blocking supported security surface; + open, implementation or interoperable-verifier work required. Phase 1 help + now states that Kit has no detached-signature verification command. +- **Affected:** `pkg sign`, package users. +- **Personas/targets:** C developers distributing binaries and language + developers distributing toolchains; target-neutral package/signature format. +- **Reproduce/evidence:** run + `T=$(mktemp -d) && "$K" pkg sign -s "$E/cold-distutils/work/pkg isolated/release key.key" -o "$T/plain.minisig" --comment 'audit signature' "$E/cold-distutils/work/source tree/plain text.txt" && "$K" pkg verify -p "$E/cold-distutils/work/pkg isolated/release key.pub" "$E/cold-distutils/work/source tree/plain text.txt"`; + exact baseline records are `pkg-sign-{file,verify-attempt}.command` and the + summary is `22-pkg-trust-sign-summary.txt`. +- **Expected/actual:** documented shipped verification path and corruption + failure; `pkg verify` treats payload as package and help identifies no verifier. +- **Impact/remediation:** signatures cannot complete a cold-user trust journey. + Document a working interoperable verifier or add one. +- **Acceptance/regression:** good, wrong-key, changed-file detached cases pass/ + fail correctly from copied examples. + +### KIT-P1-012 — update check reports missing configuration as success + +- **Severity/blocking/status:** P1; release blocking automation semantics; open. +- **Affected:** `update --check`, clean isolated installations. +- **Personas/targets:** both personas relying on update automation; + host-installation behavior, not a compilation target. +- **Reproduce/evidence:** run + `sh "$E/cold-distutils/update-check-no-index-corrected.command"`; status 0 + and stderr in adjacent files/`36-update-corrected-summary.txt`. +- **Expected/actual:** inability to check returns 1 or clearly documented useful + success state; actual says no channel index and returns 0. +- **Impact/remediation:** automation receives false success. Align check/no-arg + operational status and documented meaning. +- **Acceptance/regression:** empty isolated KIT_HOME cases distinguish usage, + no configuration, fetch failure, no update, and update available. + +### KIT-P1-013 — installed multicall links do not survive distribution relocation + +- **Severity/blocking/status:** P1; release blocking relocation contract; open. +- **Affected:** `install --all`, all installed POSIX symlinks and Windows hardlinks. +- **Personas/targets:** both personas; all installed tool names, host POSIX + symlink behavior observed and Windows hardlink behavior in regression scope. +- **Reproduce/evidence:** run + `T=$(mktemp -d) && cp -R "$D" "$T/original" && mkdir -p "$T/prefix/bin" && "$T/original/bin/kit" install --all "$T/prefix/bin" && mv "$T/original" "$T/moved" && "$T/prefix/bin/cc" --help`; + `evidence/standalone/{installed-link-before-move,move,installed-after-move}.*`. +- **Expected/actual:** installed names survive or documented repair/self-contained + install exists; absolute links dangle, shell status 127. +- **Impact/remediation:** advertised relocation breaks installed tools. Use a + relocation-safe scheme or explicit repair workflow. +- **Acceptance/regression:** direct/PATH/installed compile/run after moving + between two space-containing paths on POSIX and Windows semantics. + +### KIT-P1-014 — GNU identity selects unsupported `__typeof` + +- **Severity/blocking/status:** P1; release blocking common headers; open. +- **Affected:** C frontend/predefines, uthash and GNU-extension consumers. +- **Personas/targets:** C developers and language developers embedding the C + frontend; observed native macOS, target-independent parser/predefine path. +- **Reproduce/evidence:** run + `"$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" -I"$R/tmp/projects/uthash" "$R/test/audit/release/fixtures/uthash_probe.c" -o /tmp/kit-uthash`; + `evidence/real-projects/uthash-build.*`. +- **Expected/actual:** accept selected extension or avoid advertising it; + actual `undeclared identifier '__typeof'`; `-DNO_DECLTYPE` passes. +- **Impact/remediation:** portable headers choose a broken path. Implement the + extension consistently or correct predefines. +- **Acceptance/regression:** unmodified uthash probe plus parser/predefine tests. + +### KIT-P1-015 — common multi-input dependency generation is rejected + +- **Severity/blocking/status:** P1; release blocking drop-in compatibility; open. +- **Affected:** `cc -M/-MM`, Makefiles including Lua. +- **Personas/targets:** C developers using drop-in compiler variables; observed + native macOS but target-independent dependency mode. +- **Reproduce/evidence:** run + `"$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" -MM "$R/tmp/projects/lua-5.4.7/src/lapi.c" "$R/tmp/projects/lua-5.4.7/src/lcode.c"`; + upstream Lua result `evidence/real-projects/lua-depend-corrected.*`. +- **Expected/actual:** dependencies for every source; actual status 2, + `-M/-MM requires exactly one input`. +- **Impact/remediation:** ordinary Makefile dependency targets fail. Support + multi-input output semantics or a clearly compatible documented mode. +- **Acceptance/regression:** `-M/-MM/-MD/-MMD/-MF` one/multiple-input coverage + and upstream Lua depend lane. + +### KIT-P1-016 — common driver/binutils argument forms are rejected + +- **Severity/blocking/status:** P1 collectively; release blocking drop-in use; + open. +- **Affected:** `ar -rcs`, common `--` leading-dash operands, assembler stdin + `-`, and compiler `-Wl,-T,...`; C developers. +- **Personas/targets:** C developers using drop-in driver/binutils syntax; + all targets, with `-Wl,-T` observed on rv32/arm32 freestanding links. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" ar -rcs /tmp/libkit-audit.a add.o`, + `printf 'nop\n' | "$K" as -target aarch64 -o /tmp/stdin.o -`, and + `cd "$E/targets-freestanding/freestanding-rv32" && "$K" cc -target riscv32-none-elf -march=rv32imafc_zicsr_zifencei -mabi=ilp32f -ffreestanding "-Wl,-T,$PWD/artifacts/runner-inputs/link.ld,-e,_start" "$PWD/artifacts/runner-inputs/start.o" "$PWD/artifacts/runner-inputs/wrap.o" "$PWD/artifacts/frontback.O1.o" -o /tmp/frontback-driver.elf`; + the fully expanded freestanding argv is in `direct-driver-link.command`; + cold-toolchain cases `workflow/ar/gnu-dashed-mode`, + `workflow/cc/leading-dash`, `compatibility/{strings/leading-dash-with-double-dash,as/stdin-dash}`; + freestanding `direct-driver-link.command`. +- **Expected/actual:** familiar forms work or give a documented precise + alternative; actual is generic usage/unknown token/file read, including + `unsupported -Wl, token: -T`. +- **Impact/remediation:** common build scripts need Kit-specific rewrites. + Implement shared conventional forms or document safe exact alternatives. +- **Acceptance/regression:** direct and installed aliases cover each spelling, + paths with spaces/dashes and linker-script entry forwarding. + +### KIT-P1-017 — advertised `objcopy -O binary` rejects relocatable objects + +- **Severity/blocking/status:** P1; release blocking common objcopy workflow; + open. +- **Affected:** `objcopy`, freestanding/raw-image users. +- **Personas/targets:** C developers; format-neutral CLI, observed on native + Mach-O relocatable input with ELF/COFF/Wasm in regression scope. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" objcopy -O binary add.o /tmp/add.raw`; recorded in + `evidence/cold-toolchain/cases/workflow/objcopy/binary-format/`. +- **Expected/actual:** selected section bytes or an explicit documented input + restriction; actual status 1, linked image required, restriction absent. +- **Impact/remediation:** common binary conversion appears supported but fails. + Implement relocatable conversion or document/use a clear alternative. +- **Acceptance/regression:** binary/IHEX/SREC from relocatable and linked inputs + across supported formats. + +### KIT-P2-001 — common stack-protector selection blocks cJSON + +- **Severity/blocking/status:** P2; not independently release blocking; open. +- **Affected:** `cc -fstack-protector`, common Makefile probes. +- **Personas/targets:** C developers; observed native macOS, common across + hosted targets. +- **Reproduce/evidence:** run + `"$K" cc --support-dir "$D/support" -isysroot "$SDK_PRINTED" -fstack-protector -c "$R/test/audit/release/fixtures/group_left.c" -o /tmp/stack-protector.o`; ordinary cJSON `make static tests` with only tool + overrides; `evidence/real-projects/cjson-build.*`. +- **Expected/actual:** common mode works or incompatibility is prominent; + specific status-2 rejection stops build; `R_CFLAGS` workaround passes. +- **Impact/remediation:** compatibility friction. Implement or document the + unsupported protection mode and exact alternative. +- **Acceptance/regression:** flag parsing/behavior plus cJSON acceptance lane. + +### KIT-P2-002 — image text-format address limits/rebasing are hidden + +- **Severity/blocking/status:** P2; nonblocking but significant; open. +- **Affected:** `image` IHEX/SREC on high-address linked images. +- **Personas/targets:** C developers producing freestanding images; observed + high-address native aarch64 Mach-O input, format behavior target-sensitive. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" image --format ihex debug-exe`, + `"$K" image --format ihex --base 0x100000000 debug-exe -o /tmp/debug.ihex`, + `"$K" image --format ihex --bias -4294967296 debug-exe -o /tmp/debug-biased.ihex`, + and `"$K" image --format srec --bias -4294967296 debug-exe -o /tmp/debug.srec`; + fully expanded records/results are under `workflow/image/`. +- **Expected/actual:** documented deterministic rebasing or specific limit; + native high addresses fail, `--base` does not solve them, negative bias fixes + IHEX but not SREC. +- **Impact/remediation:** freestanding image conversion is trial-and-error. + Define address semantics/limits and make rebasing consistent. +- **Acceptance/regression:** below/above-4GiB entries/segments for both formats. + +### KIT-P2-003 — diagnostics can lose the relevant input and pollute stdout + +- **Severity/blocking/status:** P2; nonblocking; open. +- **Affected:** `ld`, `strip`, `size`. +- **Personas/targets:** C developers using binary tools; observed native + Mach-O/aarch64, with format-neutral error handling in regression scope. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" ld --support-dir "$D/support" -o /tmp/missing-link no-such.o`, + `"$K" strip /tmp/no-such.o`, and `"$K" size /tmp/no-such.o`; cold-toolchain + `cases/failure/ld-explicit-support/missing-input-file`, + `cases/failure/strip/missing-file`, and `cases/failure/size/`. +- **Expected/actual:** name path/reason and keep stdout empty on total failure; + actual can become missing-entry/unrecognized-object and size prints a table. +- **Impact/remediation:** slower diagnosis and unsafe parsing. Preserve source + error context and defer normal stdout until validation succeeds. +- **Acceptance/regression:** missing/unreadable/malformed files per command. + +### KIT-P2-004 — top-level no-argument promise is false for 21 commands + +- **Severity/blocking/status:** P2; nonblocking baseline documentation defect; + **fixed in Phase 1 help**. +- **Affected:** dispatcher and 21 stdin/REPL/operand-required tools. +- **Personas/targets:** both personas; host CLI behavior, target-neutral. +- **Reproduce/evidence:** run + `for c in build install cpio dbg strings pkg cmp disas mc gram; do "$K" "$c" </dev/null; "$K" "$c" --help; done` and compare all 41, including the + separately isolated update probe, in + `evidence/cold-help/{noargs-contract-summary.txt,help-matrix.tsv}`. +- **Expected/actual:** qualified accurate policy; unconditional equivalence is + true for only 20/41. +- **Impact/remediation:** scripts may execute a filter/REPL/update unexpectedly. + Correct top help and document per-command stdin behavior. +- **Acceptance/regression:** help harness verifies qualified statement and all + intentional exceptions. +- **Phase 1 result:** top help no longer promises universal no-argument help; + each command documents whether no arguments reads stdin, enters a REPL, + performs an operation, reports usage, or shows help. + +### KIT-P2-005 — exit/help stream conventions are incomplete and inconsistent + +- **Severity/blocking/status:** P2; nonblocking baseline documentation defect; + **fixed in Phase 1 documentation**. Functional exit inconsistencies remain + open under their command-specific findings. +- **Affected:** 12 pages missing exits plus dispatcher/no-arg usage surfaces. +- **Personas/targets:** both personas; all target classes because this is global + CLI automation/documentation. +- **Reproduce/evidence:** run + `for c in cc check build-exe build-lib build-obj build nm size addr2line symbolize cas pkg; do "$K" "$c" --help; done`; + `evidence/cold-help/{help-coverage.tsv,all-noargs.txt}`. +- **Expected/actual:** documented 0/1/2 and requested-help versus bad-usage + stream policy; pages omit it and equivalent usage errors vary widely. +- **Impact/remediation:** unreliable automation expectations. Add per-command + exits/exceptions and a global stream policy without changing runtime in Phase 1. +- **Acceptance/regression:** help assertions for success/operational/usage and + program-exit pass-through. +- **Phase 1 result:** every page has an exit section, stream policy, and relevant + exceptions; candidate help assertions pass. + +### KIT-P2-006 — ranlib documents a rejected equivalent command + +- **Severity/blocking/status:** P2; nonblocking inaccurate baseline help; + **fixed in Phase 1 help**. +- **Affected:** `ranlib`, `ar`. +- **Personas/targets:** C developers using archive tools; all object targets. +- **Reproduce/evidence:** after reading ranlib help run `"$K" ar s missing.a`; + `evidence/cold-help/{ranlib_help.stdout,ar_bare_s_claim.*}`. +- **Expected/actual:** equivalent works; actual status 2, `s requires r or c`. +- **Impact/remediation:** copy-pasted recovery fails. Document a working form or + remove the equivalence. +- **Acceptance/regression:** focused black-box help example executes. +- **Phase 1 result:** help uses the accepted `ar rs ARCHIVE` form; the + help-derived archive/index/link workflow passes. + +### KIT-P2-007 — update default channel/setup is not discoverable + +- **Severity/blocking/status:** P2; not independently release blocking; live + success BLOCKED, not a proven runtime defect; open. Phase 1 help now accurately + states that there is no built-in production index and documents curl-only + isolated setup, but cannot supply the missing release service configuration. +- **Affected:** `update` on clean installations. +- **Personas/targets:** both personas; host installation/update behavior, no + compilation target. +- **Reproduce/evidence:** run + `T=$(mktemp -d) && cp -R "$D" "$T/dist" && env -i HOME="$T/home" XDG_DATA_HOME="$T/data" XDG_CACHE_HOME="$T/cache" XDG_CONFIG_HOME="$T/config" KIT_HOME="$T/kit-home" PATH=/usr/bin:/bin "$T/dist/bin/kit" update`; + `evidence/cold-help/update_noargs.*` and cold-distutils corrected update summary. +- **Expected/actual:** default tracked channel works or initial index setup is + completely documented; actual says no index configured and no production URL + is shipped. Help also mentions `wget` despite curl-only contract. +- **Impact/remediation:** live update cannot be attempted by a cold user. Ship a + safe complete curl-based channel configuration/example. +- **Acceptance/regression:** clean isolated install can check/dry-run/update + without real-install mutation and without non-curl fetch helpers. + +### KIT-P2-008 — diagnostics do not suggest close valid values + +- **Severity/blocking/status:** P2; nonblocking usability; open. +- **Affected:** dispatcher, cc target/flags, build language, image/compress format. +- **Personas/targets:** both personas; all targets and supported format/language + registries. +- **Reproduce/evidence:** run + `"$K" objdmp`, `"$K" cc --hlep`, `"$K" cc -target aardvark-none-elf -c missing.c`, + `"$K" build-obj -x see missing.c`, and `"$K" image --format bing missing.o`; + typo cases listed in + `evidence/cold-help/` (`misspelled_command`, `typo_cc_*`, + `typo_build_obj_language`, `typo_*_format`). +- **Expected/actual:** closest valid value or discovery pointer; errors are + specific/status 2 but provide neither. +- **Impact/remediation:** avoidable trial-and-error. Add bounded suggestions and + point targets to the future discovery command. +- **Acceptance/regression:** representative typos retain status 2 and suggest + only valid nearby values. + +### KIT-P2-009 — alias/version help identity is inconsistent + +- **Severity/blocking/status:** P2; nonblocking; open, **partially remediated in + Phase 1 help**. Alias identity and canonical `kit --version` discovery are + fixed; the implemented `kit version`/`kit help version` mismatch remains. +- **Affected:** hash/compress aliases and version dispatch. +- **Personas/targets:** both personas using installed utilities; host + target-neutral behavior. +- **Reproduce/evidence:** run + `for c in sha256sum b2sum crc32 gzip gunzip lz4 lz4c; do "$K" "$c" --help; done; "$K" --version; "$K" version; "$K" help version`; + alias help files and + `evidence/cold-help/{top_version,version_tool,version_*help}.*`. +- **Expected/actual:** invoked spelling/defaults shown consistently and version + discovery advertised; aliases show generic parent syntax, `kit version` + works but `kit help version` rejects it. +- **Impact/remediation:** misleading copy/paste/default direction. Render + alias-aware help and document one consistent version route. +- **Acceptance/regression:** every alias title/usage/default matches argv[0]; + version help routes agree. + +### KIT-P2-010 — environment-variable discovery is fragmented + +- **Severity/blocking/status:** P2; nonblocking baseline documentation defect; + **fixed in Phase 1 documentation**. +- **Affected:** build, archive reproducibility, debugger, update, top help. +- **Personas/targets:** both personas; all targets where environment affects + state, reproducibility, debugging, or update. +- **Reproduce/evidence:** run + `"$K" env; "$K" help env; for c in build ar ranlib dbg update; do "$K" "$c" --help; done` and inspect saved pages in + `evidence/cold-help/`. +- **Expected/actual:** authoritative variable/default/scope/precedence reference; + no global surface exists and variables appear incidentally, including the + undefined phrase `$KIT cache/build`. +- **Impact/remediation:** state and reproducibility controls are hidden. Add one + shipped environment reference and per-command links/summary. +- **Acceptance/regression:** help audit maps every observed variable to shipped + documentation and verifies defaults/precedence text. +- **Phase 1 result:** top help now provides the authoritative environment/state + summary and affected pages document variable scope, defaults, and precedence. + +### KIT-P2-011 — common checksum and cmp long forms are absent + +- **Severity/blocking/status:** P2; nonblocking compatibility; open. +- **Affected:** `sha256sum`, `b2sum`, `cmp` scripts. +- **Personas/targets:** C developers and language developers using release + utility aliases; target-neutral byte/file behavior. +- **Reproduce/evidence:** run + `"$K" sha256sum -c /tmp/checksums`, `"$K" b2sum -c /tmp/checksums`, and + `"$K" cmp --ignore-initial=1 --bytes=1 /tmp/a /tmp/b`; + `evidence/cold-distutils/{hash-gnu-check,cmp-gnu-ignore-initial,cmp-gnu-bytes}.*`. +- **Expected/actual:** checksum `-c`, cmp `--ignore-initial`/`--bytes`, or a + documented Kit equivalent; all are specifically rejected. +- **Impact/remediation:** familiar automation needs rewrites. Implement common + forms or make alias-specific incompatibilities/promoted alternatives explicit. +- **Acceptance/regression:** alias invocation and behavior tests for each form. + +### KIT-P2-012 — cmp classifies a missing file as usage status 2 + +- **Severity/blocking/status:** P2; nonblocking exit inconsistency; open. +- **Affected:** `cmp` automation. +- **Personas/targets:** both personas using file comparison; target-neutral. +- **Reproduce/evidence:** run + `sh "$E/cold-distutils/cmp-operational-missing.command"`; + `evidence/cold-distutils/cmp-operational-missing.*`. +- **Expected/actual:** unreadable operand is operational status 1; actual 2. +- **Impact/remediation:** scripts confuse file failure with bad CLI. Return 1 + while preserving 2 for malformed arguments. +- **Acceptance/regression:** missing/unreadable operands versus missing operands/ + options. + +### KIT-P2-013 — Gram CLI usage errors omit the offending argument + +- **Severity/blocking/status:** P2; nonblocking; open. +- **Affected:** `gram` unknown options/missing arguments. +- **Personas/targets:** language developers primarily, C developers consuming + generated parsers secondarily; target-neutral CLI parsing. +- **Reproduce/evidence:** run + `sh "$E/cold-distutils/gram-usage-unknown.command"` and + `sh "$E/cold-distutils/gram-usage-missing-o.command"`; + `evidence/cold-distutils/{gram-usage-unknown,gram-usage-missing-o}.stderr`. +- **Expected/actual:** identify option and missing operand with status 2; actual + is only generic usage. +- **Impact/remediation:** poor correction path. Emit precise parser diagnostics. +- **Acceptance/regression:** targeted unknown and every required option-argument + case. + +### KIT-P3-001 — truncated fixed-width instruction disassembly returns success + +- **Severity/blocking/status:** P3; not release blocking; **fixed in Phase 1 + documentation** by choosing the acceptance criterion's rendered-success + policy; no runtime change. +- **Affected:** `disas -target aarch64` and potentially fixed-width ISAs. +- **Personas/targets:** both personas using raw-code inspection; observed + aarch64, with rv32/rv64 fixed-width/truncated encodings in regression scope. +- **Reproduce/evidence:** run + `sh "$E/cold-distutils/disas-truncated-aa64.command"`; + `evidence/cold-distutils/disas-truncated-aa64.*` and + `25-mc-disas-summary.txt`. +- **Expected/actual:** documented rendered-success policy or operational 1; + actual prints `(truncated)` and returns 0 without documenting it. +- **Impact/remediation:** ambiguous automation. Define and document/implement a + consistent undecodable-input policy. +- **Acceptance/regression:** truncated case per fixed-width ISA. +- **Phase 1 result:** help explicitly states that rendered `(truncated)` input + returns 0, removing the baseline automation ambiguity. + +### KIT-P3-002 — small help terminology and accepted-help-flag omissions + +- **Severity/blocking/status:** P3; not release blocking; **fixed in Phase 1 + help**. +- **Affected:** at least strip, objcopy, image. +- **Personas/targets:** C developers using binary utilities; all supported + object targets/formats. +- **Reproduce/evidence:** run + `for c in strip objcopy image; do "$K" "$c" -h; "$K" "$c" --help; done` and compare saved `*_short_help.*` and `*_help.stdout` in + `evidence/cold-help/`. +- **Expected/actual:** accepted `-h/--help` listed and tool-specific wording; + flags are omitted and objcopy calls status 1 a “strip error.” +- **Impact/remediation:** polish/clarity. Correct literal help text. +- **Acceptance/regression:** help substring checks per page. +- **Phase 1 result:** accepted help forms are listed and objcopy uses + tool-specific error terminology; candidate checks pass. + +### KIT-P3-003 — nm duplicate-symbol observation needs implementation confirmation + +- **Severity/blocking/status:** P3 observation; not release blocking; open for + Phase 2 confirmation. +- **Affected:** `nm` linked debug-image output. +- **Personas/targets:** C and language developers inspecting symbols; observed + native aarch64 Mach-O only, pending independent confirmation. +- **Reproduce/evidence:** run + `cd "$E/cold-toolchain/work/out" && "$K" nm debug-exe`; + cold-toolchain `workflow/nm` output contains `_add` + twice at the same address. +- **Expected/actual:** one semantically useful row unless two distinct symbol + table entries are intentionally exposed; baseline output duplicates the row. +- **Impact/remediation:** possible confusing listing, but object semantics were + not inspected in Phase 1. Confirm against independent symbol tables, then + deduplicate or document. +- **Acceptance/regression:** comparison with independent Mach-O symbol oracle; + only add a functional test if confirmed. + +## 13. Prioritized remediation todo list + +The order below follows release risk and implementation dependency. Each change +must remain tied to its finding ID and begin only after Phase 2 approval unless +it is literal Phase 1 help/shipped documentation. + +1. **Distribution foundation:** fix KIT-P0-001 first, then rerun native, + installed, relocated, Gram, API, platform, and freestanding driver cases. +2. **Native hosted foundation:** fix KIT-P0-002 and validate sanitized native + aa64/x64 SDK include/link/run before real-project/API reruns. +3. **Safety/correctness:** red-green KIT-P0-003 and KIT-P0-004 before other + compiler/linker polish; extend architecture mismatch coverage to all formats. +4. **Target blockers:** implement KIT-P1-002, KIT-P1-003, and KIT-P1-004, then + rerun the exact Android and all five freestanding cells with independent + artifact oracles. +5. **Required discovery:** implement KIT-P1-001 only in Phase 2, based on the + complete 19-cell data model; then remove circular target references. +6. **Incorrect success/damaging tools:** fix KIT-P1-005, KIT-P1-006, + KIT-P1-007, and KIT-P1-012 with targeted exit/artifact tests. +7. **Relocation/install:** fix KIT-P1-013 after support discovery semantics are + stable; retest POSIX and Windows install representations. +8. **Compatibility:** address KIT-P1-014 through KIT-P1-017, then KIT-P2-001, + KIT-P2-002, KIT-P2-011, and KIT-P2-012; rerun Lua/cJSON/uthash and archive/ + freestanding journeys. +9. **Phase 1 documentation/help (completed):** KIT-P1-008 through KIT-P1-010, + KIT-P2-004 through KIT-P2-006, KIT-P2-010, KIT-P3-001, and KIT-P3-002 are + fixed in the candidate. KIT-P1-011, KIT-P2-007, and KIT-P2-009 are truthfully + clarified but remain open because their acceptance criteria require runtime + behavior or external service configuration. Every baseline issue remains + recorded above. +10. **Diagnostic/polish:** KIT-P2-003, KIT-P2-008, KIT-P2-013, KIT-P3-001, and + confirm KIT-P3-003. +11. After narrow red-green cases, rerun affected architecture/ABI/format lanes, + then the complete black-box release harness against a new candidate while + retaining this immutable baseline. + +## 14. Exact harness rerun instructions + +The reusable harness is under `test/audit/release/`; it requires exactly one +explicit product selection and never falls back to `build/kit` or PATH. + +```sh +cd /Users/ryan/code/kit + +# Full available suite against the immutable baseline distribution. +test/audit/release/run.sh \ + --dist build/audit/release-phase1/baseline/dist/kit \ + --out build/audit/release-acceptance \ + --run-id phase1-baseline + +# A scoped help audit. +test/audit/release/run.sh \ + --dist build/audit/release-phase1/baseline/dist/kit \ + --module help --command 'cc,build-*,ld,cas,pkg,gram,update' \ + --severity 'p0,p1,p2' + +# An exact candidate binary; distribution-only cases become explicit BLOCKED +# or NOT_APPLICABLE rather than searching the repository. +test/audit/release/run.sh --kit /absolute/path/to/candidate/kit + +# Reproduce the final Phase 1 candidate help and functional/doc suites. +test/audit/release/run.sh \ + --dist build/audit/release-phase1/candidate/dist/kit \ + --module help \ + --out build/audit/release-phase1/evidence/candidate-harness \ + --run-id help-rerun +test/audit/release/run.sh \ + --dist build/audit/release-phase1/candidate/dist/kit \ + --module core,standalone,api \ + --out build/audit/release-phase1/evidence/candidate-harness \ + --run-id functional-docs-rerun +``` + +Every selected case records `command`, `stdout`, `stderr`, `exit`, `metadata`, +assertions, state, reason, and retained artifacts in a unique run directory. +`summary.tsv` is machine-readable and `summary.txt` is concise. Filters are +available for module, command, workflow, target, architecture, format, and +severity. PASS, FAIL, BLOCKED, and NOT_APPLICABLE are distinct; a filter that +matches no cases is a harness usage error rather than a silent skip. Full +instructions are in `test/audit/release/README.md`. The current reusable +modules are `help`, `core`, `standalone`, and `api`; for example: + +```sh +test/audit/release/run.sh --dist build/audit/release-phase1/baseline/dist/kit \ + --module core --workflow archive-link-run +test/audit/release/run.sh --dist build/audit/release-phase1/baseline/dist/kit \ + --module standalone +test/audit/release/run.sh --dist build/audit/release-phase1/baseline/dist/kit \ + --module api +``` + +For the already-completed broad matrix, do not reconstruct commands from prose: +use the literal per-case `.command`/`command.txt` cwd/environment/argv records +beneath the three target evidence roots with the capture wrapper identified by +the corresponding target note, preserving the exact baseline `$K`. +Provisioning recovery is `make provision TARGET=<selector>`; missing runner +details are recorded in those notes. Never substitute `build/kit`. + +All Phase 1 baseline evidence remains under +`build/audit/release-phase1/evidence/`. The final help/documentation candidate +results are under `evidence/candidate-harness/`, `evidence/candidate-examples/`, +`evidence/candidate-alias-help/`, `evidence/candidate-runtime-headers/`, and +`evidence/readme-candidate/`; none alters the baseline assessment above. diff --git a/driver/cmd/addr2line.c b/driver/cmd/addr2line.c @@ -87,6 +87,13 @@ void driver_help_addr2line(void) { "USAGE\n" " kit addr2line [OPTIONS] -e FILE [ADDR...]\n" "\n" + "ADDRESS / OUTPUT\n" + " ADDR is hexadecimal, with or without a 0x prefix, and is a\n" + " link-time address in FILE. With no ADDR operands, read one hex\n" + " address token per line from stdin. The normal result is\n" + " FILE:LINE:COLUMN (plus the function with -f); an unresolved\n" + " address prints `??:0` and, with -f, function `??`.\n" + "\n" "OPTIONS\n" " -e FILE object file with debug info (required)\n" " -a, --addresses print the address before each line\n" @@ -95,8 +102,21 @@ void driver_help_addr2line(void) { " --basenames strip directory from file paths\n" " -h, --help show this help\n" "\n" - "If no addresses are given on the command line, addresses are read\n" - "from standard input, one per line (hex).\n"))); + "EXAMPLES\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " SDK=$(\"$K\" cc -print-sysroot)\n" + " printf 'int main(void) { return 0; }\\n' > debug.c\n" + " \"$K\" cc --support-dir \"$DIST/support\" -isysroot \"$SDK\" " + "-g -no-pie debug.c -o debug-app\n" + " \"$K\" nm -n debug-app\n" + " ADDR=$(\"$K\" nm -n debug-app | awk '$3 == \"main\" || " + "$3 == \"_main\" { print $1; exit }')\n" + " \"$K\" addr2line -a -f -p -e debug-app \"0x$ADDR\"\n" + " printf '%s\\n' \"$ADDR\" | \"$K\" addr2line -f -e debug-app\n" + "\n" + "EXIT CODES\n" + " 0 success 1 debug/object or I/O error\n" + " 2 bad usage\n"))); } int driver_addr2line(int argc, char** argv) { diff --git a/driver/cmd/ar.c b/driver/cmd/ar.c @@ -63,7 +63,8 @@ void driver_help_ar(void) { "MODE is a string of one or more letters; exactly one operation " "must\n" "be selected and any number of modifiers may follow. The archive\n" - "path and (optional) member list follow the mode.\n" + "path and (optional) member list follow the mode. MODE has no\n" + "leading dash: use `rcs`, not `-rcs`, in this release.\n" "\n" "OPERATIONS (mutually exclusive)\n" " r Replace listed members in place; preserve unlisted; " @@ -107,9 +108,17 @@ void driver_help_ar(void) { "NOT YET IMPLEMENTED\n" " d (delete), q (quick append), and standalone `s` without r/c.\n" " Encountering any of those yields a usage error with exit code 2.\n" + " Refresh an existing archive index with `kit ranlib ARCHIVE.a`.\n" + "\n" + "INPUT / OUTPUT\n" + " Archive and member names are file operands; archives are not read\n" + " from stdin. Operations t/x read the archive, x writes into the\n" + " current directory, and p writes selected member bytes to stdout.\n" + " Prefix a leading-dash path with `./`; `--` is not accepted.\n" "\n" "EXAMPLES\n" " kit ar rcs libfoo.a a.o b.o c.o\n" + " kit ranlib libfoo.a\n" " kit ar t libfoo.a\n" " kit ar x libfoo.a a.o\n" " kit ar p libfoo.a a.o > a.o.copy\n" diff --git a/driver/cmd/as.c b/driver/cmd/as.c @@ -56,13 +56,26 @@ void driver_help_as(void) { " -isystem DIR Add a system include directory for INPUT.S\n" " -D NAME[=VALUE] Define a preprocessor macro for INPUT.S\n" " -U NAME Undefine a preprocessor macro for INPUT.S\n" - " -target TRIPLE Cross-assemble target. See `kit cc --help` " - "for " - "the\n" - " full list of recognized arches and OSes. " - "Default:\n" - " the host target.\n" + " -target TRIPLE Cross-assemble target (default: host)\n" " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "TARGETS AND INPUT LIMITS\n" + " Canonical assembler examples are aarch64-none-elf,\n" + " x86_64-linux-gnu, and riscv64-none-elf. The release also accepts\n" + " the corresponding hosted/freestanding triples documented by\n" + " `kit cc --help`. A literal `-` is treated as a filename, not\n" + " stdin, in this release. Write piped assembly to a temporary .s\n" + " file first. Spell a leading-dash path as ./-name.s.\n" + "\n" + "EXAMPLES\n" + " kit as -target aarch64-none-elf -g -o start.o start.s\n" + " kit as -target x86_64-linux-gnu -DDEBUG=1 -I include \\\n" + " -o entry.o entry.S\n" + "\n" + " tmp=\"${TMPDIR:-/tmp}/kit-stdin.s\"\n" + " printf '.text\\n.globl entry\\nentry:\\n ret\\n' > \"$tmp\"\n" + " kit as -target x86_64-linux-gnu -o entry.o \"$tmp\"\n" "\n" "EXIT CODES\n" " 0 success 1 assemble error 2 bad " diff --git a/driver/cmd/build.c b/driver/cmd/build.c @@ -2573,11 +2573,19 @@ void driver_help_build_exe(void) { " sources supplied in this invocation; .o/.a/-l Wasm linking is not\n" " a v1 feature.\n" "\n" - "OPTIONS (selection)\n" + "INPUTS\n" + " Registered sources are C (.c), assembly (.s/.S), and WebAssembly\n" + " (.wat/.wasm), selected by suffix or -x. Native links may also\n" + " consume target-compatible .o, .a, and ELF .so inputs.\n" + "\n" + "OPTIONS\n" " -o PATH Output (default a.out / a.exe)\n" " -O0 -O1 -O2 -g Optimization / debug info (-O2 aliases " "-O1)\n" " -target TRIPLE Cross-compile target\n" + " --sysroot DIR User-supplied hosted cross sysroot\n" + " -isysroot DIR Hosted SDK/sysroot include root\n" + " --support-dir DIR Kit distribution support root\n" " -arch ARCH Darwin-style target architecture\n" " -platform_version P MIN SDK\n" " Darwin-style target platform\n" @@ -2590,7 +2598,34 @@ void driver_help_build_exe(void) { " -Wl,... Linker pass-through\n" " --group [flags] -- sources... Scope compile flags to sources\n" " -X<lang> FLAG Per-language frontend flag\n" - " -h, --help Show this help\n"))); + " -h, --help Show this help\n" + " --version Show Kit version\n" + "\n" + "CURRENT RELEASE REQUIREMENTS\n" + " Use --support-dir \"$DIST/support\" from a relocated tree. Hosted\n" + " cross builds require --sysroot. Native macOS builds currently\n" + " require -isysroot \"$(kit cc -print-sysroot)\". Pass linker\n" + " scripts and entries directly as -T SCRIPT -e SYMBOL; the common\n" + " -Wl,-T,... spelling is not accepted in this release.\n" + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " SDK=\"$(\"$K\" cc -print-sysroot)\"\n" + " \"$K\" build-exe --support-dir \"$DIST/support\" \\\n" + " -isysroot \"$SDK\" main.c util.c -o app\n" + "\n" + " # Flag-scoped C groups plus an assembly source.\n" + " \"$K\" build-exe --support-dir \"$DIST/support\" \\\n" + " -isysroot \"$SDK\" --group -DLEFT=20 -- left.c \\\n" + " --group -DRIGHT=22 -- right.c main.c helper.s -o grouped\n" + "\n" + " # Freestanding: provide startup code and a linker script.\n" + " \"$K\" build-exe --support-dir \"$DIST/support\" \\\n" + " -target aarch64-none-elf -T link.ld -e _start \\\n" + " start.s kernel.c -o kernel.elf\n" + "\n" + "EXIT CODES\n" + " 0 success 1 compile/link/I/O error 2 bad usage\n"))); } void driver_help_build_lib(void) { @@ -2609,16 +2644,46 @@ void driver_help_build_lib(void) { " into a static library (.a), or links an ELF shared library with\n" " -dynamic/-shared. Non-ELF shared-library output is rejected.\n" "\n" - "OPTIONS (selection)\n" + "INPUTS\n" + " Registered sources are C (.c), assembly (.s/.S), and WebAssembly\n" + " (.wat/.wasm), selected by suffix or -x. Native ELF dynamic links\n" + " may also consume compatible object/archive/library inputs.\n" + "\n" + "OPTIONS\n" " -o PATH Output archive (required)\n" + " -dynamic, -shared Build an ELF shared library\n" " -fPIC Position-independent code\n" " -O0 -O1 -O2 -g Optimization / debug info (-O2 aliases " "-O1)\n" " -flto Link-time optimization for source inputs\n" " -target TRIPLE Cross-compile target\n" + " --sysroot DIR User-supplied hosted cross sysroot\n" + " -isysroot DIR Hosted SDK/sysroot include root\n" + " --support-dir DIR Kit distribution support root\n" + " -L DIR, -l NAME Shared-link library search/input\n" " --group [flags] -- sources... Scope compile flags to sources\n" " -X<lang> FLAG Per-language frontend flag\n" - " -h, --help Show this help\n"))); + " -h, --help Show this help\n" + " --version Show Kit version\n" + "\n" + "CURRENT RELEASE REQUIREMENTS\n" + " Use --support-dir \"$DIST/support\" from a relocated tree and an\n" + " explicit SDK/sysroot for hosted headers. Static .a output is the\n" + " portable library shape; -dynamic/-shared is ELF-only.\n" + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " \"$K\" build-lib --support-dir \"$DIST/support\" \\\n" + " -o libanswer.a answer.c helper.s\n" + "\n" + " # Replace SYSROOT with a supplied x86-64 Linux sysroot.\n" + " SYSROOT=/replace/with/x86_64-linux-sysroot\n" + " \"$K\" build-lib --support-dir \"$DIST/support\" \\\n" + " -target x86_64-linux-gnu --sysroot \"$SYSROOT\" \\\n" + " -dynamic -fPIC -o libanswer.so answer.c\n" + "\n" + "EXIT CODES\n" + " 0 success 1 compile/link/I/O error 2 bad usage\n"))); } void driver_help_build_obj(void) { @@ -2638,6 +2703,11 @@ void driver_help_build_obj(void) { " object/static linking is not a v1 feature. The kit-native\n" " replacement for the retired `compile` tool.\n" "\n" + "REGISTERED LANGUAGES\n" + " C (.c), assembly (.s/.S), and WebAssembly (.wat/.wasm), selected\n" + " by suffix or -x c|asm|wat|wasm. Frontend-specific options use\n" + " -X<lang> FLAG.\n" + "\n" "OPTIONS\n" " -o PATH Output (default <base>.o; required for a\n" " multi-source combine and for --emit=c)\n" @@ -2649,12 +2719,41 @@ void driver_help_build_obj(void) { " -flto Link-time optimization for multi-source " "obj\n" " -target TRIPLE Cross-compile target\n" + " --sysroot DIR User-supplied hosted cross sysroot\n" + " -isysroot DIR Hosted SDK/sysroot include root\n" + " --support-dir DIR Kit distribution support root\n" " -I/-isystem/-D/-U Preprocessor flags (C/asm frontends)\n" " -x LANG Force a registered language\n" " --group [flags] -- sources... Scope compile flags to sources\n" " -X<lang> FLAG Per-language frontend flag\n" " -o - Write the emit to stdout\n" - " -h, --help Show this help\n"))); + " -h, --help Show this help\n" + " --version Show Kit version\n" + "\n" + "OUTPUT AND RELEASE NOTES\n" + " One-source object/assembly output defaults to <base>.o/.s. -o is\n" + " required for multi-source object combination and --emit=c; use\n" + " -o for stable IR filenames. -fsyntax-only writes nothing. A\n" + " relocated distribution currently needs --support-dir. Hosted\n" + " headers require an explicit SDK/sysroot. Use --emit=c only at -O0\n" + " in this release; optimized portable-C emission can terminate Kit.\n" + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" main.c\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" \\\n" + " main.c helper.c -o combined.o\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" \\\n" + " --emit=asm source.c -o source.s\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" \\\n" + " --emit=c -O0 source.c -o portable.c\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" \\\n" + " --emit=ir -O1 source.c -o source.ir\n" + " \"$K\" build-obj --support-dir \"$DIST/support\" \\\n" + " -fsyntax-only -I include source.c\n" + "\n" + "EXIT CODES\n" + " 0 success 1 frontend/link/I/O error 2 bad usage\n"))); } int driver_build_exe_ex(int argc, char** argv, const KitDriverExtension* ext) { diff --git a/driver/cmd/build_coord.c b/driver/cmd/build_coord.c @@ -81,8 +81,36 @@ void driver_help_build(void) { " kit build need-await [--format path|id|id-path] TOKEN\n" " # inside a build recipe\n" "\n" + "BUILD DEFINITIONS AND LABELS\n" + " The default package definition is BUILD.kit under --root (or the\n" + " basename selected by WORKSPACE.kit). A minimal definition is:\n" + "\n" + " kit-build 1\n" + " [target copy]\n" + " recipe recipes/copy.sh\n" + "\n" + " The command-line TARGET names a [target NAME] in the current package.\n" + " `copy` at the workspace root is rendered canonically as `//:copy` in\n" + " traces. Package paths are resolved relative to --root; recipes and\n" + " source paths are package-relative. Arguments after -- participate in\n" + " the target key and are available to the recipe protocol.\n" + "\n" + "RECIPE PROTOCOL\n" + " A recipe is an executable that writes its output tree below\n" + " $KIT_BUILD_OUT. It receives a clean environment containing\n" + " KIT_BUILD_SOCK, KIT_BUILD_OUT, KIT_BUILD_TARGET, KIT_BUILD_REPO,\n" + " KIT_BUILD_PACKAGE, and KIT_BUILD_LOCAL, plus values explicitly\n" + " propagated with --env/--config env.NAME. `KIT` is not built in; the\n" + " example propagates it so the recipe can call the same Kit binary.\n" + "\n" + " Recipe-only `source` records a source dependency and prints its CAS\n" + " materialization path or requested id form. `need` records another\n" + " target dependency; need-submit/need-await permit parallel requests.\n" + " config-get, fetch, depfile, and glob also require KIT_BUILD_SOCK and\n" + " are rejected outside a running recipe.\n" + "\n" "OPTIONS\n" - " --store DIR Build store root (default: $KIT cache/build)\n" + " --store DIR Build store root (default: Kit platform cache/build)\n" " --root DIR Workspace root (default: .)\n" " --def FILE Package build-file basename (default: WORKSPACE def-name or BUILD.kit)\n" " --profile NAME Apply WORKSPACE.kit [config NAME] after [config default]\n" @@ -91,7 +119,35 @@ void driver_help_build(void) { " --env N[=V] Shorthand for --config env.N[=V]\n" " --verify Verify the returned output tree in the CAS\n" " --trace Print target-level resolution decisions to stderr\n" - " --stats Print build-resolution counters to stderr\n"); + " --stats Print build-resolution counters to stderr\n" + " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "OUTPUT AND CACHING\n" + " A successful top-level build prints `TREE_ID MATERIALIZED_PATH` to\n" + " stdout. --trace/--stats write diagnostics and counters to stderr. The\n" + " recipe output tree is content-addressed; an unchanged definition,\n" + " configuration, arguments, and recorded dependencies may produce a\n" + " deep cache hit with recipes_run=0. --verify checks the returned tree.\n"); + driver_printf( + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " mkdir -p demo/recipes\n" + " printf 'input from source\\n' > demo/input.txt\n" + " printf '%%s\\n' 'kit-build 1' '[target copy]' \\\n" + " 'recipe recipes/copy.sh' > demo/BUILD.kit\n" + " printf '%%s\\n' '#!/bin/sh' 'set -eu' \\\n" + " 'source_path=$(\"$KIT\" build source input.txt)' \\\n" + " '/bin/cp \"$source_path\" \"$KIT_BUILD_OUT/result.txt\"' \\\n" + " > demo/recipes/copy.sh\n" + " chmod +x demo/recipes/copy.sh\n" + " \"$K\" build --root \"$PWD/demo\" --env KIT=\"$K\" \\\n" + " --verify --stats copy\n" + " # Repeat the same command to observe a cache hit.\n" + "\n" + "EXIT CODES\n" + " 0 success 1 resolution/recipe/CAS/I/O error 2 bad usage\n"); } static const char* build_status_name(KitStatus st) { diff --git a/driver/cmd/cas.c b/driver/cmd/cas.c @@ -13,7 +13,7 @@ void driver_help_cas(void) { driver_printf( - "kit cas - shared kit blob/tree content-addressed store\n" + "kit cas — shared blob/tree content-addressed store\n" "\n" "USAGE\n" " kit cas add-blob --cas DIR FILE\n" @@ -21,7 +21,48 @@ void driver_help_cas(void) { " kit cas add-tree --cas DIR --map FILE\n" " kit cas inspect-tree --cas DIR TREE_ID\n" " kit cas verify-tree --cas DIR TREE_ID\n" - " kit cas materialize --cas DIR TREE_ID -C DIR\n"); + " kit cas materialize --cas DIR TREE_ID -C DIR\n" + "\n" + "STORE AND IDS\n" + " --cas DIR is required; there is no implicit store. add-blob and\n" + " add-tree print one lowercase 64-hex content ID to stdout. A tree ID\n" + " names a canonical manifest of relative paths, modes, sizes, and blob\n" + " identities. CAS objects are content-verified, not signed.\n" + "\n" + "ADD TREE INPUTS\n" + " --root DIR recursively store regular files from DIR.\n" + " --map FILE read whitespace-separated rows:\n" + " TREE_PATH MODE SOURCE_PATH\n" + " MODE is - for a regular file or x for an executable.\n" + " Blank lines and lines whose first non-space byte is #\n" + " are ignored. Paths cannot contain spaces or tabs in this\n" + " map format. SOURCE_PATH is resolved from the current\n" + " working directory; TREE_PATH must be a safe relative\n" + " tree path. Row order does not affect canonical ordering.\n" + "\n" + "INSPECT, VERIFY, MATERIALIZE\n" + " inspect-tree writes the canonical manifest to stdout. verify-tree\n" + " checks every referenced blob's presence, size, and content identity.\n" + " materialize performs the same verification, then recreates the tree\n" + " below -C DIR and reapplies regular/executable modes.\n" + "\n" + "EXAMPLES\n" + " CAS=./cas-store\n" + " blob=$(kit cas add-blob --cas \"$CAS\" README.txt)\n" + " tree=$(kit cas add-tree --cas \"$CAS\" --root ./release-root)\n" + " kit cas inspect-tree --cas \"$CAS\" \"$tree\"\n" + " kit cas verify-tree --cas \"$CAS\" \"$tree\"\n" + " kit cas materialize --cas \"$CAS\" \"$tree\" -C ./restored\n" + " printf 'bin/tool x ./build/tool\\n' > tree.map\n" + " kit cas add-tree --cas \"$CAS\" --map tree.map\n" + "\n" + "OUTPUT AND ERRORS\n" + " Normal IDs/manifests go to stdout and diagnostics to stderr. Missing,\n" + " malformed, or corrupted stored data is an operational failure.\n" + "\n" + "EXIT CODES\n" + " 0 success 1 store/I/O/integrity error\n" + " 2 bad command-line usage or malformed ID\n"); } static int cas_parse_id(const char* s, uint8_t out[KIT_CAS_HASH_LEN]) { diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -171,53 +171,171 @@ void driver_help_cc(void) { "kit cc — C compiler driver\n" "\n" "USAGE\n" - " kit cc [options] inputs... compile and link " - "to " - "exe\n" - " kit cc -c [options] input.c compile one source " - "to " - ".o\n" - " kit cc -E [options] input.c preprocess to -o\n" - " kit cc -fsyntax-only [options] inputs... check only\n" - " kit cc -shared [options] inputs... link an ELF shared " - "library\n" - " kit cc -M|-MM [options] input.c print header deps; " - "no " - "compile\n" - " --sysroot DIR / -isysroot DIR hosted " - "SDK/sysroot\n" - " --support-dir DIR kit support " - "root\n" - " -print-search-dirs / -print-sysroot show the dirs " - "the\n" - " -print-resource-dir compiler will " - "search\n" - " -S [options] input.c emit assembly " - "(.s)\n" - " --emit=c [options] input.c emit portable C " - "source\n" - " --emit=ir -O1 [options] input.c emit semantic IR " - "dump\n" - " -O0 | -O1 | -O2 optimization " - "(-O2 aliases -O1 for v1)\n" - " -flto link-time " - "optimization for all source inputs\n" + " kit cc [options] INPUT... compile and link\n" + " kit cc -c [options] INPUT.c emit an object\n" + " kit cc -S [options] INPUT.c emit assembly\n" + " kit cc -E [options] INPUT.c preprocess only\n" + " kit cc -M|-MM [options] INPUT.c print dependencies\n" + " kit cc --emit=c|ir [options] INPUT.c emit C or IR\n" + " kit cc -fsyntax-only [options] INPUT... check only\n" "\n" - "(see source for the full GCC-subset flag reference)\n"))); + "INPUTS AND OUTPUTS\n" + " .c is C source; .S is preprocessed assembly; .s is assembly.\n" + " Target-compatible .o, .a, and ELF .so files are accepted by the\n" + " link stage. A single `-` input reads stdin; use `-x c -` when an\n" + " input suffix is unavailable. This release does not accept `--`\n" + " before compiler operands; spell a leading-dash path as ./-name.c.\n" + "\n" + " Linking defaults to a.out (a.exe for Windows). -c and -S derive\n" + " <base>.o and <base>.s for one source. -E, -M, and -MM write stdout\n" + " when -o is absent. Use -o for --emit=c/ir and combined outputs.\n" + "\n" + "PREPROCESSING AND DEPENDENCIES\n" + " -I DIR, -isystem DIR Add user/system include directories\n" + " -D NAME[=BODY], -U NAME Define or undefine a macro\n" + " -E Preprocess without compiling\n" + " -M, -MM Dependencies with/without system headers\n" + " -MD, -MMD, -MF FILE Side-effect dependencies/output file\n" + " -MT TARGET, -MQ TARGET Dependency target spelling\n" + "\n" + " -M and -MM currently require exactly one input. Generate one\n" + " dependency result per source instead of passing several sources.\n" + "\n" + "COMPILATION\n" + " -std=c11 Select the supported C language level\n" + " -fsyntax-only Diagnose only; write no output\n" + " -c, -S Stop after object or assembly emission\n" + " --emit=c Portable C output; use -O0 in this release\n" + " --emit=ir Semantic IR dump; requires -O1 or -O2\n" + " -O0, -O1, -O2 Optimize (-O2 currently aliases -O1)\n" + " -g Emit debug information\n" + " -flto Record source inputs for LTO at link\n" + " -fPIC/-fpic, -fPIE/-fpie Position-independent code/executable\n" + " -Werror, -fmax-errors=N Diagnostic policy\n" + " -ffreestanding Freestanding language/runtime mode\n" + " -x c|assembler|assembler-with-cpp\n" + " Override suffix classification\n" + "\n" + "LINKING\n" + " -o PATH Output path\n" + " -L DIR, -l NAME Library search/input\n" + " -static, -shared Static executable / ELF shared library\n" + " -nostdlib, -nodefaultlibs, -nostartfiles\n" + " Suppress default link components\n" + " -T SCRIPT, -e SYMBOL Linker script and entry symbol\n" + " -Wl,... Forward supported linker tokens\n" + " -framework NAME, -F DIR Darwin framework/search directory\n" + "\n" + " `-Wl,-T,...` is not accepted in this release; pass -T SCRIPT and\n" + " -e SYMBOL directly. Static/shared availability follows the target\n" + " platform; non-ELF shared-library output is not provided here.\n"))); + driver_printf( + "%.*s", + KIT_SLICE_ARG(KIT_SLICE_LIT( + "\n" + "TARGET AND SEARCH PATHS\n" + " -target TRIPLE Select a canonical target below\n" + " -arch ARCH Darwin-style architecture selection\n" + " -march=ISA, -mabi=ABI Architecture/ABI selection when supported\n" + " --sysroot DIR, -isysroot DIR\n" + " Hosted SDK/sysroot supplied by the user\n" + " --support-dir DIR Kit distribution support root\n" + " -print-search-dirs Print compiler/library search paths\n" + " -print-sysroot Print the native SDK Kit can discover\n" + " -print-resource-dir Print the current compiler resource path\n" + "\n" + " Hosted: aarch64-linux-gnu, x86_64-linux-gnu, riscv64-linux-gnu;\n" + " aarch64-linux-musl, x86_64-linux-musl, riscv64-linux-musl;\n" + " aarch64-freebsd, x86_64-freebsd, riscv64-freebsd;\n" + " aarch64-windows, x86_64-windows; aarch64-linux-android21;\n" + " aarch64-apple-darwin, x86_64-apple-darwin.\n" + " Freestanding: aarch64-none-elf, x86_64-none-elf,\n" + " riscv64-none-elf, riscv32-none-elf, arm-none-eabi.\n" + " WebAssembly source target: wasm32-none (source-batch module; this\n" + " release does not link separate Wasm .o/.a inputs).\n" + "\n" + "CURRENT RELEASE REQUIREMENTS AND LIMITS\n" + " From an unrelated working directory, pass\n" + " --support-dir \"$DIST/support\"\n" + " because this release does not reliably find the sibling support\n" + " tree after relocation. On macOS, feed the value printed by\n" + " -print-sysroot back through -isysroot for native SDK headers.\n" + " Cross-hosted compilation requires an explicit user-supplied sysroot.\n" + "\n" + " -fstack-protector is not implemented; use -fno-stack-protector.\n" + " Do not combine --emit=c with -O1/-O2 in this release: that path can\n" + " terminate the compiler. Portable-C emission is demonstrated at -O0.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "EXAMPLES\n" + " # Native macOS with the current release's explicit paths.\n" + " DIST=\"$PWD/kit\"\n" + " K=\"$DIST/bin/kit\"\n" + " SDK=\"$(\"$K\" cc -print-sysroot)\"\n" + " printf 'int main(void) { return 0; }\\n' > hello.c\n" + " \"$K\" cc --support-dir \"$DIST/support\" -isysroot \"$SDK\" \\\n" + " hello.c -o hello\n" + " ./hello\n" + "\n" + " # Replace SYSROOT with a supplied hosted cross sysroot.\n" + " SYSROOT=/replace/with/aarch64-linux-sysroot\n" + " \"$K\" cc --support-dir \"$DIST/support\" \\\n" + " -target aarch64-linux-gnu --sysroot \"$SYSROOT\" hello.c -o hello.aa64\n" + "\n" + " # Freestanding final link; provide startup object and linker script.\n" + " \"$K\" cc --support-dir \"$DIST/support\" -target aarch64-none-elf \\\n" + " -ffreestanding -c kernel.c -o kernel.o\n" + " \"$K\" cc --support-dir \"$DIST/support\" -target aarch64-none-elf \\\n" + " -ffreestanding -T link.ld -e _start start.o kernel.o -o kernel.elf\n" + "\n" + " \"$K\" cc --support-dir \"$DIST/support\" -E -DVALUE=42 input.c\n" + " \"$K\" cc --support-dir \"$DIST/support\" -MM input.c -MF input.d\n" + " \"$K\" cc --support-dir \"$DIST/support\" --emit=c -O0 input.c -o input.out.c\n" + " \"$K\" cc --support-dir \"$DIST/support\" --emit=ir -O1 input.c -o input.ir\n" + "\n" + "EXIT CODES\n" + " 0 success 1 compile/link/I/O error 2 bad usage\n"))); } void driver_help_check(void) { - driver_printf("%.*s", - KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit check — run frontend checks without emitting code\n" - "\n" - "USAGE\n" - " kit check [cc-options] inputs...\n" - "\n" - "Runs the same C frontend path as `kit cc " - "-fsyntax-only`, including " - "preprocessing and diagnostics, but does not write objects " - "or link.\n"))); + driver_printf( + "%.*s", + KIT_SLICE_ARG(KIT_SLICE_LIT( + "kit check — run C frontend checks without emitting code\n" + "\n" + "USAGE\n" + " kit check [options] INPUT.c...\n" + "\n" + "DESCRIPTION\n" + " Equivalent to the preprocessing and semantic-check portion of\n" + " `kit cc -fsyntax-only`. It writes diagnostics only: no object,\n" + " assembly, executable, or dependency output is produced.\n" + "\n" + "OPTIONS AND INPUTS\n" + " Accepts the cc preprocessing, target, sysroot, diagnostic, and\n" + " language options described by `kit cc --help`, including -I,\n" + " -isystem, -D, -U, -std=c11, -Werror, -fmax-errors=N,\n" + " -target, --sysroot/-isysroot, and --support-dir. Inputs are C\n" + " sources; each is preprocessed and checked independently.\n" + "\n" + " A relocated distribution currently needs\n" + " --support-dir \"$DIST/support\". Hosted SDK headers need an\n" + " explicit --sysroot/-isysroot as described by `kit cc --help`.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/kit\"\n" + " \"$DIST/bin/kit\" check --support-dir \"$DIST/support\" \\\n" + " -I include -DDEBUG=1 src/main.c src/util.c\n" + "\n" + "EXIT CODES\n" + " 0 checks passed 1 frontend/I/O error 2 bad usage\n"))); } static int cc_alloc_arrays(CcOptions* o, int argc) { diff --git a/driver/cmd/cmp.c b/driver/cmd/cmp.c @@ -44,9 +44,22 @@ void driver_help_cmp(void) { " -n N compare at most N bytes\n" " -h, --help show this help\n" "\n" + "COMPATIBILITY AND PATHS\n" + " --ignore-initial and --bytes are not accepted; use positional\n" + " SKIP1/SKIP2 and -n. Bare -- is not accepted, so spell a\n" + " leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " kit cmp original.bin copy.bin\n" + " kit cmp -s original.bin copy.bin && echo identical\n" + " kit cmp -n 64 first.bin second.bin\n" + " kit cmp first.bin second.bin 0x100 0x200\n" + " cat copy.bin | kit cmp original.bin -\n" + "\n" "EXIT CODES\n" - " 0 identical 1 differ 2 " - "trouble/usage\n"))); + " 0 identical 1 differ 2 trouble/usage\n" + " A missing or unreadable file currently returns 2, as does malformed\n" + " command-line usage; do not treat 2 as a usage-only status.\n"))); } /* Load a named operand, or stdin when the name is "-". Returns 0 on success diff --git a/driver/cmd/compress.c b/driver/cmd/compress.c @@ -89,43 +89,149 @@ static int compress_compat_noop(const char* a) { return 0; } -void driver_help_compress(void) { - driver_printf( - "%.*s", - KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit compress — compress or decompress with gzip or LZ4 frame\n" - "\n" - "USAGE\n" - " kit compress [-z FMT] [-o OUT] [FILE]\n" - " kit compress -d [-z FMT] [-o OUT] [FILE]\n" - "\n" - "DESCRIPTION\n" - " Reads FILE (or stdin with no FILE, or `-`) and writes the result\n" - " to OUT (or stdout). Containers are standard: a `.gz` any gzip\n" - " reads, or a `.lz4` frame the lz4 CLI reads.\n" - "\n" - "OPTIONS\n" - " -d decompress (default: compress)\n" - " -z, --format FMT gzip (default) | lz4. When decompressing, the\n" - " format is auto-detected from the input's " - "magic\n" - " bytes unless -z is given.\n" - " -o OUT write output to OUT (default: stdout)\n" - " -h, --help show this help\n" - "\n" - "ALIASES\n" - " Invoked as gzip / lz4 (or lz4c) compresses to that container; as\n" - " gunzip decompresses gzip. -d flips direction; -z is rejected " - "(the\n" - " container is fixed). Common gzip/lz4 flags (-c/--stdout, -k, -f,\n" - " -1..-9, --fast, --best) are accepted; output still goes to " - "stdout\n" - " or -o, never rewriting the input in place.\n" - "\n" - "EXIT CODES\n" - " 0 success 1 I/O or codec error 2 bad usage\n"))); +static void compress_help(const CompressPersona* persona) { + if (!persona->locked_format) { + driver_printf( + "%.*s", + KIT_SLICE_ARG(KIT_SLICE_LIT( + "kit compress — compress or decompress gzip and LZ4 frames\n" + "\n" + "USAGE\n" + " kit compress [-z FMT] [-o OUT] [FILE]\n" + " kit compress -d [-z FMT] [-o OUT] [FILE]\n" + "\n" + "DESCRIPTION\n" + " Reads FILE, or stdin when FILE is absent or -, and writes to\n" + " OUT (stdout by default). gzip and LZ4-frame output interoperates\n" + " with standard gzip/lz4 tools. Input files are never rewritten.\n" + "\n" + "OPTIONS\n" + " -d decompress (default: compress)\n" + " -z, --format FMT gzip (compression default) | lz4. On\n" + " decompression, magic auto-detects the format\n" + " unless this option fixes it.\n" + " -o OUT write OUT instead of stdout\n" + " -h, --help show this help and exit\n" + "\n" + "ALIASES\n" + " gzip, gunzip, lz4, and lz4c fix the container. Their own help\n" + " describes default direction and accepted compatibility flags.\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " kit compress -z gzip -o data.gz data.txt\n" + " kit compress -d -o data.copy data.gz\n" + " kit compress -z lz4 data.txt > data.lz4\n" + " kit compress -d data.lz4 > data.copy\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O or codec error 2 bad usage\n"))); + return; + } + + if (persona == &CZ_GZIP) { + driver_printf( + "gzip — write a gzip stream\n" + "\n" + "USAGE\n" + " gzip [OPTIONS] [FILE]\n" + "\n" + "DESCRIPTION\n" + " Compresses FILE, or stdin when omitted or -, as gzip. Output goes\n" + " to stdout unless -o is given; FILE is never renamed or rewritten.\n" + "\n" + "OPTIONS\n" + " -d, --decompress, --uncompress decompress gzip instead\n" + " -o OUT write OUT instead of stdout\n" + " -c/--stdout, -k, -f, -n, -q, -1..-9, --fast, --best\n" + " accepted compatibility no-ops\n" + " -h, --help show this help and exit\n" + " -z/--format is rejected because gzip fixes the container.\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " gzip -o data.gz data.txt\n" + " gzip -d -o data.copy data.gz\n" + " printf 'hello\\n' | gzip > hello.gz\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O or codec error 2 bad usage\n"); + } else if (persona == &CZ_GUNZIP) { + driver_printf( + "gunzip — decompress a gzip stream\n" + "\n" + "USAGE\n" + " gunzip [OPTIONS] [FILE]\n" + "\n" + "DESCRIPTION\n" + " Decompresses gzip from FILE, or stdin when omitted or -. Output\n" + " goes to stdout unless -o is given; FILE is never renamed or removed.\n" + " Empty, corrupt, or non-gzip input is an operational error.\n" + "\n" + "OPTIONS\n" + " -d, --decompress, --uncompress accepted; remains decompression\n" + " -o OUT write OUT instead of stdout\n" + " -c/--stdout, -k, -f, -n, -q accepted compatibility no-ops\n" + " -h, --help show this help and exit\n" + " -z/--format is rejected because gunzip fixes gzip input.\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " gunzip -o data.txt data.gz\n" + " cat data.gz | gunzip > data.txt\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O or codec error 2 bad usage\n"); + } else { + driver_printf( + "%s — write an LZ4-frame stream%s\n" + "\n" + "USAGE\n" + " %s [OPTIONS] [FILE]\n" + "\n" + "DESCRIPTION\n" + " Compresses FILE, or stdin when omitted or -, as an LZ4 frame.\n" + " Output goes to stdout unless -o is given; FILE is never rewritten.\n" + "%s" + "\n" + "OPTIONS\n" + " -d, --decompress, --uncompress decompress LZ4-frame input\n" + " -o OUT write OUT instead of stdout\n" + " -c/--stdout, -k, -f, -n, -q, -1..-9, --fast, --best\n" + " accepted compatibility no-ops\n" + " -h, --help show this help and exit\n" + " -z/--format is rejected because the container is fixed.\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " %s -o data.lz4 data.txt\n" + " %s -d -o data.copy data.lz4\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O or codec error 2 bad usage\n", + persona->name, persona == &CZ_LZ4C ? " (lz4 alias)" : "", + persona->name, + persona == &CZ_LZ4C + ? " lz4c is the legacy compression alias for lz4.\n" + : "", + persona->name, persona->name); + } } +void driver_help_compress(void) { compress_help(&CZ_GENERIC); } +void driver_help_gzip(void) { compress_help(&CZ_GZIP); } +void driver_help_gunzip(void) { compress_help(&CZ_GUNZIP); } +void driver_help_lz4(void) { compress_help(&CZ_LZ4); } +void driver_help_lz4c(void) { compress_help(&CZ_LZ4C); } + static int compress_main(int argc, char** argv, const CompressPersona* persona) { DriverEnv env; @@ -141,7 +247,7 @@ static int compress_main(int argc, char** argv, KitCompressFormat fmt; if (driver_argv_wants_help(argc, argv, 1)) { - driver_help_compress(); + compress_help(persona); return 0; } diff --git a/driver/cmd/cpio.c b/driver/cmd/cpio.c @@ -570,6 +570,29 @@ void driver_help_cpio(void) { " gzip/lz4 archive is decompressed automatically. Concatenated\n" " archives (early-init segments) are accepted on list/extract.\n" "\n" + "OPERANDS AND EXTRACTION\n" + " Create mode takes PATH operands; list/extract take no member\n" + " operands. Extraction writes below the current working directory,\n" + " so change to the intended destination first. Prefix a create\n" + " operand beginning with `-` by `./`; `--` is not accepted.\n" + "\n" + "EXAMPLES\n" + " kit cpio -o -F root.cpio root/\n" + " kit cpio -t -F root.cpio\n" + " mkdir unpacked\n" + " (cd unpacked && kit cpio -i -F ../root.cpio)\n" + " kit cpio -o root/ | kit cpio -t\n" + " kit cpio -o -z root/ > root.cpio.gz\n" + " kit cpio -t < root.cpio.gz # gzip auto-detected\n" + " kit cpio -o --lz4 root/ > root.cpio.lz4\n" + " kit cpio -i < root.cpio.lz4 # extract into cwd\n" + "\n" + "KNOWN LIMITATION\n" + " Malformed input and some conflicting mode combinations can\n" + " currently diagnose an error but return status 0. Treat any cpio\n" + " diagnostic as failure; the exit convention below is the intended\n" + " contract after remediation.\n" + "\n" "EXIT CODES\n" " 0 success 1 I/O or format error 2 bad usage\n"))); } diff --git a/driver/cmd/cpp.c b/driver/cmd/cpp.c @@ -55,9 +55,28 @@ void driver_help_cpp(void) { " -isystem DIR Add a system include directory\n" " -D NAME[=BODY] Define a preprocessor macro\n" " -U NAME Undefine a preprocessor macro\n" - " -target TRIPLE Cross-target. See `kit cc --help` for the\n" - " accepted arches/OSes. Default: host.\n" + " -target TRIPLE Cross-target (default: host)\n" " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "TARGETS\n" + " Hosted triples: aarch64/x86_64/riscv64 with linux-gnu,\n" + " linux-musl, or freebsd where listed by the release;\n" + " aarch64/x86_64-windows, aarch64-linux-android21, and\n" + " aarch64/x86_64-apple-darwin. Freestanding triples are\n" + " aarch64-none-elf, x86_64-none-elf, riscv64-none-elf,\n" + " riscv32-none-elf, and arm-none-eabi. Hosted cross headers require\n" + " explicit -isystem/-I paths or the sysroot workflow in `kit cc`.\n" + "\n" + "INPUT/OUTPUT NOTES\n" + " `-` is accepted as stdin. A leading-dash file is not introduced\n" + " with `--` in this release; spell it as ./-name.c. Normal output\n" + " is stdout, so diagnostics remain on stderr when output is piped.\n" + "\n" + "EXAMPLES\n" + " kit cpp -I include -DVALUE=42 source.c -o source.i\n" + " printf '#if FLAG\\nok\\n#endif\\n' | kit cpp -DFLAG=1 -\n" + " kit cpp ./-generated.c\n" "\n" "EXIT CODES\n" " 0 success 1 preprocess / I/O error 2 " diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c @@ -96,8 +96,8 @@ void driver_help_dbg(void) { " finish, backtrace, registers, locals/args, variable read/write,\n" " and raw memory examine. -g is forced on so source lines and\n" " variable locations are available at runtime.\n" - " V1 support is limited to host/arch lanes where test-dbg runs\n" - " hard-green: Darwin/Linux aarch64 hosts today.\n" + " Current debugger support is limited to Darwin/Linux aarch64\n" + " hosts.\n" "\n" " Anything after `--` is passed to the JITed program as argv.\n" " With no input files, dbg starts an empty JIT session; append " @@ -109,6 +109,9 @@ void driver_help_dbg(void) { "(default -O0)\n" " -g Emit DWARF (forced on)\n" " -e SYMBOL Entry symbol (default `main`)\n" + " -target TRIPLE Host-compatible compile target\n" + " --sysroot DIR Hosted SDK/sysroot for headers with -lc\n" + " -lc Enable hosted libc headers/definitions\n" " -x LANG Default REPL language: c, asm, wasm/wat\n" " --language LANG Same as -x\n" " -I DIR Add quoted-include search path\n" @@ -133,6 +136,8 @@ void driver_help_dbg(void) { " edit [LANG|NAME] edit and append a language snippet\n" " (Ctrl-G edits the current input line " "in $EDITOR)\n" + " Set $EDITOR before starting dbg to\n" + " select the editor command.\n" " expr EXPR | expr { ... } compile and call an expression " "thunk\n" " EXPR same as expr EXPR\n" @@ -172,6 +177,10 @@ void driver_help_dbg(void) { "fit\n" " on a single line (continuation reads go to stdin, not the file).\n" "\n" + " KNOWN LIMITATION: an unreadable --script file is diagnosed but\n" + " currently returns status 0. Check stderr/file existence rather\n" + " than relying on status alone until this is remediated.\n" + "\n" "SIGNALS\n" " Ctrl-C is forwarded into the running session as an interrupt; at\n" " the REPL prompt it terminates the program normally.\n" @@ -182,6 +191,20 @@ void driver_help_dbg(void) { " command-line help; once the REPL is\n" " running, type `h` for REPL " "commands)\n" + " --version Show Kit version and exit\n" + "\n" + "EXAMPLES\n" + " # Interactive source debugging (supported host above).\n" + " kit dbg -O0 program.c -- arg1\n" + " # At the prompt: b main, r, n, p value, bt, q\n" + "\n" + " # Deterministic batch session.\n" + " printf '%s\\n' 'b main' 'r' 'bt' 'q' > commands.dbg\n" + " kit dbg --batch --script commands.dbg program.c\n" + "\n" + " # Hosted headers on macOS require Kit's printed SDK.\n" + " SDK=\"$(kit cc -print-sysroot)\"\n" + " kit dbg -lc --sysroot \"$SDK\" hosted.c\n" "\n" "EXIT CODES\n" " 0 clean exit 1 compile/link/command error 2 " diff --git a/driver/cmd/disas.c b/driver/cmd/disas.c @@ -39,12 +39,27 @@ void driver_help_disas(void) { " -target architecture (the host arch by default).\n" "\n" "OPTIONS\n" - " -target TRIPLE architecture to decode for (e.g. aarch64,\n" - " x86_64, riscv64). See `kit cc --help`.\n" + " -target TRIPLE architecture to decode. Recognized audited\n" + " spellings include aarch64, x86_64, and riscv64;\n" + " a canonical triple with one of those arches is\n" + " also accepted. Default: host architecture.\n" " -x \"HEX...\" disassemble these hex bytes (spaces allowed)\n" " --base ADDR address of the first byte (default 0)\n" " -h, --help show this help\n" "\n" + "INPUT AND LIMITATIONS\n" + " FILE is raw code, not an object file; use kit objdump -d for\n" + " objects. Bare -- is not accepted, so spell a leading-dash file\n" + " as ./-code.bin. A trailing partial fixed-width instruction is\n" + " rendered as `(truncated)` and currently still returns success.\n" + "\n" + "EXAMPLES\n" + " kit disas -target x86_64 -x 'b8 2a000000 c3'\n" + " kit disas -target aarch64 --base 0x1000 -x 'c0035fd6'\n" + " kit disas -target riscv64 code.bin\n" + " bytes=$(kit mc -target aarch64 -p 'ret')\n" + " kit disas -target aarch64 -x \"$bytes\"\n" + "\n" "EXIT CODES\n" " 0 success 1 error 2 bad usage\n"))); } diff --git a/driver/cmd/gram.c b/driver/cmd/gram.c @@ -57,6 +57,20 @@ void driver_help_gram(void) { "filename)\n" " --dump-sexpr print the parsed EBNF syntax tree to stdout\n" "\n" + "GRAMMAR\n" + " A rule has the form `name = expression;`. Quoted text is a literal\n" + " token and a bare name references another rule. The smallest\n" + " accepted grammar is:\n" + "\n" + " start = \"hello\";\n" + "\n" + " EBNF expressions may compose rule/literal sequences, alternatives,\n" + " grouping, optional terms, and repetition. Directive families used\n" + " by the release include `%lex NAME { ... }` lexer blocks, `%skip`\n" + " lexer handling, `%keywords` extraction, and `%machine NAME` token\n" + " automata. --sample-traces NAME selects a declared %machine. Use\n" + " --dump-sexpr to inspect how an input grammar was parsed.\n" + "\n" "LEXER\n" " --multiline bake newline-aware ^/$ into the lexer\n" " --lexer-standalone emit a self-contained re2c-style scanner " @@ -77,6 +91,51 @@ void driver_help_gram(void) { " --samples N --seed N --stop-prob P\n" " --max-depth N --max-repeat N --max-tokens N\n" "\n" + "GENERATED C CONTRACT\n" + " Normal generation writes <grammar>.c and <grammar>.h unless -o\n" + " and --header override them. Grammar `-` reads stdin; syntax dumps\n" + " and samples write stdout, while diagnostics use stderr. Generated\n" + " parser headers include <kit/gram_parse.h>; compile with the shipped\n" + " include directory and link the shipped lib/libkit.a runtime.\n" + "\n" + " With --parser-codegen, PREFIXstack_bounds sizes caller-owned\n" + " control/value stacks, PREFIXgenerate_scratch_count reports sample\n" + " scratch storage, and PREFIXparser_init initializes a caller-owned\n" + " KitGramParser. The generated header is the authority for exact\n" + " enum and symbol names.\n" + "\n" + "CURRENT CLI LIMITS\n" + " The common `--` end-of-options marker is not accepted here; spell\n" + " a leading-dash grammar as ./-grammar.ebnf. Unknown flags or a\n" + " missing option argument currently print only the generic usage\n" + " line, so compare the invocation with this option list.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" + "\n" + "EXAMPLES\n" + " printf '%s\\n' 'start = \"hello\";' > grammar.ebnf\n" + " kit gram --dump-sexpr grammar.ebnf\n" + "\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " \"$K\" gram -o lexer.c --header lexer.h --prefix demo grammar.ebnf\n" + " \"$K\" gram --lexer-standalone --parser-codegen \\\n" + " -o parser.c --header parser.h --prefix demop grammar.ebnf\n" + " printf '%s\\n' '#include \"parser.h\"' '#include <stdio.h>' \\\n" + " 'int main(void) {' \\\n" + " ' size_t ctl = 0, vals = 0;' \\\n" + " ' demopstack_bounds(8, &ctl, &vals);' \\\n" + " ' printf(\"tokens=%d rules=%d control=%zu values=%zu scratch=%zu\\n\",' \\\n" + " ' DEMOPTOK__COUNT, DEMOPR__COUNT, ctl, vals,' \\\n" + " ' demopgenerate_scratch_count());' \\\n" + " ' return ctl == 0 || vals == 0;' '}' > main.c\n" + " SDK=\"$(\"$K\" cc -print-sysroot)\"\n" + " \"$K\" cc --support-dir \"$DIST/support\" -isysroot \"$SDK\" \\\n" + " -I \"$DIST/include\" -I . main.c lexer.c parser.c \\\n" + " \"$DIST/lib/libkit.a\" -o generated-parser\n" + " ./generated-parser\n" + "\n" "EXIT CODES\n" " 0 success 1 compile / I/O error 2 bad usage\n"))); } diff --git a/driver/cmd/hash.c b/driver/cmd/hash.c @@ -66,32 +66,125 @@ static int hash_parse_algo(const char* s, KitHashAlgo* out) { return 1; } -void driver_help_hash(void) { - driver_printf( - "%.*s", - KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit hash — hash files with SHA-256, BLAKE2b, or CRC-32\n" - "\n" - "USAGE\n" - " kit hash [-a ALGO] [FILE...]\n" - "\n" - "DESCRIPTION\n" - " Prints one line per input: the lowercase-hex digest, two spaces,\n" - " then the file name (`-` for stdin). With no FILE, reads stdin.\n" - "\n" - "OPTIONS\n" - " -a ALGO sha256 (default) | blake2b | crc32\n" - " -h, --help show this help\n" - "\n" - "ALIASES\n" - " Invoked as sha256sum, b2sum, or crc32 the algorithm is fixed to\n" - " SHA-256, BLAKE2b-256, or CRC-32 respectively and -a is rejected.\n" - " (GNU b2sum defaults to BLAKE2b-512; this BLAKE2b is 256-bit.)\n" - "\n" - "EXIT CODES\n" - " 0 success 1 I/O error 2 bad usage\n"))); +static void hash_help(const HashPersona* persona) { + if (!persona->locked) { + driver_printf( + "%.*s", + KIT_SLICE_ARG(KIT_SLICE_LIT( + "kit hash — hash files with SHA-256, BLAKE2b-256, or CRC-32\n" + "\n" + "USAGE\n" + " kit hash [-a ALGO] [FILE...]\n" + "\n" + "DESCRIPTION\n" + " Prints one line per input: the lowercase-hex digest, two spaces,\n" + " then the file name (`-` for stdin). With no FILE, reads stdin.\n" + " Multiple files are processed in command-line order.\n" + "\n" + "OPTIONS\n" + " -a ALGO sha256 (default) | blake2b | crc32\n" + " -h, --help show this help and exit\n" + "\n" + "ALIASES\n" + " sha256sum, b2sum, and crc32 fix the algorithm and reject -a.\n" + " These tools do not implement checksum-list verification (-c).\n" + "\n" + "PATHS\n" + " A bare -- terminator is not accepted in this release. Prefix a\n" + " leading-dash file with ./, for example ./-payload.bin.\n" + "\n" + "EXAMPLES\n" + " kit hash README.txt\n" + " kit hash -a blake2b archive.a\n" + " kit hash -a crc32 one.bin two.bin\n" + " printf 'hello\\n' | kit hash -a sha256\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O error 2 bad usage\n"))); + return; + } + + if (persona->algo == KIT_HASH_SHA256) { + driver_printf( + "sha256sum — SHA-256 of files or standard input\n" + "\n" + "USAGE\n" + " sha256sum [FILE...]\n" + "\n" + "DESCRIPTION\n" + " Prints lowercase SHA-256, two spaces, then the file name. With no\n" + " FILE, or with -, reads stdin. The algorithm is fixed; -a is rejected.\n" + " Checksum-list verification (-c) is not implemented.\n" + "\n" + "OPTIONS\n" + " -h, --help show this help and exit\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " sha256sum release.kpkg\n" + " printf 'hello\\n' | sha256sum\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O error 2 bad usage\n"); + } else if (persona->algo == KIT_HASH_BLAKE2B) { + driver_printf( + "b2sum — BLAKE2b-256 of files or standard input\n" + "\n" + "USAGE\n" + " b2sum [FILE...]\n" + "\n" + "DESCRIPTION\n" + " Prints lowercase BLAKE2b-256, two spaces, then the file name. GNU\n" + " b2sum defaults to BLAKE2b-512, so its default digest is twice as\n" + " wide. The algorithm is fixed; -a is rejected, and checksum-list\n" + " verification (-c) is not implemented. With no FILE, reads stdin.\n" + "\n" + "OPTIONS\n" + " -h, --help show this help and exit\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " b2sum tree.manifest\n" + " printf 'hello\\n' | b2sum\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O error 2 bad usage\n"); + } else { + driver_printf( + "crc32 — CRC-32 of files or standard input\n" + "\n" + "USAGE\n" + " crc32 [FILE...]\n" + "\n" + "DESCRIPTION\n" + " Prints an eight-digit lowercase CRC-32, two spaces, then the file\n" + " name. With no FILE, or with -, reads stdin. The algorithm is fixed;\n" + " -a is rejected.\n" + "\n" + "OPTIONS\n" + " -h, --help show this help and exit\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " crc32 image.bin\n" + " printf 'hello\\n' | crc32\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O error 2 bad usage\n"); + } } +void driver_help_hash(void) { hash_help(&HASH_GENERIC); } +void driver_help_sha256sum(void) { hash_help(&HASH_SHA256SUM); } +void driver_help_b2sum(void) { hash_help(&HASH_B2SUM); } +void driver_help_crc32(void) { hash_help(&HASH_CRC32); } + /* Hash data[0..len) with opts->algo and print "<hex> <name>". Returns 0 on * success, 1 on failure (error already reported under `tool`). */ static int hash_one(const KitContext* ctx, const HashOpts* opts, @@ -126,7 +219,7 @@ static int hash_main(int argc, char** argv, const HashPersona* persona) { int i, rc = 1, any_input = 0; if (driver_argv_wants_help(argc, argv, 1)) { - driver_help_hash(); + hash_help(persona); return 0; } diff --git a/driver/cmd/image.c b/driver/cmd/image.c @@ -106,6 +106,38 @@ void driver_help_image(void) { "\n" "SIZE suffixes K, M, and G are accepted.\n" "\n" + "TEXT-FORMAT ADDRESS LIMITS\n" + " IHEX/SREC records require adjusted addresses that fit their\n" + " 32-bit range. Native linked images can start above that range.\n" + " --base changes flat-output placement; it does not rebase IHEX or\n" + " SREC record addresses. Use an explicit signed --bias only when\n" + " every adjusted range is representable. A bias that makes IHEX\n" + " work may still be rejected for SREC in this release.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + "\n" + "EXAMPLES\n" + " # Build a minimal aarch64 freestanding linked image.\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " printf '.global _start\\n.text\\n_start:\\n b _start\\n' > start.s\n" + " printf 'ENTRY(_start)\\nSECTIONS { . = 0x400000; .text : { " + "*(.text*) } }\\n' > link.ld\n" + " \"$K\" as -target aarch64-none-elf -o start.o start.s\n" + " \"$K\" ld --support-dir \"$DIST/support\" -T link.ld \\\n" + " -e _start -o kernel.elf start.o\n" + " \"$K\" image --format bin --require-entry \\\n" + " --require-symbol _start --require-section .text \\\n" + " --metadata kernel.json kernel.elf -o kernel.bin\n" + " \"$K\" size kernel.elf\n" + " # A high-address IHEX image needs an explicit representable bias.\n" + " printf 'ENTRY(_start)\\nSECTIONS { . = 0x100000000; .text : " + "{ *(.text*) } }\\n' > high.ld\n" + " \"$K\" ld --support-dir \"$DIST/support\" -T high.ld \\\n" + " -e _start -o high.elf start.o\n" + " \"$K\" image --format ihex --bias -4294967296 \\\n" + " high.elf -o high.hex\n" + "\n" "EXIT CODES\n" " 0 success 1 image/I/O error 2 bad " "usage\n"))); diff --git a/driver/cmd/install.c b/driver/cmd/install.c @@ -50,6 +50,25 @@ void driver_help_install(void) { " -n, --dry-run print what would be done; change nothing\n" " -v, --verbose print each link as it is created\n" " -h, --help show this help\n" + " --version show Kit version\n" + "\n" + "RELOCATION NOTE\n" + " POSIX symlinks created by this release contain the absolute path to\n" + " the current Kit binary. If the distribution tree is moved afterward,\n" + " those links become dangling. Invoke the moved bin/kit directly and\n" + " rerun `kit install --force` for the same prefix. Windows hard links\n" + " likewise belong to the installed filesystem entry rather than a\n" + " relocatable path lookup.\n" + "\n" + "EXAMPLES\n" + " DIST=\"$PWD/Kit Release\"\n" + " PREFIX=\"$PWD/isolated prefix\"\n" + " mkdir -p \"$PREFIX/bin\"\n" + " \"$DIST/bin/kit\" install --all \"$PREFIX/bin\"\n" + " PATH=\"$PREFIX/bin:/usr/bin:/bin\" cc --help\n" + "\n" + " # After moving DIST, refresh absolute symlinks from the new path.\n" + " \"$PWD/Moved Kit/bin/kit\" install --all --force \"$PREFIX/bin\"\n" "\n" "EXIT CODES\n" " 0 success 1 one or more links failed 2 bad usage\n"); diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -134,8 +134,7 @@ void driver_help_ld(void) { driver_printf( "%.*s", KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit ld — link objects/archives into an executable or ELF shared " - "library\n" + "kit ld — link objects and archives into native-format outputs\n" "\n" "USAGE\n" " kit ld -o OUT [options] inputs.o ... inputs.a ...\n" @@ -143,12 +142,21 @@ void driver_help_ld(void) { " kit ld -r -o partial.o inputs.o ...\n" "\n" "DESCRIPTION\n" - " Loads each input via host file I/O, optionally parses a -T " - "linker\n" - " script, and emits an executable (default) or ELF shared library\n" - " (-shared). The full link surface is exposed: per-archive flags,\n" + " Loads each input directly, optionally parses a -T linker script,\n" + " and emits an executable (default), partial-link object (-r), or\n" + " ELF shared library (-shared). The full link surface is exposed:\n" + " per-archive flags,\n" " cyclic-resolution groups, build-id, soname/rpath/exports.\n" "\n" + "INPUTS AND FORMATS\n" + " Relocatable ELF, Mach-O, and COFF objects and POSIX ar archives\n" + " are accepted. The first object selects the target/output format;\n" + " all following inputs must match it. This release does not\n" + " reliably diagnose every mixed-architecture input, so validate\n" + " each object's target before linking. ELF supports executable,\n" + " shared, and partial-link output. Mach-O and PE/COFF support the\n" + " executable and partial-link shapes; -shared is ELF-only.\n" + "\n" "OUTPUT\n" " -o PATH Output path (required, exactly one)\n" " -shared Emit a position-independent ELF shared\n" @@ -168,6 +176,9 @@ void driver_help_ld(void) { "runtime\n" " --sysroot DIR hosted C runtime/sysroot root\n" " (target is otherwise auto-detected from the first object input)\n" + " A relocated release currently needs an explicit --support-dir.\n" + " Hosted links need an explicit --sysroot and -l c unless the\n" + " caller supplies its own startup objects and system libraries.\n" "\n" "LIBRARY RESOLUTION\n" " -L DIR Add library search path\n" @@ -185,6 +196,11 @@ void driver_help_ld(void) { " --no-as-needed Reset to dynamic\n" " --start-group ...\n" " --end-group Cyclic-resolution archive group\n" + " Outside a group, archives are searched left to right: place an\n" + " object that references a symbol before the archive defining it.\n"))); + driver_printf( + "%.*s", + KIT_SLICE_ARG(KIT_SLICE_LIT( "\n" "SHARED-LIBRARY OPTIONS (require -shared)\n" " -soname NAME DT_SONAME / LC_ID_DYLIB\n" @@ -218,6 +234,18 @@ void driver_help_ld(void) { " --build-id Same as --build-id=sha256\n" " --build-id=MODE none | sha256 | uuid | 0xHEX\n" "\n" + "EXAMPLES\n" + " # Hosted link; replace the quoted paths for your extraction/SDK.\n" + " kit ld --support-dir \"/path/to/kit/support\" \\\n" + " --sysroot \"/path/to/sysroot\" -l c -o hello hello.o\n" + " # Archive order: main.o references symbols in libfoo.a.\n" + " kit ld -o app main.o libfoo.a\n" + " kit ld -r -o combined.o a.o b.o\n" + " kit ld -shared -soname libfoo.so.1 -o libfoo.so foo.pic.o\n" + " # Freestanding final link; link.ld must define the memory map.\n" + " kit ld --support-dir \"/path/to/kit/support\" -T link.ld \\\n" + " -e _start -o kernel.elf start.o\n" + "\n" "GETTING HELP\n" " -h, --help Show this help and exit\n" "\n" diff --git a/driver/cmd/mc.c b/driver/cmd/mc.c @@ -42,10 +42,27 @@ void driver_help_mc(void) { " emitted for undefined-symbol operands are listed beneath.\n" "\n" "OPTIONS\n" - " -target TRIPLE architecture to assemble for (host default)\n" + " -target TRIPLE architecture to assemble for. Audited spellings:\n" + " aarch64, x86_64, riscv64 (host default)\n" " -p print only the raw .text bytes as hex\n" " -h, --help show this help\n" "\n" + "INPUT AND OUTPUT\n" + " A quoted operand may contain one or more instructions. A positional\n" + " - reads instruction text from stdin. Normal output shows decoded\n" + " instructions and byte arrays; -p emits one continuous hex line.\n" + " References to undefined symbols retain relocation records and print\n" + " them below the instruction.\n" + "\n" + "EXAMPLES\n" + " kit mc -target x86_64 'movq %rax, %rbx'\n" + " kit mc -target aarch64 -p 'ret'\n" + " kit mc -target riscv64 -p 'ret'\n" + " kit mc -target x86_64 'call target_symbol'\n" + " printf 'nop\\n' | kit mc -target x86_64 -p -\n" + " bytes=$(kit mc -target riscv64 -p 'ret')\n" + " kit disas -target riscv64 -x \"$bytes\"\n" + "\n" "EXIT CODES\n" " 0 success 1 assemble error 2 bad usage\n"))); } diff --git a/driver/cmd/nm.c b/driver/cmd/nm.c @@ -247,11 +247,19 @@ void driver_help_nm(void) { driver_printf( "%.*s", KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit nm — list symbols from object files\n" + "kit nm — list symbols from objects, linked images, and archives\n" "\n" "USAGE\n" " kit nm [OPTIONS] FILE...\n" "\n" + "INPUT / DEFAULT OUTPUT\n" + " Reads ELF, Mach-O, COFF/PE, and Wasm objects or linked images; a\n" + " POSIX ar archive is opened member by member. The default static\n" + " symbol table is sorted by symbol name and printed as ADDRESS,\n" + " one-letter TYPE, and NAME. Use -A to identify multi-file or\n" + " archive-member output, -n for address order, or -D for the\n" + " dynamic symbol table of a linked image.\n" + "\n" "OPTIONS\n" " -a, --debug-syms include debug symbols\n" " -g, --extern-only show only external (global) symbols\n" @@ -263,7 +271,17 @@ void driver_help_nm(void) { " -r, --reverse-sort reverse the sort order\n" " --no-sort, -p do not sort; print in file order\n" " -A, --print-file-name prefix each line with the input file name\n" - " -h, --help show this help\n"))); + " -h, --help show this help\n" + "\n" + "EXAMPLES\n" + " kit nm add.o\n" + " kit nm -A -g -n app\n" + " kit nm --defined-only libfoo.a\n" + " kit nm -D libfoo.so\n" + "\n" + "EXIT CODES\n" + " 0 success 1 object/archive or I/O error\n" + " 2 bad usage\n"))); } int driver_nm(int argc, char** argv) { diff --git a/driver/cmd/objcopy.c b/driver/cmd/objcopy.c @@ -41,6 +41,11 @@ void driver_help_objcopy(void) { "USAGE\n" " kit objcopy [OPTIONS] INPUT [OUTPUT]\n" "\n" + "OUTPUT\n" + " With OUTPUT, write a separate file. If OUTPUT is omitted, INPUT\n" + " is rewritten in place; name an OUTPUT while evaluating a\n" + " transform so the original remains available for comparison.\n" + "\n" "SECTION OPS\n" " --remove-section=NAME drop section NAME (may repeat)\n" " --only-section=NAME keep only section NAME (may repeat)\n" @@ -66,8 +71,24 @@ void driver_help_objcopy(void) { " names: elf*, mach-o / macho*, coff*, " "wasm*, binary, ihex, srec\n" "\n" + " The binary, ihex, and srec outputs currently require a linked\n" + " image; they reject relocatable .o inputs. For a flat image from a\n" + " linked executable, `kit image --format bin` provides explicit\n" + " segment/section selection and layout validation.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + "\n" + "EXAMPLES\n" + " kit objcopy --strip-debug input.o output.o\n" + " kit objcopy --add-section=.kitnote=note.txt input.o noted.o\n" + " kit objdump -h -s -j .kitnote noted.o\n" + " kit objcopy --redefine-sym=old_name=new_name input.o renamed.o\n" + " kit nm renamed.o\n" + " kit image --format bin linked.elf -o linked.bin\n" + "\n" "EXIT CODES\n" - " 0 success 1 I/O or strip error 2 bad " + " 0 success 1 I/O or transform error 2 bad " "usage\n"))); } diff --git a/driver/cmd/objdump.c b/driver/cmd/objdump.c @@ -123,6 +123,13 @@ void driver_help_objdump(void) { " kit objdump -d a.o\n" " kit objdump -h -j .text -j .rodata a.o\n" " kit objdump -t libfoo.a\n" + " DIST=\"$PWD/kit\"; K=\"$DIST/bin/kit\"\n" + " SDK=$(\"$K\" cc -print-sysroot)\n" + " printf 'extern int value; int add(void) { return value + 1; }\\n' " + "> debug.c\n" + " \"$K\" cc --support-dir \"$DIST/support\" -isysroot \"$SDK\" " + "-g -c debug.c -o debug.o\n" + " \"$K\" objdump -t -r --dwarf=line debug.o\n" "\n" "EXIT CODES\n" " 0 success 1 parse / I/O error 2 bad " diff --git a/driver/cmd/pkg.c b/driver/cmd/pkg.c @@ -17,7 +17,7 @@ void driver_help_pkg(void) { driver_printf( - "kit pkg - signed code distribution\n" + "kit pkg — create, verify, inspect, and unpack signed packages\n" "\n" "USAGE\n" " kit pkg keygen -o BASE\n" @@ -33,7 +33,75 @@ void driver_help_pkg(void) { " [--external DIR] FILE -C DIR\n" " kit pkg inspect [--manifest | --encoding] FILE\n" " kit pkg sign -s SECKEY [-o OUT] [--comment C] FILE\n" - " kit pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n"); + " kit pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n" + "\n" + "KEYS AND TRUST\n" + " keygen -o BASE writes BASE.pub (public verification key) and BASE.key\n" + " (passwordless secret signing key) using the host CSPRNG. Keep .key\n" + " private. Verification selects trust in this order: -p PUBKEY, --tofu,\n" + " or the trusted-keys file. --tofu validates the bundled key, then pins\n" + " it. `pkg trust path` prints the active file; its preferred path is\n" + " $KIT_TRUSTED_KEYS, otherwise $KIT_HOME/config/trusted_keys. KIT_HOME\n" + " defaults to $XDG_DATA_HOME/kit (normally ~/.local/share/kit). A legacy\n" + " $HOME/.config/kit/trusted_keys is also read when the preferred file is\n" + " absent. `trust list`, `add`, and `remove` manage this state.\n" + "\n" + "CREATE\n" + " --name N, --version V required package identity\n" + " --desc D optional description\n" + " -s SECKEY required BASE.key signing key\n" + " --root DIR package a directory as the default tree\n" + " --cas DIR --tree ID package an existing CAS tree\n" + " -o OUT required output path\n" + " --format kpkg|tar.gz inferred from OUT suffix when omitted\n" + " --compression none|lz4-block-v1\n" + " native chunk compression (default: none)\n" + " --native-shape fat|metadata|thin\n" + " kpkg layout (default: fat). fat embeds all;\n" + " metadata embeds trees/index but externalizes\n" + " chunks; thin externalizes trees/index/chunks.\n" + " --external DIR CAS-shaped objects for non-fat create/verify/\n" + " unpack. Kit does not fetch missing objects.\n" + "\n" + "VERIFY, INSPECT, UNPACK\n" + " verify authenticates the manifest, package layout, trees, and blobs,\n" + " then prints package/signer identity. inspect prints a summary, the\n" + " canonical manifest (--manifest), or native encoding (--encoding); it\n" + " does not establish trust. unpack writes below -C DIR. Use --verify\n" + " with -p/--tofu/trusted keys to authenticate before materializing.\n" + " --format may force kpkg or tar.gz; otherwise input magic/suffix is used.\n" + "\n" + "DETACHED SIGNATURES\n" + " sign writes a stock-minisign-compatible detached signature to OUT, or\n" + " FILE.minisig when -o is omitted. This release has no advertised Kit\n" + " command for verifying detached signatures: `pkg verify` verifies\n" + " packages, not arbitrary FILE/FILE.minisig pairs.\n" + "\n" + "PATHS\n" + " Bare -- is not accepted; spell a leading-dash file as ./-name.\n" + "\n" + "EXAMPLES\n" + " mkdir -p package-root && printf 'hello\\n' > package-root/hello.txt\n" + " kit pkg keygen -o release-key\n" + " kit pkg create --name demo --version 1 -s release-key.key \\\n" + " --root package-root -o demo.kpkg\n" + " kit pkg verify -p release-key.pub demo.kpkg\n" + " kit pkg inspect --manifest demo.kpkg\n" + " kit pkg unpack --verify -p release-key.pub demo.kpkg -C unpacked\n" + " kit pkg trust add release-key.pub demo-release\n" + " kit pkg verify demo.kpkg\n" + " kit pkg sign -s release-key.key release.tar.gz\n" + " cp demo.kpkg corrupt.kpkg\n" + " dd if=/dev/zero of=corrupt.kpkg bs=1 seek=100 count=1 conv=notrunc\n" + " kit pkg verify -p release-key.pub corrupt.kpkg # exits 1\n" + "\n" + "OUTPUT\n" + " Results and successful summaries go to stdout; diagnostics go to\n" + " stderr.\n" + "\n" + "EXIT CODES\n" + " 0 success 1 I/O/format/integrity/trust failure\n" + " 2 bad command-line usage\n"); } /* ---------------------------------------------------------------------- */ diff --git a/driver/cmd/ranlib.c b/driver/cmd/ranlib.c @@ -35,13 +35,14 @@ void driver_help_ranlib(void) { " contents, and order are preserved. Reproducible: when\n" " SOURCE_DATE_EPOCH is set to a positive integer, that value is\n" " written to ar_date for every member.\n" - "\n" - " Equivalent to `kit ar s ARCHIVE.a` (the bare `s` modifier is\n" - " reserved by the POSIX ar grammar but not yet implemented).\n" - "\n" "OPTIONS\n" " -h, --help Show this help and exit\n" "\n" + "EXAMPLES\n" + " kit ar rc libfoo.a a.o b.o\n" + " kit ranlib libfoo.a\n" + " kit nm -g libfoo.a\n" + "\n" "EXIT CODES\n" " 0 success 1 archive I/O error 2 bad " "usage\n"))); diff --git a/driver/cmd/run.c b/driver/cmd/run.c @@ -188,7 +188,11 @@ void driver_help_run(void) { " --time, --metrics Emit scoped compile/link/JIT ticks to stderr\n" " --bench-time Emit parseable compile/JIT/execution timings\n" " -e SYMBOL Entry symbol (default `main`)\n" - " -target TRIPLE Cross-compile target (see `kit cc --help`)\n" + " -target TRIPLE Compile target. Canonical host spellings include\n" + " aarch64-apple-darwin, x86_64-apple-darwin,\n" + " aarch64-linux-gnu, and x86_64-linux-gnu. Native\n" + " in-process execution requires a host-compatible\n" + " architecture/ABI; Wasm uses the sandbox below.\n" " --sysroot DIR Hosted libc sysroot for headers/defines with " "-lc\n" " -lc Enable hosted libc headers/defines; calls " @@ -226,7 +230,8 @@ void driver_help_run(void) { " --wasm-imports=deny|wasi|test\n" " Import resolver policy (default deny). `test` " "binds\n" - " only the in-tree env.host_add test shim.\n" + " only the controlled env.host_add compatibility\n" + " import; it is not a general host-symbol policy.\n" " `wasi` binds the configured partial WASI " "Preview1 " "shim.\n" @@ -272,12 +277,23 @@ void driver_help_run(void) { " Add compile flags before --script, e.g.\n" " #!/usr/bin/env -S kit run -g --script\n" "\n" + "SOURCE/SDK REQUIREMENTS\n" + " Simple JIT source inputs do not need --support-dir. Hosted libc\n" + " headers require -lc; on macOS also pass the SDK printed by Kit:\n" + " SDK=\"$(kit cc -print-sysroot)\"\n" + " kit run -lc --sysroot \"$SDK\" hello.c\n" + " Cross sysroots affect preprocessing, but the resulting native code\n" + " still must match the current process to execute in-process.\n" + "\n" "GETTING HELP\n" " -h, --help Show this help and exit\n" + " --version Show Kit version and exit\n" "\n" "EXAMPLES\n" " kit run hello.c\n" " kit run -O2 -DNDEBUG main.c util.c\n" + " SDK=\"$(kit cc -print-sysroot)\"\n" + " kit run -lc --sysroot \"$SDK\" hosted.c\n" " kit run main.c -- arg1 arg2\n" " kit run --script script.c arg1 arg2 (as a #! interpreter)\n" "\n" diff --git a/driver/cmd/size.c b/driver/cmd/size.c @@ -166,11 +166,17 @@ void driver_help_size(void) { driver_printf( "%.*s", KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit size — display section sizes of object files\n" + "kit size — display section sizes of objects and linked images\n" "\n" "USAGE\n" " kit size [OPTIONS] FILE...\n" "\n" + "INPUT / DEFAULT OUTPUT\n" + " Reads ELF, Mach-O, COFF/PE, and Wasm objects or linked images;\n" + " POSIX ar archives are reported member by member. The default is\n" + " Berkeley format in decimal: text, data, bss, total decimal/hex,\n" + " and filename. SysV format prints each section and its address.\n" + "\n" "OPTIONS\n" " -A, --format sysv SysV output (section-by-section)\n" " -B, --format berkeley Berkeley output (default)\n" @@ -179,7 +185,17 @@ void driver_help_size(void) { " -x sizes in hexadecimal\n" " --common include COMMON symbols in bss total\n" " -t, --totals print a totals line (Berkeley only)\n" - " -h, --help show this help\n"))); + " -h, --help show this help\n" + "\n" + "EXAMPLES\n" + " kit size app\n" + " kit size -A -x app\n" + " kit size -B -t a.o b.o\n" + " kit size libfoo.a\n" + "\n" + "EXIT CODES\n" + " 0 success 1 object/archive or I/O error\n" + " 2 bad usage\n"))); } int driver_size(int argc, char** argv) { diff --git a/driver/cmd/strings.c b/driver/cmd/strings.c @@ -101,6 +101,15 @@ void driver_help_strings(void) { " print the input file name before each string\n" " -h, --help show this help\n" "\n" + " Prefix a leading-dash file name with `./` (for example\n" + " `./-firmware.bin`); `--` is not accepted in this release.\n" + "\n" + "EXAMPLES\n" + " kit strings -n 8 firmware.bin\n" + " kit strings -t x -f app.o libfoo.a\n" + " kit strings -n 6 < firmware.bin\n" + " kit strings ./-firmware.bin\n" + "\n" "EXIT CODES\n" " 0 success 1 I/O error 2 bad usage\n"))); } diff --git a/driver/cmd/strip.c b/driver/cmd/strip.c @@ -52,9 +52,20 @@ void driver_help_strip(void) { " -o PATH write to PATH (default: rewrite FILE in " "place)\n" "\n" - "INPUTS\n" - " FILE may be a relocatable .o or a static .a archive. Linked\n" - " executables / shared libraries are not supported yet.\n" + "SUPPORTED INPUTS — RELOCATABLE .o OR STATIC .a ONLY\n" + " Do not pass an executable or shared library in this release. The\n" + " open linked-image case can return 0 while silently producing a\n" + " small, non-executable, unusable output. Use -o for safe inputs so\n" + " the original object/archive remains available for comparison.\n" + "\n" + "GETTING HELP\n" + " -h, --help Show this help and exit\n" + "\n" + "EXAMPLES\n" + " kit strip --strip-debug -o foo.stripped.o foo.o\n" + " kit objdump -h foo.stripped.o\n" + " kit strip --strip-unneeded -o libfoo.stripped.a libfoo.a\n" + " kit nm -g libfoo.stripped.a\n" "\n" "EXIT CODES\n" " 0 success 1 I/O or strip error 2 bad " diff --git a/driver/cmd/symbolize.c b/driver/cmd/symbolize.c @@ -100,18 +100,34 @@ void driver_help_symbolize(void) { " --basenames strip directory from file paths\n" " -h, --help show this help\n" "\n" - "Reads the raw \"#N 0x<hex>\" lines that __kit_print_backtrace " - "writes\n" - "on standard input, resolves the address on each line via the " - "image's\n" - "debug info, and rewrites the line as\n" + "INPUT / OUTPUT\n" + " Reads raw `#N 0x<hex>` backtrace lines as text on stdin, resolves\n" + " each address via FILE's debug info, and writes annotated text to\n" + " stdout in the form\n" " #0 0x401136 bt_leaf at file.c:51:3\n" - "keeping the original \"#N\" framing. Lines with no 0x<hex> address " + " keeping the original \"#N\" framing. Lines with no 0x<hex> " + "address " "pass\n" - "through unchanged. For a static non-PIE image the runtime " - "addresses\n" - "equal the link-time addresses, so no load-bias adjustment is " - "needed.\n"))); + " through unchanged. FILE is required for normal operation; with " + "no\n" + "-e this release prints help and returns 0.\n" + "\n" + "ADDRESS RESTRICTION\n" + " Input addresses must be link-time addresses. Static/non-PIE\n" + " runtime addresses already match. For PIE/ASLR, subtract the load\n" + " bias before producing the input; no load-bias option is exposed.\n" + "\n" + "EXAMPLES\n" + " # debug-app was linked with -g -no-pie.\n" + " kit nm -n debug-app\n" + " ADDR=$(kit nm -n debug-app | awk '$3 == \"main\" || " + "$3 == \"_main\" { print $1; exit }')\n" + " printf '#0 0x%s\\nplain line\\n' \"$ADDR\" | \\\n" + " kit symbolize --basenames -e debug-app\n" + "\n" + "EXIT CODES\n" + " 0 success (also no -e help exception)\n" + " 1 debug/object or I/O error 2 bad usage\n"))); } int driver_symbolize(int argc, char** argv) { diff --git a/driver/cmd/update.c b/driver/cmd/update.c @@ -544,8 +544,9 @@ void driver_help_update(void) { " Installs a kit release into the single-root layout under $KIT_HOME\n" " (default $XDG_DATA_HOME/kit, i.e. ~/.local/share/kit), verifying its\n" " signature against the built-in release key set (or --key) before any\n" - " change. A local <file.kpkg> installs fully offline; without one, the\n" - " tracked channel index is resolved over the network (curl/wget).\n" + " change. A local <file.kpkg> installs fully offline. Network operations\n" + " use curl and require --index URL or KIT_UPDATE_INDEX_URL; this release\n" + " has no built-in channel-index URL.\n" "\n" " Put $KIT_HOME/bin on PATH once; `current` is flipped atomically and\n" " the bin links refreshed on each install. All versions are retained,\n" @@ -565,8 +566,36 @@ void driver_help_update(void) { " --dry-run print the plan; change nothing\n" " -h, --help show this help\n" "\n" + "STATE AND ENVIRONMENT\n" + " KIT_HOME installation root\n" + " XDG_DATA_HOME parent of default KIT_HOME\n" + " KIT_UPDATE_INDEX_URL channel index when --index is absent\n" + " KIT_HOME contains versions/, current, bin/, config/, and downloaded\n" + " update state. --index overrides KIT_UPDATE_INDEX_URL. Put only\n" + " $KIT_HOME/bin on PATH after a successful install. Updates retain old\n" + " versions until --prune; --rollback and --version VER switch locally\n" + " when possible. Here --version requires VER; use `kit --version` for\n" + " the running binary's identity.\n" + "\n" + "EXAMPLES\n" + " state=$(mktemp -d)\n" + " mkdir -p \"$state/home\" \"$state/data\" \"$state/cache\"\n" + " HOME=\"$state/home\" XDG_DATA_HOME=\"$state/data\" \\\n" + " XDG_CACHE_HOME=\"$state/cache\" KIT_HOME=\"$state/kit\" \\\n" + " kit update --key release.pub --from release.kpkg --dry-run\n" + " HOME=\"$state/home\" XDG_DATA_HOME=\"$state/data\" \\\n" + " KIT_HOME=\"$state/kit\" kit update --key release.pub release.kpkg\n" + " KIT_UPDATE_INDEX_URL=https://example.invalid/stable.index \\\n" + " kit update --check\n" + "\n" + "OUTPUT\n" + " Plans, version lists, and successful actions go to stdout; diagnostics\n" + " go to stderr. --dry-run and --check do not modify the installation.\n" + "\n" "EXIT CODES\n" - " 0 success 1 verify/install/IO error 2 bad usage\n"); + " 0 success 1 verify/install/I/O error 2 bad usage\n" + " Known limitation: `--check` with no configured index diagnoses the\n" + " missing index on stderr but currently returns 0.\n"); } int driver_update(int argc, char** argv) { diff --git a/driver/cmd/xxd.c b/driver/cmd/xxd.c @@ -117,6 +117,15 @@ void driver_help_xxd(void) { "NOTE\n" " -r reconstructs contiguous data; leading offsets are read for\n" " context but not used to seek/pad sparse output.\n" + " Bare -- is not accepted in this release. Prefix a leading-dash\n" + " input with ./, for example ./-payload.bin.\n" + "\n" + "EXAMPLES\n" + " kit xxd firmware.bin\n" + " kit xxd -p -u firmware.bin > firmware.hex\n" + " kit xxd -i -o firmware.inc firmware.bin\n" + " kit xxd firmware.bin | kit xxd -r -o firmware.copy\n" + " printf '4142430a\\n' | kit xxd -r -p > abc.txt\n" "\n" "EXIT CODES\n" " 0 success 1 I/O error 2 bad usage\n"))); diff --git a/driver/driver.h b/driver/driver.h @@ -101,7 +101,14 @@ void driver_help_pkg(void); void driver_help_xxd(void); void driver_help_cmp(void); void driver_help_hash(void); +void driver_help_sha256sum(void); +void driver_help_b2sum(void); +void driver_help_crc32(void); void driver_help_compress(void); +void driver_help_gzip(void); +void driver_help_gunzip(void); +void driver_help_lz4(void); +void driver_help_lz4c(void); void driver_help_disas(void); void driver_help_mc(void); void driver_help_gram(void); diff --git a/driver/main.c b/driver/main.c @@ -166,23 +166,23 @@ static const DriverToolDesc driver_tools[] = { #if KIT_TOOL_HASH_ENABLED {"hash", driver_hash, NULL, driver_help_hash, "Hash files with SHA-256, BLAKE2b, or CRC-32", DRIVER_GROUP_OTHER}, - {"sha256sum", driver_sha256sum, NULL, driver_help_hash, + {"sha256sum", driver_sha256sum, NULL, driver_help_sha256sum, "SHA-256 of files or stdin (sha256sum-compatible)", DRIVER_GROUP_BYTEUTIL}, - {"b2sum", driver_b2sum, NULL, driver_help_hash, + {"b2sum", driver_b2sum, NULL, driver_help_b2sum, "BLAKE2b-256 of files or stdin (b2sum-style)", DRIVER_GROUP_BYTEUTIL}, - {"crc32", driver_crc32, NULL, driver_help_hash, "CRC-32 of files or stdin", - DRIVER_GROUP_BYTEUTIL}, + {"crc32", driver_crc32, NULL, driver_help_crc32, + "CRC-32 of files or stdin", DRIVER_GROUP_BYTEUTIL}, #endif #if KIT_TOOL_COMPRESS_ENABLED {"compress", driver_compress, NULL, driver_help_compress, "Compress or decompress data (gzip, lz4 frame)", DRIVER_GROUP_OTHER}, - {"gzip", driver_gzip, NULL, driver_help_compress, + {"gzip", driver_gzip, NULL, driver_help_gzip, "Compress to gzip (.gz); -d to decompress", DRIVER_GROUP_BYTEUTIL}, - {"gunzip", driver_gunzip, NULL, driver_help_compress, + {"gunzip", driver_gunzip, NULL, driver_help_gunzip, "Decompress gzip (.gz) streams", DRIVER_GROUP_BYTEUTIL}, - {"lz4", driver_lz4, NULL, driver_help_compress, + {"lz4", driver_lz4, NULL, driver_help_lz4, "Compress to LZ4 frame (.lz4); -d to decompress", DRIVER_GROUP_BYTEUTIL}, - {"lz4c", driver_lz4c, NULL, driver_help_compress, + {"lz4c", driver_lz4c, NULL, driver_help_lz4c, "Compress to LZ4 frame (.lz4) (lz4 alias)", DRIVER_GROUP_BYTEUTIL}, #endif #if KIT_TOOL_DISAS_ENABLED @@ -315,13 +315,14 @@ void driver_print_version(const char* tool) { void driver_help_top(void) { unsigned i; driver_printf( - "kit — freestanding C compiler toolchain\n" + "kit — standalone compilation, linking, execution, and binary toolkit\n" "\n" "USAGE\n" " kit <tool> [args...] multi-call dispatch by name\n" " <tool> [args...] when invoked via the tool's " "symlink\n" " kit help [<tool>] this help, or per-tool help\n" + " kit --version release/build/host identity\n" "\n" "TOOLS\n"); for (i = 0; i < driver_tool_count(); ++i) { @@ -333,14 +334,26 @@ void driver_help_top(void) { "GETTING HELP\n" " kit <tool> --help full per-tool help (also -h, except " "objdump)\n" - " kit <tool> same as --help (no args ⇒ show " - "help)\n" " kit help <tool> same as `kit <tool> --help`\n" + " kit <tool> --version tool identity (except update, where\n" + " --version VER selects a release)\n" + "\n" + "NO-ARGUMENT BEHAVIOR\n" + " Compiler-like tools usually show help. Stream filters may read stdin,\n" + " dbg enters its REPL, and operand-required tools report bad usage.\n" + " Each tool's page documents its stdin and output conventions.\n" + "\n" + "OUTPUT AND ENVIRONMENT\n" + " Requested help and normal data go to stdout; diagnostics go to stderr\n" + " unless a tool page says otherwise. Per-tool pages list relevant\n" + " environment variables, defaults, and state directories.\n" "\n" "EXIT CODES\n" " 0 success\n" " 1 tool-reported error (compile, link, I/O, ...)\n" - " 2 bad command-line usage\n"); + " 2 bad command-line usage\n" + " Execution tools may pass through another program exit status when\n" + " their own page says so.\n"); } int kit_driver_main_ex(int argc, char** argv, const KitDriverExtension* ext) { diff --git a/include/kit/build_coord.h b/include/kit/build_coord.h @@ -11,7 +11,12 @@ * Content-addressed build coordinator. A long-lived object that turns a build * request (target T under configuration C) into a materialized output tree on * disk, doing the least work necessary by caching every recipe result keyed by - * the exact set of inputs that produced it. See doc/BUILD_COORDINATOR.md. + * the exact set of inputs that produced it. Targets are package-relative + * labels resolved from a build definition; recipes run in a clean environment, + * request declared inputs through the transport below, and write only below + * KIT_BUILD_OUT. The resulting tree id is both the cache value and the portable + * output identity. CLI users can find the build-file grammar, recipe protocol, + * and a complete cached-build workflow in `kit build --help`. * * Layering. The coordinator is built entirely on the public kit surface: the * content store (<kit/cas.h>) holds source bytes, output trees, config maps, diff --git a/include/kit/compile.h b/include/kit/compile.h @@ -12,6 +12,83 @@ * builder or directly emits encoded object bytes. It is the right API for a * cc/as-like driver. Language frontends that want to emit code directly * should use kit/cg.h instead. + * + * Context and ownership. The embedder owns the KitHeap, KitDiagSink, optional + * KitFileIO, and KitContext passed here and keeps them alive until every Kit + * handle has been freed. Construct a KitTarget from <kit/target.h>, then a + * compiler, then short-lived compile/object/link sessions. Writers are also + * caller-owned and must remain open through the operation that uses them. + * Diagnostics and error counts flow through the supplied KitDiagSink. + * + * A complete source-session-to-freestanding-image lifecycle is shown below. + * It deliberately stages the frontend transaction: the new declarations are + * committed only after the linked image has been emitted. On any failure after + * staging, abort restores the frontend state. Include <kit/link.h> and + * <kit/target.h> in addition to this header. + * + * static KitStatus build_answer(const KitContext* ctx, KitWriter* image) { + * KitTargetSpec spec; + * KitTargetOptions target_opts = {0}; + * KitCompileSessionOptions compile_opts = {0}; + * KitSourceInput input = { + * .name = KIT_SLICE_LIT("answer.c"), + * .bytes = KIT_SLICE_LIT("int answer(void) { return 42; }\n"), + * .lang = KIT_LANG_C, + * .input_kind = KIT_FRONTEND_INPUT_TRANSLATION_UNIT, + * }; + * KitLinkSessionOptions link_opts = {0}; + * KitTarget* target = NULL; + * KitCompiler* compiler = NULL; + * KitCompileSession* session = NULL; + * KitObjBuilder* object = NULL; + * KitLinkSession* link = NULL; + * KitStatus status = KIT_INVALID; + * bool staged = false; + * + * if (!kit_target_from_triple("aarch64-none-elf", &spec)) goto done; + * target_opts.spec = spec; + * status = kit_target_new(ctx, &target_opts, &target); + * if (status != KIT_OK) goto done; + * status = kit_compiler_new(target, ctx, &compiler); + * if (status != KIT_OK) goto done; + * + * compile_opts.lang = KIT_LANG_C; + * compile_opts.compile.input_kind = KIT_FRONTEND_INPUT_TRANSLATION_UNIT; + * status = kit_compile_session_new(compiler, &compile_opts, &session); + * if (status != KIT_OK) goto done; + * status = kit_compile_session_stage(session, &input, &object); + * if (status != KIT_OK) goto done; + * staged = true; + * + * link_opts.output_kind = KIT_LINK_OUTPUT_EXE; + * link_opts.entry = KIT_SLICE_LIT("answer"); + * link_opts.freestanding_strict = true; + * status = kit_link_session_new(compiler, &link_opts, &link); + * if (status != KIT_OK) goto done; + * status = kit_link_session_add_obj(link, object); + * if (status != KIT_OK) goto done; + * status = kit_link_session_resolve(link); + * if (status != KIT_OK) goto done; + * status = kit_link_session_emit(link, image); + * if (status != KIT_OK) goto done; + * + * kit_compile_session_commit(session); + * staged = false; + * done: + * if (staged) kit_compile_session_abort(session); + * if (link) kit_link_session_free(link); + * if (object) kit_obj_builder_free(object); + * if (session) kit_compile_session_free(session); + * if (compiler) kit_compiler_free(compiler); + * if (target) kit_target_free(target); + * return status; + * } + * + * For batch compilation, kit_compile_session_compile auto-commits on success. + * For direct language emission, replace the compile session with a KitCg and + * finish/detach it into a KitObjBuilder as documented in <kit/cg.h>. Emit a + * relocatable builder with kit_obj_builder_emit, or publish it through the + * KitLinkSession flow above. The caller closes/frees its KitWriter afterward. */ typedef uint32_t KitLanguage; diff --git a/include/kit/config.h b/include/kit/config.h @@ -96,7 +96,8 @@ * tables out, with allocation-free push runtimes for generated parsers and * lexers. Runtime + generator are gated together; the public API is * <kit/gram.h> + <kit/gram_parse.h> + <kit/gram_lex.h> + <kit/gram_pos.h> + - * <kit/gram_unicode.h>. See doc/plan/GRAM_IMPORT.md. */ + * <kit/gram_unicode.h>. Generated tables are immutable; callers provide the + * parser/lexer instances, stacks, semantic values, and callbacks. */ #define KIT_GRAM_ENABLED 1 /* Threaded-bytecode interpreter for the optimizer IR. Runs kit IR diff --git a/include/kit/core.h b/include/kit/core.h @@ -284,7 +284,8 @@ typedef struct KitCodeOptions { * The TU is still target-locked: the emitted source uses the configured * triple's struct layouts and pointer width. Forces opt_level=0. * Output is written to c_source_writer (set on the emit-side compile - * entry point); object emission is bypassed. See doc/CBACKEND.md. */ + * entry point); object emission is bypassed. The caller owns the writer and + * must keep it open through compilation. */ bool emit_c_source; /* When set, serialize the compiled object as assembly text (.s) instead * of binary object bytes (.o). Does not alter CG behavior; the normal diff --git a/include/kit/gram_parse.h b/include/kit/gram_parse.h @@ -72,8 +72,12 @@ typedef enum { /* The vtable the embedder binds. Every field may be NULL. `prod` is the * 0-based index of the matched alternative within a rule (LL(1) picks it at - * expansion time, so it is known in both enter and reduce). For the exact - * per-production layout of `kids`, see doc/DESIGN.md "Action contract". */ + * expansion time, so it is known in both enter and reduce). `kids[0..n)` holds + * the semantic values contributed by that production's value-producing RHS + * factors in left-to-right grammar order. Named rules and lifted tokens each + * contribute one value; an `X*` factor contributes one finished-list value and + * an `X?` factor contributes one optional value. Punctuation/skipped factors + * contribute no value. The array is borrowed for the duration of `reduce`. */ typedef struct { /* ---- listener channel (structural; void) ---- */ void (*enter)(void* ud, KitGramRuleId r, int prod); diff --git a/include/kit/package.h b/include/kit/package.h @@ -9,8 +9,9 @@ /* * Signed, content-addressed distribution packages. A package is a minisign * signature over a canonical manifest that claims one or more CAS trees, - * carried as either a portable .tar.gz or a native .kpkg. See - * doc/DISTRIBUTE.md. + * carried as either a portable .tar.gz or a native .kpkg. A fat .kpkg carries + * every referenced blob; metadata/thin shapes may resolve blob content from an + * external CAS. <kit/cas.h> defines those tree/blob identities and host I/O. * * Trust *policy* stays with the caller: where the trusted-keys file lives and * whether to pin a key are decided outside this layer. The verify entry point @@ -157,7 +158,11 @@ KIT_API KitStatus kit_pkg_inspect(const KitContext* ctx, * idiom as the package manifest. The library emits/parses the text and the * CalVer comparison; trust, channel selection, host-triple matching, version * policy, and signing/verification (via kit_pkg_* / minisign) stay with the - * caller. See doc/plan/SELFDIST.md. + * caller. The caller must select and fetch an index, authenticate its detached + * signature, choose the matching KitReleaseHost, fetch an advertised package, + * verify its package id/signature/content, and atomically publish the selected + * version. `kit update --help` documents the corresponding CLI state and index + * selection. * * KitReleaseIndex is large (fixed-capacity host/url tables); allocate it on the * heap, not the stack. Capacities are loud limits — emit/parse fail rather than diff --git a/include/kit/support/gram_lex_tables.h b/include/kit/support/gram_lex_tables.h @@ -27,7 +27,9 @@ typedef enum { * multiline-insensitive); LINE is `^`/`$` (text-relative when multiline is off, * newline-aware when on). The runtime no longer sees these: edge anchors are * baked into the DFA as alternate start states and per-state end-context accept - * tables (see KitGramLexGrammar below). See doc/DESIGN.md "Edge anchors". */ + * tables (see KitGramLexGrammar below). The start state selects text-start, + * line-start, or ordinary context; end-context tables distinguish end of text, + * end of line, and ordinary input without consuming a byte. */ typedef enum { KIT_GRAM_LEX_ANCHOR_NONE = 0, KIT_GRAM_LEX_ANCHOR_TEXT = 1, /* \A (start) or \z (end) */ @@ -43,7 +45,8 @@ typedef struct { * by a more general "host" recognizer (e.g. a keyword shadowed by IDENT) is * pulled out of the DFA and recorded here instead; the host recognizer yields * the same byte span, and the runtime rewrites the token kind by looking up the - * whole lexeme. See doc/DESIGN.md "Keyword extraction". */ + * whole lexeme. Only an exact length-and-byte match is rewritten; otherwise the + * host token kind is preserved. */ typedef struct { const char* lexeme; /* keyword bytes (length-counted; not NUL-reliant) */ uint16_t len; @@ -54,7 +57,9 @@ typedef struct { * token of the host kind hashes its lexeme into `seeds` to pick a displacement, * then hashes again into the `keywords` slot array, and one byte compare * confirms. The generator builds `seeds`/`keywords` so every keyword lands at a - * distinct slot. See doc/DESIGN.md "Keyword extraction". */ + * distinct slot. Empty slots have a NULL lexeme; the final length-and-byte + * comparison makes non-keyword host lexemes safe even when they hash to an + * occupied slot. */ typedef struct { KitGramTokenKind host; /* host recognizer kind whose lexemes map here */ const uint32_t* seeds; /* nseeds CHD bucket displacements (power of two) */ diff --git a/include/kit/wasm.h b/include/kit/wasm.h @@ -22,8 +22,9 @@ * 3. The runner that loads the module calls `kit_wasm_bind_host_imports` * after JIT-linking and before invoking `__kit_wasm_init`. * - * `kit run` and the in-tree JIT test harness already perform the bind - * step; embedders that drive the linker directly must do so themselves. + * `kit run` performs the bind step. Embedders that drive the linker directly + * must call `kit_wasm_bind_host_imports` themselves after JIT linking and + * before invoking `__kit_wasm_init`. */ #include <kit/core.h> diff --git a/rt/include/kit/backtrace.h b/rt/include/kit/backtrace.h @@ -13,8 +13,10 @@ * void*, so it scales to the target pointer width automatically). * * Symbolization (turning a captured address into func at file:line) is a - * separate, hosted-side concern — feed the addresses to `kit addr2line`. - * See doc/plan/BACKTRACE.md. + * separate, hosted-side concern. Feed individual link-time addresses to + * `kit addr2line -e IMAGE`, or pipe the numbered text produced by + * __kit_print_backtrace through `kit symbolize -e IMAGE`. For PIE/ASLR images, + * subtract the load bias before symbolization. */ #ifndef KIT_BACKTRACE_H #define KIT_BACKTRACE_H @@ -48,7 +50,8 @@ int __kit_backtrace(void** buf, int max, int skip); * unsymbolized return address; pipe it to `kit addr2line -e <image>` to recover * `func at file:line`. Like __kit_backtrace this is a freestanding * frame-pointer walk — no libc, no DWARF, no printf — so it is safe to call - * from a crash or panic handler. See doc/plan/BACKTRACE.md (L3a). */ + * from a crash or panic handler. The weak __kit_backtrace_write sink below is + * the only output dependency. */ void __kit_print_backtrace(void); /* Output sink for __kit_print_backtrace (and the freestanding assert handler). diff --git a/rt/include/kit/baremetal.h b/rt/include/kit/baremetal.h @@ -13,9 +13,10 @@ * is what makes the IRQ save/restore idiom and the DMA-coherent * barrier idioms correct without per-call inline-asm clobbers. * - * Per-target lowering: see doc/builtins.md. Targets where a primitive - * has no meaningful lowering (e.g. WFI on x86, DMB on WASM) raise a - * compile-time error rather than silently no-op. + * The selected architecture backend lowers each primitive to its native + * instruction sequence. Targets where a primitive has no meaningful lowering + * (e.g. WFI on x86, DMB on WASM) raise a compile-time error rather than + * silently no-op. * * What is *not* in this header. System-register access (mrs/msr, * csrr/csrw, rdmsr/wrmsr, MMU/cache config writes, ...) stays in diff --git a/rt/include/kit/coro.h b/rt/include/kit/coro.h @@ -3,8 +3,9 @@ * kit/coro.h is non-standard: C11 has no stackful-coroutine facility. * kit ships it as a native counterpart to <setjmp.h>: the underlying * per-target context payload is literally shared with setjmp/longjmp - * (256 bytes, see doc/builtins.md), and the runtime is target-specific - * assembly in libkit_rt.a. + * (256 bytes, large enough for the supported targets' callee-saved registers, + * stack pointer, and return state), and the runtime is target-specific assembly + * in libkit_rt.a. * * Two layers in this header: * diff --git a/rt/include/setjmp.h b/rt/include/setjmp.h @@ -2,7 +2,8 @@ * * setjmp.h is *not* part of the C11 freestanding subset (C11 4p6); * kit provides it as an extension. The setjmp/longjmp pair is - * target-specific assembly in libkit_rt.a -- see doc/builtins.md. + * target-specific assembly in libkit_rt.a. A target without that assembly + * support fails at link time rather than substituting a hosted implementation. * * jmp_buf is an array type (C11 7.13p2). The runtime reinterprets the * buffer as a per-target struct of callee-saved GPRs + callee-saved diff --git a/rt/lib/README.md b/rt/lib/README.md @@ -183,8 +183,9 @@ variant and linked alongside the per-arch master. ### `atomic/atomic_freestanding.c` Defines a pointer-sized `_Atomic(uintptr_t)` spinlock as the lock primitive (no OS dependency) then `#include`s `atomic_common.inc`, which contains the -dispatch logic and all `__atomic_*_N` expansions. The shim calls the GCC -`__atomic_*` builtin family (the one kit documents in `doc/builtins.md`); +dispatch logic and all `__atomic_*_N` expansions. The shim calls Kit's +GCC-compatible `__atomic_*` builtin family; the operation, size, memory-order, +and compare-exchange arguments follow the conventional GCC ABI. upstream's Clang-only `__c11_atomic_*` calls were translated. Public symbols are exported via `#pragma redefine_extname` from `_c`-suffixed names so they don't collide with the clang builtins of the same name. @@ -229,7 +230,7 @@ freestanding `__compilerrt_abort_impl` that calls `__builtin_trap()`. ## Things this lib does NOT cover -These are documented in `doc/builtins.md` but not provided here: +This runtime intentionally does not provide: - `__riscv_save_*` for `__riscv_32e` / `__riscv_64e` (rare embedded ABIs). - Big-endian targets. diff --git a/test/audit/release/README.md b/test/audit/release/README.md @@ -0,0 +1,167 @@ +# Kit black-box release acceptance + +This harness audits a shipped Kit distribution or one exact Kit binary. It has +no implicit compiler path and never falls back to `build/kit`, `PATH`, or a +freshly built binary. + +Run the full available suite: + +```sh +test/audit/release/run.sh --dist ./build/dist/kit +``` + +Audit one exact binary: + +```sh +test/audit/release/run.sh --kit ./build/dist/kit/bin/kit +``` + +The input must be selected explicitly with exactly one of `--dist` or `--kit`. +Distribution-only cases become `BLOCKED` or `NOT_APPLICABLE` when only a binary +is supplied; they never search for neighboring repository artifacts. + +## Scoped runs + +Every case declares module, command, workflow, target, architecture, format, +and severity metadata. Filters accept comma-separated shell globs: + +```sh +test/audit/release/run.sh --dist ./build/dist/kit \ + --module help --command 'cc,build-*' --severity 'p1,p2' + +test/audit/release/run.sh --dist ./build/dist/kit \ + --workflow compile --target 'aarch64-*' --arch aarch64 --format elf +``` + +Available options are printed by `run.sh --help`. A filter selects matching +cases; generic `all`/`any` metadata remains applicable to scoped target, +architecture, and format runs. Multi-command journeys declare each command as +a comma-separated metadata value, so `--command ar` selects a workflow tagged +`cc,ar,ranlib,nm`. + +## Isolation and evidence + +The default artifact root is ignored build output: +`build/audit/release-acceptance/`. Each run gets a unique UTC timestamp/PID +directory. `--out DIR` changes the artifact root and `--run-id NAME` assigns a +stable name. An existing run directory is never deleted or overwritten. + +Each selected case owns isolated directories for: + +- working files; +- `HOME`, `XDG_CACHE_HOME`, `XDG_CONFIG_HOME`, and `XDG_DATA_HOME`; +- temporary files; +- installation output; and +- retained artifacts. + +The case directory records `command`, `stdout`, `stderr`, `exit`, `metadata`, +`assertions.log`, `state`, and `reason`. Commands are executed once; assertions +inspect those saved streams. Modules can retain produced files under the case's +`artifacts/` directory. A journey with multiple commands additionally records +each exact invocation and its streams/status below `steps/STEP/`; its top-level +`command` and `exit` direct readers to that directory. The run-level +`summary.tsv` is the complete machine-readable result, while `summary.txt` is +concise. + +Result states are: + +- `PASS`: all assertions succeeded; +- `FAIL`: the product ran but violated an acceptance assertion; +- `BLOCKED`: a required SDK, runner, distribution component, or other + prerequisite was unavailable, with the reason saved; +- `NOT_APPLICABLE`: the case does not apply to the selected product/target. + +The harness exits 1 if any case is `FAIL` or `BLOCKED`, 0 otherwise, and 2 for +harness usage/setup errors (including filters that match no cases). This +prevents missing advertised target prerequisites and misspelled filters from +becoming silent skips. + +## Help and inventory module + +`modules/help.sh` inventories every advertised command and alias through: + +- no arguments; +- `-h`; +- `--help`; and +- `kit help <command>`. + +It validates stream/status conventions, equivalent help routes, examples, exit +documentation, the absence of source-code directions, and the required +`kit targets` surface. The no-argument `update` probe is run only from an +isolated copy of a supplied distribution; with `--kit` it is explicitly +`BLOCKED` rather than risking an update through an unknown layout. + +Run only this module: + +```sh +test/audit/release/run.sh --dist ./build/dist/kit --module help +``` + +The baseline release is expected to report known Phase 1 findings as failures; +the retained evidence is intended both for the audit report and for verifying +later remediations. + +## Core workflow module + +`modules/core.sh` exercises four compact compiler/linker journeys: + +- an ordinary compile/link/run from an isolated directory, without hidden + support or SDK paths; +- explicit SDK compilation, optimized/debug object emission, archive creation + and indexing, archive linking, execution, and symbol inspection; +- preprocessing, dependency generation, syntax checking, assembly output, and + optimized IR output; and +- optimized portable-C output, including the release-blocking crash case. + +These fixtures use `-print-sysroot` from the exact selected Kit and the supplied +distribution's `support/`; they do not search the repository for a compiler, +runtime, or SDK. Run only the positive archive journey with: + +```sh +test/audit/release/run.sh --dist ./build/dist/kit \ + --module core --workflow archive-link-run +``` + +## Standalone and relocation module + +`modules/standalone.sh` copies the supplied distribution to a path containing +spaces and audits direct, PATH-only, installed multicall, read-only-source, and +post-install-move invocation. Each case gets host-development-tool sentinels; +any attempted use of `clang`, `cc`, `gcc`, `as`, `ld`, archive/binutils tools, +or SDK-discovery utilities is retained in `artifacts/sentinel.log` and fails +the acceptance case. The copied distribution is made user-writable only so the +post-install relocation step can move it; the preserved input is untouched. + +```sh +test/audit/release/run.sh --dist ./build/dist/kit --module standalone +``` + +## Language API module + +`modules/api.sh` treats the installed public headers as the language-developer +surface. It compiles every shipped public header as the first include, then +builds and runs four out-of-tree programs against only the selected Kit, +`include/`, and `lib/libkit.a`: context/target/compiler lifecycle, source +compilation-session object emission plus public linker composition, direct CG +object emission plus public linker composition, and diagnostic-sink behavior. +The exact native SDK is obtained from that Kit's `-print-sysroot` output. + +```sh +test/audit/release/run.sh --dist ./build/dist/kit --module api +``` + +The `core`, `standalone`, and `api` modules require `--dist`, because their +acceptance criteria cover shipped support files, headers, libraries, and +relocation. With `--kit`, selected cases report `BLOCKED` explicitly. + +## Adding modules + +Add `modules/NAME.sh` with a POSIX-shell function named +`audit_module_NAME`. Source `lib/harness.sh` indirectly through `run.sh`; modules +should define cases with `audit_case_start`, execute the exact selected binary +with `audit_case_exec_kit` (or an explicit program with `audit_case_exec`), add +assertions, and call `audit_case_finish`. Use `audit_case_blocked` and +`audit_case_not_applicable` instead of silently skipping a selected case. + +Small immutable inputs belong in `fixtures/`. Do not modify a fixture in place; +copy it into the case work directory or artifact directory first. diff --git a/test/audit/release/fixtures/api_compile_session.c b/test/audit/release/fixtures/api_compile_session.c @@ -0,0 +1,112 @@ +#include <kit/compile.h> +#include <kit/link.h> +#include <kit/target.h> + +extern void* malloc(size_t size); +extern void* realloc(void* ptr, size_t size); +extern void free(void* ptr); + +static void* audit_alloc(KitHeap* heap, size_t size, size_t align) { + (void)heap; + (void)align; + return malloc(size ? size : 1); +} + +static void* audit_realloc(KitHeap* heap, void* ptr, size_t old_size, + size_t new_size, size_t align) { + (void)heap; + (void)old_size; + (void)align; + return realloc(ptr, new_size ? new_size : 1); +} + +static void audit_free(KitHeap* heap, void* ptr, size_t size) { + (void)heap; + (void)size; + free(ptr); +} + +static void audit_diag(KitDiagSink* sink, KitDiagKind kind, KitSrcLoc loc, + const char* fmt, va_list ap) { + (void)sink; + (void)kind; + (void)loc; + (void)fmt; + (void)ap; +} + +static KitStatus build_answer(const KitContext* context, KitHeap* heap, + size_t* image_size) { + KitTargetSpec spec; + KitTargetOptions target_options = {0}; + KitCompileSessionOptions compile_options = {0}; + KitSourceInput input = { + .name = KIT_SLICE_LIT("answer.c"), + .bytes = KIT_SLICE_LIT("int answer(void) { return 42; }\n"), + .lang = KIT_LANG_C, + .input_kind = KIT_FRONTEND_INPUT_TRANSLATION_UNIT, + }; + KitLinkSessionOptions link_options = {0}; + KitTarget* target = NULL; + KitCompiler* compiler = NULL; + KitCompileSession* session = NULL; + KitObjBuilder* object = NULL; + KitLinkSession* link = NULL; + KitWriter* image = NULL; + KitStatus status = KIT_INVALID; + bool staged = false; + + *image_size = 0; + if (!kit_target_from_triple("aarch64-none-elf", &spec)) goto done; + target_options.spec = spec; + status = kit_target_new(context, &target_options, &target); + if (status != KIT_OK) goto done; + status = kit_compiler_new(target, context, &compiler); + if (status != KIT_OK) goto done; + + compile_options.lang = KIT_LANG_C; + compile_options.compile.input_kind = KIT_FRONTEND_INPUT_TRANSLATION_UNIT; + status = kit_compile_session_new(compiler, &compile_options, &session); + if (status != KIT_OK) goto done; + status = kit_compile_session_stage(session, &input, &object); + if (status != KIT_OK) goto done; + staged = true; + + link_options.output_kind = KIT_LINK_OUTPUT_EXE; + link_options.entry = KIT_SLICE_LIT("answer"); + link_options.freestanding_strict = true; + status = kit_link_session_new(compiler, &link_options, &link); + if (status != KIT_OK) goto done; + status = kit_link_session_add_obj(link, object); + if (status != KIT_OK) goto done; + status = kit_link_session_resolve(link); + if (status != KIT_OK) goto done; + status = kit_writer_mem(heap, &image); + if (status != KIT_OK) goto done; + status = kit_link_session_emit(link, image); + if (status != KIT_OK) goto done; + (void)kit_writer_mem_bytes(image, image_size); + + kit_compile_session_commit(session); + staged = false; + +done: + if (staged) kit_compile_session_abort(session); + if (image) kit_writer_close(image); + if (link) kit_link_session_free(link); + if (object) kit_obj_builder_free(object); + if (session) kit_compile_session_free(session); + if (compiler) kit_compiler_free(compiler); + if (target) kit_target_free(target); + return status; +} + +int main(void) { + KitHeap heap = {audit_alloc, audit_realloc, audit_free, NULL}; + KitDiagSink diag = {audit_diag, NULL, 0, 0}; + KitContext context = {&heap, NULL, &diag, NULL, -1}; + size_t image_size; + + if (build_answer(&context, &heap, &image_size) != KIT_OK) return 1; + return image_size ? 0 : 2; +} diff --git a/test/audit/release/fixtures/api_context.c b/test/audit/release/fixtures/api_context.c @@ -0,0 +1,71 @@ +#include <kit/compile.h> +#include <kit/target.h> + +extern void* malloc(size_t size); +extern void* realloc(void* ptr, size_t size); +extern void free(void* ptr); + +static void* audit_alloc(KitHeap* heap, size_t size, size_t align) { + (void)heap; + (void)align; + return malloc(size ? size : 1); +} + +static void* audit_realloc(KitHeap* heap, void* ptr, size_t old_size, + size_t new_size, size_t align) { + (void)heap; + (void)old_size; + (void)align; + return realloc(ptr, new_size ? new_size : 1); +} + +static void audit_free(KitHeap* heap, void* ptr, size_t size) { + (void)heap; + (void)size; + free(ptr); +} + +static void audit_diag(KitDiagSink* sink, KitDiagKind kind, KitSrcLoc loc, + const char* fmt, va_list ap) { + (void)sink; + (void)kind; + (void)loc; + (void)fmt; + (void)ap; +} + +int main(void) { + KitHeap heap = {audit_alloc, audit_realloc, audit_free, NULL}; + KitDiagSink diag = {audit_diag, NULL, 0, 0}; + KitContext context = {&heap, NULL, &diag, NULL, -1}; + KitTargetSpec spec; + KitTargetOptions target_options = {0}; + KitTarget* target = NULL; + KitCompiler* compiler = NULL; + KitStatus status; + char triple[64]; + + if (!kit_target_from_triple("aarch64-apple-darwin", &spec)) return 10; + target_options.spec = spec; + status = kit_target_new(&context, &target_options, &target); + if (status != KIT_OK) return 11; + status = kit_compiler_new(target, &context, &compiler); + if (status != KIT_OK) { + kit_target_free(target); + return 12; + } + spec = kit_compiler_target_spec(compiler); + if (!kit_target_to_triple(spec, triple, sizeof(triple))) { + kit_compiler_free(compiler); + kit_target_free(target); + return 13; + } + if (!kit_language_name(compiler, KIT_LANG_C)) { + kit_compiler_free(compiler); + kit_target_free(target); + return 14; + } + kit_compiler_free(compiler); + kit_target_free(target); + return 0; +} diff --git a/test/audit/release/fixtures/api_context_arena.c b/test/audit/release/fixtures/api_context_arena.c @@ -0,0 +1,71 @@ +#include <kit/compile.h> +#include <kit/target.h> + +typedef struct AuditArena { + unsigned char* data; + size_t capacity; + size_t used; +} AuditArena; + +static void* audit_alloc(KitHeap* heap, size_t size, size_t align) { + AuditArena* arena = heap->user; + size_t mask = align - 1; + size_t start = (arena->used + mask) & ~mask; + if (size > arena->capacity - start) return NULL; + arena->used = start + size; + return arena->data + start; +} + +static void* audit_realloc(KitHeap* heap, void* ptr, size_t old_size, + size_t new_size, size_t align) { + unsigned char* out = audit_alloc(heap, new_size, align); + const unsigned char* old = ptr; + size_t copy_size = old_size < new_size ? old_size : new_size; + size_t i; + if (!out) return NULL; + for (i = 0; old && i < copy_size; ++i) out[i] = old[i]; + return out; +} + +static void audit_free(KitHeap* heap, void* ptr, size_t size) { + (void)heap; + (void)ptr; + (void)size; +} + +static void audit_diag(KitDiagSink* sink, KitDiagKind kind, KitSrcLoc loc, + const char* fmt, va_list ap) { + (void)sink; + (void)kind; + (void)loc; + (void)fmt; + (void)ap; +} + +int main(void) { + unsigned char storage[4u * 1024u * 1024u]; + AuditArena arena = {storage, sizeof(storage), 0}; + KitHeap heap = {audit_alloc, audit_realloc, audit_free, &arena}; + KitDiagSink diag = {audit_diag, NULL, 0, 0}; + KitContext context = {&heap, NULL, &diag, NULL, -1}; + KitTargetSpec spec; + KitTargetOptions options = {0}; + KitTarget* target = NULL; + KitCompiler* compiler = NULL; + + if (!kit_target_from_triple("aarch64-apple-darwin", &spec)) return 10; + options.spec = spec; + if (kit_target_new(&context, &options, &target) != KIT_OK) return 11; + if (kit_compiler_new(target, &context, &compiler) != KIT_OK) { + kit_target_free(target); + return 12; + } + if (!kit_language_name(compiler, KIT_LANG_C)) { + kit_compiler_free(compiler); + kit_target_free(target); + return 13; + } + kit_compiler_free(compiler); + kit_target_free(target); + return 0; +} diff --git a/test/audit/release/fixtures/api_diagnostic.c b/test/audit/release/fixtures/api_diagnostic.c @@ -0,0 +1,64 @@ +#include <kit/target.h> + +typedef struct AuditArena { + unsigned char* data; + size_t capacity; + size_t used; +} AuditArena; + +static void* audit_alloc(KitHeap* heap, size_t size, size_t align) { + AuditArena* arena = heap->user; + size_t mask = align - 1; + size_t start = (arena->used + mask) & ~mask; + if (size > arena->capacity - start) return NULL; + arena->used = start + size; + return arena->data + start; +} + +static void* audit_realloc(KitHeap* heap, void* ptr, size_t old_size, + size_t new_size, size_t align) { + unsigned char* out = audit_alloc(heap, new_size, align); + const unsigned char* old = ptr; + size_t copy_size = old_size < new_size ? old_size : new_size; + size_t i; + if (!out) return NULL; + for (i = 0; old && i < copy_size; ++i) out[i] = old[i]; + return out; +} + +static void audit_free(KitHeap* heap, void* ptr, size_t size) { + (void)heap; + (void)ptr; + (void)size; +} + +static void audit_diag(KitDiagSink* sink, KitDiagKind kind, KitSrcLoc loc, + const char* fmt, va_list ap) { + unsigned int* calls = sink->user; + (void)kind; + (void)loc; + (void)fmt; + (void)ap; + *calls += 1; +} + +int main(void) { + unsigned char storage[64u * 1024u]; + unsigned int diagnostic_calls = 0; + AuditArena arena = {storage, sizeof(storage), 0}; + KitHeap heap = {audit_alloc, audit_realloc, audit_free, &arena}; + KitDiagSink diag = {audit_diag, &diagnostic_calls, 0, 0}; + KitContext context = {&heap, NULL, &diag, NULL, -1}; + KitTargetSpec spec; + KitTargetOptions options = {0}; + KitTarget* target = NULL; + KitStatus status; + + if (!kit_target_from_triple("wasm64-none", &spec)) return 10; + options.spec = spec; + status = kit_target_new(&context, &options, &target); + if (target) kit_target_free(target); + if (status == KIT_OK) return 11; + if (diagnostic_calls == 0 || diag.errors == 0) return 12; + return 0; +} diff --git a/test/audit/release/fixtures/api_frontend.c b/test/audit/release/fixtures/api_frontend.c @@ -0,0 +1,146 @@ +#include <kit/cg.h> +#include <kit/link.h> +#include <kit/target.h> + +typedef struct AuditArena { + unsigned char* data; + size_t capacity; + size_t used; +} AuditArena; + +static void* audit_alloc(KitHeap* heap, size_t size, size_t align) { + AuditArena* arena = heap->user; + size_t mask = align - 1; + size_t start = (arena->used + mask) & ~mask; + if (size > arena->capacity - start) return NULL; + arena->used = start + size; + return arena->data + start; +} + +static void* audit_realloc(KitHeap* heap, void* ptr, size_t old_size, + size_t new_size, size_t align) { + unsigned char* out = audit_alloc(heap, new_size, align); + const unsigned char* old = ptr; + size_t copy_size = old_size < new_size ? old_size : new_size; + size_t i; + if (!out) return NULL; + for (i = 0; old && i < copy_size; ++i) out[i] = old[i]; + return out; +} + +static void audit_free(KitHeap* heap, void* ptr, size_t size) { + (void)heap; + (void)ptr; + (void)size; +} + +static void audit_diag(KitDiagSink* sink, KitDiagKind kind, KitSrcLoc loc, + const char* fmt, va_list ap) { + (void)sink; + (void)kind; + (void)loc; + (void)fmt; + (void)ap; +} + +static int emit_object(KitCompiler* compiler, KitHeap* heap) { + KitObjBuilder* object = NULL; + KitCg* cg = NULL; + KitWriter* writer = NULL; + KitWriter* link_writer = NULL; + KitLinkSession* link = NULL; + KitCodeOptions code = {0}; + KitCgUnitOptions unit = {0}; + KitCgFinishOptions finish = {0}; + KitCgFuncSig signature = {0}; + KitCgDecl declaration = {0}; + KitLinkSessionOptions link_options = {0}; + KitCgTypeId i32_type; + KitCgTypeId function_type; + KitCgSym function; + size_t object_size = 0; + size_t linked_size = 0; + int result = 20; + + if (kit_obj_builder_new(compiler, &object) != KIT_OK) goto done; + if (kit_cg_new(compiler, &cg) != KIT_OK) goto done; + if (kit_cg_begin(cg, object, &code) != KIT_OK) goto done; + unit.source_name = KIT_SLICE_LIT("audit-language"); + if (kit_cg_begin_unit(cg, &unit) != KIT_OK) goto done; + + i32_type = kit_cg_type_builtin(compiler, KIT_CG_BUILTIN_I32); + signature.result.type = i32_type; + signature.call_conv = KIT_CG_CC_TARGET_C; + function_type = kit_cg_type_func(compiler, signature); + if (function_type == KIT_CG_TYPE_NONE) goto done; + + declaration.kind = KIT_CG_DECL_FUNC; + declaration.linkage_name = kit_cg_c_linkage_name( + compiler, kit_sym_intern(compiler, KIT_SLICE_LIT("answer"))); + declaration.display_name = declaration.linkage_name; + declaration.type = function_type; + declaration.sym.bind = KIT_SB_GLOBAL; + function = kit_cg_decl(cg, declaration); + if (function == KIT_CG_SYM_NONE) goto done; + + kit_cg_func_begin(cg, function); + kit_cg_push_int(cg, 42, i32_type); + kit_cg_ret(cg); + kit_cg_func_end(cg); + + if (kit_cg_end_unit(cg) != KIT_OK) goto done; + finish.output_kind = KIT_CG_OUTPUT_RELOCATABLE; + if (kit_cg_finish(cg, &finish) != KIT_OK) goto done; + if (kit_cg_detach(cg) != KIT_OK) goto done; + if (kit_writer_mem(heap, &writer) != KIT_OK) goto done; + if (kit_obj_builder_emit(object, writer) != KIT_OK) goto done; + (void)kit_writer_mem_bytes(writer, &object_size); + if (!object_size) { + result = 21; + goto done; + } + + link_options.output_kind = KIT_LINK_OUTPUT_EXE; + link_options.entry = KIT_SLICE_LIT("answer"); + link_options.freestanding_strict = true; + if (kit_link_session_new(compiler, &link_options, &link) != KIT_OK) goto done; + if (kit_link_session_add_obj(link, object) != KIT_OK) goto done; + if (kit_link_session_resolve(link) != KIT_OK) goto done; + if (kit_writer_mem(heap, &link_writer) != KIT_OK) goto done; + if (kit_link_session_emit(link, link_writer) != KIT_OK) goto done; + (void)kit_writer_mem_bytes(link_writer, &linked_size); + result = linked_size ? 0 : 22; + +done: + if (link_writer) kit_writer_close(link_writer); + if (link) kit_link_session_free(link); + if (writer) kit_writer_close(writer); + if (cg) kit_cg_free(cg); + if (object) kit_obj_builder_free(object); + return result; +} + +int main(void) { + unsigned char storage[4u * 1024u * 1024u]; + AuditArena arena = {storage, sizeof(storage), 0}; + KitHeap heap = {audit_alloc, audit_realloc, audit_free, &arena}; + KitDiagSink diag = {audit_diag, NULL, 0, 0}; + KitContext context = {&heap, NULL, &diag, NULL, -1}; + KitTargetSpec spec; + KitTargetOptions options = {0}; + KitTarget* target = NULL; + KitCompiler* compiler = NULL; + int result; + + if (!kit_target_from_triple("aarch64-none-elf", &spec)) return 10; + options.spec = spec; + if (kit_target_new(&context, &options, &target) != KIT_OK) return 11; + if (kit_compiler_new(target, &context, &compiler) != KIT_OK) { + kit_target_free(target); + return 12; + } + result = emit_object(compiler, &heap); + kit_compiler_free(compiler); + kit_target_free(target); + return result; +} diff --git a/test/audit/release/fixtures/archive_main.c b/test/audit/release/fixtures/archive_main.c @@ -0,0 +1,3 @@ +int archive_value(void); + +int main(void) { return archive_value() == 42 ? 0 : 1; } diff --git a/test/audit/release/fixtures/archive_value.c b/test/audit/release/fixtures/archive_value.c @@ -0,0 +1 @@ +int archive_value(void) { return 42; } diff --git a/test/audit/release/fixtures/build/BUILD.kit b/test/audit/release/fixtures/build/BUILD.kit @@ -0,0 +1,3 @@ +kit-build 1 +[target copy] +recipe recipes/copy.sh diff --git a/test/audit/release/fixtures/build/input.txt b/test/audit/release/fixtures/build/input.txt @@ -0,0 +1 @@ +content-addressed hello diff --git a/test/audit/release/fixtures/build/recipes/copy.sh b/test/audit/release/fixtures/build/recipes/copy.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +source_path=$("$KIT" build source input.txt) +/bin/cp "$source_path" "$KIT_BUILD_OUT/result.txt" diff --git a/test/audit/release/fixtures/cas_map.txt b/test/audit/release/fixtures/cas_map.txt @@ -0,0 +1 @@ +bin/tool x ./build/tool diff --git a/test/audit/release/fixtures/gram_main.c b/test/audit/release/fixtures/gram_main.c @@ -0,0 +1,13 @@ +#include "parser.h" +#include <stdio.h> + +int main(void) { + size_t control = 0; + size_t values = 0; + + demopstack_bounds(8, &control, &values); + printf("tokens=%d rules=%d control=%zu values=%zu scratch=%zu\n", + DEMOPTOK__COUNT, DEMOPR__COUNT, control, values, + demopgenerate_scratch_count()); + return control == 0 || values == 0; +} diff --git a/test/audit/release/fixtures/gram_simple.ebnf b/test/audit/release/fixtures/gram_simple.ebnf @@ -0,0 +1 @@ +start = "hello"; diff --git a/test/audit/release/fixtures/group_left.c b/test/audit/release/fixtures/group_left.c @@ -0,0 +1,5 @@ +#ifndef LEFT_VALUE +#error "LEFT_VALUE must be scoped to this source" +#endif + +int grouped_left(void) { return LEFT_VALUE; } diff --git a/test/audit/release/fixtures/group_main.c b/test/audit/release/fixtures/group_main.c @@ -0,0 +1,4 @@ +int grouped_left(void); +int grouped_right(void); + +int main(void) { return grouped_left() + grouped_right() == 42 ? 0 : 1; } diff --git a/test/audit/release/fixtures/group_right.c b/test/audit/release/fixtures/group_right.c @@ -0,0 +1,5 @@ +#ifndef RIGHT_VALUE +#error "RIGHT_VALUE must be scoped to this source" +#endif + +int grouped_right(void) { return RIGHT_VALUE; } diff --git a/test/audit/release/fixtures/native_sdk_malloc.c b/test/audit/release/fixtures/native_sdk_malloc.c @@ -0,0 +1,8 @@ +#include <stdlib.h> + +int main(void) { + void* memory = malloc(64); + if (!memory) return 1; + free(memory); + return 0; +} diff --git a/test/audit/release/fixtures/poly_helper.c b/test/audit/release/fixtures/poly_helper.c @@ -0,0 +1 @@ +int poly_helper(void) { return 42; } diff --git a/test/audit/release/fixtures/poly_main.c b/test/audit/release/fixtures/poly_main.c @@ -0,0 +1,3 @@ +int poly_helper(void); + +int main(void) { return poly_helper() == 42 ? 0 : 1; } diff --git a/test/audit/release/fixtures/readme_answer.c b/test/audit/release/fixtures/readme_answer.c @@ -0,0 +1 @@ +int answer(void) { return 42; } diff --git a/test/audit/release/fixtures/readme_main.c b/test/audit/release/fixtures/readme_main.c @@ -0,0 +1,3 @@ +int answer(void); + +int main(void) { return answer() != 42; } diff --git a/test/audit/release/fixtures/select_main.c b/test/audit/release/fixtures/select_main.c @@ -0,0 +1,3 @@ +int selected_value(void); + +int main(void) { return selected_value() == 42 ? 0 : 1; } diff --git a/test/audit/release/fixtures/sentinel-tool.sh b/test/audit/release/fixtures/sentinel-tool.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -u + +if [ -n "${KIT_AUDIT_SENTINEL_LOG:-}" ]; then + printf '%s' "$0" >> "$KIT_AUDIT_SENTINEL_LOG" + for argument in "$@"; do + printf ' <%s>' "$argument" >> "$KIT_AUDIT_SENTINEL_LOG" + done + printf '\n' >> "$KIT_AUDIT_SENTINEL_LOG" +fi +printf 'audit sentinel: forbidden host development tool invoked: %s\n' "$0" >&2 +exit 97 diff --git a/test/audit/release/fixtures/strong_value.c b/test/audit/release/fixtures/strong_value.c @@ -0,0 +1 @@ +int selected_value(void) { return 42; } diff --git a/test/audit/release/fixtures/uthash_probe.c b/test/audit/release/fixtures/uthash_probe.c @@ -0,0 +1,21 @@ +#include <stdlib.h> + +#include "uthash.h" + +typedef struct Entry { + int key; + int value; + UT_hash_handle hh; +} Entry; + +int main(void) { + Entry* table = NULL; + Entry* found = NULL; + Entry first = {7, 42, {0}}; + + HASH_ADD_INT(table, key, &first); + HASH_FIND_INT(table, &first.key, found); + if (!found || found->value != 42) return 1; + HASH_DEL(table, &first); + return table != NULL; +} diff --git a/test/audit/release/fixtures/weak_value.c b/test/audit/release/fixtures/weak_value.c @@ -0,0 +1 @@ +__attribute__((weak)) int selected_value(void) { return 1; } diff --git a/test/audit/release/lib/harness.sh b/test/audit/release/lib/harness.sh @@ -0,0 +1,396 @@ +#!/bin/sh + +# Shared black-box release-audit primitives. This file is sourced by run.sh. + +HARNESS_PASS=0 +HARNESS_FAIL=0 +HARNESS_BLOCKED=0 +HARNESS_NOT_APPLICABLE=0 +HARNESS_SELECTED=0 + +audit_match_filter() { + audit_match_values=$1 + audit_match_spec=$2 + + [ -z "$audit_match_spec" ] && return 0 + + audit_match_old_ifs=$IFS + IFS=, + for audit_match_value in $audit_match_values; do + case "$audit_match_value" in + all|any) + IFS=$audit_match_old_ifs + return 0 + ;; + esac + for audit_match_pattern in $audit_match_spec; do + IFS=$audit_match_old_ifs + case "$audit_match_value" in + $audit_match_pattern) return 0 ;; + esac + IFS=, + done + done + IFS=$audit_match_old_ifs + return 1 +} + +audit_case_selected() { + audit_match_filter "$CASE_MODULE" "$HARNESS_FILTER_MODULE" || return 1 + audit_match_filter "$CASE_COMMAND" "$HARNESS_FILTER_COMMAND" || return 1 + audit_match_filter "$CASE_WORKFLOW" "$HARNESS_FILTER_WORKFLOW" || return 1 + audit_match_filter "$CASE_TARGET" "$HARNESS_FILTER_TARGET" || return 1 + audit_match_filter "$CASE_ARCH" "$HARNESS_FILTER_ARCH" || return 1 + audit_match_filter "$CASE_FORMAT" "$HARNESS_FILTER_FORMAT" || return 1 + audit_match_filter "$CASE_SEVERITY" "$HARNESS_FILTER_SEVERITY" || return 1 + return 0 +} + +audit_case_start() { + CASE_ID=$1 + CASE_MODULE=$2 + CASE_COMMAND=$3 + CASE_WORKFLOW=$4 + CASE_TARGET=$5 + CASE_ARCH=$6 + CASE_FORMAT=$7 + CASE_SEVERITY=$8 + CASE_FAILURES=0 + CASE_FINISHED=0 + CASE_ACTIVE_DIR= + CASE_STEP_COUNT=0 + + audit_case_selected || return 1 + + HARNESS_SELECTED=$((HARNESS_SELECTED + 1)) + CASE_DIR=$HARNESS_RUN_DIR/cases/$CASE_ID + if [ -e "$CASE_DIR" ]; then + printf 'audit: duplicate case id or existing case directory: %s\n' "$CASE_ID" >&2 + exit 2 + fi + + mkdir -p \ + "$CASE_DIR/artifacts" \ + "$CASE_DIR/cache" \ + "$CASE_DIR/config" \ + "$CASE_DIR/home" \ + "$CASE_DIR/install" \ + "$CASE_DIR/tmp" \ + "$CASE_DIR/work" + : > "$CASE_DIR/stdin" + : > "$CASE_DIR/stdout" + : > "$CASE_DIR/stderr" + : > "$CASE_DIR/assertions.log" + printf '%s\n' NOT_RUN > "$CASE_DIR/command" + printf '%s\n' NOT_RUN > "$CASE_DIR/exit" + + { + printf 'id=%s\n' "$CASE_ID" + printf 'module=%s\n' "$CASE_MODULE" + printf 'command=%s\n' "$CASE_COMMAND" + printf 'workflow=%s\n' "$CASE_WORKFLOW" + printf 'target=%s\n' "$CASE_TARGET" + printf 'arch=%s\n' "$CASE_ARCH" + printf 'format=%s\n' "$CASE_FORMAT" + printf 'severity=%s\n' "$CASE_SEVERITY" + } > "$CASE_DIR/metadata" + return 0 +} + +audit_case_exec() { + audit_exec_program=$1 + shift + + { + printf 'cwd=%s\n' "$CASE_DIR/work" + printf 'HOME=%s\n' "$CASE_DIR/home" + printf 'XDG_CACHE_HOME=%s\n' "$CASE_DIR/cache" + printf 'XDG_CONFIG_HOME=%s\n' "$CASE_DIR/config" + printf 'XDG_DATA_HOME=%s\n' "$CASE_DIR/home/.local/share" + printf 'TMPDIR=%s\n' "$CASE_DIR/tmp" + printf 'PATH=%s\n' "$HARNESS_PATH" + printf 'argv[0]=%s\n' "$audit_exec_program" + audit_exec_index=1 + for audit_exec_arg in "$@"; do + printf 'argv[%s]=%s\n' "$audit_exec_index" "$audit_exec_arg" + audit_exec_index=$((audit_exec_index + 1)) + done + } > "$CASE_DIR/command" + + ( + cd "$CASE_DIR/work" || exit 125 + env -i \ + HOME="$CASE_DIR/home" \ + XDG_CACHE_HOME="$CASE_DIR/cache" \ + XDG_CONFIG_HOME="$CASE_DIR/config" \ + XDG_DATA_HOME="$CASE_DIR/home/.local/share" \ + TMPDIR="$CASE_DIR/tmp" \ + PATH="$HARNESS_PATH" \ + KIT_AUDIT_INSTALL="$CASE_DIR/install" \ + KIT_AUDIT_ARTIFACTS="$CASE_DIR/artifacts" \ + "$audit_exec_program" "$@" \ + < "$CASE_DIR/stdin" + ) > "$CASE_DIR/stdout" 2> "$CASE_DIR/stderr" + audit_exec_status=$? + printf '%s\n' "$audit_exec_status" > "$CASE_DIR/exit" + CASE_ACTIVE_DIR=$CASE_DIR + return 0 +} + +audit_case_exec_kit() { + audit_case_exec "$HARNESS_KIT" "$@" +} + +audit_case_step_exec() { + audit_step_name=$1 + audit_step_program=$2 + shift 2 + + case "$audit_step_name" in + ''|*[!a-zA-Z0-9_.-]*) + printf 'audit: invalid step name: %s\n' "$audit_step_name" >&2 + exit 2 + ;; + esac + + audit_step_dir=$CASE_DIR/steps/$audit_step_name + if [ -e "$audit_step_dir" ]; then + printf 'audit: duplicate step name in %s: %s\n' "$CASE_ID" "$audit_step_name" >&2 + exit 2 + fi + mkdir -p "$audit_step_dir" + + if [ "$CASE_STEP_COUNT" -eq 0 ]; then + printf '%s\n' 'MULTI_STEP: see steps/' > "$CASE_DIR/command" + printf '%s\n' MULTI_STEP > "$CASE_DIR/exit" + fi + CASE_STEP_COUNT=$((CASE_STEP_COUNT + 1)) + + { + printf 'cwd=%s\n' "$CASE_DIR/work" + printf 'HOME=%s\n' "$CASE_DIR/home" + printf 'XDG_CACHE_HOME=%s\n' "$CASE_DIR/cache" + printf 'XDG_CONFIG_HOME=%s\n' "$CASE_DIR/config" + printf 'XDG_DATA_HOME=%s\n' "$CASE_DIR/home/.local/share" + printf 'TMPDIR=%s\n' "$CASE_DIR/tmp" + printf 'PATH=%s\n' "$HARNESS_PATH" + printf 'argv[0]=%s\n' "$audit_step_program" + audit_step_index=1 + for audit_step_arg in "$@"; do + printf 'argv[%s]=%s\n' "$audit_step_index" "$audit_step_arg" + audit_step_index=$((audit_step_index + 1)) + done + } > "$audit_step_dir/command" + + ( + cd "$CASE_DIR/work" || exit 125 + env -i \ + HOME="$CASE_DIR/home" \ + XDG_CACHE_HOME="$CASE_DIR/cache" \ + XDG_CONFIG_HOME="$CASE_DIR/config" \ + XDG_DATA_HOME="$CASE_DIR/home/.local/share" \ + TMPDIR="$CASE_DIR/tmp" \ + PATH="$HARNESS_PATH" \ + KIT_AUDIT_INSTALL="$CASE_DIR/install" \ + KIT_AUDIT_ARTIFACTS="$CASE_DIR/artifacts" \ + KIT_AUDIT_SENTINEL_LOG="$CASE_DIR/artifacts/sentinel.log" \ + "$audit_step_program" "$@" \ + < "$CASE_DIR/stdin" + ) > "$audit_step_dir/stdout" 2> "$audit_step_dir/stderr" + audit_step_status=$? + printf '%s\n' "$audit_step_status" > "$audit_step_dir/exit" + CASE_ACTIVE_DIR=$audit_step_dir + return 0 +} + +audit_case_step_exec_kit() { + audit_step_kit_name=$1 + shift + audit_case_step_exec "$audit_step_kit_name" "$HARNESS_KIT" "$@" +} + +audit_assert_dir() { + if [ -n "${CASE_ACTIVE_DIR:-}" ]; then + printf '%s\n' "$CASE_ACTIVE_DIR" + else + printf '%s\n' "$CASE_DIR" + fi +} + +audit_read_exit() { + CASE_ACTUAL_EXIT=NOT_RUN + audit_read_dir=$(audit_assert_dir) + IFS= read -r CASE_ACTUAL_EXIT < "$audit_read_dir/exit" || : +} + +audit_check_pass() { + printf 'PASS\t%s\n' "$1" >> "$CASE_DIR/assertions.log" +} + +audit_check_fail() { + CASE_FAILURES=$((CASE_FAILURES + 1)) + printf 'FAIL\t%s\n' "$1" >> "$CASE_DIR/assertions.log" +} + +audit_expect_exit() { + audit_read_exit + if [ "$CASE_ACTUAL_EXIT" = "$1" ]; then + audit_check_pass "exit status is $1" + else + audit_check_fail "expected exit $1, got $CASE_ACTUAL_EXIT" + fi +} + +audit_expect_exit_one_of() { + audit_read_exit + audit_exit_wanted=$1 + audit_exit_old_ifs=$IFS + IFS=, + for audit_exit_value in $audit_exit_wanted; do + IFS=$audit_exit_old_ifs + if [ "$CASE_ACTUAL_EXIT" = "$audit_exit_value" ]; then + audit_check_pass "exit status $CASE_ACTUAL_EXIT is one of $audit_exit_wanted" + return 0 + fi + IFS=, + done + IFS=$audit_exit_old_ifs + audit_check_fail "expected exit in $audit_exit_wanted, got $CASE_ACTUAL_EXIT" +} + +audit_expect_stdout_nonempty() { + audit_expect_dir=$(audit_assert_dir) + if [ -s "$audit_expect_dir/stdout" ]; then + audit_check_pass "stdout is non-empty" + else + audit_check_fail "expected non-empty stdout" + fi +} + +audit_expect_stderr_nonempty() { + audit_expect_dir=$(audit_assert_dir) + if [ -s "$audit_expect_dir/stderr" ]; then + audit_check_pass "stderr is non-empty" + else + audit_check_fail "expected non-empty stderr" + fi +} + +audit_expect_stderr_empty() { + audit_expect_dir=$(audit_assert_dir) + if [ ! -s "$audit_expect_dir/stderr" ]; then + audit_check_pass "stderr is empty" + else + audit_check_fail "expected empty stderr" + fi +} + +audit_expect_stdout_contains() { + audit_expect_dir=$(audit_assert_dir) + if grep -F -q -- "$1" "$audit_expect_dir/stdout"; then + audit_check_pass "stdout contains: $1" + else + audit_check_fail "stdout does not contain: $1" + fi +} + +audit_expect_stdout_regex() { + audit_expect_dir=$(audit_assert_dir) + if grep -E -q -- "$1" "$audit_expect_dir/stdout"; then + audit_check_pass "stdout matches: $1" + else + audit_check_fail "stdout does not match: $1" + fi +} + +audit_reject_stdout_regex() { + audit_expect_dir=$(audit_assert_dir) + if grep -E -q -- "$1" "$audit_expect_dir/stdout"; then + audit_check_fail "stdout unexpectedly matches: $1" + else + audit_check_pass "stdout does not match: $1" + fi +} + +audit_expect_stdout_equals_file() { + audit_expect_dir=$(audit_assert_dir) + if cmp -s "$audit_expect_dir/stdout" "$1"; then + audit_check_pass "stdout matches $1" + else + audit_check_fail "stdout differs from $1" + fi +} + +audit_expect_stdout_equals() { + audit_expect_dir=$(audit_assert_dir) + audit_expect_text=$1 + audit_expect_actual=$(cat "$audit_expect_dir/stdout") + if [ "$audit_expect_actual" = "$audit_expect_text" ]; then + audit_check_pass "stdout equals: $audit_expect_text" + else + audit_check_fail "stdout differs from expected text: $audit_expect_text" + fi +} + +audit_expect_file_exists() { + if [ -f "$1" ]; then + audit_check_pass "file exists: $1" + else + audit_check_fail "file does not exist: $1" + fi +} + +audit_expect_file_nonempty() { + if [ -s "$1" ]; then + audit_check_pass "file is non-empty: $1" + else + audit_check_fail "file is missing or empty: $1" + fi +} + +audit_expect_file_empty() { + if [ -f "$1" ] && [ ! -s "$1" ]; then + audit_check_pass "file is empty: $1" + elif [ ! -e "$1" ]; then + audit_check_pass "file was not created: $1" + else + audit_check_fail "file is unexpectedly non-empty: $1" + fi +} + +audit_record_result() { + audit_result_state=$1 + audit_result_reason=$2 + printf '%s\n' "$audit_result_state" > "$CASE_DIR/state" + printf '%s\n' "$audit_result_reason" > "$CASE_DIR/reason" + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ + "$audit_result_state" "$CASE_SEVERITY" "$CASE_MODULE" \ + "$CASE_COMMAND" "$CASE_WORKFLOW" "$CASE_TARGET" "$CASE_ARCH" \ + "$CASE_FORMAT" "$CASE_ID" "$audit_result_reason" \ + >> "$HARNESS_SUMMARY_TSV" + + case "$audit_result_state" in + PASS) HARNESS_PASS=$((HARNESS_PASS + 1)) ;; + FAIL) HARNESS_FAIL=$((HARNESS_FAIL + 1)) ;; + BLOCKED) HARNESS_BLOCKED=$((HARNESS_BLOCKED + 1)) ;; + NOT_APPLICABLE) HARNESS_NOT_APPLICABLE=$((HARNESS_NOT_APPLICABLE + 1)) ;; + *) printf 'audit: internal error: bad result state %s\n' "$audit_result_state" >&2; exit 2 ;; + esac + CASE_FINISHED=1 +} + +audit_case_finish() { + if [ "$CASE_FAILURES" -eq 0 ]; then + audit_record_result PASS "all assertions passed" + else + audit_record_result FAIL "$CASE_FAILURES assertion(s) failed; see assertions.log" + fi +} + +audit_case_blocked() { + audit_record_result BLOCKED "$1" +} + +audit_case_not_applicable() { + audit_record_result NOT_APPLICABLE "$1" +} diff --git a/test/audit/release/modules/api.sh b/test/audit/release/modules/api.sh @@ -0,0 +1,121 @@ +#!/bin/sh + +audit_api_fixture_dir=$SCRIPT_DIR/fixtures + +audit_api_require_dist() { + if [ -z "$HARNESS_DIST" ]; then + audit_case_blocked "language API workflows require --dist for include/, lib/, and support/" + return 1 + fi + for audit_api_component in include lib/libkit.a support; do + if [ ! -e "$HARNESS_DIST/$audit_api_component" ]; then + audit_case_blocked "distribution component is missing: $HARNESS_DIST/$audit_api_component" + return 1 + fi + done + return 0 +} + +audit_api_discover_sysroot() { + audit_case_step_exec_kit discover-sysroot cc \ + --support-dir "$HARNESS_DIST/support" -print-sysroot + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_nonempty + AUDIT_API_SYSROOT= + IFS= read -r AUDIT_API_SYSROOT < "$CASE_ACTIVE_DIR/stdout" || : + if [ -z "$AUDIT_API_SYSROOT" ] || [ ! -d "$AUDIT_API_SYSROOT" ]; then + audit_check_fail "native SDK reported by -print-sysroot is unavailable: $AUDIT_API_SYSROOT" + return 1 + fi + audit_check_pass "native SDK exists: $AUDIT_API_SYSROOT" + return 0 +} + +audit_api_copy_fixture() { + audit_api_name=$1 + printf 'cp %s %s\n' \ + "$audit_api_fixture_dir/$audit_api_name" "$CASE_DIR/work/$audit_api_name" \ + >> "$CASE_DIR/setup.command" + if ! cp "$audit_api_fixture_dir/$audit_api_name" "$CASE_DIR/work/$audit_api_name" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr"; then + audit_case_blocked "could not stage fixture $audit_api_name" + return 1 + fi + return 0 +} + +audit_api_headers() { + if ! audit_case_start api.public-headers api cc header-self-sufficiency native native native p1; then + return + fi + audit_api_require_dist || return + audit_api_discover_sysroot || { + audit_case_finish + return + } + + audit_api_header_list=$CASE_DIR/artifacts/public-headers.txt + find "$HARNESS_DIST/include" -type f -name '*.h' -print | sort \ + > "$audit_api_header_list" 2> "$CASE_DIR/setup.stderr" + audit_api_header_count=0 + while IFS= read -r audit_api_header_path; do + audit_api_header_count=$((audit_api_header_count + 1)) + audit_api_header_rel=${audit_api_header_path#"$HARNESS_DIST/include/"} + audit_api_header_safe=$(printf '%s' "$audit_api_header_rel" | tr '/.-' '___') + audit_api_probe=$CASE_DIR/work/header-$audit_api_header_safe.c + printf '#include <%s>\n' "$audit_api_header_rel" > "$audit_api_probe" + + audit_case_step_exec_kit "header-$audit_api_header_safe" cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_API_SYSROOT" \ + -I "$HARNESS_DIST/include" -fsyntax-only "$audit_api_probe" + audit_expect_exit 0 + audit_expect_stderr_empty + done < "$audit_api_header_list" + if [ "$audit_api_header_count" -eq 0 ]; then + audit_check_fail "no public headers found below $HARNESS_DIST/include" + else + audit_check_pass "audited $audit_api_header_count public headers" + fi + audit_case_finish +} + +audit_api_program() { + audit_api_id=$1 + audit_api_fixture=$2 + audit_api_workflow=$3 + if ! audit_case_start "$audit_api_id" api cc,libkit "$audit_api_workflow" native native native p1; then + return + fi + audit_api_require_dist || return + audit_api_copy_fixture "$audit_api_fixture" || return + audit_api_discover_sysroot || { + audit_case_finish + return + } + + audit_api_executable=${audit_api_fixture%.c} + audit_case_step_exec_kit build cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_API_SYSROOT" \ + -fno-stack-protector -I "$HARNESS_DIST/include" \ + "$audit_api_fixture" "$HARNESS_DIST/lib/libkit.a" \ + -o "$CASE_DIR/artifacts/$audit_api_executable" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/$audit_api_executable" + + if [ -f "$CASE_DIR/artifacts/$audit_api_executable" ]; then + audit_case_step_exec run "$CASE_DIR/artifacts/$audit_api_executable" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + audit_case_finish +} + +audit_module_api() { + audit_api_headers + audit_api_program api.context-lifecycle api_context.c context-target-compiler + audit_api_program api.compile-session-link api_compile_session.c source-session-link + audit_api_program api.frontend-link api_frontend.c cg-object-link + audit_api_program api.diagnostic-sink api_diagnostic.c diagnostics +} diff --git a/test/audit/release/modules/core.sh b/test/audit/release/modules/core.sh @@ -0,0 +1,193 @@ +#!/bin/sh + +audit_core_fixture_dir=$SCRIPT_DIR/fixtures + +audit_core_require_dist() { + if [ -z "$HARNESS_DIST" ]; then + audit_case_blocked "core compiler/linker workflows require --dist for shipped support and runtime files" + return 1 + fi + if [ ! -d "$HARNESS_DIST/support" ]; then + audit_case_blocked "distribution has no support directory: $HARNESS_DIST/support" + return 1 + fi + return 0 +} + +audit_core_copy() { + audit_core_source=$audit_core_fixture_dir/$1 + audit_core_destination=$CASE_DIR/work/${2:-$1} + { + printf 'cp %s %s\n' "$audit_core_source" "$audit_core_destination" + } >> "$CASE_DIR/setup.command" + if ! cp "$audit_core_source" "$audit_core_destination" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr"; then + audit_case_blocked "could not stage fixture $1" + return 1 + fi + return 0 +} + +audit_core_discover_sysroot() { + audit_case_step_exec_kit discover-sysroot cc \ + --support-dir "$HARNESS_DIST/support" -print-sysroot + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_nonempty + AUDIT_CORE_SYSROOT= + IFS= read -r AUDIT_CORE_SYSROOT < "$CASE_ACTIVE_DIR/stdout" || : + if [ -z "$AUDIT_CORE_SYSROOT" ] || [ ! -d "$AUDIT_CORE_SYSROOT" ]; then + audit_check_fail "native SDK reported by -print-sysroot is unavailable: $AUDIT_CORE_SYSROOT" + return 1 + fi + audit_check_pass "native SDK exists: $AUDIT_CORE_SYSROOT" + return 0 +} + +audit_core_default_native() { + if ! audit_case_start core.native-default core cc compile-link-run native native native p0; then + return + fi + audit_core_require_dist || return + audit_core_copy poly_helper.c || return + audit_core_copy poly_main.c || return + + audit_case_step_exec_kit compile cc poly_main.c poly_helper.c \ + -o "$CASE_DIR/artifacts/default-native" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/default-native" + + if [ -f "$CASE_DIR/artifacts/default-native" ]; then + audit_case_step_exec run "$CASE_DIR/artifacts/default-native" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + audit_case_finish +} + +audit_core_archive_journey() { + if ! audit_case_start core.archive-link-run core cc,ar,ranlib,nm archive-link-run native native native p0; then + return + fi + audit_core_require_dist || return + audit_core_copy archive_value.c || return + audit_core_copy archive_main.c || return + audit_core_discover_sysroot || { + audit_case_finish + return + } + + audit_case_step_exec_kit compile-member cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + -O1 -g -c archive_value.c -o "$CASE_DIR/artifacts/archive_value.o" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/archive_value.o" + + audit_case_step_exec_kit archive ar rc \ + "$CASE_DIR/artifacts/libaudit.a" "$CASE_DIR/artifacts/archive_value.o" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/libaudit.a" + + audit_case_step_exec_kit index ranlib "$CASE_DIR/artifacts/libaudit.a" + audit_expect_exit 0 + audit_expect_stderr_empty + + audit_case_step_exec_kit link cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + -O1 -g archive_main.c "$CASE_DIR/artifacts/libaudit.a" \ + -o "$CASE_DIR/artifacts/archive-flow" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/archive-flow" + + if [ -f "$CASE_DIR/artifacts/archive-flow" ]; then + audit_case_step_exec run "$CASE_DIR/artifacts/archive-flow" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + + audit_case_step_exec_kit symbols nm "$CASE_DIR/artifacts/libaudit.a" + audit_expect_exit 0 + audit_expect_stdout_contains archive_value + audit_expect_stderr_empty + audit_case_finish +} + +audit_core_frontend_outputs() { + if ! audit_case_start core.frontend-outputs core cc,cpp,check preprocess-deps-emit native native native p1; then + return + fi + audit_core_require_dist || return + audit_core_copy poly_helper.c || return + audit_core_discover_sysroot || { + audit_case_finish + return + } + + audit_case_step_exec_kit preprocess cpp \ + poly_helper.c -o "$CASE_DIR/artifacts/poly_helper.i" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/poly_helper.i" + + audit_case_step_exec_kit dependencies cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + -MMD -MF "$CASE_DIR/artifacts/poly_helper.d" -c poly_helper.c \ + -o "$CASE_DIR/artifacts/deps.o" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/poly_helper.d" + audit_expect_file_nonempty "$CASE_DIR/artifacts/deps.o" + + audit_case_step_exec_kit syntax check \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + poly_helper.c + audit_expect_exit 0 + audit_expect_stderr_empty + + audit_case_step_exec_kit assembly cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + -S poly_helper.c -o "$CASE_DIR/artifacts/poly_helper.s" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/poly_helper.s" + + audit_case_step_exec_kit ir cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + --emit=ir -O1 poly_helper.c -o "$CASE_DIR/artifacts/poly_helper.ir" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/poly_helper.ir" + audit_case_finish +} + +audit_core_portable_c_o1() { + if ! audit_case_start core.portable-c-o1 core cc portable-c native native c p0; then + return + fi + audit_core_require_dist || return + audit_core_copy poly_helper.c || return + audit_core_discover_sysroot || { + audit_case_finish + return + } + + audit_case_step_exec_kit emit-c cc \ + --support-dir "$HARNESS_DIST/support" --sysroot "$AUDIT_CORE_SYSROOT" \ + --emit=c -O1 poly_helper.c \ + -o "$CASE_DIR/artifacts/poly_helper.generated.c" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/poly_helper.generated.c" + audit_case_finish +} + +audit_module_core() { + audit_core_default_native + audit_core_archive_journey + audit_core_frontend_outputs + audit_core_portable_c_o1 +} diff --git a/test/audit/release/modules/help.sh b/test/audit/release/modules/help.sh @@ -0,0 +1,208 @@ +#!/bin/sh + +audit_help_commands='cc check build-exe build-lib build-obj build install cpp as ld ar cpio ranlib strip objcopy image objdump run dbg nm size addr2line symbolize strings cas pkg xxd cmp hash sha256sum b2sum crc32 compress gzip gunzip lz4 lz4c disas mc gram update' + +audit_help_safe_name() { + printf '%s' "$1" | tr '-' '_' +} + +audit_help_noargs_kind() { + case "$1" in + build|install|cpio|cmp|gram) printf '%s\n' usage2 ;; + dbg) printf '%s\n' repl0 ;; + strings|xxd|hash|sha256sum|b2sum|crc32|compress|gzip|lz4|lz4c) printf '%s\n' stdin0 ;; + gunzip) printf '%s\n' stdin1 ;; + pkg|disas|mc) printf '%s\n' help2 ;; + update) printf '%s\n' operation ;; + *) printf '%s\n' help0 ;; + esac +} + +audit_help_severity() { + case "$1" in + cc|build|build-exe|build-lib|build-obj|cas|pkg|dbg|gram|update) printf '%s\n' p1 ;; + *) printf '%s\n' p2 ;; + esac +} + +audit_help_run_noargs() { + audit_help_cmd=$1 + audit_help_safe=$2 + audit_help_kind=$(audit_help_noargs_kind "$audit_help_cmd") + audit_help_sev=$(audit_help_severity "$audit_help_cmd") + + if ! audit_case_start "help.$audit_help_safe.noargs" help "$audit_help_cmd" help all all all "$audit_help_sev"; then + return + fi + + if [ "$audit_help_cmd" = update ] && [ -n "$HARNESS_DIST" ]; then + audit_help_update_dist=$CASE_DIR/install/update-dist + if cp -R "$HARNESS_DIST" "$audit_help_update_dist" \ + > "$CASE_DIR/setup.stdout" 2> "$CASE_DIR/setup.stderr"; then + audit_case_exec "$audit_help_update_dist/bin/kit" update + else + audit_case_blocked "could not create isolated distribution copy for update" + return + fi + elif [ "$audit_help_cmd" = update ]; then + audit_case_blocked "update no-argument audit requires --dist for an isolated distribution copy" + return + else + audit_case_exec_kit "$audit_help_cmd" + fi + + case "$audit_help_kind" in + help0) + audit_expect_exit 0 + audit_expect_stdout_regex '^USAGE$' + audit_expect_stderr_empty + ;; + usage2) + audit_expect_exit 2 + audit_expect_stderr_nonempty + ;; + repl0) + audit_expect_exit 0 + audit_expect_stdout_nonempty + ;; + stdin0) + audit_expect_exit 0 + ;; + stdin1) + audit_expect_exit 1 + audit_expect_stderr_nonempty + ;; + help2) + audit_expect_exit 2 + audit_expect_stdout_regex '^USAGE$' + ;; + operation) + audit_expect_exit_one_of 0,1 + ;; + esac + audit_case_finish +} + +audit_help_run_long() { + audit_help_cmd=$1 + audit_help_safe=$2 + audit_help_sev=$(audit_help_severity "$audit_help_cmd") + if ! audit_case_start "help.$audit_help_safe.long" help "$audit_help_cmd" help all all all "$audit_help_sev"; then + return + fi + audit_case_exec_kit "$audit_help_cmd" --help + audit_expect_exit 0 + audit_expect_stdout_regex '^USAGE$' + audit_expect_stderr_empty + audit_expect_stdout_regex '^EXAMPLES?$' + audit_expect_stdout_regex '^EXIT CODES?$' + if [ "$audit_help_cmd" = cc ]; then + audit_reject_stdout_regex '[Ss]ee source|[Ss]ee the source' + fi + audit_case_finish +} + +audit_help_run_short() { + audit_help_cmd=$1 + audit_help_safe=$2 + audit_help_sev=$(audit_help_severity "$audit_help_cmd") + if ! audit_case_start "help.$audit_help_safe.short" help "$audit_help_cmd" help all all all "$audit_help_sev"; then + return + fi + audit_case_exec_kit "$audit_help_cmd" -h + if [ "$audit_help_cmd" = objdump ]; then + audit_expect_exit 2 + audit_expect_stderr_nonempty + else + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_equals_file "$HARNESS_RUN_DIR/cases/help.$audit_help_safe.long/stdout" + fi + audit_case_finish +} + +audit_help_run_routed() { + audit_help_cmd=$1 + audit_help_safe=$2 + audit_help_sev=$(audit_help_severity "$audit_help_cmd") + if ! audit_case_start "help.$audit_help_safe.routed" help "$audit_help_cmd" help all all all "$audit_help_sev"; then + return + fi + audit_case_exec_kit help "$audit_help_cmd" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_equals_file "$HARNESS_RUN_DIR/cases/help.$audit_help_safe.long/stdout" + audit_case_finish +} + +audit_help_top() { + if audit_case_start help.top.long help kit inventory all all all p2; then + audit_case_exec_kit --help + audit_expect_exit 0 + audit_expect_stdout_regex '^USAGE$' + audit_expect_stdout_regex '^TOOLS$' + audit_expect_stderr_empty + for audit_help_inventory_cmd in $audit_help_commands; do + audit_expect_stdout_regex "^ $audit_help_inventory_cmd[[:space:]]" + done + audit_reject_stdout_regex 'no args.*show help' + audit_case_finish + fi + + for audit_help_top_variant in noargs short routed; do + if audit_case_start "help.top.$audit_help_top_variant" help kit inventory all all all p2; then + case "$audit_help_top_variant" in + noargs) audit_case_exec_kit ;; + short) audit_case_exec_kit -h ;; + routed) audit_case_exec_kit help ;; + esac + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_equals_file "$HARNESS_RUN_DIR/cases/help.top.long/stdout" + audit_case_finish + fi + done +} + +audit_help_targets() { + if audit_case_start help.targets.long help targets inventory all all all p1; then + audit_case_exec_kit targets --help + audit_expect_exit 0 + audit_expect_stdout_nonempty + audit_expect_stderr_empty + audit_case_finish + fi + if audit_case_start help.targets.noargs help targets inventory all all all p1; then + audit_case_exec_kit targets + audit_expect_exit 0 + audit_expect_stdout_nonempty + audit_expect_stderr_empty + audit_case_finish + fi + if audit_case_start help.targets.short help targets inventory all all all p1; then + audit_case_exec_kit targets -h + audit_expect_exit 0 + audit_expect_stdout_nonempty + audit_expect_stderr_empty + audit_case_finish + fi + if audit_case_start help.targets.routed help targets inventory all all all p1; then + audit_case_exec_kit help targets + audit_expect_exit 0 + audit_expect_stdout_nonempty + audit_expect_stderr_empty + audit_case_finish + fi +} + +audit_module_help() { + audit_help_top + audit_help_targets + for audit_help_cmd in $audit_help_commands; do + audit_help_safe=$(audit_help_safe_name "$audit_help_cmd") + audit_help_run_noargs "$audit_help_cmd" "$audit_help_safe" + audit_help_run_long "$audit_help_cmd" "$audit_help_safe" + audit_help_run_short "$audit_help_cmd" "$audit_help_safe" + audit_help_run_routed "$audit_help_cmd" "$audit_help_safe" + done +} diff --git a/test/audit/release/modules/standalone.sh b/test/audit/release/modules/standalone.sh @@ -0,0 +1,214 @@ +#!/bin/sh + +audit_standalone_fixture_dir=$SCRIPT_DIR/fixtures + +audit_standalone_require_dist() { + if [ -z "$HARNESS_DIST" ]; then + audit_case_blocked "standalone and relocation workflows require --dist" + return 1 + fi + return 0 +} + +audit_standalone_stage() { + AUDIT_STANDALONE_RELOC="$CASE_DIR/work/Kit Release With Spaces" + AUDIT_STANDALONE_SOURCE_DIR="$CASE_DIR/work/read only source" + AUDIT_STANDALONE_SENTINELS=$CASE_DIR/work/sentinel-bin + AUDIT_STANDALONE_PATH=$AUDIT_STANDALONE_RELOC/bin:$AUDIT_STANDALONE_SENTINELS:/usr/bin:/bin + + { + printf 'cp -R %s %s\n' "$HARNESS_DIST" "$AUDIT_STANDALONE_RELOC" + printf 'chmod -R u+w %s\n' "$AUDIT_STANDALONE_RELOC" + printf 'mkdir -p %s %s\n' "$AUDIT_STANDALONE_SOURCE_DIR" "$AUDIT_STANDALONE_SENTINELS" + printf 'cp %s %s\n' \ + "$audit_standalone_fixture_dir/native_sdk_malloc.c" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" + } > "$CASE_DIR/setup.command" + if ! cp -R "$HARNESS_DIST" "$AUDIT_STANDALONE_RELOC" \ + > "$CASE_DIR/setup.stdout" 2> "$CASE_DIR/setup.stderr" || + ! chmod -R u+w "$AUDIT_STANDALONE_RELOC" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" || + ! mkdir -p "$AUDIT_STANDALONE_SOURCE_DIR" "$AUDIT_STANDALONE_SENTINELS" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" || + ! cp "$audit_standalone_fixture_dir/native_sdk_malloc.c" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" || + ! cp "$audit_standalone_fixture_dir/sentinel-tool.sh" \ + "$AUDIT_STANDALONE_SENTINELS/audit-sentinel" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" || + ! chmod +x "$AUDIT_STANDALONE_SENTINELS/audit-sentinel" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr"; then + audit_case_blocked "could not stage relocated distribution and standalone fixtures" + return 1 + fi + + for audit_standalone_tool in \ + clang cc gcc c++ g++ as ld ar ranlib objcopy strip nm \ + xcrun pkg-config cmake make lipo otool dsymutil; do + if ! ln -s audit-sentinel "$AUDIT_STANDALONE_SENTINELS/$audit_standalone_tool" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr"; then + audit_case_blocked "could not install host-tool sentinel $audit_standalone_tool" + return 1 + fi + done + : > "$CASE_DIR/artifacts/sentinel.log" + return 0 +} + +audit_standalone_discover_sysroot() { + audit_case_step_exec discover-sysroot "$AUDIT_STANDALONE_RELOC/bin/kit" cc \ + --support-dir "$AUDIT_STANDALONE_RELOC/support" -print-sysroot + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_stdout_nonempty + AUDIT_STANDALONE_SYSROOT= + IFS= read -r AUDIT_STANDALONE_SYSROOT < "$CASE_ACTIVE_DIR/stdout" || : + if [ -z "$AUDIT_STANDALONE_SYSROOT" ] || [ ! -d "$AUDIT_STANDALONE_SYSROOT" ]; then + audit_check_fail "native SDK reported by relocated Kit is unavailable: $AUDIT_STANDALONE_SYSROOT" + return 1 + fi + audit_check_pass "native SDK exists: $AUDIT_STANDALONE_SYSROOT" + return 0 +} + +audit_standalone_path_space() { + if ! audit_case_start standalone.path-space standalone kit,cc,install relocation-native native native native p0; then + return + fi + audit_standalone_require_dist || return + audit_standalone_stage || return + audit_standalone_discover_sysroot || { + audit_case_finish + return + } + + chmod 444 "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" + chmod 555 "$AUDIT_STANDALONE_SOURCE_DIR" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr" + + audit_case_step_exec direct "$AUDIT_STANDALONE_RELOC/bin/kit" cc \ + --support-dir "$AUDIT_STANDALONE_RELOC/support" \ + --sysroot "$AUDIT_STANDALONE_SYSROOT" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + -o "$CASE_DIR/artifacts/direct program" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/direct program" + + if [ -f "$CASE_DIR/artifacts/direct program" ]; then + audit_case_step_exec direct-run "$CASE_DIR/artifacts/direct program" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + + audit_case_step_exec path-only /usr/bin/env \ + "PATH=$AUDIT_STANDALONE_PATH" kit cc \ + --support-dir "$AUDIT_STANDALONE_RELOC/support" \ + --sysroot "$AUDIT_STANDALONE_SYSROOT" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + -o "$CASE_DIR/artifacts/path program" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/path program" + + audit_case_step_exec install "$AUDIT_STANDALONE_RELOC/bin/kit" install \ + --all "$CASE_DIR/install/bin" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_exists "$CASE_DIR/install/bin/cc" + + audit_case_step_exec installed-alias /usr/bin/env \ + "PATH=$CASE_DIR/install/bin:$AUDIT_STANDALONE_SENTINELS:/usr/bin:/bin" cc \ + --support-dir "$AUDIT_STANDALONE_RELOC/support" \ + --sysroot "$AUDIT_STANDALONE_SYSROOT" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + -o "$CASE_DIR/artifacts/installed program" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/installed program" + + if [ -f "$CASE_DIR/artifacts/installed program" ]; then + audit_case_step_exec installed-run "$CASE_DIR/artifacts/installed program" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + audit_expect_file_empty "$CASE_DIR/artifacts/sentinel.log" + audit_case_finish +} + +audit_standalone_support_discovery() { + if ! audit_case_start standalone.support-discovery standalone cc relocation-default native native native p0; then + return + fi + audit_standalone_require_dist || return + audit_standalone_stage || return + + audit_case_step_exec discover-sysroot "$AUDIT_STANDALONE_RELOC/bin/kit" cc -print-sysroot + audit_expect_exit 0 + audit_expect_stdout_nonempty + AUDIT_STANDALONE_SYSROOT= + IFS= read -r AUDIT_STANDALONE_SYSROOT < "$CASE_ACTIVE_DIR/stdout" || : + if [ -z "$AUDIT_STANDALONE_SYSROOT" ] || [ ! -d "$AUDIT_STANDALONE_SYSROOT" ]; then + audit_check_fail "native SDK reported by relocated Kit is unavailable: $AUDIT_STANDALONE_SYSROOT" + audit_case_finish + return + fi + + audit_case_step_exec compile "$AUDIT_STANDALONE_RELOC/bin/kit" cc \ + --sysroot "$AUDIT_STANDALONE_SYSROOT" \ + "$AUDIT_STANDALONE_SOURCE_DIR/native sdk malloc.c" \ + -o "$CASE_DIR/artifacts/default-discovery" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_nonempty "$CASE_DIR/artifacts/default-discovery" + if [ -f "$CASE_DIR/artifacts/default-discovery" ]; then + audit_case_step_exec run "$CASE_DIR/artifacts/default-discovery" + audit_expect_exit 0 + audit_expect_stderr_empty + fi + audit_expect_file_empty "$CASE_DIR/artifacts/sentinel.log" + audit_case_finish +} + +audit_standalone_post_install_move() { + if ! audit_case_start standalone.post-install-move standalone install,cc post-install-relocation native native native p1; then + return + fi + audit_standalone_require_dist || return + audit_standalone_stage || return + + audit_case_step_exec install "$AUDIT_STANDALONE_RELOC/bin/kit" install \ + --all "$CASE_DIR/install/bin" + audit_expect_exit 0 + audit_expect_stderr_empty + audit_expect_file_exists "$CASE_DIR/install/bin/cc" + + AUDIT_STANDALONE_MOVED="$CASE_DIR/work/Moved Kit Distribution" + printf 'mv %s %s\n' "$AUDIT_STANDALONE_RELOC" "$AUDIT_STANDALONE_MOVED" \ + >> "$CASE_DIR/setup.command" + if ! mv "$AUDIT_STANDALONE_RELOC" "$AUDIT_STANDALONE_MOVED" \ + >> "$CASE_DIR/setup.stdout" 2>> "$CASE_DIR/setup.stderr"; then + audit_case_blocked "could not move isolated distribution after install" + return + fi + + audit_case_step_exec installed-after-move /usr/bin/env \ + "PATH=$CASE_DIR/install/bin:$AUDIT_STANDALONE_SENTINELS:/usr/bin:/bin" cc --help + audit_expect_exit 0 + audit_expect_stdout_regex '^kit cc' + audit_expect_stderr_empty + audit_expect_file_empty "$CASE_DIR/artifacts/sentinel.log" + + audit_case_step_exec direct-after-move "$AUDIT_STANDALONE_MOVED/bin/kit" --help + audit_expect_exit 0 + audit_expect_stdout_nonempty + audit_expect_stderr_empty + audit_case_finish +} + +audit_module_standalone() { + audit_standalone_path_space + audit_standalone_support_discovery + audit_standalone_post_install_move +} diff --git a/test/audit/release/run.sh b/test/audit/release/run.sh @@ -0,0 +1,197 @@ +#!/bin/sh +set -u + +usage() { + cat <<'EOF' +Usage: test/audit/release/run.sh (--dist DIR | --kit FILE) [options] + +Required (exactly one): + --dist DIR shipped distribution root; uses DIR/bin/kit + --kit FILE exact Kit binary to audit + +Selection: + --module GLOBS comma-separated module globs + --command GLOBS comma-separated command globs + --workflow GLOBS comma-separated workflow globs + --target GLOBS comma-separated target globs + --arch GLOBS comma-separated architecture globs + --format GLOBS comma-separated format globs + --severity GLOBS comma-separated severities (p0,p1,p2,p3) + +Output: + --out DIR artifact root (default: build/audit/release-acceptance) + --run-id NAME run directory name (default: UTC timestamp plus PID) + +Other: + -h, --help show this help + +No compiler fallback exists. Existing run directories are never overwritten. +The command exits 1 when any selected case is FAIL or BLOCKED, 0 otherwise, +and 2 for harness usage/setup errors. +EOF +} + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd -P) || exit 2 +REPO_ROOT=$(CDPATH= cd "$SCRIPT_DIR/../../.." && pwd -P) || exit 2 + +DIST_ARG= +KIT_ARG= +OUT_ARG=$REPO_ROOT/build/audit/release-acceptance +RUN_ID= +HARNESS_FILTER_MODULE= +HARNESS_FILTER_COMMAND= +HARNESS_FILTER_WORKFLOW= +HARNESS_FILTER_TARGET= +HARNESS_FILTER_ARCH= +HARNESS_FILTER_FORMAT= +HARNESS_FILTER_SEVERITY= + +while [ "$#" -gt 0 ]; do + case "$1" in + --dist|--kit|--out|--run-id|--module|--command|--workflow|--target|--arch|--format|--severity) + audit_option=$1 + shift + if [ "$#" -eq 0 ]; then + printf 'audit: %s requires an argument\n' "$audit_option" >&2 + usage >&2 + exit 2 + fi + audit_value=$1 + case "$audit_option" in + --dist) DIST_ARG=$audit_value ;; + --kit) KIT_ARG=$audit_value ;; + --out) OUT_ARG=$audit_value ;; + --run-id) RUN_ID=$audit_value ;; + --module) HARNESS_FILTER_MODULE=$audit_value ;; + --command) HARNESS_FILTER_COMMAND=$audit_value ;; + --workflow) HARNESS_FILTER_WORKFLOW=$audit_value ;; + --target) HARNESS_FILTER_TARGET=$audit_value ;; + --arch) HARNESS_FILTER_ARCH=$audit_value ;; + --format) HARNESS_FILTER_FORMAT=$audit_value ;; + --severity) HARNESS_FILTER_SEVERITY=$audit_value ;; + esac + ;; + -h|--help) + usage + exit 0 + ;; + *) + printf 'audit: unknown option: %s\n' "$1" >&2 + usage >&2 + exit 2 + ;; + esac + shift +done + +if { [ -n "$DIST_ARG" ] && [ -n "$KIT_ARG" ]; } || \ + { [ -z "$DIST_ARG" ] && [ -z "$KIT_ARG" ]; }; then + printf 'audit: pass exactly one of --dist DIR or --kit FILE\n' >&2 + usage >&2 + exit 2 +fi + +HARNESS_DIST= +if [ -n "$DIST_ARG" ]; then + if [ ! -d "$DIST_ARG" ]; then + printf 'audit: distribution directory does not exist: %s\n' "$DIST_ARG" >&2 + exit 2 + fi + HARNESS_DIST=$(CDPATH= cd "$DIST_ARG" && pwd -P) || exit 2 + HARNESS_KIT=$HARNESS_DIST/bin/kit +else + if [ ! -f "$KIT_ARG" ]; then + printf 'audit: Kit binary does not exist: %s\n' "$KIT_ARG" >&2 + exit 2 + fi + audit_kit_dir=$(CDPATH= cd "$(dirname "$KIT_ARG")" && pwd -P) || exit 2 + HARNESS_KIT=$audit_kit_dir/$(basename "$KIT_ARG") +fi + +if [ ! -x "$HARNESS_KIT" ]; then + printf 'audit: Kit binary is not executable: %s\n' "$HARNESS_KIT" >&2 + exit 2 +fi + +mkdir -p "$OUT_ARG" || exit 2 +HARNESS_OUT_ROOT=$(CDPATH= cd "$OUT_ARG" && pwd -P) || exit 2 +if [ -z "$RUN_ID" ]; then + RUN_ID=$(date -u '+%Y%m%dT%H%M%SZ')-$$ +fi +case "$RUN_ID" in + ''|*/*|.|..) printf 'audit: invalid --run-id: %s\n' "$RUN_ID" >&2; exit 2 ;; +esac + +HARNESS_RUN_DIR=$HARNESS_OUT_ROOT/$RUN_ID +if [ -e "$HARNESS_RUN_DIR" ]; then + printf 'audit: refusing to overwrite existing run: %s\n' "$HARNESS_RUN_DIR" >&2 + exit 2 +fi +mkdir -p "$HARNESS_RUN_DIR/cases" || exit 2 + +if [ -n "$HARNESS_DIST" ]; then + HARNESS_PATH=$HARNESS_DIST/bin:/usr/bin:/bin +else + HARNESS_PATH=$(dirname "$HARNESS_KIT"):/usr/bin:/bin +fi + +HARNESS_SUMMARY_TSV=$HARNESS_RUN_DIR/summary.tsv +printf 'state\tseverity\tmodule\tcommand\tworkflow\ttarget\tarch\tformat\tcase\treason\n' \ + > "$HARNESS_SUMMARY_TSV" + +{ + printf 'kit=%s\n' "$HARNESS_KIT" + printf 'dist=%s\n' "$HARNESS_DIST" + printf 'path=%s\n' "$HARNESS_PATH" + printf 'filter.module=%s\n' "$HARNESS_FILTER_MODULE" + printf 'filter.command=%s\n' "$HARNESS_FILTER_COMMAND" + printf 'filter.workflow=%s\n' "$HARNESS_FILTER_WORKFLOW" + printf 'filter.target=%s\n' "$HARNESS_FILTER_TARGET" + printf 'filter.arch=%s\n' "$HARNESS_FILTER_ARCH" + printf 'filter.format=%s\n' "$HARNESS_FILTER_FORMAT" + printf 'filter.severity=%s\n' "$HARNESS_FILTER_SEVERITY" +} > "$HARNESS_RUN_DIR/run.metadata" + +. "$SCRIPT_DIR/lib/harness.sh" + +HARNESS_MODULES_RAN=0 +for audit_module_file in "$SCRIPT_DIR"/modules/*.sh; do + [ -f "$audit_module_file" ] || continue + audit_module_name=$(basename "$audit_module_file" .sh) + case "$audit_module_name" in + *[!a-z0-9_]*) printf 'audit: invalid module filename: %s\n' "$audit_module_file" >&2; exit 2 ;; + esac + audit_match_filter "$audit_module_name" "$HARNESS_FILTER_MODULE" || continue + . "$audit_module_file" + HARNESS_MODULES_RAN=$((HARNESS_MODULES_RAN + 1)) + audit_module_function=audit_module_$audit_module_name + "$audit_module_function" +done + +if [ "$HARNESS_MODULES_RAN" -eq 0 ]; then + printf 'audit: no module matched --module=%s\n' "$HARNESS_FILTER_MODULE" >&2 + exit 2 +fi + +{ + printf 'Kit release acceptance: %s\n' "$RUN_ID" + printf 'Kit: %s\n' "$HARNESS_KIT" + printf 'Selected: %s\n' "$HARNESS_SELECTED" + printf 'PASS: %s\n' "$HARNESS_PASS" + printf 'FAIL: %s\n' "$HARNESS_FAIL" + printf 'BLOCKED: %s\n' "$HARNESS_BLOCKED" + printf 'NOT_APPLICABLE: %s\n' "$HARNESS_NOT_APPLICABLE" + printf 'Details: %s\n' "$HARNESS_SUMMARY_TSV" +} > "$HARNESS_RUN_DIR/summary.txt" + +cat "$HARNESS_RUN_DIR/summary.txt" + +if [ "$HARNESS_SELECTED" -eq 0 ]; then + printf 'audit: no cases matched the requested filters\n' >&2 + exit 2 +fi + +if [ "$HARNESS_FAIL" -ne 0 ] || [ "$HARNESS_BLOCKED" -ne 0 ]; then + exit 1 +fi +exit 0