Skip to content

Commit 38858ea

Browse files
committed
Zephyr: Auto-export models from prj.conf
Add cmake utility for adding a build rule to run a python file that exports a pytorch model to pte. Enable using this utility for Zephyr builds and use this in the hello-executorch sample New config CONFIG_EXECUTORCH_EXPORT_PYTHON_SCRIPT to point to your model python file CONFIG_EXECUTORCH_EXPORT_PYTHON_GENERATED_OUTPUT to the output file that was generated by the script. CONFIG_EXECUTORCH_EXPORT_PYTHON_DEPENDENCIES Make it possible to add dependency files to regenerate the PTE on next build. If used thid will add cmake rules that will trigger a (re)generation of the PTE if needed when building. Signed-off-by: Zingo Andersen <Zingo.Andersen@arm.com> Change-Id: Iddebc0316933be3136aefbf28e4fa74e033a76f7
1 parent af90130 commit 38858ea

18 files changed

Lines changed: 1040 additions & 208 deletions

.github/workflows/trunk.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,11 @@ jobs:
174174
echo "---- ${TARGET} Board ${BOARD} FVP setup ----"
175175
run_command_block_from_readme "${ZEPHYR_SAMPLES_README_PATH}" "<!-- RUN setup_${BOARD}_fvp -->"
176176
177-
echo "---- ${TARGET} Create PTE ----"
178-
run_command_block_from_readme "${ZEPHYR_SAMPLES_README_PATH}" "<!-- RUN test_${TARGET}_generate_pte -->"
179-
180177
echo "---- ${TARGET} Build and run ----"
181178
run_command_block_from_readme "${ZEPHYR_SAMPLES_README_PATH}" "<!-- RUN test_${TARGET}_build_and_run -->"
179+
180+
echo "---- ${TARGET} Build and run Separate PTE ----"
181+
run_command_block_from_readme "${ZEPHYR_SAMPLES_README_PATH}" "<!-- RUN test_${TARGET}_generate_pte -->"
182182
done
183183
184184
# MV2 Ethos-U sample (NPU targets only — skips cortex-m55)

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ xcuserdata/
7676
*.dll
7777
*.pyd
7878

79+
# Zephyr
80+
zephyr_scratch/*
7981

8082
# Agents
8183
.claude/*.local.*

tools/cmake/ExportModel.cmake

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
include(${CMAKE_CURRENT_LIST_DIR}/Utils.cmake)
7+
8+
# Shared setup for Python-based model exporters used by both the configure-time
9+
# and build-time helper entry points below.
10+
function(
11+
_executorch_prepare_python_export
12+
caller
13+
script
14+
output
15+
working_directory
16+
python_executable
17+
out_command_var
18+
out_output_var
19+
)
20+
if(NOT script)
21+
message(FATAL_ERROR "${caller} requires SCRIPT to be set")
22+
endif()
23+
if(NOT output)
24+
message(FATAL_ERROR "${caller} requires OUTPUT to be set")
25+
endif()
26+
if(NOT working_directory)
27+
message(FATAL_ERROR "${caller} requires WORKING_DIRECTORY to be set")
28+
endif()
29+
if(NOT IS_DIRECTORY "${working_directory}")
30+
message(
31+
FATAL_ERROR
32+
"${caller} requires WORKING_DIRECTORY to exist: ${working_directory}"
33+
)
34+
endif()
35+
36+
if(NOT python_executable)
37+
resolve_python_executable()
38+
set(python_executable ${PYTHON_EXECUTABLE})
39+
endif()
40+
41+
if(NOT EXISTS "${script}")
42+
message(FATAL_ERROR "Python exporter script does not exist: ${script}")
43+
endif()
44+
45+
if(IS_ABSOLUTE "${output}")
46+
set(_et_export_output "${output}")
47+
else()
48+
# Resolve relative output paths from the exporter's working directory so
49+
# configure-time checks and build-time outputs refer to the same file.
50+
get_filename_component(
51+
_et_export_output "${output}" ABSOLUTE BASE_DIR "${working_directory}"
52+
)
53+
endif()
54+
55+
get_filename_component(_et_export_output_dir "${_et_export_output}" DIRECTORY)
56+
if(_et_export_output_dir AND NOT EXISTS "${_et_export_output_dir}")
57+
file(MAKE_DIRECTORY "${_et_export_output_dir}")
58+
endif()
59+
60+
set(_et_export_command ${python_executable} ${script} ${ARGN})
61+
set(${out_command_var}
62+
${_et_export_command}
63+
PARENT_SCOPE
64+
)
65+
set(${out_output_var}
66+
"${_et_export_output}"
67+
PARENT_SCOPE
68+
)
69+
endfunction()
70+
71+
# Run a Python exporter at configure time and require it to produce OUTPUT.
72+
#
73+
# Backend-specific lowering options should be expressed in ARGS and remain owned
74+
# by the Python script. This helper runs the exporter immediately during CMake
75+
# configure, then verifies that the expected output file was written.
76+
function(executorch_run_python_exporter out_var)
77+
cmake_parse_arguments(
78+
ARG "" "SCRIPT;OUTPUT;WORKING_DIRECTORY;PYTHON_EXECUTABLE" "ARGS" ${ARGN}
79+
)
80+
81+
_executorch_prepare_python_export(
82+
executorch_run_python_exporter
83+
"${ARG_SCRIPT}"
84+
"${ARG_OUTPUT}"
85+
"${ARG_WORKING_DIRECTORY}"
86+
"${ARG_PYTHON_EXECUTABLE}"
87+
_et_export_command
88+
_et_export_output
89+
${ARG_ARGS}
90+
)
91+
92+
execute_process(
93+
COMMAND ${_et_export_command}
94+
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
95+
RESULT_VARIABLE _et_export_status
96+
OUTPUT_VARIABLE _et_export_stdout
97+
ERROR_VARIABLE _et_export_stderr
98+
)
99+
100+
if(NOT _et_export_status EQUAL 0)
101+
message(
102+
FATAL_ERROR
103+
"Python exporter failed for ${ARG_SCRIPT}\nstdout:\n${_et_export_stdout}\nstderr:\n${_et_export_stderr}"
104+
)
105+
endif()
106+
107+
if(NOT EXISTS "${_et_export_output}")
108+
message(
109+
FATAL_ERROR
110+
"Python exporter ${ARG_SCRIPT} completed but did not produce ${_et_export_output}"
111+
)
112+
endif()
113+
114+
set(${out_var}
115+
"${_et_export_output}"
116+
PARENT_SCOPE
117+
)
118+
endfunction()
119+
120+
# Add a build-time target that runs a Python exporter and materializes OUTPUT.
121+
# Unlike executorch_run_python_exporter(), this helper defers execution until
122+
# the build graph runs and exposes the generated file through a custom target.
123+
function(executorch_add_python_export_target)
124+
cmake_parse_arguments(
125+
ARG "" "TARGET;SCRIPT;OUTPUT;WORKING_DIRECTORY;PYTHON_EXECUTABLE"
126+
"ARGS;DEPENDS" ${ARGN}
127+
)
128+
129+
if(NOT ARG_TARGET)
130+
message(
131+
FATAL_ERROR
132+
"executorch_add_python_export_target requires TARGET to be set"
133+
)
134+
endif()
135+
136+
_executorch_prepare_python_export(
137+
executorch_add_python_export_target
138+
"${ARG_SCRIPT}"
139+
"${ARG_OUTPUT}"
140+
"${ARG_WORKING_DIRECTORY}"
141+
"${ARG_PYTHON_EXECUTABLE}"
142+
_et_export_command
143+
_et_export_output
144+
${ARG_ARGS}
145+
)
146+
147+
add_custom_command(
148+
OUTPUT ${_et_export_output}
149+
COMMAND ${_et_export_command}
150+
COMMAND_EXPAND_LISTS
151+
DEPENDS ${ARG_SCRIPT} ${ARG_DEPENDS}
152+
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
153+
COMMENT "Generating model with ${ARG_SCRIPT}"
154+
VERBATIM
155+
)
156+
157+
add_custom_target(${ARG_TARGET} DEPENDS ${_et_export_output})
158+
endfunction()

zephyr/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ if(CONFIG_EXECUTORCH)
1111

1212
set(EXECUTORCH_DIR ${ZEPHYR_CURRENT_MODULE_DIR})
1313
message(STATUS "EXECUTORCH_DIR set to: ${EXECUTORCH_DIR}")
14+
include(${EXECUTORCH_DIR}/zephyr/ExecuTorchZephyrModel.cmake)
1415
include(${EXECUTORCH_DIR}/tools/cmake/common/preset.cmake)
1516
include(${EXECUTORCH_DIR}/tools/cmake/preset/zephyr.cmake)
1617

0 commit comments

Comments
 (0)