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,221 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Per-board `boards/<board>.{conf,overlay}` files (tan-cli#1351).
3
+
4
+ Zephyr applies `boards/<board>_<soc>_<cluster>.conf` / `.overlay` only when
5
+ `<board>` is the board `west build -b` names. An example scaffolded onto a
6
+ different SKU (`E1M-AEN801` -> `E1M-AEN803`) keeps files named for the OLD
7
+ board, which Zephyr silently ignores. Two halves, both pure:
8
+
9
+ * `rename_board_files` -- `tan init --from-example --som`: rename each
10
+ example per-board file onto the target SKU's board target, matched by core
11
+ id, from `metadata/e1m_modules/<sku>.yaml` `topology.<core>.board`.
12
+ * `unmatched_board_files` -- `tan build`: name every `boards/alp_e1m_*`
13
+ file that matches no slice's board.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ from collections.abc import Iterable, Mapping, Sequence
20
+ from pathlib import Path
21
+
22
+ from tan.core.scaffold import PlannedFile, SomBlockUnsupportedError, vendored_som
23
+
24
+ #: Only alp board files are judged: an example legitimately ships
25
+ #: `native_sim_native_64.conf` etc. for boards that are never a slice here.
26
+ ALP_BOARD_PREFIX = "alp_e1m_"
27
+ _BOARD_FILE = re.compile(r"^(?P<stem>.+)\.(?:conf|overlay)$")
28
+
29
+
30
+ def flat_board(board: str) -> str:
31
+ """`alp_e1m_aen803_m55_he/ae822fa0e5597ls0/rtss_he` -> the file-name form."""
32
+ return board.replace("/", "_")
33
+
34
+
35
+ def topology_boards(metadata_root: Path, sku: str) -> dict[str, str]:
36
+ """`{core_id: qualified board}` from the SoM preset; `{}` when the preset
37
+ is missing or malformed (the caller then leaves every file alone)."""
38
+ import yaml # noqa: PLC0415 -- keeps `import tan.cli` lean (tan-cli#810)
39
+
40
+ try:
41
+ doc = yaml.safe_load(
42
+ (metadata_root / "e1m_modules" / f"{sku}.yaml").read_text(encoding="utf-8")
43
+ )
44
+ except (OSError, yaml.YAMLError, UnicodeDecodeError):
45
+ return {}
46
+ topology = doc.get("topology") if isinstance(doc, dict) else None
47
+ if not isinstance(topology, dict):
48
+ return {}
49
+ return {
50
+ str(core): spec["board"]
51
+ for core, spec in topology.items()
52
+ if isinstance(spec, dict) and isinstance(spec.get("board"), str)
53
+ }
54
+
55
+
56
+ def all_topology_boards(metadata_root: Path) -> list[str]:
57
+ """Every qualified board target any SoM preset declares; `[]` when the
58
+ metadata is unreadable (the caller then judges by the slices alone)."""
59
+ try:
60
+ skus = sorted(p.stem for p in (metadata_root / "e1m_modules").glob("*.yaml"))
61
+ except OSError:
62
+ return []
63
+ return [b for sku in skus for b in topology_boards(metadata_root, sku).values()]
64
+
65
+
66
+ def board_file_renames(
67
+ source_boards: Mapping[str, str], target_boards: Mapping[str, str]
68
+ ) -> dict[str, str]:
69
+ """`{old flat board: new flat board}` for every core both SKUs declare."""
70
+ return {
71
+ flat_board(old): flat_board(target_boards[core])
72
+ for core, old in source_boards.items()
73
+ if core in target_boards and old != target_boards[core]
74
+ }
75
+
76
+
77
+ def rename_board_files(
78
+ paths: Iterable[str], renames: Mapping[str, str]
79
+ ) -> dict[str, str]:
80
+ """`{old relative path: new relative path}` for `boards/<flat>.<ext>` files
81
+ whose flat board name is a rename key."""
82
+ out: dict[str, str] = {}
83
+ for rel in paths:
84
+ parent, _, name = rel.rpartition("/")
85
+ if parent != "boards":
86
+ continue
87
+ m = _BOARD_FILE.match(name)
88
+ if m and m["stem"] in renames:
89
+ out[rel] = f"boards/{renames[m['stem']]}{name[len(m['stem']):]}"
90
+ return out
91
+
92
+
93
+ def command_board(args: Sequence[str]) -> str | None:
94
+ """The `-b`/`--board` value of a `west build` argv, if it carries one."""
95
+ for i, token in enumerate(args):
96
+ if token in ("-b", "--board") and i + 1 < len(args):
97
+ return args[i + 1]
98
+ return None
99
+
100
+
101
+ def _stem_matches(stem: str, flat: str) -> bool:
102
+ """Zephyr's own prefix rule: the whole board, or any `_`-delimited
103
+ shortening (`<board>`, `<board>_<soc>`) or revision-style extension."""
104
+ return stem == flat or flat.startswith(stem + "_") or stem.startswith(flat + "_")
105
+
106
+
107
+ def _silicon(board: str) -> tuple[str, str, str]:
108
+ """`(core, soc, cluster)` of a qualified board target, SKU dropped:
109
+ `alp_e1m_aen803_m55_he/ae822fa0e5597ls0/rtss_he` -> `(m55_he, ae822fa0e5597ls0,
110
+ rtss_he)`. Two boards with the same triple are siblings (same SoC + core)."""
111
+ head, _, rest = board.partition("/")
112
+ soc, _, cluster = rest.partition("/")
113
+ tokens = head.removeprefix(ALP_BOARD_PREFIX).split("_", 1)
114
+ return (tokens[1] if len(tokens) > 1 else "", soc, cluster)
115
+
116
+
117
+ def unmatched_board_files(
118
+ project_dirs: Sequence[Path],
119
+ slice_boards: Iterable[str],
120
+ known_boards: Iterable[str] = (),
121
+ ) -> list[tuple[Path, str]]:
122
+ """`(file, reason)` for each `boards/alp_e1m_*.{conf,overlay}` under
123
+ `project_dirs` that matches none of `slice_boards`. Empty when no slice
124
+ names a board (nothing to compare against). A file naming another real
125
+ board in `known_boards` is skipped only when it is plausibly intentional
126
+ (tan-cli#1403): no slice shares that board's SoC + core (a V2N CM33 shim
127
+ on an AEN M55 build), or a file for a same-silicon slice's own board is
128
+ also present (an intentional twin). Otherwise it is the sibling-SKU
129
+ leftover tan-cli#1351 exists to flag."""
130
+ boards = list(slice_boards)
131
+ flats = [flat_board(b) for b in boards]
132
+ if not flats:
133
+ return []
134
+ candidates: list[tuple[Path, str]] = []
135
+ seen: set[Path] = set()
136
+ for directory in project_dirs:
137
+ try:
138
+ entries = sorted((directory / "boards").iterdir())
139
+ except OSError:
140
+ continue
141
+ for entry in entries:
142
+ m = _BOARD_FILE.match(entry.name)
143
+ if not m or not m["stem"].startswith(ALP_BOARD_PREFIX) or entry in seen:
144
+ continue
145
+ seen.add(entry)
146
+ candidates.append((entry, m["stem"]))
147
+ own_present = {
148
+ i
149
+ for i, f in enumerate(flats)
150
+ if any(_stem_matches(stem, f) for _, stem in candidates)
151
+ }
152
+ slice_silicon = [_silicon(b) for b in boards]
153
+ found: list[tuple[Path, str]] = []
154
+ for entry, stem in candidates:
155
+ if any(_stem_matches(stem, f) for f in flats):
156
+ continue
157
+ real = [b for b in known_boards if _stem_matches(stem, flat_board(b))]
158
+ if real:
159
+ siblings = {
160
+ i for i, sil in enumerate(slice_silicon) if any(sil == _silicon(b) for b in real)
161
+ }
162
+ if not siblings or siblings & own_present:
163
+ continue
164
+ found.append((entry, stem))
165
+ return found
166
+
167
+
168
+ def retarget_example_board_files(
169
+ files: list[PlannedFile], som: str, metadata_root: Path
170
+ ) -> list[PlannedFile]:
171
+ """Rename an example's per-board files onto `som`'s board targets. The
172
+ example's own SKU is read from its `board.yaml`; a missing SKU or preset
173
+ leaves `files` untouched."""
174
+ board_yaml = next((f for f in files if f.relative_path == "board.yaml"), None)
175
+ if board_yaml is None:
176
+ return files
177
+ try:
178
+ source_sku, _ = vendored_som(board_yaml.content)
179
+ except SomBlockUnsupportedError:
180
+ # Refused, with its own code, by the board.yaml retarget that follows.
181
+ return files
182
+ if not source_sku or source_sku == som:
183
+ return files
184
+ renames = board_file_renames(
185
+ topology_boards(metadata_root, source_sku), topology_boards(metadata_root, som)
186
+ )
187
+ moved = rename_board_files((f.relative_path for f in files), renames)
188
+ if not moved:
189
+ return files
190
+ return sorted(
191
+ (PlannedFile(moved.get(f.relative_path, f.relative_path), f.content) for f in files),
192
+ key=lambda f: f.relative_path,
193
+ )
194
+
195
+
196
+ def unmatched_board_file_messages(
197
+ slices: Iterable[tuple[str | None, Sequence[str], str | None]],
198
+ build_root: Path,
199
+ known_boards: Iterable[str] = (),
200
+ ) -> list[str]:
201
+ """One advisory per unmatched per-board file across `slices`, each given
202
+ as `(backend, command argv, app_dir)`. Both `app_dir` and its parent are
203
+ searched: `boards/` sits beside `CMakeLists.txt`, which for the `app:
204
+ ./src` convention is the parent."""
205
+ boards: list[str] = []
206
+ dirs: list[Path] = []
207
+ for backend, args, app_dir in slices:
208
+ board = command_board(args) if backend == "zephyr" else None
209
+ if board is None or app_dir is None:
210
+ continue
211
+ boards.append(board)
212
+ app = Path(app_dir)
213
+ app = app if app.is_absolute() else build_root / app
214
+ dirs.extend(d for d in (app, app.parent) if d not in dirs)
215
+ names = ", ".join(sorted(set(boards)))
216
+ return [
217
+ f"{path} matches no slice's board ({names}) -- Zephyr will never apply it. "
218
+ f"Rename it to `{flat_board(boards[0])}{path.suffix}` (or the board of the "
219
+ f"slice it belongs to)."
220
+ for path, _ in unmatched_board_files(dirs, boards, known_boards)
221
+ ]