tan-cli 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
tan/commands/run_cmd.py
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""`tan run` -- build the project, then run it: execute the produced
|
|
3
|
+
`native_sim` binary for a host target, or flash a hardware target.
|
|
4
|
+
|
|
5
|
+
Port of `crates/tan-cli/src/commands/run/mod.rs`. A THIN ORCHESTRATOR: it
|
|
6
|
+
reuses `tan build`'s engine (`build_cmd._build`, the same engine
|
|
7
|
+
`build_cmd.build` calls) and, on a hardware target with `--flash`,
|
|
8
|
+
`tan flash`'s engine (`flash_cmd._run`) -- never re-deriving either. The pure
|
|
9
|
+
decision -- execute vs flash vs short-circuit -- lives in `tan.core.run`; this
|
|
10
|
+
file resolves paths, probes the filesystem for a runnable `native_sim`
|
|
11
|
+
binary, and spawns it.
|
|
12
|
+
|
|
13
|
+
**`run` is a DISTINCT command, not an alias for `build` or `flash`.**
|
|
14
|
+
`tan build --help` and `tan flash --help` list a disjoint option set from
|
|
15
|
+
`tan run --help` (verified against the released oracle binary: `run` has
|
|
16
|
+
`--flash`/`--core` and neither `--plan`/`--materialise`/`--native` from
|
|
17
|
+
`build` nor `--dry-run`/`--helper`/`--skip-missing-tools` from `flash`), and
|
|
18
|
+
`crates/tan-cli/src/cli.rs` declares `Run(RunArgs)` as its own `Commands`
|
|
19
|
+
variant dispatched to its own module -- never routed through `Command::Build`
|
|
20
|
+
or `Command::Flash`. What IS shared is the *engine*: `run` composes the same
|
|
21
|
+
two engines `build` and `flash` already own, exactly once each, rather than
|
|
22
|
+
re-implementing a third copy of either.
|
|
23
|
+
|
|
24
|
+
**The `native_sim_target`/`manifest_written` signal is now real.**
|
|
25
|
+
`tan.commands.build.execute.execute_slices` writes the post-build
|
|
26
|
+
`system-manifest.yaml` as a side effect of every dispatch (`tan build`'s own
|
|
27
|
+
CLI invocation gets it too, not just `run`'s), and records the two signals
|
|
28
|
+
`decide_run_action` needs via `execute.last_manifest_write()` -- a
|
|
29
|
+
same-process recorder, not a widened return value, because `execute_slices`
|
|
30
|
+
is reached only through `tan.commands.build_cmd._dispatch` / `_build`, both
|
|
31
|
+
out of THIS module's ownership for this change (a disjoint parallel-unit
|
|
32
|
+
split, not an architecture choice); see `execute.py`'s own module docstring
|
|
33
|
+
for the full reasoning and why reading the recorder here is still safe
|
|
34
|
+
against the R1 staleness defect (three attempts in the Rust oracle) that the
|
|
35
|
+
module doc below and `test_execute_native_arm_refuses_stale_exe_when_
|
|
36
|
+
manifest_write_unconfirmed` both pin. `_run` resets the recorder immediately
|
|
37
|
+
before calling the build engine (`execute.reset_last_manifest_write()`) so a
|
|
38
|
+
build that never reaches dispatch -- an early coded refusal, or a
|
|
39
|
+
monkeypatched `_build` stub in a test -- reads the honest default
|
|
40
|
+
`(False, None)` rather than a previous invocation's leftover.
|
|
41
|
+
|
|
42
|
+
**`--flash`/`--core` reach the flash engine once the build confirms a
|
|
43
|
+
hardware target.** `decide_run_action`'s own decision table still makes a
|
|
44
|
+
silent `BUILD_ONLY` no-op unreachable under `flash_requested=True` regardless
|
|
45
|
+
of `native_sim_target`/`manifest_written` (`tan.core.run`'s own tests pin
|
|
46
|
+
this) -- a hardware target without a confirmed manifest write still refuses
|
|
47
|
+
via `MANIFEST_STALE`, never guesses. `--core` is forwarded verbatim to the
|
|
48
|
+
native flash path's `--core` once the `FLASH` arm is actually reached
|
|
49
|
+
(`_flash_args_for` is unit-tested in isolation, the same way the Rust oracle
|
|
50
|
+
tests `flash_args_for`).
|
|
51
|
+
"""
|
|
52
|
+
from __future__ import annotations
|
|
53
|
+
|
|
54
|
+
import os
|
|
55
|
+
import subprocess
|
|
56
|
+
import sys
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
from typing import Any
|
|
59
|
+
|
|
60
|
+
import typer
|
|
61
|
+
|
|
62
|
+
from tan.commands import flash_cmd
|
|
63
|
+
from tan.commands.build import execute
|
|
64
|
+
from tan.commands.build_cmd import BuildError, _build
|
|
65
|
+
from tan.core.flash_plan import resolve_artefact_path
|
|
66
|
+
from tan.core.global_flags import accept_global_flags
|
|
67
|
+
from tan.core.plan_exec import normalize_path
|
|
68
|
+
from tan.core.run import RunAction, decide_run_action, native_sim_exe_beside, native_sim_slice
|
|
69
|
+
from tan.core.sdk_discovery import (
|
|
70
|
+
_abs_posix,
|
|
71
|
+
global_default_foreign_project_issue,
|
|
72
|
+
project_pin_issue,
|
|
73
|
+
resolve_sdk_root_ladder,
|
|
74
|
+
)
|
|
75
|
+
from tan.core.shapes import is_sdk_root, rejected_sdk_root_message
|
|
76
|
+
from tan.core.subprocess_env import spawn_env
|
|
77
|
+
from tan.core.system_manifest import SystemManifestError, parse_system_manifest
|
|
78
|
+
from tan.envelope import Envelope, Issue, Project, SdkInfo, emit
|
|
79
|
+
from tan.exit_codes import ExitCode
|
|
80
|
+
from tan.output_format import FORMAT_HELP, OutputFormat
|
|
81
|
+
|
|
82
|
+
#: `data.exec` skip reason JSON mode reports when a `native_sim` binary was
|
|
83
|
+
#: found but not executed (never spawned under `--format json`: a process
|
|
84
|
+
#: that never closes stdout would hang the one-envelope-per-invocation
|
|
85
|
+
#: contract). Verbatim from the Rust oracle's `NATIVE_SIM_JSON_SKIP_REASON`.
|
|
86
|
+
_NATIVE_SIM_JSON_SKIP_REASON = (
|
|
87
|
+
"native_sim exec skipped in --format json (run in text mode to execute)"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
_MANIFEST_STALE_MESSAGE = (
|
|
91
|
+
"run: --flash refused — this build's own outcome does not confirm it "
|
|
92
|
+
"is safe to flash (either the target could not be determined, or this "
|
|
93
|
+
"run's system-manifest.yaml write failed). Check the build output "
|
|
94
|
+
"above, then retry `tan run --flash`."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
_NATIVE_SIM_UNAVAILABLE_MESSAGE = (
|
|
98
|
+
"run: native_sim target, but this build produced no runnable "
|
|
99
|
+
"zephyr.exe (slice skipped/failed, or artefact missing) — see build "
|
|
100
|
+
"output above."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _find_native_sim_exe(base: str, sdk_root: str | None) -> str | None:
|
|
105
|
+
"""Locate the produced `native_sim` executable from the post-build
|
|
106
|
+
`system-manifest.yaml` under `<base>/build`. `None` when there's no
|
|
107
|
+
manifest, no native_sim slice, the slice didn't build `ok` THIS run, or
|
|
108
|
+
the binary isn't on disk -- so an unbuilt/absent binary and a
|
|
109
|
+
skipped/failed slice (which would otherwise resolve to a STALE
|
|
110
|
+
`zephyr.exe` left from a previous run) both fall through to a refusal
|
|
111
|
+
instead of silently executing old firmware. Mirrors
|
|
112
|
+
`run/mod.rs::find_native_sim_exe`, including its `<base>/build` anchor
|
|
113
|
+
(`base.join("build")`, run/mod.rs:232) -- `base` is the PROJECT root
|
|
114
|
+
(`_run`'s `build_root` argument), never the `build/` dir itself, so this
|
|
115
|
+
function does the same `os.path.join(base, "build")` the oracle does
|
|
116
|
+
rather than assuming its caller already appended it."""
|
|
117
|
+
build_root = os.path.join(base, "build")
|
|
118
|
+
try:
|
|
119
|
+
text = Path(build_root, "system-manifest.yaml").read_text(
|
|
120
|
+
encoding="utf-8", errors="replace"
|
|
121
|
+
)
|
|
122
|
+
except OSError:
|
|
123
|
+
return None
|
|
124
|
+
try:
|
|
125
|
+
manifest = parse_system_manifest(text)
|
|
126
|
+
except SystemManifestError:
|
|
127
|
+
return None
|
|
128
|
+
slice_ = native_sim_slice(manifest)
|
|
129
|
+
if slice_ is None or slice_.get("status") != "ok":
|
|
130
|
+
return None
|
|
131
|
+
artefact = slice_.get("output_artefact") or ""
|
|
132
|
+
if not artefact:
|
|
133
|
+
return None
|
|
134
|
+
elf_path = resolve_artefact_path(artefact, build_root, sdk_root, os.path.isfile)
|
|
135
|
+
exe_path = native_sim_exe_beside(elf_path)
|
|
136
|
+
return exe_path if os.path.isfile(exe_path) else None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _exec_native_sim(exe: str) -> tuple[bool, int | None]:
|
|
140
|
+
"""Run the `native_sim` binary with inherited stdio (it streams live), and
|
|
141
|
+
report `(ok, returncode)`. Only called in text mode -- see the module
|
|
142
|
+
doc."""
|
|
143
|
+
print(f"run: executing {exe}", file=sys.stderr)
|
|
144
|
+
try:
|
|
145
|
+
proc = subprocess.run([exe], env=spawn_env())
|
|
146
|
+
return proc.returncode == 0, proc.returncode
|
|
147
|
+
except OSError as err:
|
|
148
|
+
print(f"run: failed to launch {exe}: {err}", file=sys.stderr)
|
|
149
|
+
return False, None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _with_exec(
|
|
153
|
+
build_data: dict[str, Any] | None, exec_payload: dict[str, Any]
|
|
154
|
+
) -> dict[str, Any] | None:
|
|
155
|
+
"""Nest `exec_payload` under `data.exec`, mirroring the oracle's own guard
|
|
156
|
+
(`and_then(Value::as_object_mut)`, run/mod.rs:337-341 and :415): only when
|
|
157
|
+
`build_data` is already an object. `build_data: None` -- unreachable today
|
|
158
|
+
since a successful `_build` always returns a dict, but a shape the
|
|
159
|
+
envelope contract allows -- passes through unchanged rather than
|
|
160
|
+
synthesising `{"exec": ...}` the oracle would never emit for `data:
|
|
161
|
+
null`."""
|
|
162
|
+
if not isinstance(build_data, dict):
|
|
163
|
+
return build_data
|
|
164
|
+
return {**build_data, "exec": exec_payload}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _flash_args_for(build_root: str, core: str | None) -> dict[str, Any]:
|
|
168
|
+
"""The `flash_cmd._run` kwargs for `run --flash`, anchored on the resolved
|
|
169
|
+
project `build_root` (`_run`'s own project-root argument, matching the
|
|
170
|
+
Rust oracle's `base`) -- never `"."`. `flash_cmd._run` derives
|
|
171
|
+
`<app_path>/build` itself when `build_root_arg` is `None`
|
|
172
|
+
(`_abs_join(app_dir, "build")`, flash_cmd.py:752), exactly the way
|
|
173
|
+
`flash::run` derives `build_root` from `FlashArgs { build_root: None,
|
|
174
|
+
.. }` -- so passing the project root as `app_path` with
|
|
175
|
+
`build_root_arg: None` makes `run --flash` probe `<build_root>/build/
|
|
176
|
+
system-manifest.yaml`, the SAME file this run's own `tan build` step just
|
|
177
|
+
wrote, rather than `<build_root>/system-manifest.yaml` (nothing writes
|
|
178
|
+
that) or a bare `"."` resolved against a different `cwd` under
|
|
179
|
+
`--project <dir>`. Mirrors the Rust oracle's `flash_args_for`
|
|
180
|
+
(run/mod.rs:168-177)."""
|
|
181
|
+
return {"app_path": build_root, "build_root_arg": None, "core": core}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _execute_native_arm(
|
|
185
|
+
build_root: str,
|
|
186
|
+
sdk_root: str | None,
|
|
187
|
+
manifest_written: bool,
|
|
188
|
+
build_exit: ExitCode,
|
|
189
|
+
build_data: dict[str, Any] | None,
|
|
190
|
+
build_issues: list[Issue],
|
|
191
|
+
json_mode: bool,
|
|
192
|
+
) -> tuple[ExitCode, dict[str, Any] | None, list[Issue], list[str]]:
|
|
193
|
+
"""The `RunAction.EXECUTE_NATIVE` arm: run this build's `native_sim`
|
|
194
|
+
binary, or report that there isn't a trustworthy one. Mirrors the Rust
|
|
195
|
+
oracle's `execute_native_arm` (run/mod.rs:140-150), including its
|
|
196
|
+
`manifest_written` gate: `_find_native_sim_exe` trusts an on-disk
|
|
197
|
+
`zephyr.exe` from the manifest's `status: ok`, but that manifest is only
|
|
198
|
+
THIS run's when the post-build write actually succeeded. An unconfirmed
|
|
199
|
+
write (a Windows sharing violation, a failed emit) means a PREVIOUS run's
|
|
200
|
+
ok-status manifest and its `zephyr.exe` may still be on disk; executing
|
|
201
|
+
that would report success for an edit this run never compiled -- so the
|
|
202
|
+
probe is never even attempted when the write is unconfirmed."""
|
|
203
|
+
exe = _find_native_sim_exe(build_root, sdk_root) if manifest_written else None
|
|
204
|
+
if exe is None:
|
|
205
|
+
issues = [
|
|
206
|
+
*build_issues,
|
|
207
|
+
Issue("run.native-sim-unavailable", "error", _NATIVE_SIM_UNAVAILABLE_MESSAGE),
|
|
208
|
+
]
|
|
209
|
+
text = _build_text_lines(build_data, build_issues) + [_NATIVE_SIM_UNAVAILABLE_MESSAGE]
|
|
210
|
+
return ExitCode.RUNTIME_FAILURE, build_data, issues, text
|
|
211
|
+
if json_mode:
|
|
212
|
+
# Never spawned under `--format json` -- see the module doc.
|
|
213
|
+
data = _with_exec(
|
|
214
|
+
build_data,
|
|
215
|
+
{
|
|
216
|
+
"executed": False,
|
|
217
|
+
"reason": _NATIVE_SIM_JSON_SKIP_REASON,
|
|
218
|
+
"binary": exe,
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
return build_exit, data, build_issues, []
|
|
222
|
+
ok, rc = _exec_native_sim(exe)
|
|
223
|
+
exit_code = ExitCode.SUCCESS if ok else ExitCode.RUNTIME_FAILURE
|
|
224
|
+
data = _with_exec(build_data, {"binary": exe, "ok": ok, "rc": rc})
|
|
225
|
+
text = _build_text_lines(build_data, build_issues)
|
|
226
|
+
issues = list(build_issues)
|
|
227
|
+
if not ok:
|
|
228
|
+
message = (
|
|
229
|
+
f"{exe} exited with code {rc}"
|
|
230
|
+
if rc is not None
|
|
231
|
+
else f"{exe} did not run to completion"
|
|
232
|
+
)
|
|
233
|
+
issues.append(Issue("run.exec-failed", "error", message))
|
|
234
|
+
text.append(f"run: {message}")
|
|
235
|
+
return exit_code, data, issues, text
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _build_text_lines(data: dict[str, Any] | None, issues: list[Issue]) -> list[str]:
|
|
239
|
+
"""The same text-mode recap `build_cmd.build` prints, reused here so a
|
|
240
|
+
`tan run` that stops at the build step reads identically to `tan build`
|
|
241
|
+
would have."""
|
|
242
|
+
lines = [f"{issue.severity}: {issue.message}" for issue in issues]
|
|
243
|
+
for result in (data or {}).get("slices", []):
|
|
244
|
+
reason = f" — {result['reason']}" if "reason" in result else ""
|
|
245
|
+
lines.append(f"{result['status']}: {result['coreId']} [{result['backend']}]{reason}")
|
|
246
|
+
return lines
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _run(
|
|
250
|
+
*,
|
|
251
|
+
build_root: str,
|
|
252
|
+
sdk_root: str | None,
|
|
253
|
+
sdk_root_for_stamp: str | None,
|
|
254
|
+
board_yaml: str | None,
|
|
255
|
+
flash: bool,
|
|
256
|
+
core: str | None,
|
|
257
|
+
json_mode: bool,
|
|
258
|
+
# Defaulted, not required: several existing unit tests call `_run`
|
|
259
|
+
# directly with the pre-tan-cli#809 kwarg set, exercising paths this flag
|
|
260
|
+
# never reaches (`BUILD_ONLY`/`MANIFEST_STALE`/`EXECUTE_NATIVE`) or
|
|
261
|
+
# stubbing `flash_cmd._run` themselves. `False` preserves the exact
|
|
262
|
+
# pre-#809 behaviour for every one of them.
|
|
263
|
+
confirm: bool = False,
|
|
264
|
+
# tan-cli#1252: defaulted for the same reason `confirm` above is -- the
|
|
265
|
+
# tests that call `_run` directly predate it and reach paths it never
|
|
266
|
+
# touches.
|
|
267
|
+
atoc_unqueryable: bool = False,
|
|
268
|
+
# tan-cli#1267: defaulted for the same reason.
|
|
269
|
+
replace_atoc: bool = False,
|
|
270
|
+
) -> tuple[ExitCode, dict[str, Any] | None, list[Issue], list[str]]:
|
|
271
|
+
"""Everything between the resolved paths and the envelope. Returns
|
|
272
|
+
`(exit_code, data, issues, text_lines)`."""
|
|
273
|
+
# Reset BEFORE calling the build engine, not after: see the module doc
|
|
274
|
+
# and `execute.reset_last_manifest_write`'s own docstring for why an
|
|
275
|
+
# unreset recorder would leak a PREVIOUS invocation's signal into a build
|
|
276
|
+
# that never reaches dispatch (an early `BuildError`, or a monkeypatched
|
|
277
|
+
# `_build` stub in a test).
|
|
278
|
+
execute.reset_last_manifest_write()
|
|
279
|
+
try:
|
|
280
|
+
build_exit, build_data, build_issues = _build(
|
|
281
|
+
plan_from=None,
|
|
282
|
+
build_root=build_root,
|
|
283
|
+
sdk_root=sdk_root,
|
|
284
|
+
sdk_root_for_stamp=sdk_root_for_stamp,
|
|
285
|
+
board_yaml=board_yaml,
|
|
286
|
+
)
|
|
287
|
+
except BuildError as err:
|
|
288
|
+
# A CODED refusal (no SDK, no board.yaml, an unparsable plan, ...),
|
|
289
|
+
# not a tan bug -- `build_cmd.build` catches this the same way, and
|
|
290
|
+
# `run` must retag it identically: same issue code, same exit code,
|
|
291
|
+
# only `command` differs.
|
|
292
|
+
build_exit, build_data, build_issues = (
|
|
293
|
+
err.exit_code,
|
|
294
|
+
None,
|
|
295
|
+
[Issue(err.code, "error", err.message)],
|
|
296
|
+
)
|
|
297
|
+
build_ok = build_exit == ExitCode.SUCCESS
|
|
298
|
+
|
|
299
|
+
# The real signal from THIS build's own dispatch (see the module doc) --
|
|
300
|
+
# never a re-read of `system-manifest.yaml` off disk afterward.
|
|
301
|
+
manifest_written, native_sim_target = execute.last_manifest_write()
|
|
302
|
+
|
|
303
|
+
action = decide_run_action(build_ok, native_sim_target, flash, manifest_written)
|
|
304
|
+
# tan-cli#1267 review: the two ATOC flags only mean something to a write.
|
|
305
|
+
ignored = _ignored_flash_flags(action, replace_atoc, atoc_unqueryable)
|
|
306
|
+
|
|
307
|
+
if action in (RunAction.BUILD_FAILED, RunAction.BUILD_ONLY):
|
|
308
|
+
text = _build_text_lines(build_data, build_issues)
|
|
309
|
+
if action is RunAction.BUILD_ONLY and not json_mode:
|
|
310
|
+
text.append("run: built; pass --flash to program the board.")
|
|
311
|
+
return _with_ignored(ignored, (build_exit, build_data, build_issues, text))
|
|
312
|
+
|
|
313
|
+
if action is RunAction.MANIFEST_STALE:
|
|
314
|
+
issues = [*build_issues, Issue("run.manifest-stale", "error", _MANIFEST_STALE_MESSAGE)]
|
|
315
|
+
text = _build_text_lines(build_data, build_issues) + [_MANIFEST_STALE_MESSAGE]
|
|
316
|
+
return _with_ignored(ignored, (ExitCode.RUNTIME_FAILURE, build_data, issues, text))
|
|
317
|
+
|
|
318
|
+
if action is RunAction.EXECUTE_NATIVE:
|
|
319
|
+
return _with_ignored(ignored, _execute_native_arm(
|
|
320
|
+
build_root, sdk_root, manifest_written, build_exit, build_data, build_issues, json_mode
|
|
321
|
+
))
|
|
322
|
+
|
|
323
|
+
# RunAction.FLASH: hardware target, `--flash`, this run's manifest write
|
|
324
|
+
# confirmed -- reuse the native flash path, targeting the SAME project
|
|
325
|
+
# `build_root` this run just built (not a bare "." under a different cwd).
|
|
326
|
+
flash_exit, flash_data, flash_issues, flash_text, _flash_sdk = flash_cmd._run(
|
|
327
|
+
**_flash_args_for(build_root, core),
|
|
328
|
+
sdk_root_arg=sdk_root,
|
|
329
|
+
board_yaml=board_yaml,
|
|
330
|
+
helper=None,
|
|
331
|
+
dry_run=False,
|
|
332
|
+
skip_missing_tools=False,
|
|
333
|
+
capture=json_mode,
|
|
334
|
+
cwd=build_root,
|
|
335
|
+
# tan-cli#809: `run` had no way to arm the flash confirm gate, so a
|
|
336
|
+
# hardware target (e.g. E1M-AEN801's Flow D) always landed every
|
|
337
|
+
# slice as `planned` and exited 1 with `flash.nothing-flashed` --
|
|
338
|
+
# whose own remedy names `--confirm`, a flag `run` rejected. The gate
|
|
339
|
+
# itself stays deliberate (see flash_plan.py); this only gives `run`
|
|
340
|
+
# the same opt-in `tan flash --confirm` already has.
|
|
341
|
+
confirm_flag=confirm,
|
|
342
|
+
# tan-cli#1252: forwarded for a sharper reason than `--confirm` above.
|
|
343
|
+
# `dry_run=False` is HARDCODED on this call, so `run --flash --confirm`
|
|
344
|
+
# on an AEN Flow D slice can never take a preview path -- it goes
|
|
345
|
+
# straight to the whole-ATOC guard. Without this line that command
|
|
346
|
+
# would hit a refusal naming a flag `run` does not accept.
|
|
347
|
+
atoc_unqueryable=atoc_unqueryable,
|
|
348
|
+
# tan-cli#1267: same reasoning -- with `dry_run=False` hardcoded, a
|
|
349
|
+
# Flow A slice whose runner refuses would name a flag `run` lacked.
|
|
350
|
+
replace_atoc=replace_atoc,
|
|
351
|
+
)
|
|
352
|
+
# tan-cli#1482: carry the build step's own issues + recap onto this arm as
|
|
353
|
+
# every other arm does -- a partial build (a skipped slice, a manifest-write
|
|
354
|
+
# warning) still counts as build_ok, and dropping its warnings here left a
|
|
355
|
+
# flash result with no sign that a core was not rebuilt.
|
|
356
|
+
flash_issues = [*build_issues, *flash_issues]
|
|
357
|
+
flash_text = _build_text_lines(build_data, build_issues) + list(flash_text)
|
|
358
|
+
return flash_exit, flash_data, flash_issues, flash_text
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _ignored_flash_flags(
|
|
362
|
+
action: RunAction, replace_atoc: bool, atoc_unqueryable: bool
|
|
363
|
+
) -> str | None:
|
|
364
|
+
"""The warning for `--replace-atoc`/`--atoc-unqueryable` on a `run` that
|
|
365
|
+
will not flash, or `None`. Both only act on a write; dropping them with
|
|
366
|
+
no word would let an operator believe an override or acknowledgement
|
|
367
|
+
took effect (tan-cli#1267 review)."""
|
|
368
|
+
flags = [
|
|
369
|
+
flag
|
|
370
|
+
for flag, given in (("--replace-atoc", replace_atoc), ("--atoc-unqueryable", atoc_unqueryable))
|
|
371
|
+
if given
|
|
372
|
+
]
|
|
373
|
+
if action is RunAction.FLASH or not flags:
|
|
374
|
+
return None
|
|
375
|
+
return (
|
|
376
|
+
f"run: {' and '.join(flags)} had no effect -- this run did not flash "
|
|
377
|
+
"anything (they only act on a write, which needs --flash on a hardware "
|
|
378
|
+
"target whose build succeeded)."
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _with_ignored(ignored: str | None, result):
|
|
383
|
+
"""`result` (`_run`'s 4-tuple) with the ignored-flag warning appended
|
|
384
|
+
to its issues and text, when there is one."""
|
|
385
|
+
if ignored is None:
|
|
386
|
+
return result
|
|
387
|
+
exit_code, data, issues, text = result
|
|
388
|
+
return (
|
|
389
|
+
exit_code,
|
|
390
|
+
data,
|
|
391
|
+
[*issues, Issue("run.flash-flags-ignored", "warning", ignored)],
|
|
392
|
+
[*text, ignored],
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def run(
|
|
397
|
+
flash: bool = typer.Option(
|
|
398
|
+
False,
|
|
399
|
+
"--flash",
|
|
400
|
+
help="Program the board after building (hardware targets only). Required "
|
|
401
|
+
"opt-in: without it, `run` on a hardware project builds and reports but "
|
|
402
|
+
"never flashes. Arming the write also needs --confirm (or "
|
|
403
|
+
"ALP_FLASH_FORCE=1, or flash_args.confirm: true in the manifest); "
|
|
404
|
+
"without it every slice comes back `planned` and the run exits "
|
|
405
|
+
"non-zero. An Alif Flow D slice (alif_mram_jlink) has a SECOND gate and "
|
|
406
|
+
"can instead come back `failed` once armed, until --atoc-unqueryable "
|
|
407
|
+
"acknowledges that the write replaces the entire ATOC (tan-cli#1252). "
|
|
408
|
+
"Ignored for a native_sim/host target, which always runs "
|
|
409
|
+
"the produced binary and never flashes.",
|
|
410
|
+
),
|
|
411
|
+
core: str = typer.Option(
|
|
412
|
+
None,
|
|
413
|
+
"--core",
|
|
414
|
+
metavar="CORE_ID",
|
|
415
|
+
help="With --flash, flash only the slice with this core_id (forwarded "
|
|
416
|
+
"verbatim to the native flash path's --core).",
|
|
417
|
+
),
|
|
418
|
+
confirm: bool = typer.Option(
|
|
419
|
+
False,
|
|
420
|
+
"--confirm",
|
|
421
|
+
help="With --flash, arm the confirm gate and actually write the device. "
|
|
422
|
+
"Without it (and without ALP_FLASH_FORCE=1 or flash_args.confirm: true "
|
|
423
|
+
"in the manifest) a hardware target is only previewed -- every slice "
|
|
424
|
+
"comes back `planned`, nothing reaches the device, and the run exits "
|
|
425
|
+
"non-zero (tan-cli#809). On an Alif Flow D slice (alif_mram_jlink) this "
|
|
426
|
+
"arms the write but does not acknowledge that it replaces the entire "
|
|
427
|
+
"ATOC: such a slice additionally needs --atoc-unqueryable and is refused "
|
|
428
|
+
"(`failed`, not `planned`) without it (tan-cli#1252). Ignored without "
|
|
429
|
+
"--flash.",
|
|
430
|
+
),
|
|
431
|
+
atoc_unqueryable: bool = typer.Option(
|
|
432
|
+
False,
|
|
433
|
+
"--atoc-unqueryable",
|
|
434
|
+
# tan-cli#1252 review: `run` deliberately does NOT offer the manifest
|
|
435
|
+
# spelling as an alternative here, unlike `tan flash`'s own help. Every
|
|
436
|
+
# `run --flash` REGENERATES build/system-manifest.yaml before flashing
|
|
437
|
+
# (RunAction.FLASH requires THIS run's own manifest write), and
|
|
438
|
+
# `planner/orchestrator._slice_flash_recipe` composes `flash_args` from
|
|
439
|
+
# a fresh dict carrying only the keys it knows -- so an operator-added
|
|
440
|
+
# `atoc_unqueryable` is destroyed by the same command that would then
|
|
441
|
+
# demand it. On `run` the flag is the only spelling that survives.
|
|
442
|
+
help="With --flash on an Alif Flow D slice (alif_mram_jlink), acknowledge "
|
|
443
|
+
"that the write REPLACES the entire ATOC: Flow D cannot enumerate what is "
|
|
444
|
+
"resident, so any boot entry the new ATOC does not name is silently delisted "
|
|
445
|
+
"while the SES still reports \"[SES] ATOC ok\". Without it such a slice "
|
|
446
|
+
"refuses. Use the flag here rather than flash_args.atoc_unqueryable: true -- "
|
|
447
|
+
"that manifest spelling works for `tan flash`, which reads the manifest as "
|
|
448
|
+
"it stands, but every `run --flash` regenerates build/system-manifest.yaml "
|
|
449
|
+
"first and a hand-added key does not survive it. This acknowledges the "
|
|
450
|
+
"replacement only -- arming the write itself still needs --confirm "
|
|
451
|
+
"(tan-cli#1252).",
|
|
452
|
+
),
|
|
453
|
+
replace_atoc: bool = typer.Option(
|
|
454
|
+
False,
|
|
455
|
+
"--replace-atoc",
|
|
456
|
+
help="With --flash on a zephyr_west_flash slice whose west runner is "
|
|
457
|
+
"alif_flash (Flow A, over the SE-UART), override that runner's pre-burn "
|
|
458
|
+
"ATOC guard, which otherwise refuses a write that would silently delist a "
|
|
459
|
+
"resident entry it does not name, or whose read of the resident ATOC could "
|
|
460
|
+
"not be verified (tan-cli#1267). Reaches at most one write per run (narrow "
|
|
461
|
+
"with --core). Flag only: there is no manifest spelling. "
|
|
462
|
+
"Not the same as --atoc-unqueryable (Flow D), and never accepted in its "
|
|
463
|
+
"place; on any other slice tan warns instead of passing it.",
|
|
464
|
+
),
|
|
465
|
+
project: str = typer.Option(
|
|
466
|
+
None, "--project", metavar="PATH", help="Project root (defaults to '.')."
|
|
467
|
+
),
|
|
468
|
+
board_yaml: str = typer.Option(
|
|
469
|
+
None, "--board-yaml", metavar="PATH", help="Explicit board.yaml path."
|
|
470
|
+
),
|
|
471
|
+
sdk_root: str = typer.Option(
|
|
472
|
+
None, "--sdk-root", metavar="PATH", help="alp-sdk checkout root."
|
|
473
|
+
),
|
|
474
|
+
output_format: OutputFormat = typer.Option(OutputFormat.TEXT, "--format", help=FORMAT_HELP),
|
|
475
|
+
) -> None:
|
|
476
|
+
"""Build the project, then run it: execute the produced native_sim binary
|
|
477
|
+
for a host target, or (with --flash) program a hardware target."""
|
|
478
|
+
json_mode = output_format == "json"
|
|
479
|
+
|
|
480
|
+
# tan-cli#1482: the resolution prologue lives INSIDE the catch-all (as in
|
|
481
|
+
# `build_cmd.build`, tan-cli#488 defect 8) so a deleted cwd or a resolver
|
|
482
|
+
# raise becomes a `run.internal-failure` envelope, not a raw traceback.
|
|
483
|
+
project_obj = Project(root=None, board_yaml=None)
|
|
484
|
+
sdk: SdkInfo | None = None
|
|
485
|
+
sdk_tier = "none"
|
|
486
|
+
sdk_broken_pin: str | None = None
|
|
487
|
+
sdk_foreign_default: str | None = None
|
|
488
|
+
try:
|
|
489
|
+
# Same resolution `build_cmd.build` performs (`run` builds via the same
|
|
490
|
+
# engine, so it must anchor on the same project) -- see that function for
|
|
491
|
+
# the reasoning behind each step.
|
|
492
|
+
cwd = Path.cwd()
|
|
493
|
+
workspace_root = cwd if project is None else Path(os.path.join(str(cwd), project))
|
|
494
|
+
if board_yaml is not None and not os.path.isabs(board_yaml):
|
|
495
|
+
board_yaml = os.path.join(str(workspace_root), board_yaml)
|
|
496
|
+
if board_yaml is None and (workspace_root / "board.yaml").is_file():
|
|
497
|
+
board_yaml = str(workspace_root / "board.yaml")
|
|
498
|
+
build_root = str(Path(board_yaml).parent) if board_yaml else str(workspace_root)
|
|
499
|
+
build_root = _abs_posix(build_root)
|
|
500
|
+
if board_yaml is not None:
|
|
501
|
+
board_yaml = _abs_posix(board_yaml)
|
|
502
|
+
|
|
503
|
+
# Same ladder `build_cmd.build` resolves -- `--sdk-root` > `.alp/sdk-path`
|
|
504
|
+
# project pin > the machine-global default (`~/.alp/sdk-default`) > the
|
|
505
|
+
# positional walk (`resolve_sdk_root_ladder`); `run` builds via the same
|
|
506
|
+
# engine, so it must agree with `build` on which checkout that is. No
|
|
507
|
+
# `ALP_SDK_ROOT` tier (tried and reverted -- see `resolve_sdk_root_ladder`'s
|
|
508
|
+
# own docstring).
|
|
509
|
+
sdk_resolution = resolve_sdk_root_ladder(sdk_root, workspace_root)
|
|
510
|
+
resolved_sdk_root = sdk_resolution.path
|
|
511
|
+
sdk_tier = sdk_resolution.tier
|
|
512
|
+
sdk_broken_pin = sdk_resolution.broken_project_pin
|
|
513
|
+
sdk_foreign_default = sdk_resolution.foreign_global_default_for
|
|
514
|
+
# tan-cli#257/#258 -- the exact guard `build_cmd.build` applies, for the
|
|
515
|
+
# exact same reason: this line was a VERBATIM COPY of the one that carried
|
|
516
|
+
# the defect, so fixing only `build` would have left its twin here.
|
|
517
|
+
# `resolve_sdk_root_ladder` returns an explicit `--sdk-root` UNVALIDATED
|
|
518
|
+
# (I-31 terminal-for-REPORTING, matching the oracle's
|
|
519
|
+
# `resolve_sdk_tiered`), which is correct for a caller that only reports
|
|
520
|
+
# the tier and wrong for one that ACTS on the path: a bogus `--sdk-root`
|
|
521
|
+
# sailed through as `sdk.sourceTier: "sdkRootFlag"` and was then refused
|
|
522
|
+
# for the NEXT missing thing, telling the customer their project is broken
|
|
523
|
+
# when the flag they had just typed is what was wrong.
|
|
524
|
+
#
|
|
525
|
+
# Guarded HERE rather than inside the shared ladder because every other
|
|
526
|
+
# caller depends on it staying unvalidated -- the same placement
|
|
527
|
+
# `build_cmd`, `clean_cmd.sdk_root_resolves` and `flash_cmd._resolve_sdk`
|
|
528
|
+
# already chose. An unresolvable explicit root is treated as no root at
|
|
529
|
+
# all, so the refusal downstream is the honest "no alp-sdk checkout found"
|
|
530
|
+
# and no `sdk` key is emitted, matching the oracle.
|
|
531
|
+
if sdk_tier == "sdkRootFlag" and not is_sdk_root(resolved_sdk_root):
|
|
532
|
+
resolved_sdk_root = None
|
|
533
|
+
# tan-cli#1463: an EXPLICIT `--sdk-root` that did not resolve is a coded
|
|
534
|
+
# refusal, not a silent "no root". Without it the typo fell through to the
|
|
535
|
+
# build engine and surfaced as an unrelated downstream failure. No
|
|
536
|
+
# `--sdk-root` + no checkout found keeps the engine's own behaviour (the
|
|
537
|
+
# engine owns that refusal), so only the explicit-flag case is new here.
|
|
538
|
+
sdk_refusal = None
|
|
539
|
+
if sdk_root and sdk_root.strip() and resolved_sdk_root is None:
|
|
540
|
+
refusal_msg = rejected_sdk_root_message(sdk_root, "Nothing was built or run.")
|
|
541
|
+
sdk_refusal = (
|
|
542
|
+
ExitCode.RUNTIME_FAILURE,
|
|
543
|
+
None,
|
|
544
|
+
[Issue("run.sdk-root-unresolved", "error", refusal_msg)],
|
|
545
|
+
[f"run: {refusal_msg}"],
|
|
546
|
+
)
|
|
547
|
+
sdk_root = str(resolved_sdk_root) if resolved_sdk_root is not None else None
|
|
548
|
+
sdk = SdkInfo(sdk_root, sdk_tier) if sdk_root is not None else None
|
|
549
|
+
# Same normalized, workspace-root-anchored stamp identity `build_cmd.build`
|
|
550
|
+
# computes (tan-cli#163) -- `_build` now requires it (the sdk-switch-
|
|
551
|
+
# pristine guard's stamp comparison, threaded through from `execute_slices`
|
|
552
|
+
# rather than self-discovered), and `run` reuses the same engine so it must
|
|
553
|
+
# resolve it the same way, not just `sdk_root` itself.
|
|
554
|
+
sdk_root_for_stamp = (
|
|
555
|
+
str(normalize_path(workspace_root / sdk_root)) if sdk_root is not None else None
|
|
556
|
+
)
|
|
557
|
+
# tan-cli#236: `boardYaml` reported only when the file really exists -- an
|
|
558
|
+
# explicit `--board-yaml` skips the `is_file()` discovery guard above.
|
|
559
|
+
project_obj = Project.resolved(build_root, board_yaml)
|
|
560
|
+
|
|
561
|
+
if sdk_refusal is not None:
|
|
562
|
+
exit_code, data, issues, text_lines = sdk_refusal
|
|
563
|
+
else:
|
|
564
|
+
exit_code, data, issues, text_lines = _run(
|
|
565
|
+
build_root=build_root,
|
|
566
|
+
sdk_root=sdk_root,
|
|
567
|
+
sdk_root_for_stamp=sdk_root_for_stamp,
|
|
568
|
+
board_yaml=board_yaml,
|
|
569
|
+
flash=flash,
|
|
570
|
+
core=core,
|
|
571
|
+
confirm=confirm,
|
|
572
|
+
atoc_unqueryable=atoc_unqueryable,
|
|
573
|
+
replace_atoc=replace_atoc,
|
|
574
|
+
json_mode=json_mode,
|
|
575
|
+
)
|
|
576
|
+
except Exception as err: # noqa: BLE001 -- see build_cmd.build's identical guard
|
|
577
|
+
exit_code = ExitCode.INTERNAL_FAILURE
|
|
578
|
+
data = None
|
|
579
|
+
issues = [Issue("run.internal-failure", "error", f"{type(err).__name__}: {err}")]
|
|
580
|
+
text_lines = [f"run: internal failure: {type(err).__name__}: {err}"]
|
|
581
|
+
|
|
582
|
+
# tan-cli#263 review: `run` builds via the same engine as `build` and must
|
|
583
|
+
# disclose the same silently-missed project pin.
|
|
584
|
+
pin_issue = project_pin_issue(sdk_broken_pin, sdk_tier)
|
|
585
|
+
if pin_issue is not None:
|
|
586
|
+
issues = [pin_issue, *issues]
|
|
587
|
+
# tan-cli#464: same argument, one tier down -- `run` must disclose a
|
|
588
|
+
# `globalDefault` answer a DIFFERENT project's bootstrap relocation
|
|
589
|
+
# actually decided, the same as `build` now does.
|
|
590
|
+
foreign_issue = global_default_foreign_project_issue(sdk_foreign_default)
|
|
591
|
+
if foreign_issue is not None:
|
|
592
|
+
issues = [foreign_issue, *issues]
|
|
593
|
+
# tan-cli#497 defect 4: both of the above went into `issues` and NOWHERE
|
|
594
|
+
# else, while text mode prints `text_lines` -- so the warnings reached
|
|
595
|
+
# `--format json` and were silently discarded on the DEFAULT path. `tan
|
|
596
|
+
# build` in the identical workspace printed the pin line and `tan run` did
|
|
597
|
+
# not, which is precisely the silence tan-cli#263 exists to remove.
|
|
598
|
+
# Prefixed `{severity}: {message}`, the shape `_build_text_lines` already
|
|
599
|
+
# uses for a build issue, and taken from the head of `issues` so the two
|
|
600
|
+
# channels can never disagree about which warnings applied or in what
|
|
601
|
+
# order.
|
|
602
|
+
warning_count = (pin_issue is not None) + (foreign_issue is not None)
|
|
603
|
+
if warning_count:
|
|
604
|
+
text_lines = [
|
|
605
|
+
f"{issue.severity}: {issue.message}" for issue in issues[:warning_count]
|
|
606
|
+
] + text_lines
|
|
607
|
+
|
|
608
|
+
# Built ONCE, for both formats: `Envelope.__init__` appends the
|
|
609
|
+
# tan-cli#407 `sdk.discovery-divergent` warning at the shared seam
|
|
610
|
+
# (`_with_sdk_divergence`), beyond the pin/foreign pair this function
|
|
611
|
+
# already prepends by hand above (tan-cli#464). Text mode used to render
|
|
612
|
+
# `text_lines`, built before any `Envelope` existed, so a seam-appended
|
|
613
|
+
# divergence issue reached `--format json` and was silent on the default
|
|
614
|
+
# channel (tan-cli#799) -- diffed against the pre-envelope `issues` list
|
|
615
|
+
# (by value: `Issue` is a frozen dataclass) so only what the seam ADDED
|
|
616
|
+
# is rendered, never a duplicate of the pin/foreign pair already in
|
|
617
|
+
# `text_lines`.
|
|
618
|
+
envelope = Envelope("run", project_obj, data, issues, exit_code, sdk=sdk)
|
|
619
|
+
seam_extra = [issue for issue in envelope.issues if issue not in issues]
|
|
620
|
+
if seam_extra:
|
|
621
|
+
text_lines = [
|
|
622
|
+
f"{issue.severity}: {issue.message}" for issue in seam_extra
|
|
623
|
+
] + text_lines
|
|
624
|
+
|
|
625
|
+
if json_mode:
|
|
626
|
+
emit(envelope)
|
|
627
|
+
else:
|
|
628
|
+
for line in text_lines:
|
|
629
|
+
print(line, file=sys.stderr)
|
|
630
|
+
raise typer.Exit(int(exit_code))
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
# tan-cli#261: adds the seven oracle `GlobalArgs` flags this command was
|
|
634
|
+
# still missing (`--all`/`--ci`/`--no-color`/`--non-interactive`/`--quiet`/
|
|
635
|
+
# `--target`/`--verbose`) on top of `--board-yaml`, already declared and read
|
|
636
|
+
# above; see `tan.core.global_flags`.
|
|
637
|
+
run = accept_global_flags(run)
|