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.
Files changed (330) hide show
  1. tan/__init__.py +0 -0
  2. tan/__main__.py +120 -0
  3. tan/cli.py +939 -0
  4. tan/command_table.py +265 -0
  5. tan/commands/__init__.py +8 -0
  6. tan/commands/bootstrap_cmd.py +4856 -0
  7. tan/commands/bootstrap_patches.py +164 -0
  8. tan/commands/bootstrap_toolchain_fetch.py +155 -0
  9. tan/commands/bootstrap_toolchain_pin.py +36 -0
  10. tan/commands/build/__init__.py +1 -0
  11. tan/commands/build/configure_inputs.py +277 -0
  12. tan/commands/build/execute.py +1852 -0
  13. tan/commands/build/host_python.py +59 -0
  14. tan/commands/build/link_stale.py +78 -0
  15. tan/commands/build/manifest.py +428 -0
  16. tan/commands/build/materialise.py +81 -0
  17. tan/commands/build/token_substitution.py +381 -0
  18. tan/commands/build/toolchain.py +482 -0
  19. tan/commands/build/user_defines_wipe.py +115 -0
  20. tan/commands/build/workspace_patches.py +65 -0
  21. tan/commands/build_cmd.py +2236 -0
  22. tan/commands/build_output.py +331 -0
  23. tan/commands/clean_cmd.py +1183 -0
  24. tan/commands/completion_cmd.py +786 -0
  25. tan/commands/debug_config_cmd.py +2425 -0
  26. tan/commands/diff_cmd.py +940 -0
  27. tan/commands/doctor_cmd.py +4767 -0
  28. tan/commands/examples_cmd.py +644 -0
  29. tan/commands/explain_cmd.py +1319 -0
  30. tan/commands/faultdecode_cmd.py +931 -0
  31. tan/commands/flash_atoc_guard.py +240 -0
  32. tan/commands/flash_cmd.py +5731 -0
  33. tan/commands/flash_mram_guard.py +150 -0
  34. tan/commands/flash_ram.py +578 -0
  35. tan/commands/flash_raw.py +583 -0
  36. tan/commands/generate_cmd.py +1456 -0
  37. tan/commands/image_cmd.py +753 -0
  38. tan/commands/init_cmd.py +1740 -0
  39. tan/commands/inspect_cmd.py +377 -0
  40. tan/commands/kconfig_cmd.py +734 -0
  41. tan/commands/model_cmd.py +1514 -0
  42. tan/commands/model_device_cmd.py +351 -0
  43. tan/commands/model_device_live.py +234 -0
  44. tan/commands/model_host_cmd.py +430 -0
  45. tan/commands/model_zoo_cmd.py +363 -0
  46. tan/commands/monitor_actions.py +125 -0
  47. tan/commands/monitor_cmd.py +626 -0
  48. tan/commands/monitor_session.py +505 -0
  49. tan/commands/new_som_cmd.py +1522 -0
  50. tan/commands/pinmux_cmd.py +568 -0
  51. tan/commands/presets_cmd.py +1123 -0
  52. tan/commands/probe_cmd.py +421 -0
  53. tan/commands/reset_cmd.py +354 -0
  54. tan/commands/reset_confirm.py +168 -0
  55. tan/commands/run_cmd.py +637 -0
  56. tan/commands/scaffold_cmd.py +490 -0
  57. tan/commands/sdk_cmd.py +1723 -0
  58. tan/commands/size_cmd.py +1020 -0
  59. tan/commands/support_bundle_cmd.py +1073 -0
  60. tan/commands/trace_cmd.py +341 -0
  61. tan/commands/validate_cmd.py +2009 -0
  62. tan/commands/west_forward_cmd.py +736 -0
  63. tan/commands/workspace_patch_check.py +296 -0
  64. tan/core/__init__.py +1 -0
  65. tan/core/alp_conf_pregen.py +100 -0
  66. tan/core/artifact_provenance.py +240 -0
  67. tan/core/atoc_guard.py +276 -0
  68. tan/core/atoc_guard_messages.py +232 -0
  69. tan/core/atoc_replacement.py +118 -0
  70. tan/core/atomic_write.py +178 -0
  71. tan/core/board_context.py +351 -0
  72. tan/core/board_diagnostic.py +111 -0
  73. tan/core/board_files.py +221 -0
  74. tan/core/board_validator.py +542 -0
  75. tan/core/board_validator_compat.py +196 -0
  76. tan/core/board_validator_run.py +129 -0
  77. tan/core/board_validator_schema.py +186 -0
  78. tan/core/board_validator_skew.py +46 -0
  79. tan/core/board_yaml_edit.py +295 -0
  80. tan/core/board_yaml_pos.py +118 -0
  81. tan/core/bootstrap.py +2572 -0
  82. tan/core/build_plan.py +625 -0
  83. tan/core/consent.py +104 -0
  84. tan/core/console_filter.py +245 -0
  85. tan/core/debug_launch.py +1803 -0
  86. tan/core/devicetree_lint.py +168 -0
  87. tan/core/dir_removal.py +229 -0
  88. tan/core/doctor_git.py +172 -0
  89. tan/core/doctor_libraries.py +253 -0
  90. tan/core/doctor_render.py +185 -0
  91. tan/core/doctor_scope.py +66 -0
  92. tan/core/doctor_setools.py +237 -0
  93. tan/core/doctor_stale.py +180 -0
  94. tan/core/document_guards.py +723 -0
  95. tan/core/dp_id.py +97 -0
  96. tan/core/elf_load.py +63 -0
  97. tan/core/error_catalog.py +171 -0
  98. tan/core/example_catalog.py +499 -0
  99. tan/core/example_facets.py +151 -0
  100. tan/core/faultdecode.py +521 -0
  101. tan/core/flash_plan.py +2501 -0
  102. tan/core/flow_d_report.py +381 -0
  103. tan/core/flow_d_uptime.py +164 -0
  104. tan/core/fs_confine.py +130 -0
  105. tan/core/global_flags.py +230 -0
  106. tan/core/host_python.py +210 -0
  107. tan/core/image_bundle.py +247 -0
  108. tan/core/inert.py +147 -0
  109. tan/core/jlink_binary.py +128 -0
  110. tan/core/jlink_probe.py +481 -0
  111. tan/core/jsonc_splice.py +404 -0
  112. tan/core/launch_provenance.py +257 -0
  113. tan/core/link_refusal.py +76 -0
  114. tan/core/metadata_schema.py +376 -0
  115. tan/core/model_check.py +227 -0
  116. tan/core/model_device.py +449 -0
  117. tan/core/model_doctor.py +198 -0
  118. tan/core/model_host.py +48 -0
  119. tan/core/model_list.py +112 -0
  120. tan/core/model_zoo.py +353 -0
  121. tan/core/module_template.py +340 -0
  122. tan/core/mram_link.py +87 -0
  123. tan/core/new_som_ospi.py +94 -0
  124. tan/core/os_class.py +159 -0
  125. tan/core/patch_paths.py +87 -0
  126. tan/core/pending.py +40 -0
  127. tan/core/plain_zephyr_plan.py +138 -0
  128. tan/core/plan_exec.py +373 -0
  129. tan/core/plan_tokens.py +600 -0
  130. tan/core/probe.py +155 -0
  131. tan/core/probe_plan.py +237 -0
  132. tan/core/proxy.py +168 -0
  133. tan/core/publish.py +31 -0
  134. tan/core/python_floor.py +178 -0
  135. tan/core/ram_run.py +694 -0
  136. tan/core/ram_watch.py +231 -0
  137. tan/core/raw_write.py +160 -0
  138. tan/core/reset_plan.py +56 -0
  139. tan/core/run.py +161 -0
  140. tan/core/scaffold.py +2203 -0
  141. tan/core/scaffold_selftest_identity.py +225 -0
  142. tan/core/sdk_default_registry.py +565 -0
  143. tan/core/sdk_discovery.py +996 -0
  144. tan/core/sdk_removal.py +465 -0
  145. tan/core/serial_actions.py +175 -0
  146. tan/core/serial_capture.py +185 -0
  147. tan/core/serial_url.py +45 -0
  148. tan/core/setools.py +608 -0
  149. tan/core/setools_scratch.py +243 -0
  150. tan/core/shapes.py +194 -0
  151. tan/core/size.py +610 -0
  152. tan/core/som_buildability.py +204 -0
  153. tan/core/som_schema_version.py +62 -0
  154. tan/core/subprocess_env.py +201 -0
  155. tan/core/sysbuild_image.py +94 -0
  156. tan/core/system_manifest.py +827 -0
  157. tan/core/text_layout.py +73 -0
  158. tan/core/timestamp.py +182 -0
  159. tan/core/tool_lookup.py +383 -0
  160. tan/core/toolchain_pin.py +117 -0
  161. tan/core/toolchain_provision.py +1224 -0
  162. tan/core/trusted_path.py +134 -0
  163. tan/core/uboot_breakin.py +154 -0
  164. tan/core/uri_reference.py +415 -0
  165. tan/core/user_defines.py +152 -0
  166. tan/core/venv.py +332 -0
  167. tan/core/west_patches.py +143 -0
  168. tan/core/west_workspace_refusal.py +159 -0
  169. tan/core/zephyr_env.py +124 -0
  170. tan/env.py +185 -0
  171. tan/envelope.py +647 -0
  172. tan/exit_codes.py +12 -0
  173. tan/lazy_typer.py +185 -0
  174. tan/model/__init__.py +2 -0
  175. tan/model/_gen_fixture.py +132 -0
  176. tan/model/adapters/__init__.py +116 -0
  177. tan/model/adapters/cpu.py +26 -0
  178. tan/model/adapters/deepx.py +94 -0
  179. tan/model/adapters/drpai.py +242 -0
  180. tan/model/adapters/ethos_u.py +951 -0
  181. tan/model/adapters/executorch.py +55 -0
  182. tan/model/analyze.py +694 -0
  183. tan/model/build.py +304 -0
  184. tan/model/check.py +682 -0
  185. tan/model/manifest.py +625 -0
  186. tan/model/measure.py +216 -0
  187. tan/model/npu_ops_validate.py +130 -0
  188. tan/model/package.py +125 -0
  189. tan/model/perf.py +635 -0
  190. tan/model/perf_apply.py +721 -0
  191. tan/model/prep.py +193 -0
  192. tan/model/sram_fit.py +502 -0
  193. tan/model/targets.py +374 -0
  194. tan/model/tensorio.py +287 -0
  195. tan/net.py +73 -0
  196. tan/output_format.py +213 -0
  197. tan/planner/__init__.py +219 -0
  198. tan/planner/aperture.py +202 -0
  199. tan/planner/buildplan.py +1092 -0
  200. tan/planner/camera_owner.py +192 -0
  201. tan/planner/cameras.py +132 -0
  202. tan/planner/carveout.py +755 -0
  203. tan/planner/cli.py +274 -0
  204. tan/planner/headers.py +315 -0
  205. tan/planner/kconfig.py +2435 -0
  206. tan/planner/kconfig_symbols.py +473 -0
  207. tan/planner/libraries.py +677 -0
  208. tan/planner/link_target.py +237 -0
  209. tan/planner/linux_ownership.py +216 -0
  210. tan/planner/loader.py +1525 -0
  211. tan/planner/manifest.py +116 -0
  212. tan/planner/memory.py +161 -0
  213. tan/planner/memregion.py +56 -0
  214. tan/planner/models.py +533 -0
  215. tan/planner/orchestrator.py +874 -0
  216. tan/planner/ownership.py +203 -0
  217. tan/planner/partition.py +1160 -0
  218. tan/planner/paths.py +32 -0
  219. tan/planner/plain_slice.py +139 -0
  220. tan/planner/project_emit/__init__.py +60 -0
  221. tan/planner/project_emit/bom_netlist.py +375 -0
  222. tan/planner/project_emit/dts.py +580 -0
  223. tan/planner/project_emit/hw_info.py +221 -0
  224. tan/planner/project_emit/native_sim.py +136 -0
  225. tan/planner/project_emit/west_libs.py +213 -0
  226. tan/planner/project_loader.py +264 -0
  227. tan/planner/sdk_capability.py +203 -0
  228. tan/planner/sdk_compat.py +395 -0
  229. tan/planner/secure.py +350 -0
  230. tan/planner/slugs.py +394 -0
  231. tan/planner/som_metadata.py +325 -0
  232. tan/planner/strict_loaders.py +131 -0
  233. tan/planner/template.py +1037 -0
  234. tan/planner/template_pins.py +535 -0
  235. tan/planner/template_rewrite.py +714 -0
  236. tan/planner/topology.py +208 -0
  237. tan/planner/validate.py +365 -0
  238. tan/planner/whole_device_alias.py +67 -0
  239. tan/planner/zephyr_board.py +3510 -0
  240. tan/planner_cli.py +97 -0
  241. tan/planner_emit.py +617 -0
  242. tan/planner_root.py +119 -0
  243. tan/soc_ref.py +58 -0
  244. tan/templates/__init__.py +34 -0
  245. tan/templates/vendored/MANIFEST.md +1862 -0
  246. tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
  247. tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
  248. tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
  249. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
  250. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
  251. tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
  252. tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
  253. tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
  254. tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
  255. tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
  256. tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
  257. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
  258. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
  259. tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
  260. tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
  261. tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
  262. tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
  263. tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
  264. tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
  265. tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
  266. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
  267. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
  268. tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
  269. tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
  270. tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
  271. tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
  272. tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
  273. tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
  274. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
  275. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
  276. tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
  277. tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
  278. tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
  279. tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
  280. tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
  281. tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
  282. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
  283. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
  284. tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
  285. tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
  286. tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
  287. tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
  288. tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
  289. tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
  290. tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
  291. tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
  292. tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
  293. tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
  294. tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
  295. tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
  296. tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
  297. tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
  298. tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
  299. tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
  300. tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
  301. tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
  302. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
  303. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
  304. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
  305. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
  306. tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
  307. tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
  308. tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
  309. tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
  310. tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
  311. tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
  312. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
  313. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
  314. tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
  315. tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
  316. tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
  317. tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
  318. tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
  319. tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
  320. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
  321. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
  322. tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
  323. tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
  324. tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
  325. tan/version.py +102 -0
  326. tan_cli-0.7.0.dist-info/METADATA +125 -0
  327. tan_cli-0.7.0.dist-info/RECORD +330 -0
  328. tan_cli-0.7.0.dist-info/WHEEL +5 -0
  329. tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
  330. tan_cli-0.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,341 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """`tan trace` -- report the generation decisions a build would make.
3
+
4
+ Port of `crates/tan-cli/src/commands/trace.rs` + the `tan_core::loader`/
5
+ `tan_core::debug::trace` pieces it reads. For each of the four per-core
6
+ build-config emit targets (`zephyr-conf`, `dts-overlay`, `cmake-args`,
7
+ `yocto-conf` -- [`BUILD_CONFIG_EMIT_MODES`]), report the loader command line +
8
+ output path a `tan build` slice would run. Requires a resolved SDK root and an
9
+ existing `board.yaml`; refuses otherwise (exit 2) rather than reporting decisions
10
+ for a project that cannot actually build.
11
+
12
+ **Deliberately the narrower build-config set, not the full `tan generate`
13
+ surface (tan-cli#165 review finding 1).** `tan trace` reports "the generation
14
+ decisions a build would make" (this file's own name for the command); a build
15
+ only ever materialises these four -- `carrier-netlist`/`native-sim-overlay`/
16
+ `west-libraries`/`hw-info-h`/`os-topology` are real `tan generate --target`
17
+ outputs a build never runs. [`BUILD_CONFIG_EMIT_MODES`] is this port's own
18
+ copy of `tan_core::loader::BUILD_CONFIG_EMIT_MODES`, not
19
+ `generate_cmd.ALL_EMIT_MODES`.
20
+
21
+ **The output-path/command-line shape is measured, not guessed.** Rust's
22
+ `create_loader_plan` joins the workspace root onto the target's relative path
23
+ with exactly ONE `Path::join` call (never split component-wise), which on
24
+ Windows inserts a single native separator before an otherwise-untouched
25
+ forward-slash literal -- `C:/proj\\build/generated/alp.conf`, not
26
+ `C:\\proj\\build\\generated\\alp.conf`. Verified: `os.path.join(workspace_root,
27
+ "build/generated/alp.conf")` reproduces this byte-for-byte on Windows (and is a
28
+ no-op difference on POSIX, where `/` is already native), so [`_loader_plan`]
29
+ below uses a single `os.path.join` call per component the Rust also joins
30
+ separately (`sdk_root`, then `"scripts"`, then `"alp_project.py"` -- two
31
+ `.join()` calls, i.e. two inserted separators), rather than
32
+ `generate_cmd._output_path`'s fully-native, fully-split form -- the two
33
+ commands' oracle behaviour genuinely differs here, not just their code shape.
34
+
35
+ `resolve_debug_project_context`/the six-row debug context are `inspect_cmd`'s;
36
+ this file imports them rather than re-deriving -- both commands, and
37
+ `support-bundle`, must read one context, or the same project could resolve
38
+ three different ways across the three commands.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import os
44
+ from typing import Any
45
+
46
+ import typer
47
+
48
+ from tan.commands.inspect_cmd import resolve_debug_project_context
49
+ from tan.commands.sdk_cmd import NO_SDK_NEXT_STEPS
50
+ from tan.core.sdk_discovery import with_sdk_search
51
+ from tan.core.sdk_discovery import sdk_resolution_issues
52
+ from tan.core.timestamp import generated_at_iso
53
+ from tan.envelope import Envelope, Issue, Project, emit
54
+ from tan.exit_codes import ExitCode
55
+ from tan.output_format import FORMAT_HELP, OutputFormat, resolve_format
56
+
57
+ #: `data.schemaVersion` for this command's payload.
58
+ DATA_SCHEMA_VERSION = "1"
59
+
60
+ #: The output path (relative to the workspace root, `/`-separated -- an SDK
61
+ #: convention, not a host path) for each build-config emit target. Mirrors
62
+ #: `tan_core::loader::GENERATION_TARGET_CATALOG`'s four `BUILD_CONFIG_EMIT_
63
+ #: MODES` entries verbatim; NOT re-derived from `generate_cmd._OUTPUT_
64
+ #: RELATIVE_PATH` because [`_loader_plan`]'s join semantics differ from
65
+ #: `generate_cmd._output_path`'s (see the module docstring) even though the
66
+ #: four literal strings are identical today.
67
+ _OUTPUT_RELATIVE_PATH: dict[str, str] = {
68
+ "zephyr-conf": "build/generated/alp.conf",
69
+ "dts-overlay": "build/generated/alp.overlay",
70
+ "cmake-args": "build/generated/alp-cmake-args.txt",
71
+ "yocto-conf": "build/generated/alp-yocto.conf",
72
+ }
73
+
74
+ #: The four per-core build-config targets a `tan build` slice actually
75
+ #: materialises -- the set `tan trace`/`tan support-bundle` enumerate by
76
+ #: default and validate an explicit `--target` against. Order is the oracle's
77
+ #: own catalog order, pinned: `data.decisions` reports in this sequence.
78
+ BUILD_CONFIG_EMIT_MODES: tuple[str, ...] = (
79
+ "zephyr-conf",
80
+ "dts-overlay",
81
+ "cmake-args",
82
+ "yocto-conf",
83
+ )
84
+
85
+
86
+ class TraceTargetError(Exception):
87
+ """An unsupported `--target` value; `str()` is the user-facing message,
88
+ verbatim from the oracle."""
89
+
90
+
91
+ def resolve_trace_targets(raw: str | None) -> tuple[str, ...]:
92
+ """`None` (no `--target`) -> all four, in catalog order. A known target ->
93
+ that one alone. `--all` is deliberately NOT a parameter here: the oracle's
94
+ `resolve_targets` never reads it either -- verified (`--target X --all`
95
+ still narrows to `X`; `--all` alone matches the bare-no-target default)."""
96
+ if raw is None:
97
+ return BUILD_CONFIG_EMIT_MODES
98
+ if raw in BUILD_CONFIG_EMIT_MODES:
99
+ return (raw,)
100
+ raise TraceTargetError(
101
+ f"Unsupported trace target '{raw}'. Allowed values: "
102
+ f"{', '.join(BUILD_CONFIG_EMIT_MODES)}."
103
+ )
104
+
105
+
106
+ def _loader_plan(
107
+ workspace_root: str, sdk_root: str, board_yaml_path: str, python_binary: str, emit_target: str
108
+ ) -> tuple[str, str]:
109
+ """`(output_path, command_line)` for one emit target -- port of
110
+ `tan_core::loader::create_loader_plan`. See the module docstring for why
111
+ the two `os.path.join` calls below must stay exactly this shape."""
112
+ output_path = os.path.join(workspace_root, _OUTPUT_RELATIVE_PATH[emit_target])
113
+ script_path = os.path.join(sdk_root, "scripts", "alp_project.py")
114
+ command_line = (
115
+ f"{python_binary} {script_path} --input {board_yaml_path} --emit {emit_target} "
116
+ f"--output {output_path}"
117
+ )
118
+ return output_path, command_line
119
+
120
+
121
+ def build_trace_decisions(
122
+ workspace_root: str,
123
+ sdk_root: str,
124
+ board_yaml_path: str,
125
+ python_binary: str,
126
+ targets: tuple[str, ...],
127
+ focus: str | None,
128
+ ) -> list[dict[str, Any]]:
129
+ """One `Planned` decision per target, plus one more when `focus` (`--path`)
130
+ is set -- port of `trace.rs::run`'s decision-building loop. Shared with
131
+ `support_bundle_cmd`, whose bundled trace section is this exact list."""
132
+ decisions: list[dict[str, Any]] = []
133
+ for emit_target in targets:
134
+ output_path, command_line = _loader_plan(
135
+ workspace_root, sdk_root, board_yaml_path, python_binary, emit_target
136
+ )
137
+ decisions.append(
138
+ {
139
+ "key": f"generation.target.{emit_target}",
140
+ "outcome": "planned",
141
+ "outputPath": output_path,
142
+ "detail": f"Would run: {command_line}",
143
+ }
144
+ )
145
+ if focus is not None:
146
+ decisions.append(
147
+ {
148
+ "key": f"config.path.{focus}",
149
+ "outcome": "planned",
150
+ "detail": (
151
+ "Path-level tracing is currently static and reports planning "
152
+ "context only."
153
+ ),
154
+ }
155
+ )
156
+ return decisions
157
+
158
+
159
+ def _trace_text_lines(decisions: list[dict[str, Any]], quiet: bool) -> list[str]:
160
+ lines = [f"trace: decisions={len(decisions)}"]
161
+ if not quiet:
162
+ for d in decisions:
163
+ lines.append(f"[{d['outcome']}] {d['key']}: {d['detail']}")
164
+ return lines
165
+
166
+
167
+ def trace(
168
+ ctx: typer.Context,
169
+ path: str = typer.Option(
170
+ None, "--path", metavar="PATH", help="Limit tracing to this config key path."
171
+ ),
172
+ project: str = typer.Option(
173
+ None, "--project", metavar="PATH", help="Project root (defaults to current directory)."
174
+ ),
175
+ board_yaml: str = typer.Option(
176
+ None,
177
+ "--board-yaml",
178
+ metavar="PATH",
179
+ help="Explicit board.yaml path (overrides project resolution).",
180
+ ),
181
+ sdk_root: str = typer.Option(
182
+ None, "--sdk-root", metavar="PATH", help="alp-sdk checkout root."
183
+ ),
184
+ target: str = typer.Option(
185
+ None,
186
+ "--target",
187
+ metavar="EMIT",
188
+ help="Generation target (e.g. zephyr-conf, dts-overlay, cmake-args, yocto-conf).",
189
+ ),
190
+ output_format: OutputFormat = typer.Option(None, "--format", help=FORMAT_HELP),
191
+ quiet: bool = typer.Option(False, "--quiet", help="Suppress non-essential output."),
192
+ verbose: bool = typer.Option(False, "--verbose", hidden=True),
193
+ no_color: bool = typer.Option(False, "--no-color", hidden=True),
194
+ non_interactive: bool = typer.Option(False, "--non-interactive", hidden=True),
195
+ ci: bool = typer.Option(False, "--ci", hidden=True),
196
+ all_targets: bool = typer.Option(False, "--all", hidden=True),
197
+ ) -> None:
198
+ """Trace the generation decisions a build would make.
199
+
200
+ `--all` is accepted and ignored -- see [`resolve_trace_targets`]. The other
201
+ hidden flags are `global = true` clap options `trace.rs` never reads.
202
+ """
203
+ del verbose, no_color, non_interactive, ci, all_targets
204
+ resolved_format = resolve_format(output_format, ctx.obj, choices=OutputFormat)
205
+ json_mode = resolved_format == "json"
206
+
207
+ generated_at = generated_at_iso(millis=True)
208
+ focus = path
209
+ context = resolve_debug_project_context(project, board_yaml, sdk_root)
210
+
211
+ def empty_data(target_value: str | None) -> dict[str, Any]:
212
+ return {
213
+ "schemaVersion": DATA_SCHEMA_VERSION,
214
+ "generatedAt": generated_at,
215
+ "workflow": "cli.trace",
216
+ "focusPath": focus,
217
+ "target": target_value,
218
+ "decisions": [],
219
+ }
220
+
221
+ def _resolution_issues() -> list[Issue]:
222
+ """tan-cli#478, computed once and prepended on BOTH exits. `trace`'s
223
+ output names the `alp_project.py` a build would run; when that path
224
+ came from another project's `~/.alp/sdk-default` the envelope said so
225
+ nowhere. A failure path needs it as much as the success one -- more,
226
+ even: "no SDK resolved" and "resolved SOMEONE ELSE'S" read identically
227
+ without it."""
228
+ return sdk_resolution_issues(
229
+ context.broken_project_pin, context.sdk_tier, context.foreign_global_default_for
230
+ )
231
+
232
+ def fail(exit_code: ExitCode, code: str, message: str, data: dict, text_lines: list[str]) -> None:
233
+ resolution_issues = _resolution_issues()
234
+ issues = [*resolution_issues, Issue(f"trace.{code}", "error", message)]
235
+ if not json_mode:
236
+ # tan-cli#478 review finding 6: `_resolution_issues()` fed only the
237
+ # JSON envelope below -- the DEFAULT text path printed `text_lines`
238
+ # and nothing else, so `tan trace` with no `--format` stayed silent
239
+ # about resolving another project's checkout, the literal repro
240
+ # #478 opened with ("prints the alp_project.py a build would run
241
+ # from the *other* project's checkout, no warning").
242
+ for issue in resolution_issues:
243
+ typer.echo(issue.message, err=True)
244
+ for line in text_lines:
245
+ typer.echo(line, err=True)
246
+ if json_mode:
247
+ emit(
248
+ Envelope(
249
+ "trace",
250
+ Project(root=None, board_yaml=None),
251
+ data,
252
+ issues,
253
+ exit_code,
254
+ sdk=context.sdk,
255
+ )
256
+ )
257
+ raise typer.Exit(int(exit_code))
258
+
259
+ if context.sdk_root is None:
260
+ fail(
261
+ ExitCode.VALIDATION_FAILURE,
262
+ "sdk-root-unresolved",
263
+ # tan-cli#381: this port landed AFTER #305 centralised the advice
264
+ # and hardcoded `tan sdk switch` again -- a subcommand that refuses
265
+ # outright here (`sdk_cmd._run_not_ported`, `sdk.not-ported`). Same
266
+ # shape as `generate_cmd`/`model_cmd`: keep the two mechanisms that
267
+ # do work (`--sdk-root`, a checkout beside the project), take the
268
+ # third from NO_SDK_NEXT_STEPS so it cannot drift again.
269
+ with_sdk_search(
270
+ "alp-sdk root is unresolved. Use --sdk-root, place the project near an "
271
+ f"alp-sdk checkout, or {NO_SDK_NEXT_STEPS}.",
272
+ context.workspace_root,
273
+ sdk_root,
274
+ ),
275
+ empty_data(target),
276
+ ["trace: alp-sdk root is unresolved."],
277
+ )
278
+
279
+ if not context.board_yaml_exists:
280
+ fail(
281
+ ExitCode.VALIDATION_FAILURE,
282
+ "board-yaml-missing",
283
+ "board.yaml path could not be resolved or the file does not exist.",
284
+ empty_data(target),
285
+ ["trace: board.yaml path is unresolved or missing."],
286
+ )
287
+
288
+ try:
289
+ targets = resolve_trace_targets(target)
290
+ except TraceTargetError as err:
291
+ # Mirrors the oracle's catch-block data: `target: null`, unlike the two
292
+ # guard failures above (which echo the raw `--target` back).
293
+ fail(
294
+ ExitCode.INTERNAL_FAILURE,
295
+ "internal-failure",
296
+ str(err),
297
+ empty_data(None),
298
+ ["trace: internal failure", str(err)],
299
+ )
300
+ return # pragma: no cover -- fail() always raises typer.Exit
301
+
302
+ decisions = build_trace_decisions(
303
+ context.workspace_root,
304
+ context.sdk_root,
305
+ context.board_yaml_path,
306
+ context.python_binary,
307
+ targets,
308
+ focus,
309
+ )
310
+ resolved_target = targets[0] if len(targets) == 1 else None
311
+
312
+ data = {
313
+ "schemaVersion": DATA_SCHEMA_VERSION,
314
+ "generatedAt": generated_at,
315
+ "workflow": "cli.trace",
316
+ "focusPath": focus,
317
+ "target": resolved_target,
318
+ "decisions": decisions,
319
+ }
320
+
321
+ if not json_mode:
322
+ # tan-cli#478 review finding 6, success path: same silence as `fail()`
323
+ # above, unconditional (unlike the `--quiet`-suppressed decision
324
+ # lines) and printed first.
325
+ for issue in _resolution_issues():
326
+ typer.echo(issue.message, err=True)
327
+ for line in _trace_text_lines(decisions, quiet):
328
+ typer.echo(line, err=True)
329
+
330
+ if json_mode:
331
+ emit(
332
+ Envelope(
333
+ "trace",
334
+ context.project,
335
+ data,
336
+ _resolution_issues(),
337
+ ExitCode.SUCCESS,
338
+ sdk=context.sdk,
339
+ )
340
+ )
341
+ raise typer.Exit(int(ExitCode.SUCCESS))