Repository navigation
esp-generate
Added
- Several templates in one repository or directory.
--templatenow finds every template under the path or clone and lets you choose: the TUI asks, and--template-namenames one by its directory (templates/board,./templates/board/) or, failing that, thenamein itsmetadata.toml. Headless runs,list-options,explainandchecklist the candidates instead of guessing. One rule decides what a template is, for the directory named and every directory below it alike: itsmetadata.tomlparses as a manifest, or it does not but atemplate.yamlsits beside it — a broken template, listed with what is wrong rather than skipped. A template is not searched further, so templates do not nest. Any othermetadata.tomlis not a template, and is reported rather than silently passed over: in a warning when the template was not picked by name, and in the error when no template is found. A local directory is searched the same way as a clone. template_origin,template_repo,template_urlandtemplate_commit. A template can now see where it was read from.template_originis"clone"when--templatecloned a repository, which also setstemplate_urlandtemplate_committo its URL and resolved commit, and"repository"when it named a local directory inside a git repository, which also setstemplate_repoto that repository's root, relative to the generated project and/-separated. Only the values for the origin at hand are set, and a directory outside any git checkout sets none, so a template that reads one that does not apply fails instead of rendering an empty string. A template shipped inside the project it scaffolds can depend on that project either way, branching ontemplate_origin:git = "{{ template_url }}", rev = "{{ template_commit }}", orpath = "{{ template_repo }}/my-crate". (#374)- Templates that are not Cargo projects.
metadata.tomlgained a[project]section:type = "other"tells the binary not to read aCargo.toml— for theesp-halversion and the MSRV — and turns off the toolchain pre-flight,cargo fmtand theCargo.tomlreformat, leavinggit init. Each of those is also settable on its own through[project.steps], so a Rust template can drop one without dropping the rest.type = "rust"is the default, so existing templates are unaffected. (#372) esp-generate checkrenders a template across its option combinations in memory and reports every failure with the selection that reaches it, without writing a project. This catches what generating once cannot: a name in a branch that selection never evaluates.-opins an option (narrowing a required group instead of sweeping it),--all-combinationscovers every valid union rather than each option once,--exclude-group/--exclude-category/--cross-groupshape the matrix, and--buildadditionally generates each combination to a temporary directory and runs cargo check, clippy and fmt over it. It also warns when a template declares an oldersdk_versionthan the features it uses require. (#369)--templatealso accepts a repository:owner/repo[@branch-or-tag], anhttps://URL, orgit@host:path. It is cloned shallowly withgit(reusing your existing credentials, so private template repos work) into a temporary directory that is removed when esp-generate exits, and the resolved commit is logged so a generated project can be traced back to exactly what produced it. The template need not be the repository root — esp-generate finds it inside, so a template shipped inside a larger repo works; how a template is recognised, and chosen among several, is described above. A commit SHA is not supported: shallow clones cannot take one portably. (#368, #376)- Generate from an external template.
--template <dir>reads a template from a directory instead of the bundled one; it works withlist-optionsandexplaintoo. The directory is read with the same root-relative key spacebuild.rsbakes in,.git/andtarget/are skipped, symlinks are not followed, and a path that escapes the template root is refused. Using one prints a warning, since a template decides what code and dependencies end up in your project. (#368) - Templates now carry a
metadata.tomlmanifest. It declares theesp-template-sdkversion the template is written against — checked before any template file is read, so an incompatible template fails with one clear line — and per-fileemitrules:whenis the condition a file is emitted under,asthe path it is written to. A file with no rule is emitted as-is. (#357) - Clarify ESP32 support status (#377)
- ESP32-P4 (v3.0 and later) support (#377)
Changed
- The template contract (option tree, relationship engine, directive processor) now lives in a separate
esp-template-sdkcrate. Generated projects are unchanged. (#352) - Template syntax. File directives are now rendered by
somni-template. Directive lines are marked with the file's comment prefix plus%(#%if,//%endif,--%else) so ordinary comments are never parsed as directives;#ELIFis now#%else if; and#REPLACEis replaced by{{ expr }}interpolation. Generated projects are unchanged. (#352) setskeys that a template interpolates must be valid identifiers:wokwi-boardis nowwokwi_board, andcoding-agent-guidance-fileis nowcoding_agent_guidance_file. (#352)- Template directives evaluate against a single fact API.
option()andgroup_selected()are the predicates, and everything the chip decides is a field on achipstruct:chip.name,chip.rust_target,chip.dram2_uninit_size, and one field peresp-metadatasymbol (chip.soc_has_wifi). A misspelled field is an error naming the file and line, not a silentfalse. Value names aresnake_case. (#352) option()no longer matches selection-group names — usegroup_selected(). (#352)- Chip data is a versioned plugin. It lives in a new
esp-template-plugin-chipcrate whose version mirrors theesp-metadata-generatedit wraps, so the version names a symbol set rather than a release. A template declares what it needs —plugins = { chip = "0.5.0" }inmetadata.toml— and pulls the chip selection group in with!Include plugin:chip. A binary that cannot provide a declared plugin refuses the template with one line instead of failing on unknown names part-way through rendering, and can offer several versions at once so older templates keep working. (#357) - The option tree's chip list is generated from the chip plugin rather than carried as template YAML, so newly supported silicon appears in the menu without a template edit. (#357)
- A template's
.template/directory is reserved for machinery — option-tree fragments andincludepartials — and is never emitted. (#357) - What an option needs is declared in the option tree rather than hardcoded in esp-generate:
requires_nightly: truefor a nightly toolchain,requires_tools: [probe-rs]for a host tool. Both drive the pre-flight check before generation. (#357)
Fixed
- Malformed template directives now report a
file:lineerror instead of panicking or being silently ignored. - A
requires_capabilitiesentry that names no plugin namespace —soc_has_wifirather thanchip.soc_has_wifi— is now refused when the template loads. It previously read as false for every selection, silently hiding the option. (#357)
Removed
- The
#%includefileand#%include_asdirectives. Which files a generated project gets, and what they are called, is the manifest's job now — a file's own body no longer decides whether that file exists. (#357)
Full Changelog: v1.4.0...v2.0.0