ZaStoGram_desktop/.agents/shared/ninja-target-evidence.md

186 lines
9.1 KiB
Markdown

# Ninja Target Provenance Evidence
`.agents/shared/evidence/ninja_target.py` answers one question from a
configured CMake Ninja tree: for one exact consuming output (an executable or
shared library link edge), which object files does that edge consume, which
compile edge and source produced each one, which PCH each one uses, and what
command-line state requested macros have in a selected policy cohort. It reads
Ninja's evaluated graph only and never runs a graph command.
## When to use it
Use the helper for every claim of the form "target T links object O", "O was
compiled from S with PCH P", "macro M is (not) defined for T's sources", "CMake
option X reaches the compiler as macro M", and before any symbol or absence
check (`nm`, `objdump`, `strings`) that is meant to speak for a target. Feed
those tools only object paths the helper selected.
These are not ownership evidence:
- a glob or a basename (`find -name update_verify.cpp.o` finds the
`Telegram.dir` copy and the `Packer.dir` copy of the same source);
- `ninja -t compdb` or `-t compdb-targets` listings: `compdb-targets` walks
order-only dependencies, so it pulls in sibling tools such as `Debug/Packer`,
and it does not exist on Ninja 1.11;
- `ninja -t inputs`, which flattens explicit, implicit and order-only edges;
- the CMake option name read as a macro name.
## Commands
Run from the checkout root; `--build-dir`, `--manifest`, `--config` and
`--target` are mandatory and exact.
Linux (`out/`, Ninja Multi-Config):
```bash
python3 .agents/shared/evidence/ninja_target.py \
--build-dir out --manifest build-Debug.ninja --config Debug \
--target Debug/Telegram \
--policy-target Telegram \
--option-macro DESKTOP_APP_DISABLE_AUTOUPDATE=TDESKTOP_DISABLE_AUTOUPDATE \
--expect-defined _DEBUG \
--select-source SOURCE_ROOT_AS_CONFIGURED/Telegram/SourceFiles/core/update_verify.cpp
```
`SOURCE_ROOT_AS_CONFIGURED` is the source root the graph spells, which is the
container path (for example `/usr/src/tdesktop`) when `out/` was configured in
a container; take it from `CMAKE_HOME_DIRECTORY` in `out/CMakeCache.txt`.
macOS Ninja tree (the link output lives inside the bundle; `Telegram` itself
is a phony alias and is refused):
```bash
python3 .agents/shared/evidence/ninja_target.py \
--build-dir BUILD_DIR --manifest build-Debug.ninja --config Debug \
--target Debug/Telegram.app/Contents/MacOS/Telegram \
--policy-target Telegram \
--option-macro DESKTOP_APP_DISABLE_AUTOUPDATE=TDESKTOP_DISABLE_AUTOUPDATE \
--expect-defined TDESKTOP_ALLOW_CLOSED_ALPHA --expect-defined _DEBUG \
--select-source "$PWD/Telegram/SourceFiles/core/update_verify.cpp" \
--list-objects
```
Request only macros whose values may be published: readings include the
values of requested defined macros, so never pass `TDESKTOP_API_ID` or
`TDESKTOP_API_HASH`. Other options: `--ninja PATH`, repeated `--select-object PATH`,
`--expect-undefined NAME`, `--expect-defined NAME=VALUE`. A single-config
`Ninja` tree uses `--manifest build.ninja` with `--config` equal to
`CMAKE_BUILD_TYPE`. Selectors are exact graph spellings: build-relative object
paths (`Telegram/CMakeFiles/Telegram.dir/Debug/SourceFiles/core/update_verify.cpp.o`)
and absolute source paths as the graph spells them. A basename is refused.
Exit 0 prints a JSON report with `verdict: "PASS"`; any refusal or unmet
expectation prints `error: ...` and exits 1. There is no FAIL verdict to
interpret.
## Reading the report
- `objects`: consumed object count, counts by graph edge kind (`explicit`,
`implicit`) and producer rule, `external` objects with no producer in the
graph (prebuilt Qt plugin-init objects), and with `--list-objects` the
ordered list with source, rule and PCH per object.
- `selections`: each selected object; `--select-source` also lists
`excluded_siblings`, the same source's objects this target does not consume,
with their rule and graph consumers (for example Packer's copy with consumer
`Debug/Packer`).
- `pchs`: every PCH used, its producer rule, header, dialect and consumer
count. `archives` and `shared_libraries` are listed, not resolved.
- `cohort`: the policy cohort rules, object count, its PCHs, names of defined
macros (never values) and the requested macro readings, including option
contracts with their cache entries.
- `build.path_mapping`, `acquisition.snapshot_digest` and `limits` (below).
Counts are observations of that tree, not constants to copy into later work.
## Limits
Configured-graph claims only. The report does not prove that a command ran,
that an object exists on disk, which archive members the linker pulled, or
what headers, compiler built-ins or source code define. The manifest snapshot
guards graph coherence during acquisition; it does not claim the tree was
otherwise untouched.
## Supported and refused grammar
Supported: CMake-generated `Ninja` / `Ninja Multi-Config` trees with
`CMakeCache.txt`; POSIX command strings made of `&&`-joined simple commands
with `:` and `cd ABS` segments (exactly one segment links `-o TARGET`, from the
build directory); GCC/Clang drivers with an optional `ccache`/`sccache`
launcher; clang `-Xclang -include-pch`/`-emit-pch` and GCC `-include` + `.gch`
PCH dialects; response files expanded by `ninja -t compdb -x`; ordered
`-DNAME[=V]`, `-D NAME`, `-UNAME`, `-U NAME` (later wins, `-DX=0` is defined).
Refused with a message: other shell operators, `$`, backticks, newlines, a
quoted `"&&"`; unexpanded `@file`; unknown link operands (including a `.pch`
that CMake puts on the link line for `$<TARGET_OBJECTS:...>` of a PCH object
library); duplicate object operands; link objects that are not explicit or
implicit graph inputs (the message says when they are order-only only); graph
inputs missing from the link line; producer output/source mismatches;
non-compile producers; PCHs used but not graph inputs (a GCC `-Winvalid-pch`
command counts as PCH use), or graph inputs not used, or produced by another
target's rule (`REUSE_FROM`); `-imacros`, `-Wp,-D`, `-Xpreprocessor`,
`-Xclang -D`; a non-PCH forced include when a macro is requested; MSVC,
Xcode and other generators. A real graph that needs more grammar is recorded
as a refusal, not widened in place.
## Policy cohorts and option contracts
`--policy-target NAME` selects consumed objects compiled by
`(C|CXX|OBJC|OBJCXX)_COMPILER__NAME(_unscanned)?_CONFIG` plus the PCHs they
use. Every cohort object and PCH must meet each expectation and each object
must agree with its PCH. The whole link closure is never one cohort: object
libraries such as `td_export` or `td_ui` have their own definitions and PCHs,
so run one invocation per cohort. On multi-config trees a cohort
`CMAKE_INTDIR` must equal the selected configuration.
An absence claim needs a known-present control in the same run: any
`--expect-undefined` (or OFF `--option-macro`) requires at least one
`--expect-defined` (or ON `--option-macro`).
`--option-macro OPTION=MACRO` reads a BOOL cache entry (ON -> defined, OFF ->
undefined) and checks the named macro. The pair is written explicitly because
spellings differ: `Telegram/cmake/telegram_options.cmake` maps
`DESKTOP_APP_DISABLE_AUTOUPDATE` to `TDESKTOP_DISABLE_AUTOUPDATE`, and
`DESKTOP_APP_SPECIAL_TARGET` (a STRING, so usable only as an
`--expect-defined TDESKTOP_ALLOW_CLOSED_ALPHA` control) likewise. On an OFF
tree a wrong macro name and the right one are both absent, so an option claim
needs a tree where the option is ON, and the same-name contract
(`DESKTOP_APP_DISABLE_AUTOUPDATE=DESKTOP_APP_DISABLE_AUTOUPDATE`) must fail
there.
## Path identity
Paths are compared as exact strings. The only transformation strips the
configured build directory (`CMAKE_CACHEFILE_DIR`) prefix from absolute
command tokens. Ninja reports its physical working directory, which can differ
from the configured one: macOS temporary trees (`/tmp` vs `/private/tmp`,
`/var/folders` vs `/private/var/folders`) record `validated_by: "samefile"`;
a tree configured in a container (`/usr/src/tdesktop/out`) and read on the
host records `validated_by: "CMakeCache.txt CMAKE_CACHEFILE_DIR"`. A
configured path that exists locally as a different directory is refused.
No realpath, basename or suffix matching is used.
## Raw captures and symbols
The helper has no capture option. When a check needs an independent
cross-check, run `ninja -C BUILD_DIR -f MANIFEST -t query ...` or
`-t compdb -x RULE` directly into the ignored task `.local/` run directory:
Telegram compile commands carry private `TDESKTOP_API_ID`/`TDESKTOP_API_HASH`
values, so raw records never go into published evidence. Use only
`query`/`compdb`; never `ninja` without `-t` on a tree you must not build.
For symbol or absence checks run `nm BUILD_DIR/<selected object>` on the
helper-selected path, and read the excluded sibling the same way as a control
that shows the markers differ.
## Self-tests
```bash
python3 .agents/shared/evidence/ninja_target_test.py -v
```
The suite needs `ninja`; its compiled class also needs `cmake`, a C/C++
compiler and `nm`, and configures and builds a small two-target fixture where
`fx_app` and `fx_tool` compile the same `shared/common.cpp`. On a host without
cmake (the WSL build host), set `NINJA_TARGET_TEST_SKIP_CMAKE=1` to skip only
that class explicitly; a missing tool otherwise fails the suite.