Skip to content

Commit cf01617

Browse files
QNN skill buck cmake (#19197) (#19197)
1 parent c0f1d94 commit cf01617

2 files changed

Lines changed: 220 additions & 2 deletions

File tree

.claude/skills/qualcomm/SKILL.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
---
22
name: qualcomm
3-
description: Build, test, or develop the QNN (Qualcomm AI Engine Direct) backend. Use when working on backends/qualcomm/, building QNN (use backends/qualcomm/scripts/build.sh), adding new ops or passes, running QNN delegate
4-
tests, or exporting models for Qualcomm HTP/GPU targets.
3+
description: Build, test, or develop the QNN (Qualcomm AI Engine Direct) backend. Use when working on backends/qualcomm/, building QNN (use backends/qualcomm/scripts/build.sh), adding new ops or passes, running QNN delegate tests, or exporting models for Qualcomm HTP/GPU targets. Also exposes a Buck-vs-CMake parity workflow — invoke as `/qualcomm buck-fix`, `/qualcomm buck-cmake fix`, `/qualcomm buck-parity`, or any user request to fix `test-qnn-buck-build-linux` CI failures or check buck/cmake drift in backends/qualcomm/.
54
---
65

76
# QNN (Qualcomm AI Engine Direct) Backend
87

8+
## Slash command argument routing
9+
10+
When this skill is invoked with arguments (e.g. `/qualcomm <args>`), classify the args FIRST and route before doing anything else:
11+
12+
| If args contain any of… | Route to |
13+
|---|---|
14+
| `buck-fix`, `buck-cmake`, `buck cmake`, `buck-parity`, `buck parity`, `buck ci`, `qnn buck`, `fix qnn ci`, `test-qnn-buck-build-linux`, or any natural-language request to fix QNN buck CI / catch buck-cmake drift | Read `buck_parity.md` and follow it end-to-end. Default mode: full iterative-fix loop. If the args also contain `check` or `diagnose`, run buck once and report only — do not apply fixes. |
15+
| (no args) or any other args | Stay in this file; treat as a normal `/qualcomm` discovery request and use the Advanced Topics table below. |
16+
917
## Advanced Topics
1018

1119
When the user's request falls into one of these areas, read the corresponding file before proceeding:
@@ -15,6 +23,7 @@ When the user's request falls into one of these areas, read the corresponding fi
1523
| Export / lowering / quantization options / pass pipelines | `lowering_export.md` | User asks about exporting, lowering, quantization config, QuantDtype, QuantRecipe, pass pipelines |
1624
| New op development | `new_op_development.md` | User asks to add/implement a new op or op builder |
1725
| Model enablement | `model_enablement.md` | User asks to enable a new model end-to-end |
26+
| Buck vs CMake parity (pre-PR or fix red CI) | `buck_parity.md` | User changed BUCK / TARGETS / `targets.bzl` or `CMakeLists.txt` under `backends/qualcomm/`, added new `.cpp` / `.h` / `#include` there, is preparing to push a PR that touches QNN, **or** the `test-qnn-buck-build-linux` CI check on their PR is red and they want to fix it locally. Direct trigger: `/qualcomm buck-fix`. |
1827
| Profiling & debugging | `profiling.md` | User asks about profiling, optrace, QHAS, QAIRT Visualizer *(file TBD)* |
1928

2029
## Building
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Buck vs CMake Parity — QNN backend
2+
3+
This guide reproduces the `test-qnn-buck-build-linux` CI check locally and iteratively fixes it. OSS contributors typically work with CMake. Internal CI also exercises a Buck build of `//backends/qualcomm/...`. The two systems can drift — a new include, source file, or dep that CMake handles via global include paths can break Buck's strict per-target header visibility.
4+
5+
Audience: an OSS contributor (likely Qualcomm-side) editing files under `backends/qualcomm/`. All paths in this guide are repo-root-relative (no `fbcode/` or `xplat/` prefix).
6+
7+
## When to run
8+
9+
There are two entry points — both use the exact same workflow below:
10+
11+
**Pre-PR (proactive):** any of the following just touched `backends/qualcomm/`:
12+
13+
- a new `#include` in a `.cpp` or `.h`
14+
- a new `.cpp` file (especially under `runtime/backends/**`, `aot/**`, `aot/wrappers/**`)
15+
- a change to `targets.bzl`, `BUCK`, `TARGETS`, or `CMakeLists.txt`
16+
- preparing to push a PR
17+
18+
**Post-PR (fix red CI):** the GitHub check `test-qnn-buck-build-linux` is failing on the contributor's PR and they want to fix it locally before pushing again. Pull the failing branch (`git checkout <branch>`), then run this flow. The same buck command, same recipes, same iteration loop — the only difference is the contributor already has a known-failing baseline.
19+
20+
## How to invoke
21+
22+
This workflow is reachable in three ways:
23+
24+
```
25+
/qualcomm buck-fix # explicit trigger — full iterative fix loop (default)
26+
/qualcomm buck-cmake # synonym
27+
/qualcomm buck-parity # synonym
28+
/qualcomm <any natural-language request mentioning buck CI or drift> # routed by SKILL.md
29+
```
30+
31+
To skip the auto-fix loop and only diagnose, append `check` or `diagnose`:
32+
33+
```
34+
/qualcomm buck-fix check
35+
/qualcomm buck diagnose
36+
```
37+
38+
In `check`/`diagnose` mode, run pre-flight 0 + 1, run buck once, report the failure (or success) verbatim, and stop without applying any recipe.
39+
40+
For all other invocations, run the full iterative loop documented below.
41+
42+
## Pre-flight 0 — buck2 must be on $PATH
43+
44+
`./install_executorch.sh` does **not** install buck2. Check:
45+
46+
```bash
47+
command -v buck2 || echo "MISSING"
48+
```
49+
50+
If missing, install the same pinned version OSS CI uses. The pin lives at `.ci/docker/ci_commit_pins/buck2.txt`. Read it, then download the matching release from `https://github.com/facebook/buck2/releases`. Pattern (Linux x86_64; adjust the asset name for macOS / arm64):
51+
52+
```bash
53+
BUCK2_VERSION=$(cat .ci/docker/ci_commit_pins/buck2.txt)
54+
ASSET=buck2-x86_64-unknown-linux-gnu.zst # or buck2-aarch64-apple-darwin.zst on macOS
55+
wget -q "https://github.com/facebook/buck2/releases/download/${BUCK2_VERSION}/${ASSET}"
56+
zstd -d "${ASSET}" -o buck2
57+
chmod +x buck2
58+
sudo mv buck2 /usr/local/bin/ # any directory on $PATH
59+
```
60+
61+
Reference: `.ci/docker/common/install_buck.sh` (the script CI itself uses). If the contributor declines to install, halt — they will only catch this drift when CI fails after pushing.
62+
63+
## Pre-flight 1 — QNN_SDK_ROOT and `.buckconfig`
64+
65+
The Buck build reads `[qualcomm] qnn_sdk_root` from `.buckconfig`. The OSS `.buckconfig` typically resolves it from `${QNN_SDK_ROOT}`. Verify:
66+
67+
```bash
68+
echo "${QNN_SDK_ROOT:?set me to your QNN SDK install root}"
69+
test -d "${QNN_SDK_ROOT}/include/QNN" || echo "QNN_SDK_ROOT does not look right"
70+
```
71+
72+
If `QNN_SDK_ROOT` isn't set, the existing `/qualcomm` skill (see this same directory's `SKILL.md`) covers SDK setup.
73+
74+
## The CI command we replicate
75+
76+
The OSS CI job `test-qnn-buck-build-linux` runs (from `.github/workflows/pull.yml`):
77+
78+
```bash
79+
buck2 build //backends/qualcomm/...
80+
```
81+
82+
The skill runs exactly this. If it succeeds, the CI signal will pass. If it fails, the iteration loop below applies a recipe-driven fix and re-runs.
83+
84+
## Iteration loop
85+
86+
```
87+
loop:
88+
run: buck2 build //backends/qualcomm/...
89+
if green: → goto cmake-recheck
90+
if iterations >= 3: → halt and ask the user
91+
parse first error line / span
92+
match against the recipes table below
93+
if recipe is "auto": apply the fix silently
94+
if recipe is "confirm": print the proposed edit (file, diff, rationale), wait for user confirmation
95+
if no recipe matches: halt, print the error, ask the user
96+
re-run buck
97+
cmake-recheck:
98+
run: ./backends/qualcomm/scripts/build.sh --skip_linux_android # x86_64 cmake re-verification
99+
on failure: report the cmake delta — the buck-side fix probably broke parity in the other direction
100+
```
101+
102+
Hard cap: 3 automatic iterations. On the 4th attempt, stop and surface the full state to the user — which recipes were tried, which file changes are pending, the latest buck error — and ask whether to keep iterating, try a different recipe, or hand off.
103+
104+
Output policy: **quiet during the loop**, single summary at the end. Each iteration only logs `iter N: <recipe>` so the contributor can scroll the final transcript. Errors and proposed edits are printed only at confirm-points and on final report.
105+
106+
## Recipes
107+
108+
These cover ~80% of OSS-contributor drift. Each recipe lists: the buck-side error pattern to match, the auto-vs-confirm classification, and the fix.
109+
110+
### R1. `fatal error: 'X.h' file not found` (strict-header-visibility)
111+
112+
Symptom — the Buck preprocessor can't see a header that CMake's global include paths reach.
113+
114+
Decision tree:
115+
116+
1. **Find every use of symbols from `X.h` in the consuming source file**. If every use is inside a single `#ifdef <PLATFORM>` block (typically `#ifdef __hexagon__`), and the include itself is unconditional, the cheapest fix is to **gate the include** in the same `#ifdef`. (auto)
117+
118+
```cpp
119+
#ifdef __hexagon__
120+
#include "X.h"
121+
#endif
122+
```
123+
124+
2. **If the only symbol used from `X.h` is a single small constant (a `#define <NAME> <number>` or similar)**, prefer **inlining the constant as a local `constexpr`** at the use site, then drop the include entirely. (auto, when use site is single)
125+
126+
```cpp
127+
#ifdef __hexagon__
128+
constexpr size_t kFooAlignment = 64; // was: #define FOO_ALIGNMENT 64 in X.h
129+
use(kFooAlignment, …);
130+
#endif
131+
```
132+
133+
Why: removes the cross-target dependency entirely, follows fbcode-cpp.md's "constexpr everywhere possible". If the macro in `X.h` becomes unused after this, leave a trailing comment for the original author to clean up — don't unilaterally edit a header you don't own in this PR.
134+
135+
3. **If `X.h` exports symbols truly needed unconditionally** in the consumer, a Buck dep edge is required. Find the target that exports `X.h` (look at the `exported_headers` of nearby `targets.bzl` files). Then **check for a cycle before adding the dep** (see R4 — many `runtime``aot/wrappers` edges already exist). (confirm)
136+
137+
### R2. New `.cpp` file, undefined-symbol or missing-rule error
138+
139+
Symptom — a freshly added `.cpp` is invisible to Buck because the consuming target's source list doesn't pick it up. CMake handles this because it lists files explicitly in `CMakeLists.txt`; Buck typically uses globs in `targets.bzl`.
140+
141+
Decision tree:
142+
143+
1. **Identify the directory of the new file.** Read the relevant `targets.bzl` (e.g. `backends/qualcomm/runtime/targets.bzl`) and look at its `glob([...])` patterns.
144+
2. **If the directory pattern already covers the file** (e.g. file is `runtime/backends/lpai/foo.cpp` and the glob has `"backends/lpai/*.cpp"`), nothing to do — the failure is something else, look again.
145+
3. **If the directory is new** (e.g. `runtime/backends/lpai/target/`), extend the glob: add the new pattern to the appropriate target's `srcs`. (confirm — glob extension changes target surface)
146+
4. **If the file lives in a directory that has its own `BUCK` / `TARGETS`**, add it to that target's `srcs` list explicitly.
147+
148+
Special case: `runtime/backends/direct_mode/` — see R5.
149+
150+
### R3. Undefined reference at link time
151+
152+
Symptom — Buck successfully compiles each translation unit, then fails at link with `undefined reference to <symbol>`.
153+
154+
Most common cause: a target that uses a symbol declared in another target now lacks the dep edge. The lookup pattern:
155+
156+
1. Find the symbol's definition (its `.cpp` file).
157+
2. Find which Buck target's `srcs` glob covers that `.cpp`.
158+
3. Add that target as a dep of the failing target. (confirm — dep edges affect the build graph)
159+
4. **Before adding**, check for a cycle (R4).
160+
161+
### R4. Cycle detected
162+
163+
Symptom — Buck refuses to add an edge because it would create a cycle in the target graph. In `backends/qualcomm/`, the most common trap is `runtime/targets.bzl` already lists `aot/wrappers:wrappers` as a dep, so adding `runtime:runtime` to wrappers' deps cycles.
164+
165+
Do NOT auto-resolve. Halt and surface the cycle to the user. Three known-good escapes:
166+
167+
- **Factor a header-only target.** Move the header in question into a small `cxx_library` with `header_only = True` (or equivalent), and have both sides depend on it.
168+
- **Inline the symbol.** If the consumer only needs one constant or trivial helper, inline it (see R1.2).
169+
- **Conditional gate.** If the symbol use is platform-conditional, gate the consuming code path so the dep edge isn't needed in linux/Buck builds.
170+
171+
### R5. `runtime/backends/direct_mode/` — Buck has no target there, by design
172+
173+
Symptom — a contributor added a file under `runtime/backends/direct_mode/` and is trying to wire it into Buck.
174+
175+
`direct_mode/` is **CMake-only**, gated on `CMAKE_SYSTEM_PROCESSOR MATCHES Hexagon` in `backends/qualcomm/CMakeLists.txt`. Buck has no Hexagon platform configured for this backend, so `runtime/targets.bzl` deliberately does not glob `direct_mode/`.
176+
177+
Action: **do not add a Buck target for `direct_mode/`**. If the contributor's CMake change works, that's all the OSS build needs. The `test-qnn-buck-build-linux` job won't compile this directory regardless. Tell the user this and skip.
178+
179+
### R6. Adjacent-but-non-Buck error: missing `__init__.py`, missing schema codegen, etc.
180+
181+
If the buck error mentions a file outside `backends/qualcomm/` (e.g. a third-party header path, a flatbuffer codegen target, an `executorch/runtime/...` header), the drift is upstream of this skill's scope. Surface the error to the user with a one-line hint ("this looks unrelated to QNN backend parity — it's likely an `executorch/runtime` or `third-party` issue") and stop. Do not attempt to repair files outside `backends/qualcomm/`.
182+
183+
## On final green
184+
185+
After buck goes green, **re-run cmake** to confirm we didn't break parity in the other direction:
186+
187+
```bash
188+
./backends/qualcomm/scripts/build.sh --skip_linux_android
189+
```
190+
191+
If cmake still passes: print the final summary — files modified, recipes applied, and a one-line hint suggesting the contributor mention this in their PR description ("Buck-vs-CMake parity verified locally with `buck2 build //backends/qualcomm/...`").
192+
193+
If cmake fails: the buck-side fix introduced a parity regression. Surface the cmake error to the user and ask whether to revert.
194+
195+
## On halt (4th iteration or unmatched recipe)
196+
197+
Print:
198+
199+
1. The final unfixed buck error (the first ~30 lines of the actual compiler/linker error, not the buck-tool noise).
200+
2. The recipes that were tried, which iteration applied each, and the resulting error after each.
201+
3. The current `git status` / pending edits.
202+
4. A suggestion: "Open a comment on your PR mentioning the QNN reviewers, link this transcript, and ask for help. The internal `test-qnn-buck-build-linux` reviewers will know the buck graph."
203+
204+
## Out of scope
205+
206+
- Authoring brand-new Buck targets (only minor edits to existing `targets.bzl`/`BUCK`/`TARGETS`).
207+
- Fixes outside `backends/qualcomm/`.
208+
- Hexagon Buck platform setup — there is no Hexagon Buck target in this backend today (R5).
209+
- Generic backend coverage. This guide is QNN-only by design; the same patterns generalize to other backends but recipes would differ.

0 commit comments

Comments
 (0)