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
tan/planner_emit.py
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Render `alp_project.py`'s emit modes in-process, from `tan.planner`.
|
|
3
|
+
|
|
4
|
+
`tan.planner_root.emit` covers the eight modes alp-sdk's `alp_orchestrate` CLI
|
|
5
|
+
exposed. This module covers the OTHER front door: the modes whose renderer moved
|
|
6
|
+
into `tan/planner/` but whose `--emit` flag lived on `scripts/alp_project.py`.
|
|
7
|
+
`tan generate` is the only caller, and until it routed through here `tan` shelled
|
|
8
|
+
that script -- so nothing had actually moved off alp-sdk's Python, whatever the
|
|
9
|
+
relocation commit said.
|
|
10
|
+
|
|
11
|
+
**All twelve modes `tan generate` can reach now render here.** The last six --
|
|
12
|
+
`dts-overlay`, `native-sim-overlay`, `hw-info-h`, `west-libraries`,
|
|
13
|
+
`carrier-netlist` and `zephyr-board` -- relocated in the same MOVE-not-rewrite
|
|
14
|
+
shape as the first five, plus `composed-route-table` (the older debug view of
|
|
15
|
+
`carrier-netlist`'s own route rows), so `IN_PROCESS_MODES` is the whole
|
|
16
|
+
reachable set and `tan generate` spawns nothing:
|
|
17
|
+
|
|
18
|
+
zephyr-conf kconfig.py _slice_alp_conf
|
|
19
|
+
cmake-args kconfig.py _slice_cmake_args
|
|
20
|
+
yocto-conf kconfig.py _slice_local_conf
|
|
21
|
+
os-topology topology.py emit_os_topology
|
|
22
|
+
ipc-contract-h headers.py emit_ipc_contract_h
|
|
23
|
+
dts-overlay project_emit/dts.py _emit_dts_overlay
|
|
24
|
+
native-sim-overlay project_emit/native_sim.py _emit_native_sim_overlay
|
|
25
|
+
hw-info-h project_emit/hw_info.py _emit_hw_info_h
|
|
26
|
+
west-libraries project_emit/west_libs.py _emit_west_libraries
|
|
27
|
+
carrier-netlist project_emit/bom_netlist.py _emit_carrier_netlist
|
|
28
|
+
composed-route-table project_emit/bom_netlist.py _emit_composed_route_table
|
|
29
|
+
zephyr-board zephyr_board.py emit_zephyr_board
|
|
30
|
+
|
|
31
|
+
The mapping is not guessed: `metadata/emit-registry-v1.json` names each mode's
|
|
32
|
+
`owner.module`, and every module above is one of those owners, relocated.
|
|
33
|
+
|
|
34
|
+
**Byte-identity is the whole contract**, so the dispatch below mirrors
|
|
35
|
+
`_run_v2_per_core_emit` and `main()` in `scripts/alp_project.py` line for line --
|
|
36
|
+
including the details a rewrite loses:
|
|
37
|
+
|
|
38
|
+
* `os: off` cores are skipped before the OS-class check;
|
|
39
|
+
* `zephyr-conf --core <id>` emits the slice with NO `# --- core: ... ---` marker
|
|
40
|
+
while every other per-core combination emits one;
|
|
41
|
+
* the trailing newline is added only when the last part lacks it;
|
|
42
|
+
* `dts-overlay` and `west-libraries` union across ZEPHYR/BAREMETAL cores when
|
|
43
|
+
unscoped, while `hw-info-h` unions across ALL cores and picks a primary;
|
|
44
|
+
* `dts-overlay` needs the Zephyr/baremetal core-id LIST, not just the union, so
|
|
45
|
+
the AEN i3c wiring can ask whether `m55_he` is in scope at all;
|
|
46
|
+
* `native-sim-overlay` takes no `--core` scoping of any kind;
|
|
47
|
+
* `carrier-netlist` and `composed-route-table` never build a per-core slice
|
|
48
|
+
model -- both read the raw board.yaml dict, the SoM preset and the
|
|
49
|
+
inline-or-preset board, exactly as `main()` does before the per-core
|
|
50
|
+
machinery is ever reached.
|
|
51
|
+
|
|
52
|
+
**`write` uses `newline=""`, matching the SDK's own `_write_or_print`.** Windows
|
|
53
|
+
text mode would translate every `\\n` to `\\r\\n`; the SDK's emits are LF on every
|
|
54
|
+
host, and a CRLF `alp.conf` flips every emit-snapshot golden in alp-sdk.
|
|
55
|
+
|
|
56
|
+
**Which OS classes a per-core mode accepts is metadata, not a table here.**
|
|
57
|
+
`compatible.os` in the emit registry carries it (`zephyr-conf` -> zephyr,
|
|
58
|
+
`cmake-args` -> baremetal+zephyr, `yocto-conf` -> yocto) and alp-sdk's
|
|
59
|
+
`scripts/check_emit_registry.py` keeps it honest against the real code. Copying
|
|
60
|
+
it into `tan` would be a second source of truth for a fact tan must not learn
|
|
61
|
+
(ADR-0017 / I-26).
|
|
62
|
+
|
|
63
|
+
**`metadata/**` never relocated.** Every emitter here reads it out of the
|
|
64
|
+
RESOLVED `sdk_root` via `tan.planner.paths`, which is why `bind_sdk_root` runs
|
|
65
|
+
before the planner is imported at all.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
from __future__ import annotations
|
|
69
|
+
|
|
70
|
+
import json
|
|
71
|
+
from pathlib import Path
|
|
72
|
+
from typing import Any
|
|
73
|
+
|
|
74
|
+
from tan.planner_root import bind_sdk_root
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
"IN_PROCESS_MODES",
|
|
78
|
+
"TREE_MODES",
|
|
79
|
+
"PlannerEmitError",
|
|
80
|
+
"render",
|
|
81
|
+
"render_tree",
|
|
82
|
+
"unavailable",
|
|
83
|
+
"write",
|
|
84
|
+
"write_tree",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
#: Every mode `tan generate` can reach, all of them served here (see the module
|
|
88
|
+
#: docstring for how the registry decides the set).
|
|
89
|
+
IN_PROCESS_MODES = frozenset({
|
|
90
|
+
"zephyr-conf",
|
|
91
|
+
"cmake-args",
|
|
92
|
+
"yocto-conf",
|
|
93
|
+
"os-topology",
|
|
94
|
+
"ipc-contract-h",
|
|
95
|
+
"dts-overlay",
|
|
96
|
+
"native-sim-overlay",
|
|
97
|
+
"hw-info-h",
|
|
98
|
+
"west-libraries",
|
|
99
|
+
"carrier-netlist",
|
|
100
|
+
"composed-route-table",
|
|
101
|
+
"zephyr-board",
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
#: The modes that write a DIRECTORY of files rather than one stream, and so go
|
|
105
|
+
#: through `render_tree`/`write_tree` instead of `render`/`write`. One today:
|
|
106
|
+
#: `--emit zephyr-board` emits a whole Zephyr board tree named per SKU+core.
|
|
107
|
+
TREE_MODES = frozenset({"zephyr-board"})
|
|
108
|
+
|
|
109
|
+
#: The three per-core config slices this mode set names -- the membership test
|
|
110
|
+
#: `render()` uses to route into `_render_per_core` at all
|
|
111
|
+
#: (`if mode in _SLICE_RENDERER`). All three render through a `buildplan`
|
|
112
|
+
#: artefact helper instead (tan-cli#1216, `_CONFIG_ARTEFACT_HELPER`) -- see
|
|
113
|
+
#: `_render_per_core` -- so the values below only document the leaf renderer
|
|
114
|
+
#: that helper still calls; nothing resolves them by name.
|
|
115
|
+
_SLICE_RENDERER = {
|
|
116
|
+
"zephyr-conf": "_slice_alp_conf",
|
|
117
|
+
"yocto-conf": "_slice_local_conf",
|
|
118
|
+
"cmake-args": "_slice_cmake_args",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#: The modes whose bytes ARE a build-plan `configArtefacts[]` entry, and the
|
|
122
|
+
#: file name that entry carries (`metadata/schemas/build-plan-v1.schema.json`:
|
|
123
|
+
#: "byte-identical to what a consumer's own materialise step writes to
|
|
124
|
+
#: buildDir"). These render through the `buildplan` helper `emit_build_plan` also
|
|
125
|
+
#: calls for the same artefact (tan-cli#1216, ADR-0026 §D).
|
|
126
|
+
_CONFIG_ARTEFACT_FILE = {
|
|
127
|
+
"zephyr-conf": "alp.conf",
|
|
128
|
+
"yocto-conf": "local.conf",
|
|
129
|
+
"cmake-args": "cmake-args.txt",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#: The `buildplan` helper that returns each of those modes' `(file, contents)`.
|
|
133
|
+
#: `cmake-args.txt` is an ADDITIVE plan artefact (tan-cli#1216), so it has its
|
|
134
|
+
#: own helper; the other two are a slice's primary config artefact.
|
|
135
|
+
_CONFIG_ARTEFACT_HELPER = {
|
|
136
|
+
"zephyr-conf": "_slice_config_artefact",
|
|
137
|
+
"yocto-conf": "_slice_config_artefact",
|
|
138
|
+
"cmake-args": "_slice_cmake_args_artefact",
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#: `carrier-netlist` / `composed-route-table` share one resolution shape (see
|
|
142
|
+
#: `_render_route_table`); this maps each to the emitter function that owns it.
|
|
143
|
+
_ROUTE_TABLE_EMITTER = {
|
|
144
|
+
"carrier-netlist": "_emit_carrier_netlist",
|
|
145
|
+
"composed-route-table": "_emit_composed_route_table",
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#: The emit registry, relative to the SDK checkout root.
|
|
149
|
+
_REGISTRY = ("metadata", "emit-registry-v1.json")
|
|
150
|
+
|
|
151
|
+
#: The one file whose absence means "this is not a usable alp-sdk checkout".
|
|
152
|
+
#: The same probe `build_cmd._emit_plan` makes, deliberately: the planner is
|
|
153
|
+
#: tan's now, so requiring the SDK to still ship `alp_orchestrate` would refuse
|
|
154
|
+
#: precisely the releases this relocation exists to enable.
|
|
155
|
+
_SDK_PROBE = ("metadata", "schemas", "board.schema.json")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class PlannerEmitError(Exception):
|
|
159
|
+
"""The planner refused this emit. Carries the message a user should read."""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _joined(root: Path, parts: tuple[str, ...]) -> Path:
|
|
163
|
+
for part in parts:
|
|
164
|
+
root = root / part
|
|
165
|
+
return root
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _os_classes(sdk_root: Path) -> dict[str, tuple[str, ...]]:
|
|
169
|
+
"""`{mode: (os_class, ...)}` from the emit registry, for the modes that
|
|
170
|
+
restrict one. `compatible.os == ["any"]` means no restriction, so it is
|
|
171
|
+
absent from the result -- mirroring `_EMIT_OS_CLASSES.get(mode) is None`.
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
doc = json.loads(_joined(sdk_root, _REGISTRY).read_text(encoding="utf-8"))
|
|
175
|
+
modes = doc["modes"]
|
|
176
|
+
except (OSError, UnicodeDecodeError, ValueError, KeyError, TypeError) as err:
|
|
177
|
+
raise PlannerEmitError(
|
|
178
|
+
f"`{_joined(sdk_root, _REGISTRY)}` is missing or unreadable ({err}); "
|
|
179
|
+
"it declares which OS classes each per-core emit mode accepts."
|
|
180
|
+
) from err
|
|
181
|
+
classes: dict[str, tuple[str, ...]] = {}
|
|
182
|
+
for entry in modes:
|
|
183
|
+
allowed = ((entry.get("compatible") or {}).get("os")) or []
|
|
184
|
+
if allowed and "any" not in allowed:
|
|
185
|
+
classes[entry.get("mode")] = tuple(allowed)
|
|
186
|
+
return classes
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def unavailable(sdk_root: Path) -> str | None:
|
|
190
|
+
"""Why the in-process path cannot serve `sdk_root`, or `None` when it can.
|
|
191
|
+
|
|
192
|
+
A REASON, never a bool: `tan generate` reports it, because a fallback nobody
|
|
193
|
+
can see is how a port gets believed before it happened.
|
|
194
|
+
|
|
195
|
+
Three ways it comes back non-`None`, and all three are a customer's layout
|
|
196
|
+
rather than a bug: the path is not a usable checkout, its emit registry is
|
|
197
|
+
unreadable, or this build of `tan` cannot import the planner at all (a
|
|
198
|
+
source checkout or frozen binary without `PyYAML`/`jsonschema` -- which
|
|
199
|
+
`tan.planner.loader` reports by calling `sys.exit`, not by raising).
|
|
200
|
+
"""
|
|
201
|
+
probe = _joined(sdk_root, _SDK_PROBE)
|
|
202
|
+
if not probe.is_file():
|
|
203
|
+
return f"`{probe}` is absent, so `{sdk_root}` is not a usable alp-sdk checkout"
|
|
204
|
+
try:
|
|
205
|
+
_os_classes(sdk_root)
|
|
206
|
+
bind_sdk_root(sdk_root)
|
|
207
|
+
import tan.planner # noqa: F401,PLC0415 -- the availability probe IS the import
|
|
208
|
+
except SystemExit as err:
|
|
209
|
+
return f"the relocated planner exited on import (code {err.code})"
|
|
210
|
+
except PlannerEmitError as err:
|
|
211
|
+
return str(err)
|
|
212
|
+
except Exception as err: # noqa: BLE001 -- any import failure is a fallback, not a crash
|
|
213
|
+
return f"the relocated planner could not be imported: {type(err).__name__}: {err}"
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def render(
|
|
218
|
+
mode: str,
|
|
219
|
+
*,
|
|
220
|
+
sdk_root: Path,
|
|
221
|
+
board_yaml: Path,
|
|
222
|
+
core: str | None = None,
|
|
223
|
+
skip_advisories: list[str] | None = None,
|
|
224
|
+
) -> str:
|
|
225
|
+
"""One relocated emit mode as text. Raises on any refusal.
|
|
226
|
+
|
|
227
|
+
Every exception is the CALLER's to turn into an envelope: in a subprocess a
|
|
228
|
+
planner blow-up arrived as a non-zero exit code, and in-process it lands
|
|
229
|
+
here instead.
|
|
230
|
+
|
|
231
|
+
`TREE_MODES` are refused here rather than silently mis-served: they write a
|
|
232
|
+
directory, so a caller that asked for text would get a path it never wrote.
|
|
233
|
+
|
|
234
|
+
*skip_advisories* (tan-cli#964 review, major 6): optional, caller-owned;
|
|
235
|
+
threaded to `load_board_yaml`/`_render_route_table`'s own schema checks so
|
|
236
|
+
a caller that writes the rendered text to disk (`generate_cmd.py`) can
|
|
237
|
+
disclose "validated against nothing -- the schema itself is absent" rather
|
|
238
|
+
than silently writing an unvalidated artefact at `ok: true`.
|
|
239
|
+
"""
|
|
240
|
+
if mode in TREE_MODES:
|
|
241
|
+
raise PlannerEmitError(
|
|
242
|
+
f"--emit {mode} writes a directory of files, not a stream; "
|
|
243
|
+
"render_tree() serves it.")
|
|
244
|
+
|
|
245
|
+
bind_sdk_root(sdk_root)
|
|
246
|
+
import tan.planner as planner # noqa: PLC0415 -- must follow the bind
|
|
247
|
+
|
|
248
|
+
if mode in _ROUTE_TABLE_EMITTER:
|
|
249
|
+
# NOT via `load_board_yaml`: `alp_project.main()` dispatches these two
|
|
250
|
+
# modes before the per-core slice machinery is reached, off the raw
|
|
251
|
+
# board.yaml dict alone. Routing them through the v2 loader would
|
|
252
|
+
# resolve a topology the emitter never looks at -- and could refuse a
|
|
253
|
+
# board the SDK's own front door serves.
|
|
254
|
+
return _render_route_table(mode, board_yaml, skip_advisories=skip_advisories)
|
|
255
|
+
|
|
256
|
+
project = planner.load_board_yaml(Path(board_yaml), skip_advisories=skip_advisories)
|
|
257
|
+
if mode in _SLICE_RENDERER:
|
|
258
|
+
return _render_per_core(planner, project, mode, core=core, sdk_root=sdk_root)
|
|
259
|
+
if mode == "os-topology":
|
|
260
|
+
# Not in `emit_artefact`: alp-sdk's `alp_orchestrate` CLI never exposed
|
|
261
|
+
# it, only `alp_project.py` did.
|
|
262
|
+
return planner.emit_os_topology(project)
|
|
263
|
+
if mode in _V1_SHAPED_MODES:
|
|
264
|
+
return _render_v1_shaped(project, mode, core=core)
|
|
265
|
+
# `ipc-contract-h` (and anything else relocated later that the planner CLI
|
|
266
|
+
# already knows) goes through the ONE dispatch, so a mode cannot render
|
|
267
|
+
# differently depending on which front door reached it.
|
|
268
|
+
from tan.planner.cli import emit_artefact # noqa: PLC0415
|
|
269
|
+
|
|
270
|
+
return emit_artefact(project, mode, board_yaml=Path(board_yaml), core=core)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def render_tree(
|
|
274
|
+
mode: str,
|
|
275
|
+
*,
|
|
276
|
+
sdk_root: Path,
|
|
277
|
+
board_yaml: Path,
|
|
278
|
+
core: str | None = None,
|
|
279
|
+
) -> dict[str, str]:
|
|
280
|
+
"""`{filename: content}` for a mode that writes a DIRECTORY.
|
|
281
|
+
|
|
282
|
+
Keys are the FILE names, with the generator's own `<board_dir>/` prefix
|
|
283
|
+
already stripped -- exactly the split `alp_project.py` does before joining
|
|
284
|
+
each name onto `--output`, because `--output` names the board directory
|
|
285
|
+
itself (`docs/porting-new-som.md`'s `--output build/boards/<board>/`, then
|
|
286
|
+
`west build --board-root build/boards`).
|
|
287
|
+
"""
|
|
288
|
+
if mode not in TREE_MODES:
|
|
289
|
+
raise PlannerEmitError(
|
|
290
|
+
f"--emit {mode} writes a single stream, not a directory; "
|
|
291
|
+
"render() serves it.")
|
|
292
|
+
|
|
293
|
+
bind_sdk_root(sdk_root)
|
|
294
|
+
import tan.planner as planner # noqa: PLC0415 -- must follow the bind
|
|
295
|
+
|
|
296
|
+
if core is None:
|
|
297
|
+
# `alp_project.py` refuses the same way: one board tree is one core's.
|
|
298
|
+
raise PlannerEmitError(
|
|
299
|
+
f"--emit {mode} requires --core (it generates one core's board tree)")
|
|
300
|
+
|
|
301
|
+
project = planner.load_board_yaml(Path(board_yaml))
|
|
302
|
+
if core not in project.cores:
|
|
303
|
+
raise PlannerEmitError(
|
|
304
|
+
f"--core {core} not present in board.yaml "
|
|
305
|
+
f"(known: {sorted(project.cores.keys())})")
|
|
306
|
+
|
|
307
|
+
from tan.planner.paths import METADATA_ROOT # noqa: PLC0415
|
|
308
|
+
from tan.planner.zephyr_board import emit_zephyr_board # noqa: PLC0415
|
|
309
|
+
|
|
310
|
+
files = emit_zephyr_board(project.sku, core, METADATA_ROOT)
|
|
311
|
+
return {relpath.split("/", 1)[1]: content for relpath, content in files.items()}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _render_route_table(
|
|
315
|
+
mode: str, board_yaml: Path, *, skip_advisories: list[str] | None = None
|
|
316
|
+
) -> str:
|
|
317
|
+
"""`--emit carrier-netlist` / `--emit composed-route-table`, mirroring
|
|
318
|
+
`alp_project.main()`'s own branch (the two share one dispatch there too)."""
|
|
319
|
+
from tan.core.metadata_schema import ( # noqa: PLC0415
|
|
320
|
+
missing_schema_note,
|
|
321
|
+
som_preset_schema_path,
|
|
322
|
+
validate_document,
|
|
323
|
+
)
|
|
324
|
+
from tan.planner.loader import _load_yaml # noqa: PLC0415
|
|
325
|
+
from tan.planner.paths import METADATA_ROOT # noqa: PLC0415
|
|
326
|
+
from tan.planner.project_loader import ( # noqa: PLC0415
|
|
327
|
+
_resolve_inline_or_preset_board,
|
|
328
|
+
_resolve_sku,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
project = _load_yaml(Path(board_yaml))
|
|
332
|
+
# PHRASED, not a bare KeyError. alp-sdk reached this point only after its
|
|
333
|
+
# rich diagnostic validator had run; here the schema check happens in
|
|
334
|
+
# `load_board_yaml`, which this mode deliberately does not call, so the one
|
|
335
|
+
# field the emitter dereferences is checked by hand.
|
|
336
|
+
sku = (project.get("som") or {}).get("sku")
|
|
337
|
+
if not isinstance(sku, str) or not sku:
|
|
338
|
+
raise PlannerEmitError(
|
|
339
|
+
f"{board_yaml}: `som.sku` is missing or is not a string; "
|
|
340
|
+
f"--emit {mode} renders the SoM's pad routes against it.")
|
|
341
|
+
sku_preset = _resolve_sku(sku, METADATA_ROOT)
|
|
342
|
+
# tan-cli#964 review (major 4): both route-table modes write a real file
|
|
343
|
+
# to disk (`generate_cmd.py`'s `build/generated/{carrier,composed-route
|
|
344
|
+
# -table}-netlist.json`) WITHOUT going through `load_board_yaml` -- the one
|
|
345
|
+
# place #964's REFUSE gate otherwise lives -- so the gate is repeated here,
|
|
346
|
+
# against the raw preset `_resolve_sku` just returned, before it reaches
|
|
347
|
+
# either emitter. Same wording `tan.planner.loader._refuse_on_schema_errors`
|
|
348
|
+
# uses for `build`/`generate`'s other modes.
|
|
349
|
+
som_preset_path = METADATA_ROOT / "e1m_modules" / f"{sku}.yaml"
|
|
350
|
+
# major 6: "skip-but-disclose", checked BEFORE the refuse-worthy
|
|
351
|
+
# violations, same ordering `_refuse_on_schema_errors` uses -- an absent
|
|
352
|
+
# schema means `validate_document` below returns `[]` and this function
|
|
353
|
+
# would otherwise write the file having said nothing at all.
|
|
354
|
+
if skip_advisories is not None:
|
|
355
|
+
note = missing_schema_note(som_preset_schema_path(METADATA_ROOT), source=som_preset_path)
|
|
356
|
+
if note is not None:
|
|
357
|
+
skip_advisories.append(note)
|
|
358
|
+
som_schema_errors = validate_document(
|
|
359
|
+
sku_preset, som_preset_schema_path(METADATA_ROOT), som_preset_path,
|
|
360
|
+
)
|
|
361
|
+
if som_schema_errors:
|
|
362
|
+
raise PlannerEmitError(
|
|
363
|
+
f"SoM preset {sku} does not validate against som-preset-v2:\n"
|
|
364
|
+
+ "\n".join(f" - {e}" for e in som_schema_errors))
|
|
365
|
+
board_preset = _resolve_inline_or_preset_board(project, METADATA_ROOT)
|
|
366
|
+
from tan.planner.project_emit import bom_netlist # noqa: PLC0415
|
|
367
|
+
|
|
368
|
+
emitter = getattr(bom_netlist, _ROUTE_TABLE_EMITTER[mode])
|
|
369
|
+
return emitter(project, sku_preset, board_preset, METADATA_ROOT)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
#: The four modes `alp_project.py` re-fitted onto the v2 schema by projecting the
|
|
373
|
+
#: resolved project back into the v1 `board:`-wrapper dict its emitters read.
|
|
374
|
+
#: That projection is `_run_v2_per_core_emit`'s `project_v1_shaped`, reproduced
|
|
375
|
+
#: verbatim in `_v1_shaped_project` -- these emitters take a plain dict, not a
|
|
376
|
+
#: `BoardProject`, and changing that would be the rewrite this move is not.
|
|
377
|
+
_V1_SHAPED_MODES = frozenset({
|
|
378
|
+
"dts-overlay", "native-sim-overlay", "hw-info-h", "west-libraries",
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _v1_shaped_project(project) -> dict[str, Any]:
|
|
383
|
+
"""The legacy `board:`-wrapper dict the four relocated emitters consume --
|
|
384
|
+
the one `buildplan._v1_shaped_project` (alp-sdk's `alp_project.py` shares
|
|
385
|
+
it with the plan emit, #2771)."""
|
|
386
|
+
from tan.planner.buildplan import _v1_shaped_project as shaped # noqa: PLC0415
|
|
387
|
+
|
|
388
|
+
return shaped(project)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def _render_dts_overlay(project, shaped, core: str | None) -> str:
|
|
392
|
+
"""`--emit dts-overlay`, mirroring `alp_project._run_v2_per_core_emit`:
|
|
393
|
+
the board overlay, then the nodes board.yaml `ownership:` hands the M33."""
|
|
394
|
+
from tan.planner.models import OrchestratorError # noqa: PLC0415
|
|
395
|
+
from tan.planner.ownership import project_m33_overlay # noqa: PLC0415
|
|
396
|
+
from tan.planner.project_emit.dts import _emit_dts_overlay # noqa: PLC0415
|
|
397
|
+
|
|
398
|
+
# The DTS overlay is shaped by the board header (bus aliases +
|
|
399
|
+
# alp,pin-array) which is a SoM-mounting fact, not a per-core fact.
|
|
400
|
+
# v2 contributes only the peripherals list: union across
|
|
401
|
+
# Zephyr/baremetal cores (or one core when --core is set).
|
|
402
|
+
if core is not None:
|
|
403
|
+
# The slice's own overlay IS the build plan's `alp.overlay`
|
|
404
|
+
# configArtefact (tan-cli#1216, ADR-0026 §D): one function, so
|
|
405
|
+
# `tan generate --core` and `tan build`'s plan cannot disagree. It
|
|
406
|
+
# also appends the M33-ownership nodes, so this path returns early.
|
|
407
|
+
from tan.planner.buildplan import _slice_dts_overlay # noqa: PLC0415
|
|
408
|
+
|
|
409
|
+
try:
|
|
410
|
+
return _slice_dts_overlay(project, project.cores[core])
|
|
411
|
+
except OrchestratorError as err:
|
|
412
|
+
raise PlannerEmitError(str(err)) from err
|
|
413
|
+
else:
|
|
414
|
+
union: set[str] = set()
|
|
415
|
+
zephyr_core_ids: list[str] = []
|
|
416
|
+
for core_id, slice_ in project.cores.items():
|
|
417
|
+
if slice_.os in ("zephyr", "baremetal"):
|
|
418
|
+
union.update(slice_.peripherals)
|
|
419
|
+
zephyr_core_ids.append(core_id)
|
|
420
|
+
out = _emit_dts_overlay(
|
|
421
|
+
shaped, project.som_preset, project.board_preset,
|
|
422
|
+
v2_peripherals=sorted(union),
|
|
423
|
+
v2_core_ids=zephyr_core_ids,
|
|
424
|
+
)
|
|
425
|
+
# Per-product core ownership: enable the assignable nodes this project
|
|
426
|
+
# assigned to the M33 (the board tree carries them disabled).
|
|
427
|
+
try:
|
|
428
|
+
own_dts, _ = project_m33_overlay(project, core)
|
|
429
|
+
except OrchestratorError as err:
|
|
430
|
+
raise PlannerEmitError(str(err)) from err
|
|
431
|
+
if own_dts:
|
|
432
|
+
out += ("\n/* Assignable peripherals owned by the M33 "
|
|
433
|
+
"(board.yaml `ownership:`). */\n" + "\n".join(own_dts) + "\n")
|
|
434
|
+
return out
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _render_v1_shaped(project, mode: str, *, core: str | None) -> str:
|
|
438
|
+
"""`dts-overlay` / `native-sim-overlay` / `hw-info-h` / `west-libraries`,
|
|
439
|
+
mirroring `alp_project._run_v2_per_core_emit`'s project-wide section.
|
|
440
|
+
|
|
441
|
+
The `--core` validity check runs first for all four, as it does there: an
|
|
442
|
+
unknown core id is a refusal, never a `KeyError` from inside an emitter.
|
|
443
|
+
"""
|
|
444
|
+
if core is not None and core not in project.cores:
|
|
445
|
+
raise PlannerEmitError(
|
|
446
|
+
f"--core {core} not present in board.yaml "
|
|
447
|
+
f"(known: {sorted(project.cores.keys())})")
|
|
448
|
+
|
|
449
|
+
from tan.planner.buildplan import ( # noqa: PLC0415
|
|
450
|
+
_slice_hw_info_h,
|
|
451
|
+
_slice_west_libraries,
|
|
452
|
+
)
|
|
453
|
+
from tan.planner.project_emit.hw_info import _emit_hw_info_h # noqa: PLC0415
|
|
454
|
+
from tan.planner.project_emit.native_sim import ( # noqa: PLC0415
|
|
455
|
+
_emit_native_sim_overlay,
|
|
456
|
+
)
|
|
457
|
+
from tan.planner.project_emit.west_libs import ( # noqa: PLC0415
|
|
458
|
+
_emit_west_libraries,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
shaped = _v1_shaped_project(project)
|
|
462
|
+
|
|
463
|
+
if mode == "dts-overlay":
|
|
464
|
+
return _render_dts_overlay(project, shaped, core)
|
|
465
|
+
|
|
466
|
+
if mode == "native-sim-overlay":
|
|
467
|
+
# native_sim GPIO emulation -- board-agnostic (the E1M pad map is a
|
|
468
|
+
# SoM-mounting fact), so no --core / peripheral scoping applies.
|
|
469
|
+
return _emit_native_sim_overlay(shaped)
|
|
470
|
+
|
|
471
|
+
if mode == "hw-info-h":
|
|
472
|
+
# hw-info-h is a project-level emit even under v2 -- consumers
|
|
473
|
+
# `#include` it from any slice. --core picks which slice's OS lands in
|
|
474
|
+
# ALP_HW_BUILD_OS; absent --core, primary-core rules apply.
|
|
475
|
+
if core is not None:
|
|
476
|
+
# Single source shared with the build plan's
|
|
477
|
+
# `alp_hw_info_build.h` configArtefact (ADR-0026 §D).
|
|
478
|
+
return _slice_hw_info_h(project, project.cores[core])
|
|
479
|
+
return _emit_hw_info_h(
|
|
480
|
+
shaped, project.som_preset, project.board_preset,
|
|
481
|
+
v2_cores={cid: s.os for cid, s in project.cores.items()},
|
|
482
|
+
v2_selected_core=None,
|
|
483
|
+
metadata_root=project.effective_metadata_root(),
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
# west-libraries
|
|
487
|
+
if core is not None:
|
|
488
|
+
# Single source shared with the build plan's `alp-west-libs.yml`
|
|
489
|
+
# configArtefact (ADR-0026 §D).
|
|
490
|
+
return _slice_west_libraries(project, project.cores[core])
|
|
491
|
+
else:
|
|
492
|
+
union_l: set[str] = set()
|
|
493
|
+
for slice_ in project.cores.values():
|
|
494
|
+
if slice_.os in ("zephyr", "baremetal"):
|
|
495
|
+
union_l.update(slice_.libraries)
|
|
496
|
+
v2_libraries = sorted(union_l)
|
|
497
|
+
return _emit_west_libraries(
|
|
498
|
+
shaped, project.som_preset, project.board_preset,
|
|
499
|
+
v2_libraries=v2_libraries,
|
|
500
|
+
v2_project_libraries=sorted(project.libraries),
|
|
501
|
+
metadata_root=project.effective_metadata_root(),
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _render_per_core(planner, project, mode: str, *, core: str | None,
|
|
506
|
+
sdk_root: Path) -> str:
|
|
507
|
+
"""`zephyr-conf` / `cmake-args` / `yocto-conf`, mirroring
|
|
508
|
+
`alp_project._run_v2_per_core_emit`'s per-core section exactly.
|
|
509
|
+
|
|
510
|
+
`zephyr-conf` and `yocto-conf` render through `buildplan._slice_config_artefact` -- the
|
|
511
|
+
SAME helper `emit_build_plan` calls to fill a slice's
|
|
512
|
+
`configArtefacts[].contents` (tan-cli#1216, ADR-0026 §D) -- rather than a
|
|
513
|
+
second, independent dispatch straight to the leaf renderer. The bytes are
|
|
514
|
+
unchanged (`_slice_config_artefact`'s zephyr branch IS `_slice_alp_conf`, its yocto
|
|
515
|
+
branch `_slice_local_conf`);
|
|
516
|
+
what changes is that `tan generate --target zephyr-conf` and `tan build`'s
|
|
517
|
+
plan are now structurally pinned to the one call site the schema's own
|
|
518
|
+
words describe ("byte-identical to what a consumer's own materialise step
|
|
519
|
+
writes to buildDir") instead of merely agreeing today by coincidence of
|
|
520
|
+
two dispatch tables that happen to name the same function.
|
|
521
|
+
"""
|
|
522
|
+
if core is not None and core not in project.cores:
|
|
523
|
+
raise PlannerEmitError(
|
|
524
|
+
f"--core {core} not present in board.yaml "
|
|
525
|
+
f"(known: {sorted(project.cores.keys())})"
|
|
526
|
+
)
|
|
527
|
+
if project.libraries:
|
|
528
|
+
# Resolved up front purely to VALIDATE: an unknown library name or a
|
|
529
|
+
# failed `requires:` constraint must be one clean line (ADR 0018), not a
|
|
530
|
+
# traceback from the middle of a slice render.
|
|
531
|
+
from tan.planner.libraries import resolve_selection # noqa: PLC0415
|
|
532
|
+
|
|
533
|
+
resolve_selection(project, project.effective_metadata_root())
|
|
534
|
+
|
|
535
|
+
allowed_os = _os_classes(sdk_root).get(mode)
|
|
536
|
+
# Already imported (`tan.planner.__init__` imports `.buildplan` at
|
|
537
|
+
# module scope) by the time `render()` reaches here -- this is a
|
|
538
|
+
# `sys.modules` cache hit, not a fresh load, and it must stay lazy
|
|
539
|
+
# like every other `tan.planner*` import in this file: importing it
|
|
540
|
+
# before `bind_sdk_root` has run would raise.
|
|
541
|
+
from tan.planner import buildplan # noqa: PLC0415
|
|
542
|
+
|
|
543
|
+
wanted = _CONFIG_ARTEFACT_FILE[mode]
|
|
544
|
+
artefact_of = getattr(buildplan, _CONFIG_ARTEFACT_HELPER[mode])
|
|
545
|
+
|
|
546
|
+
from tan.planner.models import OrchestratorError # noqa: PLC0415
|
|
547
|
+
|
|
548
|
+
def slice_renderer(proj, sl):
|
|
549
|
+
try:
|
|
550
|
+
artefact = artefact_of(proj, sl)
|
|
551
|
+
except OrchestratorError as err:
|
|
552
|
+
if mode != "cmake-args":
|
|
553
|
+
raise
|
|
554
|
+
# alp-sdk ed57bd025 (#2791): `alp_project._run_v2_per_core_emit`
|
|
555
|
+
# turns a cmake-args OrchestratorError -- e.g. a `cameras:` entry
|
|
556
|
+
# unbuildable for this core -- into one clean refusal line.
|
|
557
|
+
raise PlannerEmitError(str(err)) from err
|
|
558
|
+
if artefact is None or artefact[0] != wanted:
|
|
559
|
+
# Unreachable in practice: `allowed_os` above already confines
|
|
560
|
+
# each mode to the one OS whose artefact IS `wanted`. A coded
|
|
561
|
+
# refusal beats a bare `NoneType` subscript -- or silently
|
|
562
|
+
# emitting a different file's bytes -- if that invariant breaks.
|
|
563
|
+
raise PlannerEmitError(
|
|
564
|
+
f"no {wanted} config artefact for core `{sl.core_id}` "
|
|
565
|
+
f"(os: {sl.os})")
|
|
566
|
+
return artefact[1]
|
|
567
|
+
|
|
568
|
+
core_ids = [core] if core is not None else sorted(project.cores.keys())
|
|
569
|
+
|
|
570
|
+
parts: list[str] = []
|
|
571
|
+
for core_id in core_ids:
|
|
572
|
+
slice_ = project.cores[core_id]
|
|
573
|
+
if slice_.os == "off":
|
|
574
|
+
continue
|
|
575
|
+
if allowed_os is not None and slice_.os not in allowed_os:
|
|
576
|
+
if core is None:
|
|
577
|
+
# The unscoped sum-across-cores form is explicitly a "give me
|
|
578
|
+
# everything applicable" query, so it skips rather than refuses.
|
|
579
|
+
continue
|
|
580
|
+
raise PlannerEmitError(
|
|
581
|
+
f"--core {core_id} has os: {slice_.os}, which --emit {mode} does "
|
|
582
|
+
f"not support (supported os: {', '.join(allowed_os)})"
|
|
583
|
+
)
|
|
584
|
+
if mode == "zephyr-conf" and core is not None:
|
|
585
|
+
# The canonical per-core path emits the slice VERBATIM -- no section
|
|
586
|
+
# marker -- so a `west build` driven by alp-sdk's CMakeLists.txt and
|
|
587
|
+
# one driven by `tan` off the build plan cannot diverge on Kconfig.
|
|
588
|
+
parts.append(slice_renderer(project, slice_))
|
|
589
|
+
else:
|
|
590
|
+
parts.append(f"# --- core: {core_id} ({slice_.os}) ---")
|
|
591
|
+
parts.append(slice_renderer(project, slice_))
|
|
592
|
+
return "\n".join(parts) + ("\n" if parts and not parts[-1].endswith("\n") else "")
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def write(text: str, destination: Path) -> None:
|
|
596
|
+
"""Write `text` to `destination`, byte for byte, creating its parent.
|
|
597
|
+
|
|
598
|
+
`newline=""` is load-bearing -- see the module docstring. The SDK's
|
|
599
|
+
`_write_or_print` writes exactly this way, and the whole point of the
|
|
600
|
+
in-process path is that the bytes on disk do not change.
|
|
601
|
+
"""
|
|
602
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
603
|
+
destination.write_text(text, encoding="utf-8", newline="")
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def write_tree(files: dict[str, str], destination: Path) -> None:
|
|
607
|
+
"""Write a `render_tree` result into `destination`, which IS the board
|
|
608
|
+
directory. Same `newline=""` contract as `write`, per file.
|
|
609
|
+
|
|
610
|
+
Sorted, so the order files appear on disk (and in any log that follows the
|
|
611
|
+
writes) does not depend on dict iteration order across interpreters.
|
|
612
|
+
"""
|
|
613
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
614
|
+
for name in sorted(files):
|
|
615
|
+
target = destination / name
|
|
616
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
617
|
+
target.write_text(files[name], encoding="utf-8", newline="")
|