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,874 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""Slice-command resolution -- the planner-side helpers `buildplan.py`'s
|
|
4
|
+
`emit_build_plan` reads to describe (never run) each slice's build command.
|
|
5
|
+
|
|
6
|
+
ADR-0020 Phase 4 (preview) retired the SDK-side executor -- the
|
|
7
|
+
`Orchestrator` class that fanned build sub-processes out and materialised
|
|
8
|
+
artefacts to disk. What remains here is pure, side-effect-free: resolving
|
|
9
|
+
what a slice's build command WOULD be, so `emit_build_plan` can describe it
|
|
10
|
+
to an external consumer (`tan`, alplabai/tan-cli, / alp-studio) that owns
|
|
11
|
+
execution.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Optional
|
|
18
|
+
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
21
|
+
from tan.core.link_refusal import RAM_RUN_ONLY_METHOD
|
|
22
|
+
from .link_target import (
|
|
23
|
+
CONF_NAME as ITCM_CONF_NAME,
|
|
24
|
+
OVERLAY_NAME as ITCM_OVERLAY_NAME,
|
|
25
|
+
UNSUPPORTED_CODE,
|
|
26
|
+
LinkTargetError,
|
|
27
|
+
applies_to as link_applies_to,
|
|
28
|
+
)
|
|
29
|
+
from . import cameras as _cameras
|
|
30
|
+
from .models import BoardProject, OrchestratorError, Slice
|
|
31
|
+
from .paths import REPO
|
|
32
|
+
from .secure import (emit_sysbuild_conf, emit_tfm_sysbuild_conf,
|
|
33
|
+
sysbuild_family_base_conf)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def iter_buildable_slices(project: BoardProject):
|
|
37
|
+
"""Yield every non-`off` core's `Slice`, sorted by `core_id`.
|
|
38
|
+
|
|
39
|
+
ADR-0020 Phase 1: the SINGLE source of WHICH cores build and in WHAT
|
|
40
|
+
ORDER, so `emit_build_plan()` (the plan `tan` reads) always
|
|
41
|
+
enumerates slices the same way.
|
|
42
|
+
"""
|
|
43
|
+
for core_id in sorted(project.cores):
|
|
44
|
+
slice_ = project.cores[core_id]
|
|
45
|
+
if slice_.os == "off":
|
|
46
|
+
continue
|
|
47
|
+
yield slice_
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _slice_flash_recipe(
|
|
51
|
+
slice_: Slice,
|
|
52
|
+
) -> tuple[Optional[str], Optional[dict[str, Any]]]:
|
|
53
|
+
"""Per-runtime default flash backend + args for a slice.
|
|
54
|
+
|
|
55
|
+
Used by `Slice.to_manifest_entry` to record how an external flash
|
|
56
|
+
step should program the slice's output artefact.
|
|
57
|
+
|
|
58
|
+
Returns ``(None, None)`` for `os: off` slices (skipped at flash
|
|
59
|
+
time) and unknown `os:` values; the manifest emitter drops keys
|
|
60
|
+
with None values, so off slices stay tidy.
|
|
61
|
+
"""
|
|
62
|
+
if slice_.os == "yocto":
|
|
63
|
+
return ("yocto_wic_to_sd_or_emmc",
|
|
64
|
+
{"target": slice_.machine or ""})
|
|
65
|
+
if slice_.os == "zephyr" and slice_.link_target == "itcm":
|
|
66
|
+
# `diagnostics.link: itcm` (tan-cli#1350): this image is linked at 0x0
|
|
67
|
+
# for a J-Link RAM-run. The MRAM recipe below would sign it and write
|
|
68
|
+
# it to slot0 -- a broken, unbootable image -- so the slice carries NO
|
|
69
|
+
# MRAM flash recipe: `flash_method` is the unregistered
|
|
70
|
+
# `ram_run_only` (plain `tan flash` refuses it and points at
|
|
71
|
+
# `tan flash --ram`), and `flash_args` keeps ONLY the wrong-board
|
|
72
|
+
# identity pair `--ram`'s probe guard reads. No `jlink_flash_device`
|
|
73
|
+
# / `slot0_load_address`, so Flow D is never offered.
|
|
74
|
+
ram_args: dict[str, Any] = {}
|
|
75
|
+
if slice_.expect_dpidr and slice_.jlink_device:
|
|
76
|
+
ram_args["expect_dpidr"] = slice_.expect_dpidr
|
|
77
|
+
ram_args["jlink_device"] = slice_.jlink_device
|
|
78
|
+
return (RAM_RUN_ONLY_METHOD, ram_args)
|
|
79
|
+
if slice_.os == "zephyr":
|
|
80
|
+
# No runner is forced here: not every in-tree board registers
|
|
81
|
+
# an openocd runner (e.g. AEN's board.cmake sets
|
|
82
|
+
# flash-runner: alif_flash), so `west flash --runner openocd`
|
|
83
|
+
# FATAL-errors on those boards. Emit no runner and let `west
|
|
84
|
+
# flash` fall back to the board.cmake default; an explicit
|
|
85
|
+
# runner can still be set on flash_args downstream when one is
|
|
86
|
+
# actually known.
|
|
87
|
+
#
|
|
88
|
+
# `jlink_flash_device` is added only when this slice's SoC
|
|
89
|
+
# variant actually publishes one: it's the part-number J-Link
|
|
90
|
+
# profile that arms Flow D's built-in Alif MRAM loader, a fact a
|
|
91
|
+
# downstream consumer (tan) needs to pick that path over the
|
|
92
|
+
# SETOOLS/SE-UART fallback. Absent for every non-AEN slice today,
|
|
93
|
+
# so the args dict stays `{}` -- no shape change for them.
|
|
94
|
+
#
|
|
95
|
+
# `expect_dpidr` + `jlink_device` are the read-only SW-DP IDR
|
|
96
|
+
# wrong-board preflight (alp-sdk #1355) and are emitted as ONE
|
|
97
|
+
# inseparable pair: the expected debug-port ID, and the live-core
|
|
98
|
+
# attach profile the read is performed with. Emitting exactly one of
|
|
99
|
+
# the two is NOT a partial win: tan's own
|
|
100
|
+
# `validate_flow_d_preflight_args` refuses a half-armed pair at plan
|
|
101
|
+
# time, so it would hard-fail every AEN flash including a dry run.
|
|
102
|
+
# `loader._resolve_flow_d_preflight` guarantees the both-or-neither
|
|
103
|
+
# shape upstream; the `and` here makes that guarantee locally
|
|
104
|
+
# readable rather than assumed at a distance.
|
|
105
|
+
#
|
|
106
|
+
# `slot0_load_address` (tan-cli#353) is the AEN MRAM slot0-XIP
|
|
107
|
+
# address the slice's application blob is linked at -- the fact
|
|
108
|
+
# Flow D's auto-sign-via-SETOOLS path needs. Independent of the
|
|
109
|
+
# `expect_dpidr`/`jlink_device` pair above -- see
|
|
110
|
+
# `loader._resolve_slot0_load_address` for where it comes from.
|
|
111
|
+
args: dict[str, Any] = {}
|
|
112
|
+
# tan-cli#734: PRESENCE, not truthiness. A schema-declared
|
|
113
|
+
# `jlink_flash_device: null` means "this variant has no known J-Link
|
|
114
|
+
# flash profile -- refuse loudly", and a truthiness test dropped it,
|
|
115
|
+
# so `flash_plan.flow_d_available` -- which decides on key presence
|
|
116
|
+
# via `_fa_has_key`, deliberately -- never saw it and `tan flash`
|
|
117
|
+
# silently downgraded Flow D to Flow A over the SE-UART. On Windows
|
|
118
|
+
# that is a downgrade to a runner that cannot run there at all.
|
|
119
|
+
if slice_.jlink_flash_device_declared or slice_.jlink_flash_device is not None:
|
|
120
|
+
args["jlink_flash_device"] = slice_.jlink_flash_device
|
|
121
|
+
if slice_.expect_dpidr and slice_.jlink_device:
|
|
122
|
+
args["expect_dpidr"] = slice_.expect_dpidr
|
|
123
|
+
args["jlink_device"] = slice_.jlink_device
|
|
124
|
+
if slice_.slot0_load_address:
|
|
125
|
+
args["slot0_load_address"] = slice_.slot0_load_address
|
|
126
|
+
return ("zephyr_west_flash", args)
|
|
127
|
+
if slice_.os == "baremetal":
|
|
128
|
+
return ("baremetal_cmake_flash", {})
|
|
129
|
+
return (None, None)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# M-core "stock shim" app token (Zephyr side). Accepted by the SoM-preset
|
|
133
|
+
# schema and defaulted into M-core slots (AEN m55_hp/he, V2N m33_sm).
|
|
134
|
+
# The token resolves to the SDK-owned app below rather than a
|
|
135
|
+
# project-local path.
|
|
136
|
+
STOCK_SHIM_APP = "alp-stock-shim"
|
|
137
|
+
STOCK_SHIM_DIR = REPO / "firmware" / "alp-stock-shim"
|
|
138
|
+
|
|
139
|
+
# A-core "stock image" app token (Yocto side). Every shipped SoM preset's
|
|
140
|
+
# topology.<a-core-id> defaults `app:` to this value (see
|
|
141
|
+
# metadata/e1m_modules/*.yaml) -- unlike a customer's own `app:` (a
|
|
142
|
+
# filesystem path to their app source), this token IS already the real
|
|
143
|
+
# bitbake recipe name for the stock alp-image-edge image, so it is exempt
|
|
144
|
+
# from the `recipe:` requirement `_slice_command` enforces for a
|
|
145
|
+
# project-supplied app-only Yocto slice (issue #597). That exemption
|
|
146
|
+
# assumes `bitbake <STOCK_IMAGE_APP>` is actually a buildable target for
|
|
147
|
+
# the slice's `machine:` -- see YOCTO_MACHINE_UNBUILDABLE below for the
|
|
148
|
+
# machines where that assumption is currently false (issue #1982).
|
|
149
|
+
STOCK_IMAGE_APP = "alp-image-edge"
|
|
150
|
+
|
|
151
|
+
# Yocto MACHINEs that cannot build today, keyed to the issue(s) that
|
|
152
|
+
# establish why -- consulted by `_slice_command` so the planner refuses
|
|
153
|
+
# these rather than hand a consumer a `bitbake` command guaranteed to
|
|
154
|
+
# fail. This is the SAME dict `scripts/check_yocto_machine_tree_parity.py`
|
|
155
|
+
# consults (issue #1982 follow-up) -- do not fork a second list. Be
|
|
156
|
+
# precise about what that gate does and does not buy you: it is a
|
|
157
|
+
# ONE-WAY absence check. It fails the PR only for a `machine:` that
|
|
158
|
+
# has NO conf under `meta-alp-sdk/conf/machine/` AND no entry here, so
|
|
159
|
+
# a new SKU cannot fall through both unnoticed. It does NOT fire when
|
|
160
|
+
# a conf ships for a MACHINE listed here, and it does NOT fire when a
|
|
161
|
+
# listed MACHINE's conf disappears -- a `.conf` merely existing is not
|
|
162
|
+
# proof the MACHINE builds (`e1m-aen801-a32.conf` and
|
|
163
|
+
# `e1m-aen701-a32.conf` both exist and are both still unbuildable).
|
|
164
|
+
# So this dict is NOT self-maintaining: removing an entry once its
|
|
165
|
+
# MACHINE genuinely builds is a human call, and nothing in CI will
|
|
166
|
+
# remind you. Re-read the per-entry reasons below before trusting
|
|
167
|
+
# them; the gate's own docstring draws the same line.
|
|
168
|
+
#
|
|
169
|
+
# Five AEN A32-cluster carriers declare a `topology.a32_cluster.machine:`
|
|
170
|
+
# today (`metadata/e1m_modules/E1M-AEN{501,601,701,801,803}.yaml`); all
|
|
171
|
+
# five are unbuildable, split into two distinct failure classes -- do
|
|
172
|
+
# not conflate them:
|
|
173
|
+
#
|
|
174
|
+
# * `e1m-aen801-a32.conf` has an ACTIVE, uncommented `require
|
|
175
|
+
# conf/machine/devkit-e8.conf` -- and that file exists in NEITHER
|
|
176
|
+
# branch of the public meta-alif-ensemble upstream (issue #1968), so
|
|
177
|
+
# this MACHINE fails at BitBake's own parse step.
|
|
178
|
+
# * `e1m-aen701-a32.conf`'s `require conf/machine/devkit-e7.conf` is
|
|
179
|
+
# already commented out in-tree (its own header: "Until the layer is
|
|
180
|
+
# vendored this require has no target ... intentional") -- unlike
|
|
181
|
+
# AEN801, `devkit-e7.conf` DOES exist on meta-alif-ensemble's
|
|
182
|
+
# `devkit-ex-b0` branch, but nothing here references it yet, so this
|
|
183
|
+
# MACHINE parses with no DEFAULTTUNE / kernel provider / TF-A
|
|
184
|
+
# platform set at all.
|
|
185
|
+
# * `e1m-aen501-a32` / `e1m-aen601-a32` / `e1m-aen803-a32` ship NO
|
|
186
|
+
# `meta-alp-sdk/conf/machine/*.conf` at all -- strictly MORE
|
|
187
|
+
# unbuildable than the two above, since BitBake fails to find the
|
|
188
|
+
# MACHINE before it can parse a single `require`. E1M-AEN803 is the
|
|
189
|
+
# SoM issue #1982 names as the bench module.
|
|
190
|
+
#
|
|
191
|
+
# All five are unbuildable regardless of the above: meta-alif-ensemble
|
|
192
|
+
# declares `LAYERSERIES_COMPAT = "warrior zeus"` and is structurally
|
|
193
|
+
# incompatible with this repo's Scarthgap baseline (issue #1971:
|
|
194
|
+
# pre-honister override syntax, a stale 5.4 kernel pin, obsolete TF-A
|
|
195
|
+
# build knobs), so even a corrected/uncommented `require` (or a shipped
|
|
196
|
+
# conf, for the three missing ones) would not make any of them buildable
|
|
197
|
+
# on its own. Issue #264 is rebuilding this path on a real base; remove
|
|
198
|
+
# an entry here only once its MACHINE resolves against a
|
|
199
|
+
# Scarthgap-compatible layer.
|
|
200
|
+
YOCTO_MACHINE_UNBUILDABLE: dict[str, str] = {
|
|
201
|
+
"e1m-aen801-a32": (
|
|
202
|
+
"MACHINE 'e1m-aen801-a32' cannot build: its base `require "
|
|
203
|
+
"conf/machine/devkit-e8.conf` names a file that exists in no "
|
|
204
|
+
"branch of the public meta-alif-ensemble upstream (issue #1968), "
|
|
205
|
+
"and that upstream layer's LAYERSERIES_COMPAT (\"warrior zeus\") "
|
|
206
|
+
"is incompatible with this repo's Scarthgap baseline regardless "
|
|
207
|
+
"(issue #1971). Tracked by issue #264."
|
|
208
|
+
),
|
|
209
|
+
"e1m-aen701-a32": (
|
|
210
|
+
"MACHINE 'e1m-aen701-a32' cannot build: its base `require "
|
|
211
|
+
"conf/machine/devkit-e7.conf` is commented out pending "
|
|
212
|
+
"meta-alif-ensemble being vendored (no DEFAULTTUNE / kernel "
|
|
213
|
+
"provider / TF-A platform is set), and even once wired up that "
|
|
214
|
+
"upstream layer's LAYERSERIES_COMPAT (\"warrior zeus\") is "
|
|
215
|
+
"incompatible with this repo's Scarthgap baseline regardless "
|
|
216
|
+
"(issue #1971). Tracked by issue #264."
|
|
217
|
+
),
|
|
218
|
+
"e1m-aen501-a32": (
|
|
219
|
+
"MACHINE 'e1m-aen501-a32' cannot build: meta-alp-sdk/conf/machine/ "
|
|
220
|
+
"ships no conf for it at all, so BitBake fails before any `require` "
|
|
221
|
+
"is even parsed -- strictly more unbuildable than 'e1m-aen801-a32' / "
|
|
222
|
+
"'e1m-aen701-a32' above, and, like them, on a meta-alif-ensemble "
|
|
223
|
+
"base that is Yocto-series-incompatible with this repo's Scarthgap "
|
|
224
|
+
"baseline regardless (issue #1971). Tracked by issue #264."
|
|
225
|
+
),
|
|
226
|
+
"e1m-aen601-a32": (
|
|
227
|
+
"MACHINE 'e1m-aen601-a32' cannot build: meta-alp-sdk/conf/machine/ "
|
|
228
|
+
"ships no conf for it at all, so BitBake fails before any `require` "
|
|
229
|
+
"is even parsed -- strictly more unbuildable than 'e1m-aen801-a32' / "
|
|
230
|
+
"'e1m-aen701-a32' above, and, like them, on a meta-alif-ensemble "
|
|
231
|
+
"base that is Yocto-series-incompatible with this repo's Scarthgap "
|
|
232
|
+
"baseline regardless (issue #1971). Tracked by issue #264."
|
|
233
|
+
),
|
|
234
|
+
"e1m-aen803-a32": (
|
|
235
|
+
"MACHINE 'e1m-aen803-a32' cannot build: meta-alp-sdk/conf/machine/ "
|
|
236
|
+
"ships no conf for it at all, so BitBake fails before any `require` "
|
|
237
|
+
"is even parsed -- strictly more unbuildable than 'e1m-aen801-a32' / "
|
|
238
|
+
"'e1m-aen701-a32' above, and, like them, on a meta-alif-ensemble "
|
|
239
|
+
"base that is Yocto-series-incompatible with this repo's Scarthgap "
|
|
240
|
+
"baseline regardless (issue #1971). E1M-AEN803 is the bench module "
|
|
241
|
+
"issue #1982 names. Tracked by issue #264."
|
|
242
|
+
),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class UnbuildableYoctoMachineError(ValueError):
|
|
247
|
+
"""Raised by `_slice_command` when a yocto slice's `machine:` is a
|
|
248
|
+
known-non-buildable MACHINE (`YOCTO_MACHINE_UNBUILDABLE`, issue
|
|
249
|
+
#1982): the planner refuses to emit `bitbake` for it rather than
|
|
250
|
+
hand a consumer a command that cannot succeed -- see
|
|
251
|
+
`YOCTO_MACHINE_UNBUILDABLE`'s own comment for why each listed
|
|
252
|
+
MACHINE fails (not always the same proximate failure mode).
|
|
253
|
+
Carries the machine name + reason so the caller can render both,
|
|
254
|
+
same as `UnknownBoardTargetError` below.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
def __init__(self, core_id: str, machine: str, reason: str) -> None:
|
|
258
|
+
self.core_id = core_id
|
|
259
|
+
self.machine = machine
|
|
260
|
+
self.reason = reason
|
|
261
|
+
super().__init__(f"core '{core_id}': {reason}")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class UnrootedPathError(ValueError):
|
|
265
|
+
"""Raised by `_tokenize` when a path resolves under neither
|
|
266
|
+
`${PROJECT_ROOT}` nor `${SDK_ROOT}` (issue #865's split-brain guard).
|
|
267
|
+
|
|
268
|
+
A single exception type raised from the ONE place paths get tokenized
|
|
269
|
+
means every call site -- the five command-arg sites in `_slice_command`
|
|
270
|
+
plus `buildplan.py`'s `appDir` -- is guarded uniformly by construction;
|
|
271
|
+
nothing can add a sixth call site and forget the check. Callers catch
|
|
272
|
+
this and turn it into a `warnings` entry (never a hard crash of the
|
|
273
|
+
whole emit), matching the existing `no-command`/`yocto-recipe-missing`
|
|
274
|
+
"block the one broken slice, keep going" convention.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _real_zephyr_board_names(repo: Path) -> set[str]:
|
|
279
|
+
"""Every Zephyr board name that actually has a tree under
|
|
280
|
+
`zephyr/boards/alp/`, read from each tree's own `board.yml`
|
|
281
|
+
`board: name:` field -- existence, not naming shape, is what makes a
|
|
282
|
+
target buildable. Mirrors `check_board_target_tree_parity.py`'s
|
|
283
|
+
`_load_real_board_names` (issue #999's own list of what's real),
|
|
284
|
+
duplicated here rather than imported: that script is a CI gate over
|
|
285
|
+
declared SoM presets, this is a runtime planner check over one
|
|
286
|
+
resolved slice, and a ~10-line directory glob is cheaper than
|
|
287
|
+
cross-importing a top-level gate script into the package.
|
|
288
|
+
"""
|
|
289
|
+
boards_dir = repo / "zephyr" / "boards" / "alp"
|
|
290
|
+
names: set[str] = set()
|
|
291
|
+
if not boards_dir.is_dir():
|
|
292
|
+
return names
|
|
293
|
+
for board_yml in sorted(boards_dir.glob("*/board.yml")):
|
|
294
|
+
doc = yaml.safe_load(board_yml.read_text(encoding="utf-8")) or {}
|
|
295
|
+
name = (doc.get("board") or {}).get("name")
|
|
296
|
+
if isinstance(name, str):
|
|
297
|
+
names.add(name)
|
|
298
|
+
return names
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class UnknownBoardTargetError(ValueError):
|
|
302
|
+
"""Raised by `_slice_command` when a zephyr slice's `board:` target
|
|
303
|
+
(resolved from the SoM preset's `topology.<core>.board:`) names no
|
|
304
|
+
tree under `zephyr/boards/alp/` (issue #999 one layer down: the
|
|
305
|
+
*declaration* gate `check_board_target_tree_parity.py` already
|
|
306
|
+
flags this at the metadata level; this is the same fact checked at
|
|
307
|
+
*emit* time, so the planner never hands a consumer -- `tan`, `west`
|
|
308
|
+
itself -- a `west build -b <board>` command that is guaranteed to
|
|
309
|
+
fail with Zephyr's own "No board named ... Invalid BOARD" error).
|
|
310
|
+
|
|
311
|
+
Carries the SKU/core/board/what-exists facts so the caller can
|
|
312
|
+
render them in the customer's own terms, same as `UnrootedPathError`
|
|
313
|
+
above.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
def __init__(self, sku: str, core_id: str, board: str,
|
|
317
|
+
real_boards: set[str]) -> None:
|
|
318
|
+
self.sku = sku
|
|
319
|
+
self.core_id = core_id
|
|
320
|
+
self.board = board
|
|
321
|
+
self.real_boards = real_boards
|
|
322
|
+
super().__init__(
|
|
323
|
+
f"SoM '{sku}' core '{core_id}' wants Zephyr board '{board}', "
|
|
324
|
+
f"which has no tree under zephyr/boards/alp/ -- board bring-up "
|
|
325
|
+
f"for this target has not happened yet."
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _tokenize(path: Path, base_dir: Path, repo: Path) -> str:
|
|
330
|
+
"""Render an absolute path as a portable `${PROJECT_ROOT}`/`${SDK_ROOT}`
|
|
331
|
+
token (issue #865) instead of baking in THIS checkout's absolute path.
|
|
332
|
+
tan-cli (PR #24) substitutes both tokens at materialise time, so a plan
|
|
333
|
+
emitted on one machine/checkout can be materialised faithfully on
|
|
334
|
+
another -- the split-brain risk a baked-in absolute path invites.
|
|
335
|
+
|
|
336
|
+
Prefers `${PROJECT_ROOT}` (the project's own board.yaml directory) since
|
|
337
|
+
most anchored paths are project-owned; falls back to `${SDK_ROOT}` for
|
|
338
|
+
paths the SDK itself owns (e.g. the family sysbuild base, the stock
|
|
339
|
+
M-core shim app). Raises `UnrootedPathError` for a path under neither
|
|
340
|
+
root -- never returns a bare absolute path, so a non-hermetic path can
|
|
341
|
+
never silently reach a `planPathMode: tokened` plan.
|
|
342
|
+
"""
|
|
343
|
+
path = Path(path)
|
|
344
|
+
for root, token in ((base_dir, "${PROJECT_ROOT}"), (repo, "${SDK_ROOT}")):
|
|
345
|
+
try:
|
|
346
|
+
rel = path.relative_to(root)
|
|
347
|
+
except ValueError:
|
|
348
|
+
continue
|
|
349
|
+
return token if rel == Path(".") else f"{token}/{rel.as_posix()}"
|
|
350
|
+
raise UnrootedPathError(path.as_posix())
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _slice_command(
|
|
354
|
+
project: BoardProject,
|
|
355
|
+
slice_: Slice,
|
|
356
|
+
base_dir: Path,
|
|
357
|
+
) -> Optional[list[str]]:
|
|
358
|
+
"""Resolve the build command for a slice. Returns None when there is no
|
|
359
|
+
buildable command yet -- the caller carries the slice as `skipped` /
|
|
360
|
+
`no-command`, never dropped. Raises `UnrootedPathError` /
|
|
361
|
+
`UnknownBoardTargetError` / `UnbuildableYoctoMachineError` for a slice
|
|
362
|
+
the plan must block rather than mis-emit.
|
|
363
|
+
|
|
364
|
+
`base_dir` anchors every relative `app:` path -- the directory holding
|
|
365
|
+
the project's `board.yaml` (or an equivalent explicit root), NEVER the
|
|
366
|
+
caller's process CWD. A relative `app:` means "relative to the project
|
|
367
|
+
file that named it", so the same board.yaml must resolve identically no
|
|
368
|
+
matter where the emitting process happens to be invoked from
|
|
369
|
+
(issue #596).
|
|
370
|
+
"""
|
|
371
|
+
# `cameras:` unbuildable for this core (none/ambiguous owner, missing
|
|
372
|
+
# shield or overlay): block the command, whatever the OS.
|
|
373
|
+
_cameras.check(project, slice_)
|
|
374
|
+
if slice_.os == "zephyr":
|
|
375
|
+
if not slice_.app or not slice_.board:
|
|
376
|
+
return None
|
|
377
|
+
real_boards = _real_zephyr_board_names(REPO)
|
|
378
|
+
# `board:` may carry Zephyr's extended `board/soc/variant`
|
|
379
|
+
# qualifier (e.g. AEN801's `alp_e1m_aen801_m55_hp/ae822fa0e5597ls0/
|
|
380
|
+
# rtss_hp`) -- a tree's board.yml `name:` is always the bare board
|
|
381
|
+
# only, so check existence on the bare form (same `bare =
|
|
382
|
+
# raw.split()[0].split("/")[0]` normalisation
|
|
383
|
+
# check_board_target_tree_parity.py uses); the full qualified
|
|
384
|
+
# string still goes to `west build -b` unchanged below.
|
|
385
|
+
bare_board = slice_.board.split()[0].split("/")[0]
|
|
386
|
+
if bare_board not in real_boards:
|
|
387
|
+
raise UnknownBoardTargetError(
|
|
388
|
+
project.sku, slice_.core_id, slice_.board, real_boards)
|
|
389
|
+
# NB: no explicit `-d`. west's default output is <cwd>/build (a
|
|
390
|
+
# subdirectory of the command's cwd = buildDir), so the tree lands
|
|
391
|
+
# at <buildDir>/build/. Adding `-d <buildDir>` here would
|
|
392
|
+
# double-nest (west resolves a relative -d against its cwd,
|
|
393
|
+
# already = buildDir) -- see finding M14.
|
|
394
|
+
#
|
|
395
|
+
# The consumer does NOT have to reconcile that nesting any more:
|
|
396
|
+
# `_slice_artifacts` reports the `<buildDir>/build/...` paths west
|
|
397
|
+
# actually writes (issue #1360). `-d .` was the alternative -- it
|
|
398
|
+
# would move west's tree to <buildDir> itself and make the old
|
|
399
|
+
# un-nested `artifacts` spelling true -- and was REJECTED: this
|
|
400
|
+
# slice's `alp.conf` is materialised at <buildDir>/alp.conf and
|
|
401
|
+
# handed to this very command via `-DEXTRA_CONF_FILE=`, so making
|
|
402
|
+
# <buildDir> west's own build dir puts that file inside the tree
|
|
403
|
+
# `west build -p` (or a `--pristine=auto` board/app change) wipes,
|
|
404
|
+
# deleting the fragment the command line points at. Today it sits
|
|
405
|
+
# one level ABOVE west's tree, where pristine cannot reach it.
|
|
406
|
+
# Tokenized (issue #865): `_zephyr_app_dir` resolves an absolute
|
|
407
|
+
# path (project-anchored for a customer app, SDK-anchored for the
|
|
408
|
+
# stock M-core shim) -- see `_tokenize`.
|
|
409
|
+
cmd = [
|
|
410
|
+
"west", "build",
|
|
411
|
+
"-b", slice_.board,
|
|
412
|
+
_tokenize(_zephyr_app_dir(slice_.app, base_dir), base_dir, REPO),
|
|
413
|
+
]
|
|
414
|
+
# ADR 0014 Phase-3 conf->build: wire the generated sysbuild
|
|
415
|
+
# overlays into the build command itself. `_shared_artefacts`
|
|
416
|
+
# emits the top-level overlay at build_root/alp_sysbuild.conf and
|
|
417
|
+
# the TF-M child overlay at build_root/sysbuild/tfm/tfm.conf.
|
|
418
|
+
# Pass --sysbuild whenever a sysbuild child image is configured (a
|
|
419
|
+
# `boot:` or `security.psa:` block), and point sysbuild at the
|
|
420
|
+
# top-level overlay only when it is non-empty (the TF-M overlay is
|
|
421
|
+
# picked up by sysbuild convention from its sysbuild/tfm/ path).
|
|
422
|
+
# Absent both, the stock per-family sysbuild defaults apply.
|
|
423
|
+
#
|
|
424
|
+
# Zephyr normally derives Python3_EXECUTABLE from the interpreter
|
|
425
|
+
# that launched west (WEST_PYTHON). A pre-existing CMake cache can
|
|
426
|
+
# already define Python3_EXECUTABLE, however, which prevents that
|
|
427
|
+
# hand-off and can select a host Python without the west package.
|
|
428
|
+
# The orchestrator itself runs under the intended workspace Python,
|
|
429
|
+
# so pin it as an explicit CMake cache override (issue #787).
|
|
430
|
+
# ${PYTHON} token, not `sys.executable` (issue #865): this plan is
|
|
431
|
+
# emitted once by whichever Python ran the planner, but may be
|
|
432
|
+
# materialised on a different host/checkout than that -- baking in
|
|
433
|
+
# a concrete interpreter path here would pin THIS run's Python, not
|
|
434
|
+
# the consumer's. tan-cli (PR #24) substitutes ${PYTHON} with its
|
|
435
|
+
# own resolved interpreter (forward-slashed for the same CMake
|
|
436
|
+
# backslash-escape reason issue #787/#849 fixed: CMake parses
|
|
437
|
+
# `\U`/`\N` etc. in a Windows path as an invalid character escape)
|
|
438
|
+
# at materialise time; the SDK never emits a bare fallback here.
|
|
439
|
+
defines = ["-DPython3_EXECUTABLE=${PYTHON}"]
|
|
440
|
+
is_sysbuild = emit_sysbuild_conf(project) or emit_tfm_sysbuild_conf(project)
|
|
441
|
+
if is_sysbuild:
|
|
442
|
+
cmd.append("--sysbuild")
|
|
443
|
+
if emit_sysbuild_conf(project):
|
|
444
|
+
# SB_CONF_FILE is the only supported way to name a
|
|
445
|
+
# non-default top-level sysbuild overlay: `west build` is a
|
|
446
|
+
# ZEPHYR extension command, and no Zephyr has ever had a
|
|
447
|
+
# `--sysbuild-config` flag -- west forwarded the unknown
|
|
448
|
+
# argument to CMake, which failed the configure step with
|
|
449
|
+
# `Unknown argument --sysbuild-config` (issue #805).
|
|
450
|
+
#
|
|
451
|
+
# The path must be ABSOLUTE. sysbuild resolves a relative
|
|
452
|
+
# SB_CONF_FILE against APP_DIR, not the command's cwd
|
|
453
|
+
# (share/sysbuild/cmake/modules/sysbuild_kconfig.cmake),
|
|
454
|
+
# so the cwd-relative form this used to emit would silently
|
|
455
|
+
# look for the overlay under the application's source dir.
|
|
456
|
+
#
|
|
457
|
+
# Anchor on `base_dir` (the board.yaml directory), never the
|
|
458
|
+
# emitting process's CWD: `slice_.build_dir`'s parent is the
|
|
459
|
+
# build root, which may itself be a relative path, and
|
|
460
|
+
# resolving a relative path bare falls back to
|
|
461
|
+
# `Path.cwd()` -- the same #596 CWD-dependence bug class
|
|
462
|
+
# `_resolve_app_path` guards against for `app:`.
|
|
463
|
+
build_root_dir = Path(slice_.build_dir).parent
|
|
464
|
+
if not build_root_dir.is_absolute():
|
|
465
|
+
build_root_dir = base_dir / build_root_dir
|
|
466
|
+
sb_conf = (build_root_dir.resolve() / "alp_sysbuild.conf")
|
|
467
|
+
# LAYER, don't replace: SB_CONF_FILE accepts a `;`-joined
|
|
468
|
+
# list, and sysbuild merges every listed file in order
|
|
469
|
+
# (later files win on a repeated symbol). When the SoM
|
|
470
|
+
# family ships a curated zephyr/sysbuild/<family>/
|
|
471
|
+
# sysbuild.conf, put it FIRST so the customer's `boot:`
|
|
472
|
+
# overlay lands as deltas on top of the curated base
|
|
473
|
+
# instead of forking family boot policy into two
|
|
474
|
+
# divergent places (issue #807).
|
|
475
|
+
family_base = sysbuild_family_base_conf(project)
|
|
476
|
+
# Forward slashes: CMake's `cmake_path()` (which
|
|
477
|
+
# sysbuild_kconfig.cmake uses to split the `;`-joined
|
|
478
|
+
# SB_CONF_FILE list) only recognises `/` -- a native
|
|
479
|
+
# Windows backslash path here dies the configure step
|
|
480
|
+
# with "File ... not found", the same class of bug
|
|
481
|
+
# #849 fixed for -DPython3_EXECUTABLE. `_tokenize` (#865)
|
|
482
|
+
# already emits posix-form tokens, so this holds for both:
|
|
483
|
+
# `sb_conf` is project-anchored (`${PROJECT_ROOT}/...`),
|
|
484
|
+
# `family_base` is SDK-anchored (`${SDK_ROOT}/...`) -- the
|
|
485
|
+
# `;`-joined list correctly carries both roots.
|
|
486
|
+
sb_conf_files = (
|
|
487
|
+
[_tokenize(family_base, base_dir, REPO),
|
|
488
|
+
_tokenize(sb_conf, base_dir, REPO)]
|
|
489
|
+
if family_base else [_tokenize(sb_conf, base_dir, REPO)]
|
|
490
|
+
)
|
|
491
|
+
defines.append(f"-DSB_CONF_FILE={';'.join(sb_conf_files)}")
|
|
492
|
+
# Wire the slice's materialised per-core Kconfig fragment
|
|
493
|
+
# (`_slice_config_artefact` -> build_dir/alp.conf, carried in the
|
|
494
|
+
# plan's `configArtefacts`) into the build command via
|
|
495
|
+
# EXTRA_CONF_FILE -- Zephyr's supported extra-fragment merge point
|
|
496
|
+
# (layered on prj.conf). The path is ABSOLUTE and anchored on
|
|
497
|
+
# `base_dir` (issue #596), never Path.cwd(), so the plan is
|
|
498
|
+
# byte-identical wherever it is emitted.
|
|
499
|
+
#
|
|
500
|
+
# A --sysbuild build scopes per-image variables as
|
|
501
|
+
# -D<image>_VAR: a bare -DEXTRA_CONF_FILE would land on the
|
|
502
|
+
# SYSBUILD image, not the application, silently dropping the
|
|
503
|
+
# per-core alp.conf on boot:/OTA projects. The application image's
|
|
504
|
+
# name IS derivable: sysbuild names it after the basename of the
|
|
505
|
+
# app directory (`get_filename_component(app_name ${APP_DIR}
|
|
506
|
+
# NAME)` in share/sysbuild/CMakeLists.txt), i.e. the directory
|
|
507
|
+
# `west build` is handed above -- so emit the image-prefixed form
|
|
508
|
+
# (#866, the plan-native replacement for the per-example
|
|
509
|
+
# CMakeLists.txt bridge #870).
|
|
510
|
+
#
|
|
511
|
+
# CAVEAT: the prefix is the app directory's real basename, but the
|
|
512
|
+
# command's app dir is a `${PROJECT_ROOT}` token. When the app dir
|
|
513
|
+
# IS the project root (`app: ./src` falls back to the example
|
|
514
|
+
# root), the image name is the project root's directory name, so a
|
|
515
|
+
# tokened plan materialised under a differently-named root names a
|
|
516
|
+
# stale image and Zephyr silently ignores the arg. A consumer that
|
|
517
|
+
# relocates the project root must re-derive the prefix from the
|
|
518
|
+
# substituted app dir (documented in docs/heterogeneous-builds.md).
|
|
519
|
+
alp_conf = Path(slice_.build_dir) / "alp.conf"
|
|
520
|
+
if not alp_conf.is_absolute():
|
|
521
|
+
alp_conf = Path(base_dir) / alp_conf
|
|
522
|
+
alp_conf = alp_conf.resolve()
|
|
523
|
+
extra_var = "EXTRA_CONF_FILE"
|
|
524
|
+
if is_sysbuild:
|
|
525
|
+
image = _zephyr_app_dir(slice_.app, base_dir).name
|
|
526
|
+
extra_var = f"{image}_EXTRA_CONF_FILE"
|
|
527
|
+
conf_files = [_tokenize(alp_conf, base_dir, REPO)]
|
|
528
|
+
if link_applies_to(project.diagnostics, slice_):
|
|
529
|
+
# `diagnostics.link: itcm` (tan-cli#1350): layer the Flow C ITCM
|
|
530
|
+
# retarget AFTER alp.conf (a later fragment wins) and hand Zephyr
|
|
531
|
+
# the devicetree half. Both are `_slice_config_artefact`
|
|
532
|
+
# siblings in the same build dir, materialised from the plan.
|
|
533
|
+
if is_sysbuild:
|
|
534
|
+
raise LinkTargetError(
|
|
535
|
+
UNSUPPORTED_CODE,
|
|
536
|
+
"diagnostics.link: itcm cannot be combined with a "
|
|
537
|
+
"sysbuild project (`boot:` / `ota:` / TF-M).")
|
|
538
|
+
itcm_conf_path = alp_conf.with_name(ITCM_CONF_NAME)
|
|
539
|
+
itcm_overlay_path = alp_conf.with_name(ITCM_OVERLAY_NAME)
|
|
540
|
+
conf_files.append(_tokenize(itcm_conf_path, base_dir, REPO))
|
|
541
|
+
defines.append(
|
|
542
|
+
"-DEXTRA_DTC_OVERLAY_FILE="
|
|
543
|
+
f"{_tokenize(itcm_overlay_path, base_dir, REPO)}")
|
|
544
|
+
defines.append(f"-D{extra_var}={';'.join(conf_files)}")
|
|
545
|
+
# `cameras:` -> ONE -DSHIELD (carrier + module shields), shared with
|
|
546
|
+
# the cmake-args listing via cameras.zephyr_shield_define.
|
|
547
|
+
# Sysbuild: `-D<image>_SHIELD` so MCUboot does not get the shields.
|
|
548
|
+
shield = _cameras.shield_define_for_build(project, slice_, base_dir)
|
|
549
|
+
if shield:
|
|
550
|
+
defines.append(f"-D{shield}")
|
|
551
|
+
cmd += ["--", *defines]
|
|
552
|
+
return cmd
|
|
553
|
+
if slice_.os == "yocto":
|
|
554
|
+
# Refuse a known-non-buildable MACHINE before considering
|
|
555
|
+
# image/app/recipe at all (issue #1982): none of those fields
|
|
556
|
+
# matter if `bitbake`'s own MACHINE parse cannot succeed.
|
|
557
|
+
if slice_.machine in YOCTO_MACHINE_UNBUILDABLE:
|
|
558
|
+
raise UnbuildableYoctoMachineError(
|
|
559
|
+
slice_.core_id, slice_.machine,
|
|
560
|
+
YOCTO_MACHINE_UNBUILDABLE[slice_.machine])
|
|
561
|
+
# `image:` always names a real recipe (e.g. `alp-image-edge`) --
|
|
562
|
+
# safe to hand straight to bitbake. `app:` is a filesystem path to
|
|
563
|
+
# the app's source directory (mirrors the zephyr/baremetal `app:`
|
|
564
|
+
# convention), NOT a recipe name -- `bitbake <path>` is never a
|
|
565
|
+
# valid target (issue #597), so an app-only slice needs an explicit
|
|
566
|
+
# `recipe:` naming the bitbake recipe that packages that source.
|
|
567
|
+
if slice_.image:
|
|
568
|
+
return ["bitbake", str(slice_.image)]
|
|
569
|
+
if slice_.app == STOCK_IMAGE_APP:
|
|
570
|
+
return ["bitbake", slice_.app]
|
|
571
|
+
if slice_.app:
|
|
572
|
+
if not slice_.recipe:
|
|
573
|
+
return None
|
|
574
|
+
return ["bitbake", str(slice_.recipe)]
|
|
575
|
+
return None
|
|
576
|
+
if slice_.os == "baremetal":
|
|
577
|
+
if not slice_.app:
|
|
578
|
+
return None
|
|
579
|
+
# `-S` tokenized (issue #865) same as the zephyr app-dir arg above.
|
|
580
|
+
app_path = _tokenize(_resolve_app_path(slice_.app, base_dir),
|
|
581
|
+
base_dir, REPO)
|
|
582
|
+
# `-B .`, NOT `-B <slice_.build_dir>`: the plan pins this command's
|
|
583
|
+
# `cwd` to the slice's buildDir (schema: "always equal to this
|
|
584
|
+
# slice's buildDir"), and cmake resolves a relative `-B` against
|
|
585
|
+
# its own cwd -- so the historical `-B build/<core>-baremetal`
|
|
586
|
+
# double-nested the tree at `<buildDir>/build/<core>-baremetal/`,
|
|
587
|
+
# where nothing that reads `artifacts`/`buildDir` would ever find
|
|
588
|
+
# it (tan-cli#550). The zephyr branch avoids that SECOND level by
|
|
589
|
+
# emitting no `-d` at all (finding M14, above); the one level west
|
|
590
|
+
# still adds on its own is reported honestly by `_slice_artifacts`
|
|
591
|
+
# rather than left for the reader to add back (issue #1360).
|
|
592
|
+
return [
|
|
593
|
+
"cmake", "-S", app_path, "-B", ".",
|
|
594
|
+
# Every `-D` below is set BY THIS PLANNER, not by the customer,
|
|
595
|
+
# and an app is free to consume none of them -- CMake would then
|
|
596
|
+
# end each configure with `CMake Warning: Manually-specified
|
|
597
|
+
# variables were not used by the project: ALP_SOM_FAMILY,
|
|
598
|
+
# ALP_TOOLCHAIN, ...`, a warning about the planner's own
|
|
599
|
+
# behaviour that the customer can neither act on nor silence.
|
|
600
|
+
"--no-warn-unused-cli",
|
|
601
|
+
# A best-effort IDE convenience, NOT a promise: CMake implements
|
|
602
|
+
# `CMAKE_EXPORT_COMPILE_COMMANDS` "only by Makefile Generators
|
|
603
|
+
# and Ninja Generators. It is ignored on other generators"
|
|
604
|
+
# (CMake's own docs for the variable). This planner does not
|
|
605
|
+
# choose the generator, so `artifacts.compileCommands` stays
|
|
606
|
+
# null -- see `buildplan._slice_artifacts`.
|
|
607
|
+
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
|
608
|
+
# Pins WHERE the slice's linked executables land
|
|
609
|
+
# (`artifacts.outputDir`). The app's own CMakeLists.txt picks
|
|
610
|
+
# the target NAMES -- not an SDK convention this planner may
|
|
611
|
+
# invent -- so the directory is the strongest honest claim
|
|
612
|
+
# available.
|
|
613
|
+
#
|
|
614
|
+
# ABSOLUTE (tokened, #865) on purpose: CMake resolves a RELATIVE
|
|
615
|
+
# CMAKE_RUNTIME_OUTPUT_DIRECTORY against each target's own
|
|
616
|
+
# CMAKE_CURRENT_BINARY_DIR, so a relative value would scatter a
|
|
617
|
+
# multi-subdirectory app's outputs instead of pinning them.
|
|
618
|
+
#
|
|
619
|
+
# Wrapped in the no-op generator expression `$<1:...>` on
|
|
620
|
+
# purpose too: "Multi-configuration generators (Visual Studio,
|
|
621
|
+
# Xcode, Ninja Multi-Config) append a per-configuration
|
|
622
|
+
# subdirectory to the specified directory UNLESS A GENERATOR
|
|
623
|
+
# EXPRESSION IS USED" (CMake's own RUNTIME_OUTPUT_DIRECTORY
|
|
624
|
+
# docs). Without it the plan says `<buildDir>/output` while the
|
|
625
|
+
# binary is at `<buildDir>/output/Debug/` on every
|
|
626
|
+
# multi-config generator -- measured on Ninja Multi-Config, and
|
|
627
|
+
# the default generator on Windows (Visual Studio) is one.
|
|
628
|
+
f"-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$<1:"
|
|
629
|
+
f"{_tokenize(_baremetal_output_dir(slice_, base_dir), base_dir, REPO)}>",
|
|
630
|
+
# tan-cli#551: the slice's `-DALP_SOM_SKU` / `-DALP_SOM_FAMILY`
|
|
631
|
+
# / `-DALP_CORE_ID` / `-DALP_TOOLCHAIN` / NPU-dispatch cache
|
|
632
|
+
# entries. They used to be rendered ONLY into a `cmake-args.txt`
|
|
633
|
+
# nothing ever read, then dropped outright with that artefact
|
|
634
|
+
# (#1278) -- the configure has never carried a single one of
|
|
635
|
+
# them. The bare `#if defined(...)` guards from the same source
|
|
636
|
+
# (`ALP_BOARD_<slug>`, `ALP_SOM_<SKU>`) CANNOT ride here (cmake
|
|
637
|
+
# rejects a `-D` with no `=value`); they arrive as real compiler
|
|
638
|
+
# definitions via -DCMAKE_PROJECT_INCLUDE below.
|
|
639
|
+
*_baremetal_cache_args(project, slice_),
|
|
640
|
+
*_baremetal_project_include_arg(project, slice_, base_dir),
|
|
641
|
+
]
|
|
642
|
+
return None
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _baremetal_output_dir(slice_: Slice, base_dir: Path) -> Path:
|
|
646
|
+
"""Absolute directory a baremetal slice's linked executables land in.
|
|
647
|
+
|
|
648
|
+
Anchored on `base_dir` (the board.yaml's own directory), never
|
|
649
|
+
`Path.cwd()`, so the emitted plan is byte-identical wherever it is
|
|
650
|
+
emitted from -- the same rule `_resolve_app_path` and the zephyr
|
|
651
|
+
branch's `-DEXTRA_CONF_FILE` follow (issue #596).
|
|
652
|
+
"""
|
|
653
|
+
out = Path(slice_.build_dir)
|
|
654
|
+
if not out.is_absolute():
|
|
655
|
+
out = Path(base_dir) / out
|
|
656
|
+
return (out / "output").resolve()
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
#: Filename of the generated CMake include a baremetal slice's configure
|
|
660
|
+
#: pulls in via `-DCMAKE_PROJECT_INCLUDE`. Shared by `_slice_command`
|
|
661
|
+
#: (which references it) and `buildplan._slice_config_artefact` (which
|
|
662
|
+
#: renders its contents) so the two cannot name different files.
|
|
663
|
+
BAREMETAL_PROJECT_INCLUDE = "alp-baremetal.cmake"
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _baremetal_alp_lines(project: BoardProject, slice_: Slice) -> list[str]:
|
|
667
|
+
"""The slice's `-D` lines, read from the SINGLE source that renders
|
|
668
|
+
them -- `kconfig.py::_slice_cmake_args`, the same text
|
|
669
|
+
`alp_project.py --emit cmake-args` prints -- so the plan's configure
|
|
670
|
+
and that emit can never drift.
|
|
671
|
+
|
|
672
|
+
That emit is a TEXT format, not an argv: it opens with a
|
|
673
|
+
`# Auto-generated ...` banner, and `libraries.baremetal_cmake_args`
|
|
674
|
+
contributes `# library <name>: <cmake hint>` lines that are prose FOR
|
|
675
|
+
A HUMAN (no `-D` in them at all). Only real flags survive here --
|
|
676
|
+
handing cmake a `#`-prefixed line would make it a stray source-dir
|
|
677
|
+
argument, not a define.
|
|
678
|
+
"""
|
|
679
|
+
# Lazy: `kconfig` is a heavier sibling, and `buildplan` already imports
|
|
680
|
+
# this module lazily for the same cycle-avoidance reason.
|
|
681
|
+
from .kconfig import _slice_cmake_args
|
|
682
|
+
|
|
683
|
+
return [line for line in _slice_cmake_args(project, slice_).splitlines()
|
|
684
|
+
if line.startswith("-D")]
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def _baremetal_cache_args(
|
|
688
|
+
project: BoardProject,
|
|
689
|
+
slice_: Slice,
|
|
690
|
+
) -> list[str]:
|
|
691
|
+
"""The `-DNAME=VALUE` subset of the slice's `-D` lines -- CMake CACHE
|
|
692
|
+
entries, safe to pass on the configure command line verbatim.
|
|
693
|
+
|
|
694
|
+
Split from the bare `-DNAME` guards on the one thing that actually
|
|
695
|
+
distinguishes them: `cmake -D` REQUIRES `VAR[:type]=value` and exits
|
|
696
|
+
1 with `Parse error in command line argument: ALP_BOARD_E1M_EVK /
|
|
697
|
+
Should be: VAR:type=value` on anything else (measured, not assumed).
|
|
698
|
+
`docs/board-config-emit.md` documents that split from the other side.
|
|
699
|
+
"""
|
|
700
|
+
return [line for line in _baremetal_alp_lines(project, slice_)
|
|
701
|
+
if "=" in line]
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def _baremetal_compile_guards(
|
|
705
|
+
project: BoardProject,
|
|
706
|
+
slice_: Slice,
|
|
707
|
+
) -> list[str]:
|
|
708
|
+
"""The bare `-DNAME` subset of the slice's `-D` lines, `-D` stripped.
|
|
709
|
+
|
|
710
|
+
These are compile-time `#if defined(...)` guards, NOT cache entries:
|
|
711
|
+
`ALP_BOARD_<SLUG>` gates `include/alp/board.h`'s board facade and
|
|
712
|
+
`ALP_SOM_<SKU>` gates the per-SKU override block in the generated
|
|
713
|
+
`<alp/soc_caps.h>`. A CMake cache variable of the same name is
|
|
714
|
+
invisible to the preprocessor, so they only do their job as real
|
|
715
|
+
compiler definitions -- which is what `_baremetal_project_include`
|
|
716
|
+
turns them into. Zephyr slices get the identical guards through
|
|
717
|
+
`CONFIG_COMPILER_OPT="-D..."` in their alp.conf.
|
|
718
|
+
"""
|
|
719
|
+
return [line[len("-D"):]
|
|
720
|
+
for line in _baremetal_alp_lines(project, slice_)
|
|
721
|
+
if "=" not in line]
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def _baremetal_project_include(
|
|
725
|
+
project: BoardProject,
|
|
726
|
+
slice_: Slice,
|
|
727
|
+
) -> Optional[str]:
|
|
728
|
+
"""Contents of the slice's generated `alp-baremetal.cmake`, or None
|
|
729
|
+
when the slice needs no compile-time guards.
|
|
730
|
+
|
|
731
|
+
Wired into the configure as `-DCMAKE_PROJECT_INCLUDE=<abs path>`,
|
|
732
|
+
which CMake includes at the end of every `project()` call (CMake
|
|
733
|
+
3.15+, comfortably below the repo-wide `cmake_minimum_required(VERSION
|
|
734
|
+
3.20)`). Deliberately NOT `-DCMAKE_C_FLAGS=-DALP_BOARD_...`: setting
|
|
735
|
+
that variable from the command line seeds the cache entry itself, so a
|
|
736
|
+
firmware toolchain file's `CMAKE_C_FLAGS_INIT` (`-mcpu=cortex-m55
|
|
737
|
+
-mfloat-abi=hard`, ...) would never be applied -- silently building the
|
|
738
|
+
slice for the wrong core. `add_compile_definitions` only ADDS, and
|
|
739
|
+
cannot drop a flag.
|
|
740
|
+
"""
|
|
741
|
+
guards = _baremetal_compile_guards(project, slice_)
|
|
742
|
+
if not guards:
|
|
743
|
+
return None
|
|
744
|
+
lines = [
|
|
745
|
+
"# Auto-generated by scripts/alp_orchestrate -- do not edit.",
|
|
746
|
+
"# Pulled in by the slice's configure via -DCMAKE_PROJECT_INCLUDE.",
|
|
747
|
+
"# Compile-time guards <alp/board.h> / <alp/soc_caps.h> test with",
|
|
748
|
+
"# #if defined(...); a CMake cache variable would not reach the",
|
|
749
|
+
"# preprocessor at all.",
|
|
750
|
+
f"add_compile_definitions({' '.join(guards)})",
|
|
751
|
+
]
|
|
752
|
+
return "\n".join(lines) + "\n"
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def _baremetal_project_include_arg(
|
|
756
|
+
project: BoardProject,
|
|
757
|
+
slice_: Slice,
|
|
758
|
+
base_dir: Path,
|
|
759
|
+
) -> list[str]:
|
|
760
|
+
"""`['-DCMAKE_PROJECT_INCLUDE=<abs tokened path>']`, or `[]` when the
|
|
761
|
+
slice has no guards to carry (absence-emits-nothing: no dangling
|
|
762
|
+
reference to a file `_slice_config_artefact` would not have written).
|
|
763
|
+
|
|
764
|
+
ABSOLUTE and `base_dir`-anchored, never cwd-relative: CMake resolves a
|
|
765
|
+
relative `CMAKE_PROJECT_INCLUDE` against the SOURCE dir of the
|
|
766
|
+
`project()` that pulls it in, which is the app's tree, not the slice's
|
|
767
|
+
build dir. Same anchoring rule as the zephyr branch's
|
|
768
|
+
`-DEXTRA_CONF_FILE` (issue #596) and tokened the same way (#865).
|
|
769
|
+
"""
|
|
770
|
+
if _baremetal_project_include(project, slice_) is None:
|
|
771
|
+
return []
|
|
772
|
+
inc = Path(slice_.build_dir)
|
|
773
|
+
if not inc.is_absolute():
|
|
774
|
+
inc = Path(base_dir) / inc
|
|
775
|
+
inc = (inc / BAREMETAL_PROJECT_INCLUDE).resolve()
|
|
776
|
+
return [f"-DCMAKE_PROJECT_INCLUDE={_tokenize(inc, base_dir, REPO)}"]
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def _slice_post_commands(slice_: Slice) -> list[list[str]]:
|
|
780
|
+
"""Argv steps that MUST run, in order, AFTER the slice's `command`.
|
|
781
|
+
|
|
782
|
+
`west build` (zephyr) and `bitbake` (yocto) each configure AND build in
|
|
783
|
+
one invocation, so they need none. `cmake -S ... -B ...` only
|
|
784
|
+
CONFIGURES: without the `cmake --build` step below, a baremetal slice
|
|
785
|
+
exits 0 having produced a `CMakeCache.txt` and no object file, no
|
|
786
|
+
archive and no executable -- a green build over an empty output
|
|
787
|
+
directory (tan-cli#550).
|
|
788
|
+
|
|
789
|
+
`.` (not the build dir path) for the same cwd-relative reason
|
|
790
|
+
`_slice_command`'s baremetal `-B .` uses; the caller pairs every step
|
|
791
|
+
with the slice's buildDir as its `cwd`.
|
|
792
|
+
|
|
793
|
+
No `--parallel`: job-count is the consumer's scheduling policy, the
|
|
794
|
+
same reason the plan carries no `sequential` key.
|
|
795
|
+
"""
|
|
796
|
+
if slice_.os == "baremetal":
|
|
797
|
+
return [["cmake", "--build", "."]]
|
|
798
|
+
return []
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def _resolve_app_path(app: str, base_dir: Path) -> Path:
|
|
802
|
+
"""Resolve `./linux` or absolute paths from a slice.app.
|
|
803
|
+
|
|
804
|
+
Relative paths resolve against `base_dir` (the project's board.yaml
|
|
805
|
+
directory) -- never the process's current working directory, so the
|
|
806
|
+
result is identical regardless of the caller's CWD (issue #596).
|
|
807
|
+
|
|
808
|
+
`cores.<id>.app:` is a raw caller-supplied filesystem path -- like
|
|
809
|
+
`--input` and `extra_libraries[].profile:` (#1961) -- and unlike
|
|
810
|
+
`preset:`/`som.sku`, board.schema.json types it a bare `"string"`
|
|
811
|
+
with no pattern, so it reaches this `.resolve()` unsanitised. A
|
|
812
|
+
symlink loop (ELOOP) surfaces differently per platform: on POSIX,
|
|
813
|
+
`Path.resolve()` detects the cycle itself and raises `RuntimeError`
|
|
814
|
+
before returning; on Windows it returns without raising at all (the
|
|
815
|
+
`.is_file()` calls in `_zephyr_app_dir` below are where the Windows
|
|
816
|
+
crash actually surfaces for a zephyr slice) -- confirmed against a
|
|
817
|
+
real symlink loop on both platforms, not a mock. Same `except`
|
|
818
|
+
shape as `validate.py`'s `profile:` guard and `loader.py`'s
|
|
819
|
+
`_load_yaml` (#1961/#1987): raise a clean `OrchestratorError`
|
|
820
|
+
instead of letting the raw `RuntimeError`/`OSError` crash the CLI.
|
|
821
|
+
"""
|
|
822
|
+
if app == STOCK_SHIM_APP:
|
|
823
|
+
return STOCK_SHIM_DIR
|
|
824
|
+
p = Path(app)
|
|
825
|
+
if p.is_absolute():
|
|
826
|
+
return p
|
|
827
|
+
try:
|
|
828
|
+
return (Path(base_dir) / p).resolve()
|
|
829
|
+
except (OSError, RuntimeError) as e:
|
|
830
|
+
raise OrchestratorError(
|
|
831
|
+
f"app: '{app}' could not be resolved: {e}") from e
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _zephyr_app_dir(app: str, base_dir: Path) -> Path:
|
|
835
|
+
"""Resolve a Zephyr slice's `app:` to the directory holding the
|
|
836
|
+
application `CMakeLists.txt` (what `west build` needs).
|
|
837
|
+
|
|
838
|
+
`base_dir` anchors relative paths -- see `_resolve_app_path`.
|
|
839
|
+
|
|
840
|
+
Two example conventions are supported:
|
|
841
|
+
|
|
842
|
+
* multicore examples point `app:` straight at a self-contained
|
|
843
|
+
Zephyr app directory (e.g. ``./m33_sm`` -- carries its own
|
|
844
|
+
CMakeLists.txt + prj.conf); used verbatim.
|
|
845
|
+
* single-core examples keep one CMakeLists.txt at the example
|
|
846
|
+
root and point `app:` at the sources subdir (e.g. ``./src`` with
|
|
847
|
+
``target_sources(app PRIVATE src/main.c)``). The sources dir has
|
|
848
|
+
no CMakeLists.txt of its own, so fall back to its parent (the
|
|
849
|
+
example root) which does.
|
|
850
|
+
|
|
851
|
+
Same unguarded-path defect class as `_resolve_app_path` above
|
|
852
|
+
(#1961/#1987): a symlink-loop `app:` reaches `.resolve()` clean on
|
|
853
|
+
Windows (see there), then crashes HERE instead -- `Path.is_file()`
|
|
854
|
+
raising a plain `OSError` (`WinError 1920`, "The file cannot be
|
|
855
|
+
accessed by the system") -- confirmed by driving a real WSL-made
|
|
856
|
+
symlink loop through the real CLI on Windows CPython 3.11.3, not a
|
|
857
|
+
mock. Both `.is_file()` calls are wrapped the same way.
|
|
858
|
+
"""
|
|
859
|
+
p = _resolve_app_path(app, base_dir)
|
|
860
|
+
try:
|
|
861
|
+
has_cmakelists = (p / "CMakeLists.txt").is_file()
|
|
862
|
+
except (OSError, RuntimeError) as e:
|
|
863
|
+
raise OrchestratorError(
|
|
864
|
+
f"app: '{app}' could not be resolved: {e}") from e
|
|
865
|
+
if has_cmakelists:
|
|
866
|
+
return p
|
|
867
|
+
try:
|
|
868
|
+
parent_has_cmakelists = (p.parent / "CMakeLists.txt").is_file()
|
|
869
|
+
except (OSError, RuntimeError) as e:
|
|
870
|
+
raise OrchestratorError(
|
|
871
|
+
f"app: '{app}' could not be resolved: {e}") from e
|
|
872
|
+
if parent_has_cmakelists:
|
|
873
|
+
return p.parent
|
|
874
|
+
return p
|