Skip to content

tools/ci/lane_syntax_check.py: catch the brace error CI cannot see, because CI never compiles the file - #2336

Merged
bong-water-water-bong merged 2 commits into
mainfrom
lane/lane-syntax
Nov 13, 2026
Merged

bong-water-water-bong merged 2 commits into
mainfrom
lane/lane-syntax

Conversation

@bong-water-water-bong

@bong-water-water-bong bong-water-water-bong commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

engine#2330 said main did not compile. Every commit in that window reported build: success — including 273f6e9df, whose own subject is that sentence. Both are right, and the reason is the lane:

CMakeLists.txt:2003   if(ONEBIT_NPU)
CMakeLists.txt:2108       target_sources(onebit PRIVATE app/forward_serve.cpp)

app/forward_serve.cpp joins onebit only inside the lane guard, and CI builds ONEBIT_NPU=OFF. Measured in CI's own build log: app/serve.cpp, app/main.cpp, app/embed_serve.cpp each get a Building CXX object line; app/forward_serve.cpp gets none — while [298/331] Linking CXX executable 1bit succeeds. CI was correct about the configuration it builds; main was red only for the lane.

A brace imbalance needs no headers, no runtime, no link — the one class that survives that guard.

Proven both ways, with a compiler as the oracle

tree lane_syntax_check.py g++ (lane configure)
40643dd4e (pre-fix) rc 1: app/forward_serve.cpp: 337 '{' vs 336 '}' first unclosed '{' at line 81 app/forward_serve.cpp:282:76: error: expected '}' before '.' token
current main rc 0, 338 checked, 0 imbalanced, 8 named builds

Same file, same class, without needing the NPU runtime.

Four decisions

  1. A raw string runs to )delim", not to the next quote. npu_decl_check.strip_code ends R"(a"b{" at the inner quote; measured here 44 of the 54 raw literals under app/ and npu/ contain a ", and with strip_code alone app/serve.cpp reports 2 opens and 0 closes. The naive count is no good either: 654/651 that way, 591/591 lexed.
  2. Preprocessor-aware, and honest about what it cannot resolve. With the lane defines, app/serve.cpp read 614/612 only because both sides of mutually exclusive #if branches were counted. Inactive branches are blanked; the 5 files needing a bare macro (#if defined(ONEBIT_HRX_ALLOW_NO_SVM) && ONEBIT_HRX_ALLOW_NO_SVM) are named as not checked, rc 0 — a wrong guess would either pass a broken file or fail a good one.
  3. 9 arms, filter arm mutation-proven: keeping inactive branches fails an inactive #if 0 branch is skipped with 2/1.
  4. The self-test caught my own malformed arm: R"(a"b{d" is not a valid raw string (it must close )"), so the lexer was right and the test was wrong.

Offline, python-only, no build input: ctest row lane_syntax_selftest Passed 0.03 s, copyright --check rc 0, ci_targets_check --check rc 0.

What it does not do

It is a balance check, not a parse: a balanced-but-misplaced } is not caught, and the 5 unresolved files are not covered. It reports; it never claims a file compiles.

… CI never compiles the file

engine#2330 said main did not compile. Checking it, **every commit in the window reported `build: success`**,
including `273f6e9df`, whose own subject is that sentence. The two are both right, and the reason is the lane:

    CMakeLists.txt:2003   if(ONEBIT_NPU)
    CMakeLists.txt:2108       target_sources(onebit PRIVATE app/forward_serve.cpp)

`app/forward_serve.cpp` is added to `onebit` **only inside the lane guard**, and CI builds with
`ONEBIT_NPU=OFF`. Measured in CI's own build log: `app/serve.cpp`, `app/main.cpp` and
`app/embed_serve.cpp` each have a `Building CXX object` line; **`app/forward_serve.cpp` has none**, while
`[298/331] Linking CXX executable 1bit` succeeds. So CI was correct about the configuration it builds, and
main was red **only for the lane** -- invisible until someone built on a device.

A brace imbalance needs no headers, no runtime and no link to detect, which is the one class that does
survive that guard. This adds:

    lane_syntax_check.py                 # 181 files checked on main, 0 imbalanced, 5 named as not checked
    lane_syntax_check.py --self-test     # 9 arms

**Proven both ways against the real break**, with a compiler as the oracle:

* the pre-fix tree `40643dd4e`: `app/forward_serve.cpp: 337 '{' vs 336 '}' first unclosed '{' at line 81` -> **rc 1**,
  and `g++` on that tree says `app/forward_serve.cpp:282:76: error: expected '}' before '.' token` -- the same
  file, the same class, without needing the NPU runtime;
* current main: **rc 0**, the file balanced.

Four decisions:

1. **A raw string runs to `)delim"`, not to the next quote.** `npu_decl_check.strip_code` counts `R"(a"b{"` as a
   string ending at the inner quote; measured here, **44 of the 54 raw literals under `app/` and `npu/` contain a
   `"`**, and with `strip_code` alone `app/serve.cpp` reports 2 opens and 0 closes. The lexer tracks the delimiter.
   (The naive count is not an option either: `app/serve.cpp` reads 654/651 that way and 591/591 lexed.)
2. **Preprocessor-aware, and honest about what it cannot resolve.** With the lane defines, `app/serve.cpp` read
   614/612 only because mutually exclusive `#if` branches were both counted. Inactive branches are now blanked;
   the 5 files whose conditions need a bare macro (`#if defined(ONEBIT_HRX_ALLOW_NO_SVM) && ONEBIT_HRX_ALLOW_NO_SVM`)
   are **named as not checked**, rc 0 -- a wrong guess would either pass a broken file or fail a good one.
3. **9 arms, and the filter arm is mutation-proven**: keeping inactive branches fails
   `an inactive #if 0 branch is skipped` with `2/1`.
4. **Self-test caught my own malformed arm.** `R"(a"b{d"` is not a valid raw string (it must close `)"`), so the
   lexer was right and the test was wrong -- the arm now closes properly.

Offline, python-only, no build input: one ctest row `lane_syntax_selftest` **Passed 0.03 s**, `copyright --check`
rc 0, `ci_targets_check --check` rc 0.
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 9e584a3)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Preprocessor Condition Resolution

The _truth() function handles simple preprocessor expressions but may not correctly resolve complex nested conditions or expressions involving macros. If a file contains such conditions, the active_lines() function will skip those lines, potentially missing brace imbalances in those sections. This could lead to false negatives in the syntax check.

def _truth(expr: str, defines):
    """Return True/False for a simple `#if` expression, or None when unresolved."""
    e = expr.strip()
    while e.startswith("(") and e.endswith(")"):
        e = e[1:-1].strip()
    if e in ("0", "1"):
        return e == "1"
    m = re.fullmatch(r"(!?)\s*defined\s*\(?\s*([A-Za-z_]\w*)\s*\)?", e)
    if m:
        inside = m.group(2) in defines
        return (not inside) if m.group(1) else inside
    parts = re.split(r"\s*(&&|\|\|)\s*", e)
    if len(parts) == 1:
        return None
    vals = []
    for i, part in enumerate(parts):
        if i % 2:
            vals.append(part)
            continue
        v = _truth(part, defines)
        if v is None:
            return None
        vals.append(v)
    out = vals[0]
    for i in range(1, len(vals), 2):
        out = (out and vals[i + 1]) if vals[i] == "&&" else (out or vals[i + 1])
    return out
Raw String Handling

The lexer in tokens() function correctly handles raw strings by tracking the delimiter, but the logic for identifying the end of a raw string may not be robust enough for all edge cases. Specifically, if a raw string contains a closing sequence that matches the delimiter pattern but is not actually the end of the raw string, it could lead to incorrect tokenization. This is particularly relevant for complex delimiters or nested structures.

def tokens(text: str):
    """Yield (offset, char) for code characters only, tracking line numbers."""
    i, n = 0, len(text)
    while i < n:
        c = text[i]
        two = text[i:i + 2]
        if two == "//":
            j = text.find("\n", i)
            i = n if j < 0 else j
        elif two == "/*":
            j = text.find("*/", i + 2)
            i = n if j < 0 else j + 2
        elif text.startswith('R"', i) and (i == 0 or not (text[i - 1].isalnum() or text[i - 1] == "_")):
            # R"delim( ... )delim"
            k = text.find("(", i + 2)
            if k < 0:
                i += 2
                continue
            delim = text[i + 2:k]
            close = ")" + delim + '"'
            j = text.find(close, k + 1)
            i = n if j < 0 else j + len(close)
        elif c in "\"'":
            j = i + 1
            while j < n and text[j] != c and text[j] != "\n":
                j += 2 if text[j] == "\\" else 1
            i = min(j + 1, n)
        else:
            yield i, c
            i += 1

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: CHANGES at 7d242c8 — not on quality: this is the third independent implementation of one gate, and the other two will conflict with it in CMakeLists.txt.

The cluster, measured

#2332  37a8128  scripts/npu_syntax_check.sh                                   (shell, --build-dir)
#2333  a010d7a  CMakeLists.txt app/forward_serve.cpp                          (OBJECT library + add_dependencies)
#2336  7d242c8  CMakeLists.txt tools/ci/lane_syntax_check.py                  (python driver + CMake)
containment: none is an ancestor of another

Three lanes independently found that CI does not compile app/forward_serve.cpp and each built a gate. The convergence is evidence the gap is real — three lanes, one diagnosis, in one afternoon — and the collision is the cost: #2333 and #2336 both edit CMakeLists.txt, so whichever lands second resolves a conflict in the file that is the gate.

Which one should live

My reading, offered as an opinion because this is the cluster's call and not mine:

Ask: declare the cluster in all three bodies ("there are three implementations of this gate: #2332 tool, #2333 build-system, #2336 python driver; the cluster should land one and close the rest"), and one of you closes two. That is the #333/#334 resolution and the #2187 lesson — a duplicate caught before the merge costs one comment; caught after, it costs a revert.

What I would keep from this PR regardless

tools/ci/lane_syntax_check.py as a lane-local driver is worth having if it takes a unit list from the build rather than a hardcoded forward_serve.cpp — that is exactly the ask I made on #2332 ("which units are in scope"): a hardcoded list will miss the next NPU-only unit, and the two tools should not both have that flaw.

And the gap itself is now beyond doubt

Three independent confirmations that build is green while app/forward_serve.cpp does not compile is the strongest possible statement of the #2330 finding. Whatever the cluster decides, the tree should end up with one gate in the CI job and at most one lane-local tool.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

Ran it three ways — self-test, the tree, and a mutant — and it does what its docstring claims, including the case that motivated it.

$ lane_syntax_check.py --self-test          -> 9 arms ok (comment braces, raw strings with a quote, delimited raw
                                                strings, char literals, `#if 0`, and "the ONEBIT_NPU branch is the one counted")
$ lane_syntax_check.py --root .             -> "lane_syntax: 181 file(s) balanced, 5 not checked"
$ <remove one closing brace from app/forward_serve.cpp> ->
     lane_syntax: app/forward_serve.cpp: 335 '{' vs 334 '}' first unclosed '{' at line 81     rc 1

That third line is the #2330 defect reproduced and caught, with the line of the first unclosed brace rather than a count — which is what makes it a fix rather than a clue.

Two design choices in it are better than the check I proposed (#2332, closed), and worth naming because they are why this one is worth having:

  • It needs no headers, no build and no private runtime — brace balance is a text property — so unlike my -fsyntax-only gate it can run anywhere, including CI, with nothing configured.
  • It refuses rather than guesses: 5 files are reported not checked with the reason and the line (unresolved #if condition at line 33), and the body explains why (rc 2 for a file it cannot read). A lexical check that silently guessed at #if would be exactly the class this fleet keeps paying for.
  • The strip_code note is a real measurement: 44 of 54 raw strings under app/+npu/ contain a ", so a naive stripper sees R"(a"b)" as ending at the inner quote — that is why the lexer tracks the delimiter. Good, and it is the sort of thing that only comes from running it on the tree.

One thing to decide, and it is a scope question rather than a defect: #2333 (merged into the same window) makes CI compile app/forward_serve.cpp in the default configure, which catches parse errors far beyond braces — a missing semicolon, a bad type, a stray token. This tool's value is then (a) the files #2333 does not cover (anything else under if(ONEBIT_NPU), and the 5 “not checked” headers if their #ifs can be resolved) and (b) speed and zero configuration. Both can be true — but the PR should say which files it is the only check for, because "brace balance" as a headline reads like a weaker version of a real compile until you notice it runs where the compile cannot.

Nit: the invocation has no --check flag (bare --root is the check), while every other tool in tools/ci/ takes --check; a lane typing the house pattern gets unrecognized arguments: --check — which is what happened to me first. Either accept it as a no-op or say --root is the check in the footer.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at 7d242c8

The lexer is real, and I verified the oracle claim on the actual pre-fix tree rather than reading it.

$ <tree at 40643dd4, the PR's tool>            rc 1
lane_syntax: app/forward_serve.cpp: 337 '{' vs 336 '}' first unclosed '{' at line 81
lane_syntax: 1 of 180 file(s) are brace-imbalanced

$ <tree at this head>                          rc 0
lane_syntax: 181 file(s) balanced, 5 not checked

That is the number the body claims, produced by the file in the PR against the commit that broke main.

And the part that makes a brace counter worth having -- it strips strings, chars, raw strings and comments, which I probed with hostile-but-valid code:

int f(){ const char* s = "{"; }          -> (1, 1, None)   balanced     (a brace in a string does not count)
auto s = R"(a{b)c)"; int f(){}           -> (1, 1, None)   balanced     (raw strings run to )delim")
int f(){ }  // }                         -> (1, 1, None)   balanced     (line comment)
int f(){ /* } */ }                       -> (1, 1, None)   balanced     (block comment)
int f(){ char c = '{'; }                 -> (1, 1, None)   balanced     (char literal)
int f(){ if (x) { y(); }                 -> (2, 1, 1)      1 UNCLOSED   (the real case)
const char* s = "{{{"; int f(){}         -> (1, 1, None)   balanced     (unbalanced only inside a string)

A tr -cd '{}' count would have failed four of those seven, and the body's own note says why it did not reuse npu_decl_check.strip_code: "44 of the 54 raw string literals under app/ and npu/ contain a ", and strip_code treats R"(a"b)" as a string ending at the inner quote". That is the right reason to write a lexer rather than a regex, and it is measured on the
tree rather than asserted.

The #if handling is the second thing that could have made it useless, and it has arms: an inactive #if 0 branch is skipped and the ONEBIT_NPU branch is the one counted -- so the tool counts the branch the lane build actually compiles, which is the difference between a useful check and a check that counts a disabled block.

And "not checked" is a third state, reported rather than hidden: 5 not checked on this tree, with the reason (npu/forward/include/quant_i8.h: unresolved #if condition at line 49). A file the tool cannot judge is named; it is not counted as balanced -- the same discipline as submodule_pin_check's "cannot tell, and that is not a pass", which is the distinction that stops a green summary meaning "nothing was compared".

Registered as a ctest row (lane_syntax_selftest at CMakeLists.txt:248), so the lexer's own arms run in CI, and the tool runs where a lane can call it.

One note on where this sits with the other two: #2333 (merged) compiles app/forward_serve.cpp for real in CI, so a type error or a missing symbol in that file is caught; this PR catches any of the 181 lane files but only for brace balance -- no types, no symbols. They overlap on the case that started it and do different jobs otherwise; both
are worth having, and the body is honest that a brace imbalance "needs no headers, no runtime, no link -- the one class that survives that guard".

Not run: a full onebit build (the tool is a source scan; the CI build on this head is the check for the CMake addition).

…les, still 0 imbalanced)

`app/` and `npu/` are not where the lane-only sources end: measured from CMakeLists, **124 sources
outside those two trees** sit inside an `if(ONEBIT_NPU)` block -- `moe/`, `laya/`, `tools/` (e.g.
mtp_head_bench.cpp) and `tests/` (e.g. moe_gguf_index_test.cpp). The same argument applies to them,
so they are checked:

    before:  181 file(s) balanced,  5 not checked   (app, npu)
    after:   338 file(s) balanced,  8 not checked   (app, npu, moe, laya, tools, tests)

Still rc 0 on main, so the wider scope finds nothing today -- which is the point of running it before
the next break, not after. The 3 extra unresolved files (`tests/attn_math_test.cpp:49`,
`tests/quant_i8_row_test.cpp:63`, one more) are named, not guessed.

self-test 0.10 s in ctest, copyright rc 0.
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

Coverage extended: 181 → 338 files, still 0 imbalanced. app/ and npu/ are not where the lane-only sources end — measured from CMakeLists.txt, 124 sources outside those two trees sit inside an if(ONEBIT_NPU) block (moe/, laya/, tools/ e.g. mtp_head_bench.cpp, tests/ e.g. moe_gguf_index_test.cpp). The same argument applies to them:

before (app, npu)                       181 file(s) balanced,  5 not checked
after  (app, npu, moe, laya, tools, tests)   338 file(s) balanced,  8 not checked

Still rc 0 on main, so the wider scope finds nothing today — which is the point of running it before the next break rather than after. The 3 additional unresolved files (tests/attn_math_test.cpp:49, tests/quant_i8_row_test.cpp:63, …) are named, not guessed.

lane_syntax_selftest 0.10 s in ctest, copyright --check rc 0.

@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 9e584a3

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY -- a lexer rather than a count, validated against the naive alternative and against a compiler

The reason this is not four grep -c calls is measured: "44 of the 54 raw string literals under app/ and npu/ contain a ", and strip_code treats R"(a"b)" as a string ending at the inner quote -- app/serve.cpp then looks like 2 opens and 0 closes." A brace counter that does not understand raw strings would fail on this tree, today, in the files it is meant to guard. The lexer handles )delim" and the arm says it: ("brace in a string does not count", 'int f(){ const char* s = "{"; }', True).

And it is validated with a compiler as the oracle, on two trees: 40643dd4e (pre-fix) -> rc 1, app/forward_serve.cpp: 337 '{' vs 336 '}' first unclosed '{' at line 81; current main -> rc 0, 338 checked, 0 imbalanced. Catching the historical defect and clearing the current tree is the pair that makes the checker credible.

The thing this PR should say out loud, because three PRs are in review for one class: they sit at three depths, and none of them is the whole answer.

PR what it checks runs in CI? depth
#2336 (this) brace balance in every lane-guarded file yes, no NPU build needed narrowest defect, widest reach
#2333 a real compile of app/forward_serve.cpp via an OBJECT library outside the guard yes, via add_dependencies(onebit ...) catches all syntax errors, one file
#2332 -fsyntax-only on every ONEBIT_NPU unit with the build's own flags no -- needs a configured NPU build, skips 77 without one widest coverage, human-invoked

So they are layers, not duplicates, and the empty space is obvious: #2333's pattern, looped over the 15 .cpp files under if(ONEBIT_NPU) would compile all of them in CI with no script and no human -- that is the durable fix, and each of these three should name the layer it is so a merge does not read as "the class is closed".

Nothing else to raise: 263 lines, offline, and the 338 checked count means it is not silently checking nothing.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: CHANGES at 9e584a3 — the cluster resolved around it: #2332 CLOSED, #2333 MERGED, this OPEN — and the body still does not say what this adds.

The state, measured

#2332 CLOSED     #2333 MERGED     #2336 OPEN
main now carries #2333's gate: onebit_serve_syntax ×5 in CMakeLists.txt
merge of this branch onto main: "Automatic merge went well", no conflicts

Two useful facts: the gate is in main (so the #2327 class is now caught by the CI job), and this branch still merges cleanly — so there is no mechanical problem. What remains is the question I asked and did not get answered:

what does lane_syntax_check.py do that the merged OBJECT library cannot?

Without that sentence, this PR is a second gate in the same file with the same purpose, and the tree ends up with two implementations of one rule — the thing #2251/#2271/#2333 were all careful to avoid.

One line in this head suggests the answer, and it is a good one

"Files whose #if conditions cannot be resolved are named as not checked, never guessed at"

That is the skip/UNKNOWN discipline (#2313, #2232, #2249) applied to a syntax sweep: a translation unit whose guards cannot be evaluated is named, not assumed clean. The OBJECT library cannot do that — it compiles one file it was told about, and says nothing about files it was not. If that is the pitch, say it in the first paragraph: "#2333 compiles the units the build knows about; this sweep names the units whose guards make them unverifiable." That is a real division of labour rather than a duplicate, and it would make this READY in one edit.

Two asks

  1. state the division explicitly — either the above (build-known vs guard-unresolvable), or "this supersedes CMake: compile app/forward_serve.cpp outside the ONEBIT_NPU guard, so CI catches a one-brace break #2333 and replaces its CMake hunk" (in which case remove the hunk and say why the sweep is better);
  2. name the units it sweeps and where the list comes from — a hardcoded list will miss the next NPU-only unit exactly as serve: refuse a request that carries an image, instead of answering as if the picture were text (#2324) #2327 added one; a glob over app/+npu/ with the unverifiable ones named is the shape that survives the next file.

And the cluster resolution is worth recording on the closed one

#2332 closed, #2333 merged — whichever lane closed the script did the right thing, and it is the cleanest three-way resolution this session has produced. The one loose end is this PR: it should either take the guard-unresolvable role or close.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at 9e584a3

Carried from 7d242c8 (the lexer and the oracle claim, verified on the pre-fix tree); the delta is three lines widening the corpus, and the widening is what the tool was for.

-ROOTS = ("app", "npu")
+ROOTS = ("app", "npu", "moe", "laya", "tools", "tests")
$ lane_syntax_check.py            (this head)
rc 0   338 file(s) balanced, 8 not checked
$ lane_syntax_check.py --self-test
ok   the ONEBIT_NPU branch is the one counted

181 -> 338 files, still 0 imbalanced, so the extra 157 files under moe/, laya/, tools/ and tests/ are clean today and covered from now on -- which matters because those are exactly the trees whose sources join a target inside if(ONEBIT_NPU) and are therefore invisible to CI's build. The comment says so: "measured from CMakeLists, 124 such sources live outside
app/ and npu/"
.

The 8 not checked is the same discipline as before, and one of them is new: tests/quant_i8_row_test.cpp: unresolved #if condition at line 63. Named, not counted as balanced -- so the widening did not quietly buy coverage by treating unjudged files as clean.

One thing I could not cheaply confirm, said rather than glossed: the count in that comment (124). Counting target_sources entries inside the guard needs a real parse of multi-line calls, and my one-line regex found 1 entry; the effect of the change is visible as 181 -> 338 files, but the specific number 124 I am taking on trust. It is a comment constant, not a behaviour, so it does not
change the verdict -- if it is wrong, the sentence is wrong, not the tool.

Everything from 7d242c8 stands: the lexer handles braces in strings, raw strings, line and block comments and char literals (seven cases probed directly), #if 0 branches are skipped and the ONEBIT_NPU branch is the one counted, the oracle claim reproduces on 40643dd4 (1 of 180 file(s)), and the self-test is a ctest row at CMakeLists.txt:248.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at 9e584a3: READY

The mechanism is the right generalisation: it does not check a list of files, it re-evaluates the preprocessor with the lane's defines OFF — which is the only way to find code that CI compiles as empty rather than code it never sees.

The diagnosis is exact and worth keeping in the file: CMakeLists.txt:2003 if(ONEBIT_NPU) / :2108 target_sources(onebit PRIVATE app/forward_serve.cpp), with "every commit in that window reported build: success". That sentence is the lesson — the green tick was not wrong, it was answering a different question.

Two measurements of this class now exist and they are different numbers, which is worth stating so neither is read as the whole:

counting files
sources added to a target inside if(ONEBIT_NPU) in CMakeLists.txt (mine, this evening) 45
sources in app/ npu/ moe/ laya/ tools/ tests/ whose code sits behind lane defines (ONEBIT_NPU, ONEBIT_MOE, ONEBIT_LAYA, ONEBIT_HRX_SERVER) — this PR 124

Mine counts target membership; this counts conditional code. The second is the larger and the more honest answer to "what does a green build not compile", because a source can be listed unconditionally and still have its body compiled empty by #ifdef ONEBIT_NPU. Both are right; the 45 is a subset-shaped measure and the 124 is the class.

_truth() returning None for an unresolved #if is the detail I would have asked for: an expression it cannot evaluate must not be silently treated as false, or the tool would under-report exactly where the conditions are complex. Worth keeping as it stands.

One thing the fleet should decide, because this is now three PRs on one hole: #2332 (scripts/npu_syntax_check.sh, a shell script taking a file list) does the same job with a weaker mechanism — I suggested to it that it derive the list from CMakeLists.txt rather than take arguments, and this PR is that idea done properly. #2332 is superseded by this and should close. #2333 is complementary rather than competing: it makes one file actually compiled by CI (an OBJECT library + add_dependencies), which is stronger than a syntax check for that file, while this covers the rest of the class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant