commit 4528ed88ea666e52a2e2a81bffe57011a50c1995
parent ed999ee8e1b053dc5cf1e7fba06cd1ea4d9fe9f2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 15 Jul 2026 20:46:16 -0700
driver: standardize command help behavior
Diffstat:
19 files changed, 265 insertions(+), 41 deletions(-)
diff --git a/driver/cmd/addr2line.c b/driver/cmd/addr2line.c
@@ -87,7 +87,7 @@ void driver_help_addr2line(void) {
"USAGE\n"
" kit addr2line [OPTIONS] -e FILE [ADDR...]\n"
"\n"
- "ADDRESS / OUTPUT\n"
+ "DESCRIPTION\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"
diff --git a/driver/cmd/build_coord.c b/driver/cmd/build_coord.c
@@ -1513,7 +1513,7 @@ int driver_build(int argc, char** argv) {
char hex[2u * KIT_BUILD_HASH_LEN + 1u];
int rc;
- if (driver_argv_wants_help(argc, argv, 1)) {
+ if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
driver_help_build();
return 0;
}
diff --git a/driver/cmd/cas.c b/driver/cmd/cas.c
@@ -23,11 +23,23 @@ void driver_help_cas(void) {
" kit cas verify-tree --cas DIR TREE_ID\n"
" kit cas materialize --cas DIR TREE_ID -C DIR\n"
"\n"
+ "DESCRIPTION\n"
+ " Stores immutable blobs and canonical file trees by content ID, then\n"
+ " inspects, verifies, or materializes those trees. CAS objects are\n"
+ " content-verified but are not signed.\n"
+ "\n"
+ "OPTIONS\n"
+ " --cas DIR content-addressed store (required)\n"
+ " --root DIR recursively add a directory tree\n"
+ " --map FILE add a tree described by a mapping file\n"
+ " -C DIR materialization destination\n"
+ " -h, --help show this help and exit\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"
+ " identities.\n"
"\n"
"ADD TREE INPUTS\n"
" --root DIR recursively store regular files from DIR.\n"
diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c
@@ -179,7 +179,12 @@ void driver_help_cc(void) {
" kit cc --emit=c|ir [options] INPUT.c emit C or IR\n"
" kit cc -fsyntax-only [options] INPUT... check only\n"
"\n"
- "INPUTS AND OUTPUTS\n"
+ "DESCRIPTION\n"
+ " Compiles C and assembly sources, optionally linking compatible\n"
+ " objects, archives, and shared libraries into a program. It can\n"
+ " also stop after preprocessing, dependency generation, semantic\n"
+ " checks, assembly, object, portable C, or semantic IR output.\n"
+ "\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"
@@ -190,7 +195,7 @@ void driver_help_cc(void) {
" <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"
+ "OPTIONS\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"
@@ -314,7 +319,7 @@ void driver_help_check(void) {
" `kit cc -fsyntax-only`. It writes diagnostics only: no object,\n"
" assembly, executable, or dependency output is produced.\n"
"\n"
- "OPTIONS AND INPUTS\n"
+ "OPTIONS\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"
diff --git a/driver/cmd/cmp.c b/driver/cmd/cmp.c
@@ -119,7 +119,7 @@ int driver_cmp(int argc, char** argv) {
size_t sl1 = 0, sl2 = 0;
int loaded1 = 0, loaded2 = 0;
- if (driver_argv_wants_help(argc, argv, 1)) {
+ if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
driver_help_cmp();
return 0;
}
diff --git a/driver/cmd/cpio.c b/driver/cmd/cpio.c
@@ -1002,7 +1002,7 @@ int driver_cpio(int argc, char** argv) {
size_t nops = 0;
int i, rc = 2;
- if (driver_argv_wants_help(argc, argv, 1)) {
+ if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
driver_help_cpio();
return 0;
}
diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c
@@ -14,6 +14,7 @@
#include "backtrace.h"
#include "cflags.h"
#include "driver.h"
+#include "hosted.h"
#include "inputs.h"
/* `kit dbg` — interactive JIT debugger.
@@ -63,9 +64,12 @@ typedef struct DbgOpts {
int opt_level;
int debug_info;
const char* entry;
+ const char* sysroot;
+ int wants_hosted_libc;
KitLanguage default_lang;
int has_default_lang;
+ DriverHostedPlan hosted;
DriverCflags cf;
DriverInputs inputs;
@@ -88,13 +92,12 @@ void driver_help_dbg(void) {
" kit dbg [options] [input.{c,s,wat,wasm} ...] [-- prog-arg ...]\n"
"\n"
"DESCRIPTION\n"
- " Mirrors `kit run` for compile flags and argv shape, but "
- "instead\n"
- " of calling the entry directly drops into a REPL that drives the\n"
- " JIT session: breakpoints, source-line stepping (into / over),\n"
- " instruction stepping,\n"
- " finish, backtrace, registers, locals/args, variable read/write,\n"
- " and raw memory examine. -g is forced on so source lines and\n"
+ " Uses `kit run`'s preprocessing, hosted-SDK, and argv shape, but\n"
+ " instead of calling the entry directly drops into a REPL that\n"
+ " drives the JIT session: breakpoints, source-line stepping into or\n"
+ " over calls, instruction stepping, finish, backtrace, registers,\n"
+ " locals/args, variable read/write, and raw memory examine. -g is\n"
+ " forced on so source lines and\n"
" variable locations are available at runtime.\n"
" Current debugger support is limited to Darwin/Linux aarch64\n"
" hosts.\n"
@@ -109,9 +112,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"
+ " --sysroot DIR, -isysroot DIR\n"
+ " Hosted SDK/sysroot for headers with -lc\n"
+ " -lc, -l c 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"
@@ -222,13 +225,40 @@ static int dbg_alloc_arrays(DbgOpts* o, int argc) {
}
if (driver_inputs_init(&o->inputs, o->env, DBG_TOOL, argc) != 0) return 1;
o->inputs.frontends = o->frontends;
- if (driver_cflags_init(&o->cf, o->env, argc) != 0) {
+ if (driver_cflags_init(
+ &o->cf, o->env,
+ argc + DRIVER_HOSTED_MAX_INCLUDES + DRIVER_HOSTED_MAX_DEFINES) != 0) {
driver_errf(DBG_TOOL, "out of memory");
return 1;
}
return 0;
}
+static int dbg_apply_hosted_profile(DbgOpts* o) {
+ DriverHostedRequest req;
+ uint32_t i;
+ if (!o->wants_hosted_libc) return 0;
+ {
+ DriverHostedRequest z = {0};
+ req = z;
+ }
+ req.env = o->env;
+ req.tool = DBG_TOOL;
+ req.target = driver_host_target();
+ req.sysroot = o->sysroot;
+ req.static_link = 0;
+ req.link_inputs = 0;
+ if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1;
+ for (i = 0; i < o->hosted.nsystem_includes; ++i) {
+ o->cf.system_include_dirs[o->cf.nsystem_include_dirs++] =
+ o->hosted.system_includes[i];
+ }
+ for (i = 0; i < o->hosted.ndefines; ++i) {
+ o->cf.defines[o->cf.ndefines++] = o->hosted.defines[i];
+ }
+ return 0;
+}
+
static int dbg_parse_language_name(DbgOpts* o, const char* name,
KitLanguage* out) {
KitLanguage lang;
@@ -314,6 +344,46 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) {
continue;
}
+ if (driver_streq(a, "--sysroot") || driver_streq(a, "-isysroot")) {
+ if (++i >= argc) {
+ driver_errf(DBG_TOOL, "%.*s requires an argument",
+ KIT_SLICE_ARG(kit_slice_cstr(a)));
+ return 1;
+ }
+ o->sysroot = argv[i];
+ continue;
+ }
+ if (driver_strneq(a, "--sysroot=", 10)) {
+ o->sysroot = a + 10;
+ continue;
+ }
+ if (driver_streq(a, "-lc")) {
+ o->wants_hosted_libc = 1;
+ continue;
+ }
+ if (driver_streq(a, "-l")) {
+ if (++i >= argc) {
+ driver_errf(DBG_TOOL, "-l requires an argument");
+ return 1;
+ }
+ if (!driver_streq(argv[i], "c")) {
+ driver_errf(DBG_TOOL, "unsupported hosted library for JIT: -l%.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
+ return 1;
+ }
+ o->wants_hosted_libc = 1;
+ continue;
+ }
+ if (driver_strneq(a, "-l", 2)) {
+ if (!driver_streq(a + 2, "c")) {
+ driver_errf(DBG_TOOL, "unsupported hosted library for JIT: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(a)));
+ return 1;
+ }
+ o->wants_hosted_libc = 1;
+ continue;
+ }
+
if (driver_streq(a, "-e")) {
if (++i >= argc) {
driver_errf(DBG_TOOL, "-e requires an argument");
@@ -381,6 +451,7 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) {
}
}
+ if (dbg_apply_hosted_profile(o) != 0) return 1;
if (!o->entry) o->entry = "main";
if (!o->debug_info) {
/* Without -g there are no source lines or variable locations to
@@ -394,6 +465,7 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) {
static void dbg_options_release(DbgOpts* o) {
size_t bound = o->argv_bound;
+ driver_hosted_plan_fini(o->env, &o->hosted);
driver_inputs_release(&o->inputs);
driver_cflags_fini(&o->cf, o->env);
driver_free(o->env, o->prog_argv, bound * sizeof(*o->prog_argv));
diff --git a/driver/cmd/install.c b/driver/cmd/install.c
@@ -86,7 +86,7 @@ int driver_install(int argc, char** argv) {
unsigned done_count = 0, failures = 0;
int i, rc = 2;
- if (driver_argv_wants_help(argc, argv, 1)) {
+ if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
driver_help_install();
return 0;
}
diff --git a/driver/cmd/nm.c b/driver/cmd/nm.c
@@ -252,7 +252,7 @@ void driver_help_nm(void) {
"USAGE\n"
" kit nm [OPTIONS] FILE...\n"
"\n"
- "INPUT / DEFAULT OUTPUT\n"
+ "DESCRIPTION\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"
diff --git a/driver/cmd/objcopy.c b/driver/cmd/objcopy.c
@@ -41,6 +41,14 @@ void driver_help_objcopy(void) {
"USAGE\n"
" kit objcopy [OPTIONS] INPUT [OUTPUT]\n"
"\n"
+ "DESCRIPTION\n"
+ " Copies an object while transforming its sections, symbols, strip\n"
+ " policy, or output format. If OUTPUT is omitted, INPUT is replaced\n"
+ " in place.\n"
+ "\n"
+ "OPTIONS\n"
+ " -h, --help Show this help and exit\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"
@@ -76,9 +84,6 @@ void driver_help_objcopy(void) {
" 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"
diff --git a/driver/cmd/pkg.c b/driver/cmd/pkg.c
@@ -35,6 +35,22 @@ void driver_help_pkg(void) {
" kit pkg sign -s SECKEY [-o OUT] [--comment C] FILE\n"
" kit pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n"
"\n"
+ "DESCRIPTION\n"
+ " Creates signed native or tar.gz packages, verifies package identity\n"
+ " and integrity, inspects manifests, manages trusted keys, and safely\n"
+ " unpacks authenticated contents.\n"
+ "\n"
+ "OPTIONS\n"
+ " -o OUT output path for keygen/create/sign\n"
+ " -s SECKEY secret signing key\n"
+ " -p PUBKEY explicit verification key\n"
+ " --tofu trust on first use\n"
+ " --format kpkg|tar.gz select or force the package format\n"
+ " --external DIR external CAS-shaped object store\n"
+ " -C DIR unpack destination\n"
+ " -h, --help show this help and exit\n");
+ driver_printf(
+ "\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"
@@ -759,7 +775,7 @@ int driver_pkg(int argc, char** argv) {
int rc;
if (driver_argv_wants_help(argc, argv, 1) || argc < 2) {
driver_help_pkg();
- return argc < 2 ? 2 : 0;
+ return 0;
}
sub = argv[1];
driver_env_init(&env);
diff --git a/driver/cmd/size.c b/driver/cmd/size.c
@@ -171,7 +171,7 @@ void driver_help_size(void) {
"USAGE\n"
" kit size [OPTIONS] FILE...\n"
"\n"
- "INPUT / DEFAULT OUTPUT\n"
+ "DESCRIPTION\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"
diff --git a/driver/cmd/strip.c b/driver/cmd/strip.c
@@ -37,6 +37,14 @@ void driver_help_strip(void) {
"USAGE\n"
" kit strip [OPTIONS] FILE\n"
"\n"
+ "DESCRIPTION\n"
+ " Copies a relocatable object or static archive while removing\n"
+ " selected debug sections and symbols. By default FILE is replaced\n"
+ " in place; use -o PATH to preserve the original.\n"
+ "\n"
+ "OPTIONS\n"
+ " -h, --help Show this help and exit\n"
+ "\n"
"OPERATIONS (last one wins; default is --strip-all)\n"
" --strip-debug remove debug-info sections\n"
" --strip-unneeded remove debug + symbols not needed by "
@@ -58,9 +66,6 @@ void driver_help_strip(void) {
" 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"
diff --git a/driver/cmd/symbolize.c b/driver/cmd/symbolize.c
@@ -95,12 +95,7 @@ void driver_help_symbolize(void) {
" <prog that prints a backtrace> | kit symbolize -e FILE\n"
" kit symbolize -e FILE < backtrace.txt\n"
"\n"
- "OPTIONS\n"
- " -e FILE object file with debug info (required)\n"
- " --basenames strip directory from file paths\n"
- " -h, --help show this help\n"
- "\n"
- "INPUT / OUTPUT\n"
+ "DESCRIPTION\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"
@@ -112,6 +107,11 @@ void driver_help_symbolize(void) {
"no\n"
"-e this release prints help and returns 0.\n"
"\n"
+ "OPTIONS\n"
+ " -e FILE object file with debug info (required)\n"
+ " --basenames strip directory from file paths\n"
+ " -h, --help show this help\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"
diff --git a/driver/main.c b/driver/main.c
@@ -339,8 +339,8 @@ void driver_help_top(void) {
" --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"
+ " Operand-oriented tools usually show help. Stream filters may read\n"
+ " stdin, dbg enters its REPL, and a few operational tools act directly.\n"
" Each tool's page documents its stdin and output conventions.\n"
"\n"
"OUTPUT AND ENVIRONMENT\n"
diff --git a/mk/test.mk b/mk/test.mk
@@ -71,6 +71,7 @@ TEST_TARGETS = \
test-driver-cpio \
test-driver-cas \
test-driver-cc \
+ test-driver-help \
test-driver-build \
test-driver-build-coord \
test-driver-lsan \
@@ -332,7 +333,10 @@ test-images:
test-cf-corpus-selftest:
@bash test/lib/kit_corpus_selftest.sh
-test-driver: test-driver-cc test-driver-build test-driver-build-coord test-driver-ar test-driver-cpio test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools test-driver-wasm
+test-driver: test-driver-help test-driver-cc test-driver-build test-driver-build-coord test-driver-ar test-driver-cpio test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools test-driver-wasm
+
+test-driver-help: bin
+ @KIT=$(abspath $(BIN)) sh test/driver/help_uniform.sh
test-driver-cc: bin
@KIT=$(abspath $(BIN)) sh test/driver/run.sh
diff --git a/test/TESTMAP.md b/test/TESTMAP.md
@@ -18,7 +18,7 @@ area you changed.
| `src/debug/**`, `src/dbg/**` | `make test-t1-debug` | `make test-debug test-dwarf test-dbg` |
| `src/emu/**`, `src/interp/**` | `make test-t1-emu-interp` | `make test-emu test-emu-unit test-interp` |
| `lang/wasm/**`, `src/arch/wasm/**` | `make test-t1-wasm` | `make test-wasm test-wasm-target` |
-| `driver/cmd/**`, byte utility tools | `make test-t1-tools` | matching `test-driver-*` target |
+| `driver/cmd/**`, byte utility tools | `make test-t1-tools` | `make test-driver-help` plus the matching `test-driver-*` target |
| `src/dist/**`, `src/api/cas.c`, `src/api/package.c` | `make test-t1-dist` | `make test-dist test-selfdist` |
| `src/gram/**`, `driver/cmd/gram.c`, `test/gram/**` | `make test-t1-gram` | `make test-gram test-driver-gram` |
| `test/lib/**`, `mk/test.mk` | `make test-t1-harness test-tier1` | affected suite |
diff --git a/test/audit/release/modules/help.sh b/test/audit/release/modules/help.sh
@@ -8,11 +8,11 @@ audit_help_safe_name() {
audit_help_noargs_kind() {
case "$1" in
- build|install|cpio|cmp|gram) printf '%s\n' usage2 ;;
+ 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 ;;
+ disas|mc) printf '%s\n' help2 ;;
update) printf '%s\n' operation ;;
*) printf '%s\n' help0 ;;
esac
diff --git a/test/driver/help_uniform.sh b/test/driver/help_uniform.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+# Focused CLI help-shape and no-argument behavior checks.
+
+set -u
+
+script_dir=$(cd "$(dirname "$0")" && pwd)
+repo_root=$(cd "$script_dir/../.." && pwd)
+
+KIT="${KIT:-$repo_root/build/kit}"
+
+if [ ! -x "$KIT" ]; then
+ echo "driver-help: kit binary not found at $KIT" >&2
+ exit 2
+fi
+
+work=$(mktemp -d "${TMPDIR:-/tmp}/kit-driver-help-test.XXXXXX")
+trap 'rm -rf "$work"' EXIT
+
+KIT_KIT_DIR="$repo_root/test/lib"
+. "$repo_root/test/lib/kit_sh_kit.sh"
+kit_report_init
+
+help_shape() {
+ tool=$1
+ out="$work/$tool.help"
+ err="$work/$tool.help.err"
+ if ! "$KIT" "$tool" --help > "$out" 2> "$err"; then
+ not_ok "help-shape-$tool" "$err"
+ return
+ fi
+ usage=$(awk '$0 == "USAGE" { print NR; exit }' "$out")
+ desc=$(awk '$0 == "DESCRIPTION" { print NR; exit }' "$out")
+ opts=$(awk '$0 == "OPTIONS" { print NR; exit }' "$out")
+ exits=$(awk '$0 == "EXIT CODES" { print NR; exit }' "$out")
+ if [ -n "$usage" ] && [ -n "$desc" ] && [ -n "$opts" ] &&
+ [ -n "$exits" ] && [ "$usage" -lt "$desc" ] &&
+ [ "$desc" -lt "$opts" ] && [ "$opts" -lt "$exits" ] &&
+ [ ! -s "$err" ]; then
+ ok "help-shape-$tool"
+ else
+ {
+ printf 'USAGE=%s DESCRIPTION=%s OPTIONS=%s EXIT_CODES=%s\n' \
+ "$usage" "$desc" "$opts" "$exits"
+ sed 's/^/stdout: /' "$out"
+ sed 's/^/stderr: /' "$err"
+ } > "$work/$tool.help.diag"
+ not_ok "help-shape-$tool" "$work/$tool.help.diag"
+ fi
+}
+
+for tool in cc check cas pkg build-exe build-lib nm size addr2line symbolize \
+ strip objcopy; do
+ help_shape "$tool"
+done
+
+for tool in build install cpio pkg cmp; do
+ noargs_out="$work/$tool.noargs.out"
+ noargs_err="$work/$tool.noargs.err"
+ help_out="$work/$tool.explicit-help.out"
+ help_err="$work/$tool.explicit-help.err"
+ if "$KIT" "$tool" > "$noargs_out" 2> "$noargs_err"; then
+ noargs_rc=0
+ else
+ noargs_rc=$?
+ fi
+ if "$KIT" "$tool" --help > "$help_out" 2> "$help_err"; then
+ help_rc=0
+ else
+ help_rc=$?
+ fi
+ if [ "$noargs_rc" -eq 0 ] && [ "$help_rc" -eq 0 ] &&
+ cmp -s "$noargs_out" "$help_out" && [ ! -s "$noargs_err" ] &&
+ [ ! -s "$help_err" ]; then
+ ok "noargs-help-$tool"
+ else
+ {
+ sed 's/^/noargs stdout: /' "$noargs_out"
+ sed 's/^/noargs stderr: /' "$noargs_err"
+ sed 's/^/help stdout: /' "$help_out"
+ sed 's/^/help stderr: /' "$help_err"
+ } > "$work/$tool.noargs.diag"
+ not_ok "noargs-help-$tool" "$work/$tool.noargs.diag"
+ fi
+done
+
+# A minimal synthetic hosted SDK proves that dbg accepts the same -lc/sysroot
+# workflow as run and actually adds the sysroot's system include directory.
+mkdir -p "$work/sysroot/usr/include" "$work/sysroot/usr/lib"
+printf '%s\n' '#define KIT_DBG_SYSROOT_VALUE 42' \
+ > "$work/sysroot/usr/include/kit_dbg_sysroot_test.h"
+: > "$work/sysroot/usr/lib/libc.so"
+cat > "$work/dbg-sysroot.c" <<'SRC'
+#include <kit_dbg_sysroot_test.h>
+int main(void) { return KIT_DBG_SYSROOT_VALUE == 42 ? 0 : 1; }
+SRC
+if "$KIT" dbg -lc --sysroot "$work/sysroot" --batch --command q \
+ "$work/dbg-sysroot.c" > "$work/dbg-sysroot.out" \
+ 2> "$work/dbg-sysroot.err"; then
+ ok "dbg-sysroot-hosted-headers"
+else
+ not_ok "dbg-sysroot-hosted-headers" "$work/dbg-sysroot.err"
+fi
+
+kit_summary driver-help
+kit_exit