Skip to content

Commit bf96e77

Browse files
committed
Stamp ESP-IDF component versions from git and add registry metadata
The component's version was version.txt plus a hand-bumped packaging revision (1.6.0~3), which nothing tied to what was being packaged. The generator now derives it from git and refuses stamps the checkout cannot back: a plain X.Y.Z needs HEAD at the vX.Y.Z tag, X.Y.Z-rc.N needs the vX.Y.Z-rcN tag, and an X.Y.Z~N respin needs HEAD after vX.Y.Z on release/X.Y with no changes since the tag outside examples/espressif/, .ci/, .github/ and docs/. Those three also refuse a checkout with changes to what the component ships: to tracked files, or untracked files where the generator globs sources, the flatbuffers submodule's headers included. Builds from any other commit pass --allow-dev-stamp and get X.Y.Z-dev.<UTC commit date>, which is never published. An ExecuTorch tree that is not the top of its own git checkout gets no stamp, so a copy inside another repository is never stamped from that repository's history. examples/espressif/test_version_stamp.sh checks these guards in a scratch repository, and CI runs it first. idf_component.yml gains the registry's repository, documentation (at the packaged commit), issues, discussion and tags fields, and its license now lists every license in the vendored code: ExecuTorch's BSD-3-Clause, FlatBuffers' Apache-2.0, and the LLVM (Apache-2.0 WITH LLVM-exception, NCSA) and musl (MIT) code in a few runtime and c10 headers. The component ships LLVM's LICENSE.TXT, whose legacy NCSA text array_ref.h refers to, next to FlatBuffers' license. sbom.yml with one sub-manifest each for FlatBuffers and c10 describes the vendored code for esp-idf-sbom, and CI validates them along with compote manifest lint. The component ships examples/hello_executorch, a project that runs the smoke model with the model and kernel bundle made at generation time by the component's own gen_kernels.py. The generator therefore needs ExecuTorch installed from the exact commit it packages, and checks that before it starts. The example depends on exactly this component version, since its bundle is made for it, through override_path. CI builds it that way and runs it on the ESP32-S3 emulator, and the consumer test also builds it as a registry user gets it, copied out of the packed archive without override_path so the pin resolves from the mirror. The component's README, which becomes its registry page, tells projects to depend on one exact version and commit dependencies.lock, since a bundle only fits the version it was made for. Authored with Claude Code.
1 parent 29ecee2 commit bf96e77

12 files changed

Lines changed: 867 additions & 81 deletions

File tree

‎.ci/scripts/test_espressif_idf_component.sh‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
# build runs all three with a kernel bundle the build makes itself; an ESP32-C6
1212
# build runs the first two, with the Cadence kernels too, and must not link the
1313
# delegate; two more ESP32-S3 builds run the delegate alone, from a bundle with
14-
# no kernel libraries and from executorch_add_backend with no bundle. Also
15-
# checks that stale or mismatched bundles fail at configure time, and that an
16-
# app declaring nothing links to the same size as one without the component.
14+
# no kernel libraries and from executorch_add_backend with no bundle, and the
15+
# component's own example runs too. Also checks the version stamping, the
16+
# manifest and SBOM, that stale or mismatched bundles fail at configure time,
17+
# and that an app declaring nothing links to the same size as one without the
18+
# component.
1719

1820
set -euo pipefail
1921

@@ -37,6 +39,9 @@ collect_artifacts() {
3739
}
3840
trap collect_artifacts EXIT
3941

42+
# Needs only git, so a stamp regression fails before the toolchain installs.
43+
bash examples/espressif/test_version_stamp.sh
44+
4045
bash examples/espressif/setup.sh --minimal --targets=esp32s3,esp32c6 2>&1 \
4146
| tee "${build_dir}/setup.log"
4247
# shellcheck source=/dev/null
@@ -66,8 +71,10 @@ models="${cpu_models};${build_dir}/delegate.pte"
6671

6772
component_dir="${build_dir}/components/executorch"
6873
kernels_dir="${build_dir}/smoke_kernels"
69-
bash examples/espressif/build_idf_component.sh --out "${component_dir}" \
74+
bash examples/espressif/build_idf_component.sh --allow-dev-stamp --out "${component_dir}" \
7075
2>&1 | tee "${build_dir}/generate.log"
76+
compote manifest lint "${component_dir}"
77+
(cd "${component_dir}" && esp-idf-sbom manifest validate idf_component.yml sbom.yml sbom_*.yml)
7178
gen_kernels() {
7279
local out=$1
7380
shift
@@ -261,9 +268,9 @@ run_firmware() {
261268
local firmware_dir="${build_dir}/firmware-${name}"
262269
IDF_TARGET="${chip}" idf.py -C "${project}" -B "${firmware_dir}" merge-bin \
263270
> "${build_dir}/merge-${name}.log" 2>&1
271+
local elf=("${firmware_dir}"/*.elf)
264272
timeout 130s esp-emu --chip "${chip}" \
265-
--firmware "${firmware_dir}/merged-binary.bin" \
266-
--elf "${firmware_dir}/et_component_smoke.elf" \
273+
--firmware "${firmware_dir}/merged-binary.bin" --elf "${elf[0]}" \
267274
--net user --timeout 120s --exit-on 'Program complete.' \
268275
2>&1 | tee "${build_dir}/emulator-${name}.log"
269276
for marker in "$@" "ET_COMPONENT_SMOKE: PASS" "Program complete."; do
@@ -349,4 +356,9 @@ if (( 16#${registry} > 64 )); then
349356
exit 1
350357
fi
351358

359+
# The example the component ships, built through its override_path.
360+
example="${component_dir}/examples/hello_executorch"
361+
project="${example}" consumer example esp32s3 build
362+
project="${example}" run_firmware example esp32s3 "smoke: PASS"
363+
352364
echo "PASS: generated ESP-IDF component ran every build on the emulator."

‎.ci/scripts/test_espressif_idf_consumer.sh‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
# ESP32-S3 build an ESP-NN delegate program.
1717
# ESPRESSIF_IDF_VERSION selects the ESP-IDF release: v6.1 builds every target
1818
# an emulator runs (QEMU for ESP32, esp-emu for the rest), other releases
19-
# ESP32-S3 and ESP32-C6. Also lints the component's manifest.
19+
# ESP32-S3 and ESP32-C6. Also builds the component's example the way a registry
20+
# user gets it, and lints the component's manifest.
2021

2122
set -euo pipefail
2223

@@ -83,9 +84,9 @@ print(entry["atol"], entry["rtol"])
8384
python "${split}" "${assets}/delegate.bpte" --atol "${atol}" --rtol "${rtol}"
8485

8586
component_dir="${build_dir}/component/executorch"
86-
bash examples/espressif/build_idf_component.sh --out "${component_dir}" \
87+
bash examples/espressif/build_idf_component.sh --allow-dev-stamp --out "${component_dir}" \
8788
2>&1 | tee "${build_dir}/generate.log"
88-
compote manifest lint "${component_dir}/idf_component.yml"
89+
compote manifest lint "${component_dir}"
8990
version=$(sed -n 's/^version: "\(.*\)"$/\1/p' "${component_dir}/idf_component.yml")
9091
(cd "${component_dir}" && compote component pack --name executorch --dest-dir "${build_dir}/dist")
9192
archive="${build_dir}/dist/executorch_${version}.tgz"
@@ -164,7 +165,7 @@ run_firmware() {
164165
consumer "${name}" "${target}" merge-bin > "${build_dir}/merge-${name}.log" 2>&1
165166
timeout 130s esp-emu --chip "${target}" \
166167
--firmware "${firmware_dir}/merged-binary.bin" \
167-
--elf "${firmware_dir}/et_component_smoke.elf" \
168+
--elf "$(find "${firmware_dir}" -maxdepth 1 -name '*.elf')" \
168169
--net user --timeout 120s --exit-on 'Program complete.' \
169170
> "${log}" 2>&1 || true
170171
fi
@@ -213,4 +214,25 @@ if (str(entry["version"]), entry.get("component_hash")) != (
213214
sys.exit(f"FAIL: pytorch/executorch did not come from the mirror: {entry}")
214215
PY
215216
done
217+
218+
# The component's example as idf.py create-project-from-example gives it to a
219+
# registry user: copied out of the archive, without the override_path that the
220+
# registry removes, so its exact version pin resolves from the mirror. The
221+
# component manager cannot download examples from a file:// mirror itself.
222+
example="${sources}/hello_executorch"
223+
rm -rf "${example}"
224+
mkdir -p "${example}"
225+
tar -xzf "${archive}" -C "${example}" --strip-components=3 ./examples/hello_executorch
226+
python - "${example}/main/idf_component.yml" <<'PY'
227+
import sys
228+
229+
import yaml
230+
231+
manifest = yaml.safe_load(open(sys.argv[1]))
232+
del manifest["dependencies"]["pytorch/executorch"]["override_path"]
233+
yaml.safe_dump(manifest, open(sys.argv[1], "w"))
234+
PY
235+
project="${example}" consumer hello_executorch esp32s3 build \
236+
> "${build_dir}/firmware-hello_executorch.log" 2>&1
237+
project="${example}" run_firmware hello_executorch esp32s3 "smoke: PASS"
216238
echo "PASS: the packed ESP-IDF component built from a mirror and ran on ${targets[*]}."

‎examples/espressif/README.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@ cross-compile-then-link flow below, generate a self-contained ESP-IDF
1515
component from sources in this repository:
1616

1717
```bash
18-
./examples/espressif/build_idf_component.sh \
18+
./examples/espressif/build_idf_component.sh --allow-dev-stamp \
1919
--out /path/to/your_project/components/executorch
2020
```
2121

22+
Run it from a Python environment with ExecuTorch installed from this exact
23+
commit (reinstall after committing or pulling): the generator exports the
24+
component's example model with it. `--allow-dev-stamp` versions the component
25+
`X.Y.Z-dev.<commit date>`; without it the generator stamps a release
26+
version and requires the checkout to be at that release's tag (see the
27+
script's header for respin and RC stamps).
28+
2229
The output is a registry-shaped component (`idf_component.yml`,
2330
`CMakeLists.txt`, `Kconfig`, `VERSION`) containing the runtime, the
2431
portable, quantized, Cadence generic and `esp_nn::` kernel libraries,

0 commit comments

Comments
 (0)