Skip to content

fix(ci): build the clang-tidy lanes without LTO so the gate can pass at all - #1326

Merged
lusoris merged 1 commit into
masterfrom
fix/tidy-lane-lto-flag
Sep 5, 2026
Merged

lusoris merged 1 commit into
masterfrom
fix/tidy-lane-lto-flag

Conversation

@lusoris

@lusoris lusoris commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The required Tidy Changed and Tidy Ratchet gates have failed on every C/C++ pull request since #1290 merged, whatever the diff contained — the queue could not merge a single C change.

Cause, and it is mine: #1290 (ADR-1172) added b_lto_threads=4 to core/meson.build's default_options to cap link-time parallelism on the workstation. Meson renders that as GCC's -flto=4. The tidy lane configures with CC=gcc-14, so the flag lands in compile_commands.json — and clang-tidy parses those entries with clang, which rejects the argument outright:

clang: error: unsupported argument '4' to option '-flto='

Every translation unit therefore ended in 1 error generated, and the gate could never pass. Reproduced against a stock origin/master build: 1257 -flto=4 entries in compile_commands.json, and clang-tidy -p core/build core/src/picture.c → Error while processing.

Both tidy builds now configure with -Db_lto=false. They exist only to emit compile_commands.json and the generated headers, so LTO buys nothing there, and the SYCL tidy lane already configured that way for its own toolchain reason. After the change compile_commands.json holds zero -flto entries and the same file processes cleanly (459 suppressed, no error). Release builds keep the ADR-1172 cap unchanged.

Note for reviewers: the branch of #1312 carries its own copy of the T-TIDY-CHANGED-LTO-FLAG-2026-09-05 row, written while its agent was diagnosing the same failure. When #1312 rebases on this, drop its copy — the ADR-0165 duplicate-row gate added in #1322 will otherwise flag it.

Type

  • fix — CI gate unblocked

Checklist

  • Commits follow Conventional Commits.
  • make format && make lint is green locally (pre-commit on every touched file; actionlint reports only the seven SC2317 notes that are already on master).
  • Unit tests: n/a — a workflow configure flag; the reproducer below is the test.
  • Docs — no docs needed: CI-internal build flag; the invariant is recorded in docs/rebase-notes.md and the workflow comment.
  • SIMD/GPU, twins, new C sources, breaking change, ADR — n/a (ADR-1172 stands; only the tidy lane opts out).

Bug-status hygiene (ADR-0165)

  • docs/state.md — T-TIDY-CHANGED-LTO-FLAG-2026-09-05 added to Recently closed.

Netflix golden-data gate (ADR-0024)

  • I did not modify any assertAlmostEqual(...) score in the Netflix golden Python tests.

Deep-dive deliverables (ADR-0108)

  • Research digest — no digest needed: one incompatible flag, reproduced in the state.md row and below.
  • Decision matrix — no alternatives: only-one-way fix. The tidy build needs no LTO; stripping the flag from compile_commands.json afterwards, or reverting ADR-1172, would both be worse.
  • AGENTS.md invariant note — no rebase-sensitive invariants: the coupling is a rebase note (any compile-commands-only build configures -Db_lto=false).
  • Reproducer / smoke-test command — below.
  • CHANGELOG fragment — changelog.d/fixed/tidy-lane-lto-flag.md.
  • Rebase note — docs/rebase-notes.md entry.

Reproducer

printf 'int main(void){return 0;}\n' > /tmp/t.c
clang -flto=4 -c /tmp/t.c -o /dev/null     # error: unsupported argument '4' to option '-flto='
gcc   -flto=4 -c /tmp/t.c -o /dev/null     # accepted

# before: a stock master build
meson setup /tmp/b core -Denable_cuda=false -Denable_sycl=false && meson compile -C /tmp/b
grep -c 'flto=4' /tmp/b/compile_commands.json          # 1257
clang-tidy -p /tmp/b core/src/picture.c                # "Error while processing"

# after: what the lane now does
meson setup /tmp/b2 core -Denable_cuda=false -Denable_sycl=false -Db_lto=false && meson compile -C /tmp/b2
grep -c 'flto' /tmp/b2/compile_commands.json           # 0
clang-tidy -p /tmp/b2 core/src/picture.c               # processes cleanly

🤖 Generated with Claude Code

…at all

Since PR #1290 merged, the required Tidy Changed and Tidy Ratchet gates failed on
every C/C++ pull request regardless of its diff. core/meson.build's
default_options gained b_lto_threads=4 (ADR-1172) to cap link-time parallelism;
meson renders that as GCC's -flto=4, the tidy lane's gcc-14 build writes it into
compile_commands.json, and clang-tidy parses those entries with clang, which
rejects the argument outright:

    clang: error: unsupported argument '4' to option '-flto='

so every translation unit ended in "1 error generated". Reproduced against a
stock master build: 1257 -flto=4 entries, and clang-tidy on core/src/picture.c
reports "Error while processing".

Both tidy builds now configure with -Db_lto=false. They exist only to emit
compile_commands.json and the generated headers, so LTO buys nothing there, and
the SYCL tidy lane already configured that way for its own toolchain reason.
After the change compile_commands.json holds zero -flto entries and the same
file processes cleanly (459 suppressed, no error).

docs/state.md: T-TIDY-CHANGED-LTO-FLAG-2026-09-05 (Recently closed).
@lusoris lusoris added this to the 1.0.0 — First release milestone Sep 5, 2026
@lusoris
lusoris marked this pull request as ready for review September 5, 2026 20:33
@lusoris
lusoris merged commit 5a08030 into master Sep 5, 2026
113 of 114 checks passed
@lusoris
lusoris deleted the fix/tidy-lane-lto-flag branch September 5, 2026 20:58
lusoris added a commit that referenced this pull request Sep 5, 2026
`core/src/feature/feature_extractor.h` was the last file this branch touches
that still carried clang-tidy findings (8). clang-tidy has no compile command
for a header and falls back to the nearest matching TU's —
`feature_extractor.cpp` — so it parses this C header as C++ and proposes
rewrites that would break the roughly one hundred C translation units which
include it: `<cstdint>` / `<cstdlib>` for the C headers, `using` for the
`typedef struct`s, a C23 fixed underlying type on the two flag enums (which
MSVC does not document under `/std:clatest`, and the Windows MSVC build is a
required check), and a rename of the upstream `__VMAF_FEATURE_EXTRACTOR_H__`
include guard that every other header under `core/src/` also uses.

Discharged with one file-scoped `NOLINTBEGIN` / `NOLINTEND` bracket — the
shape ADR-1138 prescribes and `core/test/test_feature_extractor.c` already
uses — whose justification and its ADR-0141 / ADR-1138 / ADR-0148 / ADR-0150
citations live inside the marker's own block comment, where
`scripts/ci/tidy-ratchet.py::count_uncited_nolints` looks for them. No
declaration, macro, type or guard changed, so no includer is affected.

The `-Db_lto=false` half of the original commit is dropped: PR #1326 landed
the identical fix on master while this branch was in review.

Verified: clang-tidy 22.1.8 against a CPU-only build dir configured like the
CI lane reports 0 warnings, 0 errors, exit 0 for all four CI-linted files
(`feature_extractor.cpp`, `feature_extractor.h`, `libvmaf.c`,
`test_feature_extractor.c`); `count_uncited_nolints` on the header returns 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 5, 2026
`core/src/feature/feature_extractor.h` was the last file this branch touches
that still carried clang-tidy findings (8). clang-tidy has no compile command
for a header and falls back to the nearest matching TU's —
`feature_extractor.cpp` — so it parses this C header as C++ and proposes
rewrites that would break the roughly one hundred C translation units which
include it: `<cstdint>` / `<cstdlib>` for the C headers, `using` for the
`typedef struct`s, a C23 fixed underlying type on the two flag enums (which
MSVC does not document under `/std:clatest`, and the Windows MSVC build is a
required check), and a rename of the upstream `__VMAF_FEATURE_EXTRACTOR_H__`
include guard that every other header under `core/src/` also uses.

Discharged with one file-scoped `NOLINTBEGIN` / `NOLINTEND` bracket — the
shape ADR-1138 prescribes and `core/test/test_feature_extractor.c` already
uses — whose justification and its ADR-0141 / ADR-1138 / ADR-0148 / ADR-0150
citations live inside the marker's own block comment, where
`scripts/ci/tidy-ratchet.py::count_uncited_nolints` looks for them. No
declaration, macro, type or guard changed, so no includer is affected.

The `-Db_lto=false` half of the original commit is dropped: PR #1326 landed
the identical fix on master while this branch was in review.

Verified: clang-tidy 22.1.8 against a CPU-only build dir configured like the
CI lane reports 0 warnings, 0 errors, exit 0 for all four CI-linted files
(`feature_extractor.cpp`, `feature_extractor.h`, `libvmaf.c`,
`test_feature_extractor.c`); `count_uncited_nolints` on the header returns 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 5, 2026
`core/src/feature/feature_extractor.h` was the last file this branch touches
that still carried clang-tidy findings (8). clang-tidy has no compile command
for a header and falls back to the nearest matching TU's —
`feature_extractor.cpp` — so it parses this C header as C++ and proposes
rewrites that would break the roughly one hundred C translation units which
include it: `<cstdint>` / `<cstdlib>` for the C headers, `using` for the
`typedef struct`s, a C23 fixed underlying type on the two flag enums (which
MSVC does not document under `/std:clatest`, and the Windows MSVC build is a
required check), and a rename of the upstream `__VMAF_FEATURE_EXTRACTOR_H__`
include guard that every other header under `core/src/` also uses.

Discharged with one file-scoped `NOLINTBEGIN` / `NOLINTEND` bracket — the
shape ADR-1138 prescribes and `core/test/test_feature_extractor.c` already
uses — whose justification and its ADR-0141 / ADR-1138 / ADR-0148 / ADR-0150
citations live inside the marker's own block comment, where
`scripts/ci/tidy-ratchet.py::count_uncited_nolints` looks for them. No
declaration, macro, type or guard changed, so no includer is affected.

The `-Db_lto=false` half of the original commit is dropped: PR #1326 landed
the identical fix on master while this branch was in review.

Verified: clang-tidy 22.1.8 against a CPU-only build dir configured like the
CI lane reports 0 warnings, 0 errors, exit 0 for all four CI-linted files
(`feature_extractor.cpp`, `feature_extractor.h`, `libvmaf.c`,
`test_feature_extractor.c`); `count_uncited_nolints` on the header returns 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 5, 2026
…del options (ADR-1183) (#1312)

* fix(cuda): make the default model run on CUDA — cambi context push, option validation, CPU-twin dispatch (ADR-1183)

- integer_cambi_cuda.c: push/pop the CUDA context around the submit path (the DtoH
  readback ran with no current context: CUDA_ERROR_INVALID_CONTEXT), mirror the CPU
  cambi option table (cambi_high_res_speedup/hrs, vlt, cmxv) and the TVI table init
  from cambi.c so the emitted feature name matches the model's.
- integer_motion_cuda.c: route scores through the feature_name_dict so motion3 emits
  integer_motion3_mmxv_18 like the CPU twin.
- feature_extractor.cpp: an opts_dict key the chosen extractor does not declare is an
  error (-EINVAL, logged) instead of being silently dropped.
- libvmaf.c: when a GPU twin lacks an option the model sets, resolve the CPU twin for
  that feature and log it (ADR-1183) — adm3 of vmaf_v1.0.16_3d0h runs on the CPU under
  CUDA/SYCL/HIP until the csf_mode port lands (docs/state.md
  T-GPU-ADM-CSF-MODE-NOT-PORTED-2026-09-05).
- python/test/cuda_default_model_test.py (new, skips without CUDA); .gitleaks.toml
  allowlists VMAF pooled-metric names (the test's `<name>_key = "<metric>"` lines
  tripped generic-api-key on every PR because gitleaks scans all branches).
Verified on the RTX 4090: 576x324 and 1080p reproducers exit 0; adm3 bit-exact vs CPU,
cambi delta 2.66e-3 / 1.76e-2 (tracked), v0.6.1 unchanged, typo option rejected;
golden gate 271 passed / 12 skipped / 0 failed.

* fix(core): keep the model-option gating inside the tidy budget

The ADR-1183 GPU-twin fallback pushed vmaf_use_features_from_model past the
readability-function-size threshold and the two new test cases in
test_feature_extractor.c added eight uncited modernize-use-nullptr findings, so
the whole-tree ratchet (ADR-1142) refused the PR.

- The fallback moves into fex_honouring_model_options(), a helper that returns
  the extractor able to honour every option the model sets for one feature (the
  GPU twin, or the CPU twin with an INFO line, or NULL when nothing provides the
  feature). vmaf_use_features_from_model is back under the threshold and reads
  as one loop again.
- The new tests' NULL arguments and returns carry the ADR-1138 citation the
  other C TUs use (MSVC /std:clatest has no C nullptr).
- feature_extractor.cpp lost four findings to the option-validation refactor, so
  its baseline entry is tightened 54 -> 50 with the count CI measured (the local
  clang is a different version and is not the authority).

Behaviour is unchanged: the CUDA default-model run still exits 0 with the CPU
dispatch line for adm3, test_feature_extractor is 13/13.

* test(core): clear clang-tidy debt in test_feature_extractor.c (59 -> 0)

The touched-file cleanup rule (ADR-0141) makes this PR inherit every
pre-existing clang-tidy finding in the test TUs it edits.  Discharge all
59 in `core/test/test_feature_extractor.c`:

* modernize-use-nullptr (54): replace the 17 scattered, partly stale
  `NOLINTNEXTLINE` markers (several of which sat above comment lines and
  suppressed nothing) with the single file-scoped
  `NOLINTBEGIN(modernize-use-nullptr)` / `NOLINTEND` bracket that
  ADR-1138 prescribes and that `core/src/libvmaf.c`,
  `core/test/test_output.c` and the other C TUs already use.  The TU
  keeps spelling the null pointer constant `NULL`: the required MSVC
  C lane has no documented C `nullptr`, and upstream parity matters for
  the sync story.

* readability-function-size (5): split the oversized cases into one
  behaviour per test instead of suppressing them.
  - `test_feature_extractor_flush` and
    `test_feature_extractor_initialization_options` now share a
    `FexFixture` (context + ref/dist pictures + collector) with
    `fex_fixture_open` / `fex_fixture_close` /
    `fex_fixture_extract_two_frames_and_flush` helpers.
  - `test_fex_vector_dedup_by_provided_feature_name` gained a
    `fex_vector_create_and_append` helper.
  - `test_feature_extractor_supports_options_helper` split into
    `test_supports_options_empty_dict`, `_known_key`,
    `_unknown_key_reports_missing`, `_alias_key` and `_null_extractor`.
  - the SpEED GPU-twin registry guard moved out of
    `test_get_feature_extractor_by_name_and_feature_name` into
    `test_speed_gpu_twins_resolve_by_name`, which also keeps that case
    inside budget in an all-backends build.
  - `run_tests` now delegates to `run_registry_tests`,
    `run_context_tests` and `run_option_tests`, the grouping pattern
    `test_cli_parse.c` and `test_iqa_helpers.c` already use.

No assertion was dropped: 17 cases / 79 assertions become 18 cases / 88
assertions, and the conditions folded into the fixture helpers are still
checked there and reported through the fixture's status.  The Netflix
golden assertions are untouched, and the PR's CUDA CAMBI fix is
unaffected.

Verified: `clang-tidy -p core/build core/test/test_feature_extractor.c`
59 -> 0 warnings (clang-tidy 22.1.8); `core/build/test/test_feature_extractor`
18/18 pass; `meson test -C core/build --suite fast --no-suite gpu`
115/115 Ok; `pre-commit run --files` clean.  Baseline tightened in
`scripts/ci/tidy-baseline-cpu.json` (entry removed, total 3060 -> 3001)
per the ADR-1142 ratchet, and a rebase note recorded for the new group
runners and the NOLINT bracket.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(core): clear clang-tidy debt in feature_extractor.cpp (51 -> 0)

The touched-file cleanup rule (ADR-0141) makes this PR inherit every
pre-existing clang-tidy finding in the TUs it edits.  Discharge all 51 in
`core/src/feature/feature_extractor.cpp` with no `NOLINT`:

* modernize-use-nullptr (29): this TU is C++, so ADR-1138's keep-`NULL`
  exemption (which exists for the required MSVC C lane and upstream C
  parity) does not apply — spell the null pointer constant `nullptr`.
  Prose in comments still says "NULL" where it refers to the C twin.

* misc-const-correctness (14): `const` on the error-code locals, on
  `ctx_array_sz`, and on the three read-only pointees (`ref_priv` in
  extract, `fex` in `vmaf_fex_ctx_pool_release` / `_flush`).

* misc-use-anonymous-namespace (5): move `feature_extractor_list[]`,
  `vmaf_fex_ctx_parse_options`, `get_fex_list_entry`,
  `ctx_pool_ensure_slot_ctx` and `ctx_pool_claim_slot` out of `static`
  and into anonymous namespaces.  The C twin keeps `static`.

* readability-function-size (2): split rather than suppress.
  - `vmaf_feature_extractor_context_extract` (85 lines / 16 branches):
    the CUDA and HIP picture buf_type validation moves verbatim into
    `check_pic_buf_type(fex, ref_priv)`, which returns 0 or -EINVAL.
    Same predicates, same log messages, same return value.
  - `get_fex_list_entry` (68 lines) splits into `find_fex_list_entry`
    (the existing-entry lookup), `grow_fex_list` (the geometric realloc
    plus placement-new of the fresh slots) and `init_fex_list_slot`
    (capacity/condvar/ctx_list/opts_dict init, with the same unwind on
    each failure).  The helpers return an errno; `get_fex_list_entry`
    still returns nullptr on any failure, which the caller still turns
    into -EINVAL, and `pool->cnt` is still bumped only on success.

* cert-dcl03-c,misc-static-assert (1): clang-tidy 22 flags every
  `assert()` whose condition contains no non-constexpr call, so
  `assert(pool->capacity > 0)` cannot stay.  Replaced with a hard
  `if (pool->capacity == 0) return -EINVAL;` in `grow_fex_list` — an
  always-on guard is strictly stronger than a check compiled out under
  NDEBUG, and the state is unreachable either way (`pool_create` seeds
  capacity = 8 and this is its only writer).  `<cassert>` is now unused
  and dropped.

Dropping `static` from the registry declaration broke the
`check-dispatch-registry` pre-commit / CI gate, whose `sed` range
hard-coded the `static VmafFeatureExtractor *feature_extractor_list[]`
spelling; it now accepts both spellings, with a new self-test case
(Test 4) covering the anonymous-namespace form.

No behaviour change: the extractor registry, the context pool and the
option validation this PR adds all keep their exact control flow, and
the CUDA CAMBI fix is untouched.

Verified: `clang-tidy -p core/build core/src/feature/feature_extractor.cpp`
51 -> 0 warnings (clang-tidy 22.1.8, CUDA-enabled build) and 0 in a
CPU-only build dir matching the CI lane; `core/build/test/test_feature_extractor`
18/18 pass; `meson test -C core/build --suite fast --no-suite gpu` 115/115 Ok;
`bash scripts/ci/tests/test-check-dispatch-registry.sh` 11/11 pass;
`pre-commit run --files` clean.  Baseline tightened in
`scripts/ci/tidy-baseline-cpu.json` (entry removed, total 3001 -> 2951)
per the ADR-1142 ratchet, and a rebase note recorded for the anonymous
namespaces and the two function splits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(core): clear the last clang-tidy debt in feature_extractor.h

`core/src/feature/feature_extractor.h` was the last file this branch touches
that still carried clang-tidy findings (8). clang-tidy has no compile command
for a header and falls back to the nearest matching TU's —
`feature_extractor.cpp` — so it parses this C header as C++ and proposes
rewrites that would break the roughly one hundred C translation units which
include it: `<cstdint>` / `<cstdlib>` for the C headers, `using` for the
`typedef struct`s, a C23 fixed underlying type on the two flag enums (which
MSVC does not document under `/std:clatest`, and the Windows MSVC build is a
required check), and a rename of the upstream `__VMAF_FEATURE_EXTRACTOR_H__`
include guard that every other header under `core/src/` also uses.

Discharged with one file-scoped `NOLINTBEGIN` / `NOLINTEND` bracket — the
shape ADR-1138 prescribes and `core/test/test_feature_extractor.c` already
uses — whose justification and its ADR-0141 / ADR-1138 / ADR-0148 / ADR-0150
citations live inside the marker's own block comment, where
`scripts/ci/tidy-ratchet.py::count_uncited_nolints` looks for them. No
declaration, macro, type or guard changed, so no includer is affected.

The `-Db_lto=false` half of the original commit is dropped: PR #1326 landed
the identical fix on master while this branch was in review.

Verified: clang-tidy 22.1.8 against a CPU-only build dir configured like the
CI lane reports 0 warnings, 0 errors, exit 0 for all four CI-linted files
(`feature_extractor.cpp`, `feature_extractor.h`, `libvmaf.c`,
`test_feature_extractor.c`); `count_uncited_nolints` on the header returns 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@lusoris lusoris added the type:bug Something isn't working label Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant