tan-cli 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""`--emit kconfig` -- the board-scoped, user-settable Kconfig symbol menu
|
|
4
|
+
for the vscode `prj.conf` LSP (#893); unblocks tan-cli #35 (`tan kconfig`).
|
|
5
|
+
|
|
6
|
+
Deliberately separate from `kconfig.py` (the alp.conf/local.conf string
|
|
7
|
+
templater, which never runs kconfiglib/west -- see its module docstring):
|
|
8
|
+
this module is the SDK's first workspace-dependent emit. Every other
|
|
9
|
+
`--emit` mode is hermetic (provable from board.yaml + this repo's own
|
|
10
|
+
metadata alone); this one needs a bootstrapped Zephyr workspace
|
|
11
|
+
(`ZEPHYR_BASE`, v4.4.0) because only the real Kconfig solver knows which
|
|
12
|
+
symbols are user-promptable for a given board.
|
|
13
|
+
|
|
14
|
+
Two independent halves:
|
|
15
|
+
|
|
16
|
+
* `_envelope` -- pure JSON shaping (the schemaVersion/board/core/symbols
|
|
17
|
+
wrap), unit-tested with NO Zephyr installed (see
|
|
18
|
+
tests/core/test_kconfig_symbols.py). The actual symbol projection
|
|
19
|
+
("first prompt-bearing node, not nodes[0]" -- alp-sdk #893) lives ONLY
|
|
20
|
+
inside `_DUMPER_SOURCE` below, the rendered dumper script: it has to run
|
|
21
|
+
as a standalone subprocess inside Zephyr's own Kconfig env, importing
|
|
22
|
+
nothing from this package, so there is no separate copy here for it to
|
|
23
|
+
import -- `tests/core/test_kconfig_symbols.py` asserts the rendered
|
|
24
|
+
projection directly against a literal expected result (an independent
|
|
25
|
+
oracle) instead of hand-syncing a second copy.
|
|
26
|
+
* `_load_board_symbols` -- Approach A: a stub `west build --cmake-only`
|
|
27
|
+
registers Zephyr's `EXTRA_KCONFIG_TARGET` custom-target mechanism
|
|
28
|
+
(`cmake/modules/kconfig.cmake` ~199-243 -- the same seam `west build -t
|
|
29
|
+
menuconfig` uses) pointed at a dumper script this module RENDERS fresh
|
|
30
|
+
onto disk for every run (`_DUMPER_SOURCE`, written under the run's own
|
|
31
|
+
scratch tree -- never a file living in alp-sdk, and never a PyInstaller
|
|
32
|
+
onefile extraction path, which is deleted at process exit and would
|
|
33
|
+
leave the baked-in CMake command pointing at nothing); `west build -t
|
|
34
|
+
alpkconfigjson` then runs it INSIDE the exact Kconfig env Zephyr's own
|
|
35
|
+
CMake computed for that board/toolchain/module set (no env
|
|
36
|
+
reconstruction needed -- Zephyr hands it over directly). This is
|
|
37
|
+
workspace-dependent and is skipped locally without a bootstrapped
|
|
38
|
+
ZEPHYR_BASE; alp-sdk's own dumper copy under `scripts/kconfig/
|
|
39
|
+
alp_kconfig_dump.py` is verified by the pr-twister CI job's
|
|
40
|
+
`scripts/check_emit_kconfig_contract.py` -- that gate covers alp-sdk's
|
|
41
|
+
copy only, not tan's rendered one above.
|
|
42
|
+
|
|
43
|
+
An earlier version tried overriding `-DPYTHON_EXECUTABLE=<spy>` to
|
|
44
|
+
capture kconfig.py's own env instead -- discarded: Zephyr's
|
|
45
|
+
`cmake/modules/python.cmake` re-derives `PYTHON_EXECUTABLE` via a
|
|
46
|
+
plain `set()` (not `_ifndef`), so a `-D` override is silently
|
|
47
|
+
clobbered before `kconfig.cmake` ever runs, and kconfig.py never
|
|
48
|
+
actually invokes the spy.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from __future__ import annotations
|
|
52
|
+
|
|
53
|
+
import json
|
|
54
|
+
import os
|
|
55
|
+
import subprocess
|
|
56
|
+
import sys
|
|
57
|
+
import tempfile
|
|
58
|
+
import textwrap
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
from typing import Any, Optional
|
|
61
|
+
|
|
62
|
+
from tan.core.document_guards import DocumentGuards
|
|
63
|
+
from tan.core.subprocess_env import spawn_env
|
|
64
|
+
|
|
65
|
+
from .models import BoardProject, OrchestratorError
|
|
66
|
+
|
|
67
|
+
#: The malformed-document register, bound to THIS module's curated class.
|
|
68
|
+
_GUARDS = DocumentGuards(OrchestratorError)
|
|
69
|
+
|
|
70
|
+
# RELOCATED divergence from alp-sdk's own scripts/alp_orchestrate/
|
|
71
|
+
# kconfig_symbols.py (tan-cli#459 review): `west_program` resolves the
|
|
72
|
+
# ABSOLUTE `west` inside `tan bootstrap`'s workspace venv, the same helper
|
|
73
|
+
# `build`/`flash`/`west_forward_cmd` already spawn `west` through -- alp-sdk's
|
|
74
|
+
# own copy runs AS a `west` extension, so a bare `"west"` on PATH is always
|
|
75
|
+
# already the one that found it; tan's frozen binary has no such shell, and a
|
|
76
|
+
# literal `"west"` here resolved to nothing (tan-cli#453's actual defect --
|
|
77
|
+
# the caller's PATH-prepend was a per-command mitigation for this, not a fix
|
|
78
|
+
# of it). `tan.core.venv` is importable here: this file, unlike alp-sdk's, is
|
|
79
|
+
# `tan/planner/kconfig_symbols.py`, part of the `tan` package.
|
|
80
|
+
from tan.core.venv import west_program
|
|
81
|
+
|
|
82
|
+
SCHEMA_VERSION = 1
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------
|
|
85
|
+
# Workspace guard (Task 1)
|
|
86
|
+
# ---------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _kconfig_dir(zephyr_base: Path) -> Path:
|
|
90
|
+
return zephyr_base / "scripts" / "kconfig"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _kconfiglib_path(zephyr_base: Path) -> Path:
|
|
94
|
+
return _kconfig_dir(zephyr_base) / "kconfiglib.py"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _require_workspace() -> Path:
|
|
98
|
+
"""Return the bootstrapped `ZEPHYR_BASE`, or print the actionable
|
|
99
|
+
message + exit(2) -- this mode's one deliberate deviation from the
|
|
100
|
+
rest of the orchestrator's `OrchestratorError` -> exit(1) convention,
|
|
101
|
+
marking "no workspace" as distinct from an ordinary usage error.
|
|
102
|
+
|
|
103
|
+
This process itself never imports kconfiglib (only the rendered dumper
|
|
104
|
+
does -- see `_DUMPER_SOURCE` -- in its own subprocess running inside
|
|
105
|
+
Zephyr's own env; see `_load_board_symbols`); this just probes
|
|
106
|
+
`kconfiglib.py`'s presence as the "is this ZEPHYR_BASE real" check.
|
|
107
|
+
"""
|
|
108
|
+
raw = os.environ.get("ZEPHYR_BASE")
|
|
109
|
+
if raw:
|
|
110
|
+
zephyr_base = Path(raw)
|
|
111
|
+
if _kconfiglib_path(zephyr_base).is_file():
|
|
112
|
+
return zephyr_base
|
|
113
|
+
print(
|
|
114
|
+
"alp_orchestrate: --emit kconfig requires a bootstrapped Zephyr "
|
|
115
|
+
"workspace (set ZEPHYR_BASE; west init/update v4.4.0)",
|
|
116
|
+
file=sys.stderr,
|
|
117
|
+
)
|
|
118
|
+
sys.exit(2)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------
|
|
122
|
+
# Hermetic symbol projection + envelope (Task 2) -- no kconfiglib import
|
|
123
|
+
# at module scope, so this half unit-tests with no Zephyr installed.
|
|
124
|
+
# ---------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _envelope(board: str, core: str, symbols: list[dict[str, Any]]) -> dict[str, Any]:
|
|
128
|
+
return {
|
|
129
|
+
"schemaVersion": SCHEMA_VERSION,
|
|
130
|
+
"board": board,
|
|
131
|
+
"core": core,
|
|
132
|
+
"symbols": symbols,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------
|
|
137
|
+
# Approach-A Zephyr load (Task 3) -- workspace-dependent
|
|
138
|
+
# ---------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
_STUB_CMAKELISTS = textwrap.dedent("""\
|
|
141
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
142
|
+
# Auto-generated by alp_orchestrate/kconfig_symbols.py (#893) -- a
|
|
143
|
+
# throwaway stub app whose sole purpose is to make Zephyr's own CMake
|
|
144
|
+
# configure the Kconfig tree for one board so kconfiglib can be
|
|
145
|
+
# pointed at the real env it computed. Never built.
|
|
146
|
+
cmake_minimum_required(VERSION 3.20.0)
|
|
147
|
+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
|
148
|
+
project(alp_emit_kconfig_stub)
|
|
149
|
+
target_sources(app PRIVATE src/main.c)
|
|
150
|
+
""")
|
|
151
|
+
|
|
152
|
+
_STUB_MAIN_C = "int main(void)\n{\n\treturn 0;\n}\n"
|
|
153
|
+
|
|
154
|
+
_KCONFIG_TARGET = "alpkconfigjson"
|
|
155
|
+
|
|
156
|
+
# The dumper Zephyr's `EXTRA_KCONFIG_TARGET` custom target runs, RENDERED onto
|
|
157
|
+
# disk fresh for every `_load_board_symbols` call rather than read off a fixed
|
|
158
|
+
# path -- see `_load_board_symbols`'s docstring for the PyInstaller-onefile /
|
|
159
|
+
# stale-build-dir hazard a fixed path would reintroduce. This replaces the
|
|
160
|
+
# earlier design: first a `__file__`-relative sibling walk, then a path
|
|
161
|
+
# anchored on the bound alp-sdk checkout (`REPO / "scripts" / "kconfig" /
|
|
162
|
+
# "alp_kconfig_dump.py"`) -- both coupled this module to a file living
|
|
163
|
+
# outside `tan`.
|
|
164
|
+
#
|
|
165
|
+
# Deliberately self-contained: this text becomes a standalone .py file that
|
|
166
|
+
# runs as a *subprocess*, inside Zephyr's own Kconfig environment, with its
|
|
167
|
+
# own path baked into the generated build tree by CMake -- so it must import
|
|
168
|
+
# NOTHING from `tan` or `alp_orchestrate` (neither package is guaranteed to
|
|
169
|
+
# be importable from wherever this file ends up, and under a frozen
|
|
170
|
+
# PyInstaller build there is no `tan` *source* on disk to import at all).
|
|
171
|
+
# Only the stdlib plus `kconfiglib`, which Zephyr's own env injects via
|
|
172
|
+
# ZEPHYR_BASE.
|
|
173
|
+
#
|
|
174
|
+
# `_project_symbols` therefore lives ONLY here, as plain literal source, not
|
|
175
|
+
# imported and not mirrored elsewhere in this module;
|
|
176
|
+
# `tests/core/test_kconfig_symbols.py` execs this constant in an isolated
|
|
177
|
+
# namespace (with a fake `kconfiglib` module) and asserts its projection
|
|
178
|
+
# output against a literal expected result -- an independent oracle, so a
|
|
179
|
+
# regression here fails a fast, hermetic test instead of surfacing only in a
|
|
180
|
+
# Zephyr-bootstrapped CI job.
|
|
181
|
+
_DUMPER_SOURCE = '''\
|
|
182
|
+
#!/usr/bin/env python3
|
|
183
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
184
|
+
"""Dumps one board's promptable Kconfig symbols as JSON.
|
|
185
|
+
|
|
186
|
+
RENDERED BY tan (tan.planner.kconfig_symbols._load_board_symbols) fresh for
|
|
187
|
+
every `--emit kconfig` run, into that run's own scratch build tree -- this
|
|
188
|
+
file is never checked in and never lives in alp-sdk.
|
|
189
|
+
|
|
190
|
+
Runs INSIDE Zephyr's own Kconfig environment via the `EXTRA_KCONFIG_TARGET`
|
|
191
|
+
mechanism (`cmake/modules/kconfig.cmake` ~199-243, the same seam
|
|
192
|
+
`menuconfig`/`guiconfig`/`hardenconfig`/`traceconfig` use): a
|
|
193
|
+
`west build --cmake-only -- -DEXTRA_KCONFIG_TARGETS=alpkconfigjson
|
|
194
|
+
-DEXTRA_KCONFIG_TARGET_COMMAND_FOR_alpkconfigjson=<this>;--output;<path>`
|
|
195
|
+
registers a custom target that (per kconfig.cmake:227-242) runs
|
|
196
|
+
|
|
197
|
+
${CMAKE_COMMAND} -E env <COMMON_KCONFIG_ENV_SETTINGS...> \\\\
|
|
198
|
+
${PYTHON_EXECUTABLE} <this> --output <path> ${KCONFIG_ROOT}
|
|
199
|
+
|
|
200
|
+
i.e. Zephyr's CMake hands this script the *exact* env it computed for that
|
|
201
|
+
board/toolchain/module set (srctree, ARCH, BOARD_DIR, module Kconfigs, ...)
|
|
202
|
+
plus `KCONFIG_ROOT` as the trailing positional -- no env reconstruction
|
|
203
|
+
needed. `west build -d <build> -t alpkconfigjson` (a second, separate
|
|
204
|
+
invocation -- `add_custom_target` isn't built by `--cmake-only` alone) is
|
|
205
|
+
what actually runs it. See `tan.planner.kconfig_symbols._load_board_symbols`
|
|
206
|
+
for the caller.
|
|
207
|
+
|
|
208
|
+
Deliberately self-contained (imports nothing from `tan` or
|
|
209
|
+
`alp_orchestrate`): this file's own path is baked into the generated build
|
|
210
|
+
tree by the CMake invocation above, so it has to keep working from wherever
|
|
211
|
+
`tan` chose to write it -- see the caller for why that path is a fresh
|
|
212
|
+
scratch directory rather than a fixed one.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
from __future__ import annotations
|
|
216
|
+
|
|
217
|
+
import argparse
|
|
218
|
+
import json
|
|
219
|
+
import os
|
|
220
|
+
import sys
|
|
221
|
+
from pathlib import Path
|
|
222
|
+
|
|
223
|
+
# kconfiglib is a plain module file under $ZEPHYR_BASE/scripts/kconfig, not
|
|
224
|
+
# pip-installed -- ZEPHYR_BASE is guaranteed set in this script's env (see
|
|
225
|
+
# module docstring: COMMON_KCONFIG_ENV_SETTINGS always sets it).
|
|
226
|
+
sys.path.insert(0, os.path.join(os.environ["ZEPHYR_BASE"], "scripts", "kconfig"))
|
|
227
|
+
import kconfiglib # noqa: E402
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _project_symbols(syms):
|
|
231
|
+
"""Project promptable symbols to `{name, type, prompt, depends,
|
|
232
|
+
default, help}`, sorted by name.
|
|
233
|
+
|
|
234
|
+
Only symbols with a prompt on ANY of their `MenuNode`s are kept -- a
|
|
235
|
+
symbol can be declared in multiple locations (Zephyr's own
|
|
236
|
+
`scripts/kconfig/kconfig.py::promptless()` checks the same way, with the
|
|
237
|
+
same reasoning: "the symbol might be defined in multiple locations, we
|
|
238
|
+
need to check all locations"). This matters in practice: many board/SoC
|
|
239
|
+
`Kconfig.defconfig` fragments redeclare a symbol with a promptless
|
|
240
|
+
`default`-only override block, and `Kconfig.zephyr` sources those BEFORE
|
|
241
|
+
the canonical declaration -- so taking `nodes[0]` alone silently dropped
|
|
242
|
+
real, user-facing symbols like `LOG` / `SERIAL` / `MAIN_STACK_SIZE`
|
|
243
|
+
(alp-sdk #893). This is the only copy of this projection logic (see the
|
|
244
|
+
module docstring) -- verified against a literal expected result in
|
|
245
|
+
`tests/core/test_kconfig_symbols.py`, so the "first prompt-bearing node,
|
|
246
|
+
not nodes[0]" behavior is pinned on purpose.
|
|
247
|
+
"""
|
|
248
|
+
projected = []
|
|
249
|
+
for sym in syms:
|
|
250
|
+
node = next((n for n in sym.nodes if n.prompt), None)
|
|
251
|
+
if node is None:
|
|
252
|
+
continue
|
|
253
|
+
default = None
|
|
254
|
+
if sym.orig_defaults:
|
|
255
|
+
default_expr, _cond = sym.orig_defaults[0]
|
|
256
|
+
default = kconfiglib.expr_str(default_expr)
|
|
257
|
+
projected.append({
|
|
258
|
+
"name": sym.name,
|
|
259
|
+
"type": kconfiglib.TYPE_TO_STR.get(sym.type, "unknown"),
|
|
260
|
+
"prompt": node.prompt[0],
|
|
261
|
+
"depends": kconfiglib.expr_str(sym.direct_dep) or "",
|
|
262
|
+
"default": default,
|
|
263
|
+
"help": getattr(node, "help", None) or "",
|
|
264
|
+
})
|
|
265
|
+
projected.sort(key=lambda entry: entry["name"])
|
|
266
|
+
return projected
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main() -> int:
|
|
270
|
+
ap = argparse.ArgumentParser(description=__doc__)
|
|
271
|
+
ap.add_argument("--output", required=True, type=Path)
|
|
272
|
+
ap.add_argument("kconfig_root")
|
|
273
|
+
args = ap.parse_args()
|
|
274
|
+
|
|
275
|
+
kconf = kconfiglib.Kconfig(args.kconfig_root, warn=True, warn_to_stderr=False)
|
|
276
|
+
symbols = _project_symbols(kconf.unique_defined_syms)
|
|
277
|
+
# write-text-newline-exempt: scratch build dir, read back only by
|
|
278
|
+
# tan.planner.kconfig_symbols._load_board_symbols
|
|
279
|
+
args.output.write_text(json.dumps(symbols), encoding="utf-8")
|
|
280
|
+
return 0
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if __name__ == "__main__":
|
|
284
|
+
sys.exit(main())
|
|
285
|
+
'''
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _write_stub_app(app_dir: Path) -> None:
|
|
289
|
+
(app_dir / "src").mkdir(parents=True)
|
|
290
|
+
# write-text-newline-exempt: throwaway stub app in a scratch build dir
|
|
291
|
+
(app_dir / "CMakeLists.txt").write_text(_STUB_CMAKELISTS, encoding="utf-8")
|
|
292
|
+
# write-text-newline-exempt: throwaway stub app in a scratch build dir
|
|
293
|
+
(app_dir / "prj.conf").write_text("", encoding="utf-8")
|
|
294
|
+
# write-text-newline-exempt: throwaway stub app in a scratch build dir
|
|
295
|
+
(app_dir / "src" / "main.c").write_text(_STUB_MAIN_C, encoding="utf-8")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _write_dumper(dumper_path: Path) -> None:
|
|
299
|
+
"""Render `_DUMPER_SOURCE` onto disk at `dumper_path`.
|
|
300
|
+
|
|
301
|
+
Called once per `_load_board_symbols` run, into that run's own scratch
|
|
302
|
+
tree (never a fixed/cached path) -- see `_DUMPER_SOURCE`'s docstring for
|
|
303
|
+
why: a fixed path under a PyInstaller onefile extraction directory would
|
|
304
|
+
be deleted at process exit while the CMake command baked into the build
|
|
305
|
+
tree still names it, and a fixed path anywhere else would let a stale
|
|
306
|
+
render survive a project change instead of being regenerated.
|
|
307
|
+
"""
|
|
308
|
+
dumper_path.parent.mkdir(parents=True, exist_ok=True)
|
|
309
|
+
# write-text-newline-exempt: rendered fresh into a scratch build dir
|
|
310
|
+
dumper_path.write_text(_DUMPER_SOURCE, encoding="utf-8")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _load_board_symbols(zephyr_base: Path, board_triple: str) -> list[dict[str, Any]]:
|
|
314
|
+
"""Approach A: configure a stub app for `board_triple`, render the
|
|
315
|
+
dumper (`_DUMPER_SOURCE`) into this run's own scratch tree, then run it
|
|
316
|
+
INSIDE Zephyr's own Kconfig env via the `EXTRA_KCONFIG_TARGET` mechanism
|
|
317
|
+
(the same seam `west build -t menuconfig` uses -- see `_DUMPER_SOURCE`'s
|
|
318
|
+
own module docstring). Returns the already-projected `list[dict]` the
|
|
319
|
+
dumper wrote.
|
|
320
|
+
|
|
321
|
+
`west build` is a west extension command -- it needs to run from
|
|
322
|
+
inside a west workspace (a `.west/` upward from cwd), which
|
|
323
|
+
`alp_orchestrate` itself is never a part of. `$ZEPHYR_BASE/..` is
|
|
324
|
+
that workspace's topdir under every documented Zephyr `west init`
|
|
325
|
+
layout (Getting Started's `~/zephyrproject/{.west, zephyr,
|
|
326
|
+
modules,...}`, and the same layout pr-twister.yml's own `west init
|
|
327
|
+
-m .../zephyr .` step produces).
|
|
328
|
+
|
|
329
|
+
The dumper is rendered fresh into `tmp_dir` on every call and `tmp_dir`
|
|
330
|
+
is deleted in this function's own `finally` below -- well after both
|
|
331
|
+
`west build` invocations that read the rendered path have already run,
|
|
332
|
+
so the render neither survives to be reused stale by a later call nor
|
|
333
|
+
depends on anything outliving this process (see `_DUMPER_SOURCE`).
|
|
334
|
+
"""
|
|
335
|
+
tmp_dir = Path(tempfile.mkdtemp(prefix="alp-emit-kconfig-"))
|
|
336
|
+
try:
|
|
337
|
+
app_dir = tmp_dir / "stub"
|
|
338
|
+
_write_stub_app(app_dir)
|
|
339
|
+
|
|
340
|
+
dumper_path = tmp_dir / "alp_kconfig_dump.py"
|
|
341
|
+
_write_dumper(dumper_path)
|
|
342
|
+
|
|
343
|
+
build_dir = tmp_dir / "build"
|
|
344
|
+
output_json = build_dir / "alp_kconfig.json"
|
|
345
|
+
# A CMake list (semicolon-separated -- COMMAND_EXPAND_LISTS on the
|
|
346
|
+
# custom target splits it into argv tokens): the dumper script,
|
|
347
|
+
# then its own `--output <path>` -- Zephyr appends ${KCONFIG_ROOT}
|
|
348
|
+
# as the final token itself (kconfig.cmake:238).
|
|
349
|
+
target_cmd = f"{dumper_path};--output;{output_json}"
|
|
350
|
+
|
|
351
|
+
# `zephyr_base.parent` IS the workspace topdir (this function's own
|
|
352
|
+
# docstring above), already the one `west_workspace_dir` verified
|
|
353
|
+
# before `ZEPHYR_BASE` was ever set -- so `sdk_root=None` here is not
|
|
354
|
+
# a second, unverified guess, just skipping a manifest re-check the
|
|
355
|
+
# caller already made.
|
|
356
|
+
west = west_program(str(zephyr_base.parent), None)
|
|
357
|
+
configure_cmd = [
|
|
358
|
+
west, "build", "--cmake-only",
|
|
359
|
+
"-b", board_triple,
|
|
360
|
+
"-d", str(build_dir),
|
|
361
|
+
str(app_dir),
|
|
362
|
+
"--",
|
|
363
|
+
f"-DEXTRA_KCONFIG_TARGETS={_KCONFIG_TARGET}",
|
|
364
|
+
f"-DEXTRA_KCONFIG_TARGET_COMMAND_FOR_{_KCONFIG_TARGET}={target_cmd}",
|
|
365
|
+
]
|
|
366
|
+
# RELOCATED divergence from alp-sdk's own scripts/alp_orchestrate/
|
|
367
|
+
# kconfig_symbols.py: `env=spawn_env()` on both `west build` spawns
|
|
368
|
+
# below is a tan-only addition (tan-cli#992) -- alp-sdk's own copy
|
|
369
|
+
# has no such restore because it never ships as a frozen PyInstaller
|
|
370
|
+
# bundle whose LD_LIBRARY_PATH would otherwise leak into the child.
|
|
371
|
+
proc = subprocess.run(
|
|
372
|
+
configure_cmd, cwd=zephyr_base.parent,
|
|
373
|
+
capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
374
|
+
env=spawn_env({"PYTHONIOENCODING": "utf-8"}))
|
|
375
|
+
if proc.returncode != 0:
|
|
376
|
+
raise OrchestratorError(
|
|
377
|
+
f"--emit kconfig: `west build --cmake-only -b "
|
|
378
|
+
f"{board_triple}` failed:\n{proc.stderr.strip()}")
|
|
379
|
+
|
|
380
|
+
# `add_custom_target` never runs at configure time -- `-t` builds
|
|
381
|
+
# it explicitly (a second, separate `west build`).
|
|
382
|
+
build_cmd = [west, "build", "-d", str(build_dir), "-t", _KCONFIG_TARGET]
|
|
383
|
+
proc = subprocess.run(
|
|
384
|
+
build_cmd, cwd=zephyr_base.parent,
|
|
385
|
+
capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
386
|
+
env=spawn_env({"PYTHONIOENCODING": "utf-8"}))
|
|
387
|
+
if proc.returncode != 0:
|
|
388
|
+
raise OrchestratorError(
|
|
389
|
+
f"--emit kconfig: `west build -t {_KCONFIG_TARGET}` failed "
|
|
390
|
+
f"for board '{board_triple}':\n{proc.stderr.strip()}")
|
|
391
|
+
# tan-cli#1162, and a RELOCATED divergence from alp-sdk's own
|
|
392
|
+
# `scripts/alp_orchestrate/kconfig_symbols.py`, which still spells
|
|
393
|
+
# this as an `is_file()` pre-flight plus a bare `read_text`. This
|
|
394
|
+
# artefact is written by an EXTERNAL `west build -t` subprocess, so
|
|
395
|
+
# a directory in its place, a non-UTF-8 dump or an unreadable mode
|
|
396
|
+
# are things a third-party build step really leaves -- each escaped
|
|
397
|
+
# raw before, into `kconfig_cmd.py:541`'s `kconfig.emit-failed`
|
|
398
|
+
# envelope (or `generate_cmd.py:890`'s) naming the exception TYPE.
|
|
399
|
+
# `never wrote` is preserved byte for byte through `absent=`; what
|
|
400
|
+
# the pre-flight silently folded into it now says `cannot read`.
|
|
401
|
+
raw = _GUARDS.require_readable_text(
|
|
402
|
+
output_json,
|
|
403
|
+
what=f"the `{_KCONFIG_TARGET}` dumper's output",
|
|
404
|
+
absent=(f"--emit kconfig: `west build -t {_KCONFIG_TARGET}` "
|
|
405
|
+
f"completed but never wrote {output_json} -- never emit "
|
|
406
|
+
f"a partial/empty menu"))
|
|
407
|
+
if not raw.strip():
|
|
408
|
+
# `west build -t <target>` can report success (rc 0) while the
|
|
409
|
+
# custom target's own command silently produced nothing -- a
|
|
410
|
+
# 0-byte `output_json` used to reach `json.loads` below and
|
|
411
|
+
# surface as a bare, uncoded `JSONDecodeError` rather than a
|
|
412
|
+
# loud refusal. Never emit a partial/empty menu from an empty
|
|
413
|
+
# artefact.
|
|
414
|
+
raise OrchestratorError(
|
|
415
|
+
f"--emit kconfig: `west build -t {_KCONFIG_TARGET}` "
|
|
416
|
+
f"reported success but wrote an empty {output_json} -- "
|
|
417
|
+
f"never emit a partial/empty menu")
|
|
418
|
+
try:
|
|
419
|
+
symbols = json.loads(raw)
|
|
420
|
+
except json.JSONDecodeError as e:
|
|
421
|
+
raise OrchestratorError(
|
|
422
|
+
f"--emit kconfig: {output_json} is not valid JSON ({e}) -- "
|
|
423
|
+
f"the `{_KCONFIG_TARGET}` dumper wrote a corrupt artefact, "
|
|
424
|
+
f"not a partial/empty menu") from e
|
|
425
|
+
if not isinstance(symbols, list):
|
|
426
|
+
raise OrchestratorError(
|
|
427
|
+
f"--emit kconfig: {output_json} parsed as JSON but is not a "
|
|
428
|
+
f"list of symbols (got {type(symbols).__name__}) -- the "
|
|
429
|
+
f"`{_KCONFIG_TARGET}` dumper wrote a corrupt artefact, not "
|
|
430
|
+
f"a partial/empty menu")
|
|
431
|
+
return symbols
|
|
432
|
+
finally:
|
|
433
|
+
import shutil
|
|
434
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# ---------------------------------------------------------------------
|
|
438
|
+
# Public entry point
|
|
439
|
+
# ---------------------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def emit_kconfig(project: BoardProject, core: Optional[str]) -> str:
|
|
443
|
+
"""`--emit kconfig`: the board-scoped, user-settable Kconfig symbol
|
|
444
|
+
menu for the resolved `--core <id>` slice, as JSON.
|
|
445
|
+
|
|
446
|
+
Validates `core` against the project's own resolved slices first
|
|
447
|
+
(a plain `OrchestratorError` -- exit(1) via the cli's usual path),
|
|
448
|
+
THEN checks the Zephyr workspace is bootstrapped (exit(2) -- see
|
|
449
|
+
`_require_workspace`).
|
|
450
|
+
"""
|
|
451
|
+
if not core:
|
|
452
|
+
raise OrchestratorError(
|
|
453
|
+
"--emit kconfig requires --core <id> (the SDK plans one "
|
|
454
|
+
"Kconfig symbol menu per board, not per project)")
|
|
455
|
+
if core not in project.cores:
|
|
456
|
+
raise OrchestratorError(
|
|
457
|
+
f"--core {core} not present in this board.yaml's cores "
|
|
458
|
+
f"(have: {', '.join(sorted(project.cores))})")
|
|
459
|
+
slice_ = project.cores[core]
|
|
460
|
+
if not slice_.board:
|
|
461
|
+
raise OrchestratorError(
|
|
462
|
+
f"core '{core}' has no resolved Zephyr board target "
|
|
463
|
+
f"(os: {slice_.os}); --emit kconfig only applies to "
|
|
464
|
+
f"Zephyr cores")
|
|
465
|
+
|
|
466
|
+
zephyr_base = _require_workspace()
|
|
467
|
+
|
|
468
|
+
# The rendered dumper (`_DUMPER_SOURCE`, run inside Zephyr's own Kconfig
|
|
469
|
+
# env -- see `_load_board_symbols`) already projects with the real
|
|
470
|
+
# kconfiglib, so this is just the envelope wrap.
|
|
471
|
+
symbols = _load_board_symbols(zephyr_base, slice_.board)
|
|
472
|
+
envelope = _envelope(slice_.board, core, symbols)
|
|
473
|
+
return json.dumps(envelope, indent=2) + "\n"
|