Skip to content

Commit 5683d02

Browse files
committed
Make the ESP-IDF kernel bundle at build time when Python has executorch
When a project declares models but no kernel bundle, the component's configure step now checks whether EXECUTORCH_PYTHON (set with -D or in the environment; by default the Python ESP-IDF builds with) can import executorch. If it can, it runs gen_kernels.py on the declared models into the build directory and uses that bundle, so a project that exports and builds in one environment no longer has to run gen_kernels.py or keep a bundle in its tree. The result is byte-identical to a bundle made by hand, which CI checks by building the ESP32-S3 smoke consumer this way. Without such a Python, or if generation fails, the configure error names the Python, shows why, and prints the gen_kernels.py command. Authored with Claude Code.
1 parent 10f0224 commit 5683d02

4 files changed

Lines changed: 87 additions & 18 deletions

File tree

‎.ci/scripts/test_espressif_idf_component.sh‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
# LICENSE file in the root directory of this source tree.
77

88
# Generates the ExecuTorch ESP-IDF component and a kernel bundle for the smoke
9-
# model, builds the idf_component_smoke consumer for ESP32-S3 and, with the
10-
# Cadence kernels too, for ESP32-C6, and runs the ESP32-S3 build on the
11-
# emulator. Also checks that stale or mismatched bundles fail at configure time,
12-
# and that an app declaring nothing links to the same size as one without the
13-
# component.
9+
# model, builds the idf_component_smoke consumer for ESP32-C6 with that bundle,
10+
# plus the Cadence kernels, and for ESP32-S3 with one the build generates
11+
# itself, and runs the ESP32-S3 build on the emulator. Also checks that stale or
12+
# mismatched bundles fail at configure time, and that an app declaring nothing
13+
# links to the same size as one without the component.
1414

1515
set -euo pipefail
1616

@@ -126,7 +126,12 @@ expect_configure_error commit-mismatch component_commit \
126126
-DET_SMOKE_PTE="${build_dir}/smoke.pte" \
127127
-DET_SMOKE_KERNELS="${build_dir}/mismatch_kernels"
128128

129-
expect_configure_error no-bundle gen_kernels.py -DET_SMOKE_PTE="${build_dir}/smoke.pte"
129+
# Without an ExecuTorch Python to generate one, a missing bundle is an error.
130+
python -m venv --without-pip --clear "${build_dir}/bare_python"
131+
EXECUTORCH_PYTHON="${build_dir}/bare_python/bin/python" expect_configure_error \
132+
no-bundle "cannot import executorch" -DET_SMOKE_PTE="${build_dir}/smoke.pte"
133+
expect_configure_error relative-python "must be the absolute path" \
134+
-DET_SMOKE_PTE="${build_dir}/smoke.pte" -DEXECUTORCH_PYTHON=python
130135

131136
# A kernel registry smaller than the bundle; the emulator run below uses one
132137
# larger than it.
@@ -172,11 +177,14 @@ if ! tr -s ' \n' ' ' < "${build_dir}/firmware-nano.log" \
172177
exit 1
173178
fi
174179

175-
consumer smoke-esp32s3 esp32s3 -DET_COMPONENT_DIR="${component_dir}" \
176-
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;${build_dir}/large_registry.defaults" \
177-
-DET_SMOKE_PTE="${build_dir}/smoke.pte" -DET_SMOKE_KERNELS="${kernels_dir}" build
178180
consumer smoke-esp32c6 esp32c6 -DET_COMPONENT_DIR="${component_dir}" \
179181
-DET_SMOKE_PTE="${build_dir}/smoke.pte" -DET_SMOKE_KERNELS="${build_dir}/wakeword_kernels" build
182+
# No bundle declared: the build makes one with its own Python, which has
183+
# executorch here, and it must match the one gen_kernels.py made above.
184+
consumer smoke-esp32s3 esp32s3 -DET_COMPONENT_DIR="${component_dir}" \
185+
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;${build_dir}/large_registry.defaults" \
186+
-DET_SMOKE_PTE="${build_dir}/smoke.pte" build
187+
diff -r "${build_dir}/firmware-smoke-esp32s3/executorch_kernels" "${kernels_dir}"
180188

181189
firmware_dir="${build_dir}/firmware-smoke-esp32s3"
182190
idf.py -C "${project}" -B "${firmware_dir}" merge-bin \

‎examples/espressif/idf_component_smoke/CMakeLists.txt‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
# Minimal ESP-IDF consumer of the generated ExecuTorch component, used by
88
# .ci/scripts/test_espressif_idf_component.sh. Pass the generated component
99
# directory as ET_COMPONENT_DIR, the plain smoke .pte from export_smoke_model.py
10-
# as ET_SMOKE_PTE, and its kernel bundle from the component's
11-
# tools/gen_kernels.py as ET_SMOKE_KERNELS. Without ET_SMOKE_PTE it builds an
12-
# app that does not use ExecuTorch, for size comparisons.
10+
# as ET_SMOKE_PTE, and optionally its kernel bundle from the component's
11+
# tools/gen_kernels.py as ET_SMOKE_KERNELS; without one the build makes it.
12+
# Without ET_SMOKE_PTE it builds an app that does not use ExecuTorch, for size
13+
# comparisons.
1314

1415
cmake_minimum_required(VERSION 3.16)
1516

‎examples/espressif/idf_component_template/README.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ executorch_add_model(PTE "${CMAKE_CURRENT_SOURCE_DIR}/model.pte")
4444
executorch_add_kernels(DIR "${CMAKE_CURRENT_SOURCE_DIR}/et_kernels")
4545
```
4646

47+
If the Python ESP-IDF builds with can import `executorch` (or you
48+
point `EXECUTORCH_PYTHON` at one that can, with `-D` or in the
49+
environment, for example
50+
`idf.py -DEXECUTORCH_PYTHON=$HOME/venvs/et/bin/python build`), you can
51+
leave out `executorch_add_kernels`: the build then makes the bundle in
52+
its build directory from the declared models, with the same result as
53+
running `gen_kernels.py` yourself.
54+
4755
The build fails at configure time if the bundle doesn't match the
4856
declared models (a model was re-exported, added or removed) or was
4957
made for another version of this component, and prints the

‎examples/espressif/idf_component_template/project_include.cmake‎

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# executorch_add_kernels(DIR <bundle-dir>) # from tools/gen_kernels.py
1313
# executorch_add_backend(<BackendId>) # force-link a delegate
1414
#
15-
# Calls are unioned across the project; a project has one kernel bundle.
15+
# Calls are unioned across the project; a project has one kernel bundle. With
16+
# models but no bundle, the build makes one if EXECUTORCH_PYTHON (-D or the
17+
# environment; default: the build's Python) can import executorch.
1618

1719
function(executorch_add_model)
1820
if(NOT ARGV0 STREQUAL "PTE" OR ARGC LESS 2)
@@ -80,20 +82,70 @@ function(__executorch_finalize)
8082

8183
list(REMOVE_DUPLICATES _models)
8284
set(_command "python \"${_dir}/tools/gen_kernels.py\"")
85+
set(_pte_args "")
8386
foreach(_pte IN LISTS _models)
8487
if(NOT EXISTS "${_pte}")
8588
message(FATAL_ERROR "ExecuTorch: declared model ${_pte} does not exist")
8689
endif()
8790
string(APPEND _command " --pte \"${_pte}\"")
91+
list(APPEND _pte_args --pte "${_pte}")
8892
endforeach()
8993
if(NOT _bundle)
94+
if(NOT EXECUTORCH_PYTHON)
95+
set(EXECUTORCH_PYTHON "$ENV{EXECUTORCH_PYTHON}")
96+
endif()
97+
if(NOT EXECUTORCH_PYTHON)
98+
idf_build_get_property(EXECUTORCH_PYTHON PYTHON)
99+
elseif(NOT IS_ABSOLUTE "${EXECUTORCH_PYTHON}" OR NOT EXISTS
100+
"${EXECUTORCH_PYTHON}"
101+
)
102+
message(
103+
FATAL_ERROR
104+
"ExecuTorch: EXECUTORCH_PYTHON must be the absolute path of a "
105+
"python, not ${EXECUTORCH_PYTHON}"
106+
)
107+
endif()
108+
string(
109+
CONCAT
110+
_manual
111+
"Make the bundle with the Python environment the models were exported "
112+
"with:\n ${_command} --out <bundle-dir>\nthen call "
113+
"executorch_add_kernels(DIR <bundle-dir>), or set EXECUTORCH_PYTHON to "
114+
"that environment's python."
115+
)
116+
execute_process(
117+
COMMAND "${EXECUTORCH_PYTHON}" -c
118+
"import executorch.codegen.tools.selective_build"
119+
RESULT_VARIABLE _no_executorch
120+
OUTPUT_QUIET
121+
ERROR_VARIABLE _probe_error
122+
)
123+
if(_no_executorch)
124+
message(
125+
FATAL_ERROR
126+
"ExecuTorch: models are declared but no kernel bundle is, and "
127+
"${EXECUTORCH_PYTHON} cannot import executorch to make one:\n"
128+
"${_probe_error}${_manual}"
129+
)
130+
endif()
131+
set(_bundle "${CMAKE_BINARY_DIR}/executorch_kernels")
90132
message(
91-
FATAL_ERROR
92-
"ExecuTorch: models are declared but no kernel bundle is. Make one "
93-
"with the Python environment the models were exported with:\n"
94-
" ${_command} --out <bundle-dir>\n"
95-
"then call executorch_add_kernels(DIR <bundle-dir>)."
133+
STATUS "ExecuTorch: making the kernel bundle with ${EXECUTORCH_PYTHON}"
134+
)
135+
execute_process(
136+
COMMAND "${EXECUTORCH_PYTHON}" "${_dir}/tools/gen_kernels.py" ${_pte_args}
137+
--out "${_bundle}"
138+
RESULT_VARIABLE _failed
139+
OUTPUT_VARIABLE _output
140+
ERROR_VARIABLE _output
96141
)
142+
if(_failed)
143+
message(
144+
FATAL_ERROR
145+
"ExecuTorch: ${EXECUTORCH_PYTHON} failed to make the kernel bundle:\n"
146+
"${_output}${_manual}"
147+
)
148+
endif()
97149
endif()
98150
string(APPEND _command " --out \"${_bundle}\"")
99151

0 commit comments

Comments
 (0)