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,381 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""The IO/glue side of build-plan token substitution (alp-sdk #865): resolves
|
|
3
|
+
the SDK root, project root and planner python, drives the pure
|
|
4
|
+
`tan.core.plan_tokens` pass, and runs the one guard that pass cannot do
|
|
5
|
+
itself -- comparing the plan's `sdkCommit` against the resolved SDK
|
|
6
|
+
checkout's actual git HEAD (a subprocess call, so it belongs here, not in the
|
|
7
|
+
pure module).
|
|
8
|
+
|
|
9
|
+
`board_yaml_path`, `sdk_root`, `python` and `toolchain_root` are each
|
|
10
|
+
resolved exactly ONCE by the caller and handed in -- never re-resolved here
|
|
11
|
+
per-slice or on retry, the same "one resolution for the whole plan" contract
|
|
12
|
+
`tan_core::plan_tokens::TokenValues` documents. `toolchain_advice` travels
|
|
13
|
+
WITH `toolchain_root` for that reason: it is the second half of one
|
|
14
|
+
resolution (`toolchain.ToolchainResolution`), not an independent knob, and
|
|
15
|
+
re-deriving it here would let the reason drift from the value it explains."""
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from tan.commands.build.toolchain import NO_TOOLCHAIN_ADVICE
|
|
22
|
+
from tan.commands.sdk_cmd import NO_SDK_NEXT_STEPS
|
|
23
|
+
from tan.core.build_plan import BuildPlan
|
|
24
|
+
from tan.core.plan_tokens import (
|
|
25
|
+
DeferredPlaceholder,
|
|
26
|
+
DeferredPolicy,
|
|
27
|
+
LeftoverToken,
|
|
28
|
+
PLAN_TOKEN_NAMES,
|
|
29
|
+
PLAN_PATH_MODE_TOKENED,
|
|
30
|
+
TokenValues,
|
|
31
|
+
UnknownPlanPathMode,
|
|
32
|
+
UnresolvedToolchainRoot,
|
|
33
|
+
project_root_diverges_from_exec_base,
|
|
34
|
+
sdk_commit_mismatches,
|
|
35
|
+
substitute_plan_tokens,
|
|
36
|
+
)
|
|
37
|
+
from tan.envelope import Issue
|
|
38
|
+
from tan.core.subprocess_env import spawn_env
|
|
39
|
+
from tan.core.sysbuild_image import rescope_sysbuild_extra_conf
|
|
40
|
+
from tan.core.tool_lookup import resolve_tool
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TokenSubstitutionError(Exception):
|
|
44
|
+
def __init__(self, code: str, message: str) -> None:
|
|
45
|
+
super().__init__(message)
|
|
46
|
+
self.code = code
|
|
47
|
+
self.message = message
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class SliceDemotion:
|
|
52
|
+
"""A slice demoted by `substitute_plan_tokens` (tan-cli #89): its fields
|
|
53
|
+
still name a literal `${TOOLCHAIN_ROOT}` because this host has none
|
|
54
|
+
resolved, but the demotion has an owning slice AND an owning dispatch
|
|
55
|
+
seam (`executionPolicy.missingTool`) to route to, so it did not fail the
|
|
56
|
+
whole plan. `reason` is the full sentence the executor surfaces verbatim
|
|
57
|
+
at dispatch."""
|
|
58
|
+
|
|
59
|
+
slice_index: int
|
|
60
|
+
core_id: str
|
|
61
|
+
reason: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _resolve_git_executable() -> str | None:
|
|
65
|
+
"""The absolute path to `git`, resolved through the shared
|
|
66
|
+
`tool_lookup.resolve_tool` walk -- never `shutil.which`, which on Windows
|
|
67
|
+
inserts the current directory ahead of `%PATH%`. `None` when `git` is not
|
|
68
|
+
on PATH at all, which both callers below treat as "no git provenance",
|
|
69
|
+
never a crash (tan-cli#797, mirroring `doctor_cmd._resolve_git_executable`
|
|
70
|
+
-- the same fix, duplicated here rather than imported for the same reason
|
|
71
|
+
`_is_own_git_checkout`'s own docstring gives: this is the second of the
|
|
72
|
+
two sites the issue names, not a shared library call).
|
|
73
|
+
"""
|
|
74
|
+
return resolve_tool("git", os.environ).resolved
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _is_own_git_checkout(sdk_root: Path, git_exe: str | None) -> bool:
|
|
78
|
+
"""Whether `sdk_root` is itself the TOP of a git checkout, not merely
|
|
79
|
+
nested somewhere inside an unrelated ENCLOSING one (tan-cli#488 defect 4,
|
|
80
|
+
mirroring `doctor_cmd._is_own_git_checkout` -- the SAME defect, the SAME
|
|
81
|
+
fix, duplicated here rather than imported because this is the second of
|
|
82
|
+
the two sites the issue names, not a shared library call).
|
|
83
|
+
|
|
84
|
+
`git -C <root> ...` discovery walks UPWARD looking for a `.git`, so an SDK
|
|
85
|
+
with no `.git` of its own -- an extracted release archive vendored under a
|
|
86
|
+
customer's own application repository, a setup this port explicitly
|
|
87
|
+
supports (`sdk_cmd.check_sdk_readiness`'s own docstring names exactly this
|
|
88
|
+
shape) -- answers every git query with the ENCLOSING repo's state instead
|
|
89
|
+
of "not a checkout". `git_short_head` below calls this first and returns
|
|
90
|
+
no signal (`""`) rather than misattributing a foreign repository's HEAD as
|
|
91
|
+
the SDK's own -- which, left unguarded, both stamps a build plan's
|
|
92
|
+
`sdkCommit` with the wrong provenance and lets `sdk_commit_mismatches` fire
|
|
93
|
+
a false split-brain refusal (or miss a real one) keyed off the enclosing
|
|
94
|
+
app repo's commit instead of the SDK's.
|
|
95
|
+
|
|
96
|
+
Compares the RESOLVED `--show-toplevel` against the RESOLVED `sdk_root`:
|
|
97
|
+
lexical string comparison alone would false-negative on a symlinked or
|
|
98
|
+
differently-cased path to the same directory.
|
|
99
|
+
|
|
100
|
+
`git_exe` -- the PATH-RESOLVED absolute `git` from `_resolve_git_executable`
|
|
101
|
+
(tan-cli#797): this runs on the BUILD path with cwd set to the CUSTOMER's
|
|
102
|
+
project, the Windows-reachable strong form of the cwd-shadowing hazard.
|
|
103
|
+
`None` refuses outright rather than falling back to a bare spawn.
|
|
104
|
+
"""
|
|
105
|
+
if git_exe is None:
|
|
106
|
+
return False
|
|
107
|
+
try:
|
|
108
|
+
out = subprocess.run(
|
|
109
|
+
["git", "-C", str(sdk_root), "rev-parse", "--show-toplevel"],
|
|
110
|
+
executable=git_exe,
|
|
111
|
+
capture_output=True,
|
|
112
|
+
text=True,
|
|
113
|
+
timeout=10,
|
|
114
|
+
env=spawn_env(),
|
|
115
|
+
check=False,
|
|
116
|
+
)
|
|
117
|
+
except (OSError, subprocess.SubprocessError):
|
|
118
|
+
return False
|
|
119
|
+
if out.returncode != 0:
|
|
120
|
+
return False
|
|
121
|
+
top = out.stdout.strip()
|
|
122
|
+
if not top:
|
|
123
|
+
return False
|
|
124
|
+
try:
|
|
125
|
+
return Path(top).resolve() == Path(sdk_root).resolve()
|
|
126
|
+
except OSError:
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def git_short_head(sdk_root: Path) -> str:
|
|
131
|
+
"""`git rev-parse --short HEAD`. Empty string when git is missing, the
|
|
132
|
+
checkout has no `.git` OF ITS OWN (see `_is_own_git_checkout` --
|
|
133
|
+
tan-cli#488 defect 4), or the command otherwise fails -- all NO SIGNAL,
|
|
134
|
+
never a hard failure: an SDK checkout with no `.git` (a release tarball,
|
|
135
|
+
including one vendored inside a customer's own git repository) is a
|
|
136
|
+
normal, supported setup.
|
|
137
|
+
|
|
138
|
+
`git` is resolved ONCE, through `_resolve_git_executable`, and threaded
|
|
139
|
+
into both the `_is_own_git_checkout` guard and the spawn below as
|
|
140
|
+
`executable=` (tan-cli#797) -- neither hands `subprocess` the bare
|
|
141
|
+
`"git"` identity.
|
|
142
|
+
"""
|
|
143
|
+
git_exe = _resolve_git_executable()
|
|
144
|
+
if not _is_own_git_checkout(sdk_root, git_exe):
|
|
145
|
+
return ""
|
|
146
|
+
try:
|
|
147
|
+
out = subprocess.run(
|
|
148
|
+
["git", "-C", str(sdk_root), "rev-parse", "--short", "HEAD"],
|
|
149
|
+
executable=git_exe,
|
|
150
|
+
capture_output=True,
|
|
151
|
+
text=True,
|
|
152
|
+
timeout=10,
|
|
153
|
+
env=spawn_env(),
|
|
154
|
+
check=False,
|
|
155
|
+
)
|
|
156
|
+
except (OSError, subprocess.SubprocessError):
|
|
157
|
+
return ""
|
|
158
|
+
return out.stdout.strip() if out.returncode == 0 else ""
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _deferred_extra_note(plan: BuildPlan, e: LeftoverToken, policy: DeferredPolicy) -> str:
|
|
162
|
+
"""What to append to a `build.plan-token-unresolved` message so the
|
|
163
|
+
refusal also says what a malformed `deferredPlaceholders` cost, or how a
|
|
164
|
+
config-artefact placeholder could have been accepted (tan-cli#1302)."""
|
|
165
|
+
notes = ""
|
|
166
|
+
if plan.deferred_placeholders_problem is not None:
|
|
167
|
+
notes += (
|
|
168
|
+
f" Note: {plan.deferred_placeholders_problem} -- the field was ignored, so no "
|
|
169
|
+
f"`${{NAME}}` placeholder was exempt."
|
|
170
|
+
)
|
|
171
|
+
elif e.deferrable:
|
|
172
|
+
name = e.token[2:-1]
|
|
173
|
+
if policy.has_plan_list:
|
|
174
|
+
notes += (
|
|
175
|
+
f" If `{e.token}` is a placeholder meant for the build host or the device, "
|
|
176
|
+
f"the plan's `deferredPlaceholders` has to list `{name}` "
|
|
177
|
+
f"(alplabai/alp-sdk#2696); it does not."
|
|
178
|
+
)
|
|
179
|
+
else:
|
|
180
|
+
notes += (
|
|
181
|
+
f" If `{e.token}` is a placeholder meant for the build host or the device, "
|
|
182
|
+
f"it is accepted only when the plan's `deferredPlaceholders` lists `{name}` "
|
|
183
|
+
f"(alplabai/alp-sdk#2696); this plan carries no `deferredPlaceholders` at "
|
|
184
|
+
f"all -- re-emit it with a current planner."
|
|
185
|
+
)
|
|
186
|
+
return notes
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _deferred_text(d: DeferredPlaceholder) -> str:
|
|
190
|
+
if d.live_kconfig:
|
|
191
|
+
return (
|
|
192
|
+
f"placeholder `${{{d.name}}}` in `{d.field}` sits on a live line of a Kconfig "
|
|
193
|
+
f"fragment; Kconfig does not expand `${{{d.name}}}`, so the firmware will carry "
|
|
194
|
+
f"the literal text unless something substitutes it before the build"
|
|
195
|
+
)
|
|
196
|
+
return (
|
|
197
|
+
f"placeholder `${{{d.name}}}` in `{d.field}` is left as written for the build "
|
|
198
|
+
f"host or the device to supply; tan does not substitute it"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def deferred_placeholder_issues(
|
|
203
|
+
plan: BuildPlan, deferred: list[DeferredPlaceholder]
|
|
204
|
+
) -> list[Issue]:
|
|
205
|
+
"""The envelope issues for tan-cli#1302: one issue per placeholder NAME
|
|
206
|
+
left standing in a config artefact (`info`, or `warning` when any
|
|
207
|
+
occurrence is on a live Kconfig line), plus `build.plan-warning`s for a
|
|
208
|
+
malformed `deferredPlaceholders` and for a plan path token listed in it."""
|
|
209
|
+
issues: list[Issue] = []
|
|
210
|
+
if plan.deferred_placeholders_problem is not None:
|
|
211
|
+
text = f"[deferred-placeholders-malformed] {plan.deferred_placeholders_problem} -- ignoring the field"
|
|
212
|
+
if plan.plan_path_mode == PLAN_PATH_MODE_TOKENED:
|
|
213
|
+
text += (
|
|
214
|
+
", so no `${NAME}` placeholder is exempt from the unresolved-token refusal"
|
|
215
|
+
)
|
|
216
|
+
issues.append(Issue("build.plan-warning", "warning", text))
|
|
217
|
+
bugs = [n for n in plan.deferred_placeholders or () if n in PLAN_TOKEN_NAMES]
|
|
218
|
+
if bugs:
|
|
219
|
+
listed = ", ".join(f"`{n}`" for n in bugs)
|
|
220
|
+
issues.append(
|
|
221
|
+
Issue(
|
|
222
|
+
"build.plan-warning",
|
|
223
|
+
"warning",
|
|
224
|
+
f"[deferred-placeholders-plan-token] `deferredPlaceholders` lists the plan "
|
|
225
|
+
f"path token(s) {listed} -- a planner bug: tan substitutes those and they are "
|
|
226
|
+
f"never deferred; ignoring the entries",
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
by_name: dict[str, list[DeferredPlaceholder]] = {}
|
|
230
|
+
for d in deferred:
|
|
231
|
+
by_name.setdefault(d.name, []).append(d)
|
|
232
|
+
for occurrences in by_name.values():
|
|
233
|
+
pick = next((d for d in occurrences if d.live_kconfig), occurrences[0])
|
|
234
|
+
severity = "warning" if pick.live_kconfig else "info"
|
|
235
|
+
issues.append(Issue("build.deferred-placeholder", severity, _deferred_text(pick)))
|
|
236
|
+
return issues
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def apply_plan_token_substitution(
|
|
240
|
+
plan: BuildPlan,
|
|
241
|
+
*,
|
|
242
|
+
board_yaml_path: str | None,
|
|
243
|
+
exec_base: str,
|
|
244
|
+
sdk_root: str | None,
|
|
245
|
+
python: str,
|
|
246
|
+
toolchain_root: str | None,
|
|
247
|
+
toolchain_advice: str = NO_TOOLCHAIN_ADVICE,
|
|
248
|
+
deferred_out: list[DeferredPlaceholder] | None = None,
|
|
249
|
+
project_root: str | None = None,
|
|
250
|
+
) -> tuple[BuildPlan, list[SliceDemotion]]:
|
|
251
|
+
"""Apply the build-plan token-substitution pass to `plan` before
|
|
252
|
+
materialise writes anything or a slice command runs. A no-op unless
|
|
253
|
+
`plan.plan_path_mode` is present; a present-but-unrecognised mode is a
|
|
254
|
+
hard error, never a half-applied guess.
|
|
255
|
+
|
|
256
|
+
`toolchain_advice` is the reason an unresolved `${TOOLCHAIN_ROOT}` gets,
|
|
257
|
+
both in the plan-fatal refusal and in each demoted slice's `reason`.
|
|
258
|
+
It defaults to the no-toolchain wording this pass always used, so a
|
|
259
|
+
caller that has not resolved a toolchain at all -- and every existing
|
|
260
|
+
test -- keeps its exact previous message. `build_cmd` passes the
|
|
261
|
+
resolver's own reason instead, which distinguishes "this host has none"
|
|
262
|
+
from "this host has SEVERAL and nothing picking between them"
|
|
263
|
+
(tan-cli#547): two different fixes, and only the second one names the
|
|
264
|
+
installs the caller can choose from.
|
|
265
|
+
|
|
266
|
+
`deferred_out` (tan-cli#1302), when given, receives every `${NAME}` that
|
|
267
|
+
was deliberately left standing in a `configArtefacts[*].contents` value
|
|
268
|
+
(see `DeferredPolicy` for the exact rule). A caller that passes nothing
|
|
269
|
+
still gets the exemption -- it just cannot report it."""
|
|
270
|
+
if plan.plan_path_mode is None:
|
|
271
|
+
return plan, []
|
|
272
|
+
if plan.plan_path_mode != PLAN_PATH_MODE_TOKENED:
|
|
273
|
+
raise TokenSubstitutionError(
|
|
274
|
+
"build.plan-invalid",
|
|
275
|
+
f'unknown planPathMode `{plan.plan_path_mode}` (only "tokened" is defined) -- '
|
|
276
|
+
f"refusing rather than silently treating it as legacy or applying token "
|
|
277
|
+
f"substitution",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Guard: PROJECT_ROOT (board.yaml's directory) vs the executor's actual
|
|
281
|
+
# base dir. They coincide only in the default config -- a tokened plan
|
|
282
|
+
# substituting ${PROJECT_ROOT} from one while slices actually run under
|
|
283
|
+
# the other would silently build against the wrong tree.
|
|
284
|
+
# `project_root` is the `board.yaml`-less route's own (tan-cli#1359: `tan
|
|
285
|
+
# build --board`): there is no board.yaml to derive it from, so the caller
|
|
286
|
+
# names it (the build root) instead.
|
|
287
|
+
if board_yaml_path is None and project_root is None:
|
|
288
|
+
raise TokenSubstitutionError(
|
|
289
|
+
"build.plan-invalid",
|
|
290
|
+
"a `planPathMode: tokened` plan needs a resolved board.yaml to derive "
|
|
291
|
+
"${PROJECT_ROOT} from -- pass `--board-yaml <PATH>` or run from a project.",
|
|
292
|
+
)
|
|
293
|
+
if project_root is None:
|
|
294
|
+
project_root = str(Path(board_yaml_path).parent).replace("\\", "/")
|
|
295
|
+
|
|
296
|
+
if project_root_diverges_from_exec_base(project_root, exec_base):
|
|
297
|
+
raise TokenSubstitutionError(
|
|
298
|
+
"build.project-root-mismatch",
|
|
299
|
+
f"plan is `planPathMode: tokened`, but ${{PROJECT_ROOT}} (`{project_root}`, the "
|
|
300
|
+
f"board.yaml directory) differs from where tan actually runs each slice's "
|
|
301
|
+
f"command (`{exec_base}`) -- refusing to build against the wrong tree. This only "
|
|
302
|
+
f"happens with a non-default `west_cwd`/`--project` split; align them, or drop "
|
|
303
|
+
f"`planPathMode` from the plan.",
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# ONE resolved sdk_root for the whole substitution. A tokened plan NEEDS
|
|
307
|
+
# a real ${SDK_ROOT} value: degrading an unresolved sdk_root to "" would
|
|
308
|
+
# substitute ${SDK_ROOT}/scripts into the bare relative path /scripts,
|
|
309
|
+
# sailing straight past the leftover-token guard -- refuse instead.
|
|
310
|
+
if sdk_root is None:
|
|
311
|
+
# `tan sdk switch` refuses in this build (tan-cli#305) -- kept the
|
|
312
|
+
# two mechanisms `resolve_sdk_root_ladder` (this value's caller)
|
|
313
|
+
# actually walks (`--sdk-root`, the lateral discovery tail) and
|
|
314
|
+
# swapped the third for NO_SDK_NEXT_STEPS's honest "how to get a
|
|
315
|
+
# checkout at all".
|
|
316
|
+
raise TokenSubstitutionError(
|
|
317
|
+
"build.sdk-root-unresolved",
|
|
318
|
+
"plan is `planPathMode: tokened` (needs ${SDK_ROOT} substituted with a real "
|
|
319
|
+
"path), but no alp-sdk checkout resolved -- pass `--sdk-root <PATH>`, run from "
|
|
320
|
+
f"a project near an alp-sdk checkout, or {NO_SDK_NEXT_STEPS}.",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# The two-SDK split-brain guard: whenever the plan carries a sdkCommit,
|
|
324
|
+
# a mismatch against the resolved SDK checkout's actual HEAD means tan
|
|
325
|
+
# is about to build against a DIFFERENT SDK checkout/SHA than the plan
|
|
326
|
+
# was emitted from.
|
|
327
|
+
if plan.sdk_commit:
|
|
328
|
+
resolved = git_short_head(Path(sdk_root))
|
|
329
|
+
if sdk_commit_mismatches(plan.sdk_commit, resolved):
|
|
330
|
+
raise TokenSubstitutionError(
|
|
331
|
+
"build.sdk-commit-mismatch",
|
|
332
|
+
f"plan was emitted from alp-sdk commit `{plan.sdk_commit}`, but the resolved "
|
|
333
|
+
f"SDK checkout is at `{resolved}` -- building against a different SDK "
|
|
334
|
+
f"checkout than the plan was captured from can silently produce the wrong "
|
|
335
|
+
f"image. Re-emit the plan from the current SDK checkout, or point "
|
|
336
|
+
f"`--sdk-root` at the checkout the plan names.",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
values = TokenValues(
|
|
340
|
+
sdk_root=sdk_root, project_root=project_root, python=python, toolchain_root=toolchain_root
|
|
341
|
+
)
|
|
342
|
+
policy = DeferredPolicy(plan.deferred_placeholders)
|
|
343
|
+
try:
|
|
344
|
+
out, demoted = substitute_plan_tokens(plan, values, policy)
|
|
345
|
+
except LeftoverToken as e:
|
|
346
|
+
raise TokenSubstitutionError(
|
|
347
|
+
"build.plan-token-unresolved",
|
|
348
|
+
f"plan is `planPathMode: tokened` but field `{e.field}` still names the literal "
|
|
349
|
+
f"token `{e.token}` after substitution -- an SDK-side token this CLI does not "
|
|
350
|
+
f"resolve (only ${{SDK_ROOT}}, ${{PROJECT_ROOT}}, ${{PYTHON}}, ${{TOOLCHAIN_ROOT}} "
|
|
351
|
+
f"are known). Upgrade tan, or check the plan for a bug."
|
|
352
|
+
+ _deferred_extra_note(plan, e, policy),
|
|
353
|
+
) from e
|
|
354
|
+
except UnresolvedToolchainRoot as e:
|
|
355
|
+
raise TokenSubstitutionError(
|
|
356
|
+
"build.toolchain-root-unresolved",
|
|
357
|
+
f"plan field `{e.field}` names ${{TOOLCHAIN_ROOT}}, but {toolchain_advice}. "
|
|
358
|
+
f"tan refuses rather than substituting an empty path, which would silently build "
|
|
359
|
+
f"against the host root.",
|
|
360
|
+
) from e
|
|
361
|
+
except UnknownPlanPathMode as e:
|
|
362
|
+
raise TokenSubstitutionError("build.plan-invalid", str(e)) from e
|
|
363
|
+
|
|
364
|
+
if deferred_out is not None:
|
|
365
|
+
deferred_out.extend(policy.exempted)
|
|
366
|
+
|
|
367
|
+
# alp-sdk#866: a sysbuild slice's `-D<image>_EXTRA_CONF_FILE` prefix was
|
|
368
|
+
# derived from the project root the plan was EMITTED in; re-derive it from
|
|
369
|
+
# the substituted app dir so a relocated plan still configures its image
|
|
370
|
+
# (see `tan.core.sysbuild_image` for why this rewrites rather than refuses).
|
|
371
|
+
out = rescope_sysbuild_extra_conf(out)
|
|
372
|
+
|
|
373
|
+
slice_demotions = [
|
|
374
|
+
SliceDemotion(
|
|
375
|
+
slice_index=d.slice_index,
|
|
376
|
+
core_id=d.core_id,
|
|
377
|
+
reason=f"plan field `{d.field}` names ${{TOOLCHAIN_ROOT}}, but {toolchain_advice}",
|
|
378
|
+
)
|
|
379
|
+
for d in demoted
|
|
380
|
+
]
|
|
381
|
+
return out, slice_demotions
|