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/core/zephyr_env.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Consumer-mechanism env gap-filler for `tan build`'s native executor: the
|
|
3
|
+
`ZEPHYR_BASE` / `EXTRA_ZEPHYR_MODULES` keys the plan deliberately does NOT
|
|
4
|
+
carry, filled in only when the plan didn't pin them ("plan wins / CLI fills
|
|
5
|
+
gaps"). Port of `zephyr_env_overrides`, `crates/tan-cli/src/commands/build/
|
|
6
|
+
execute/env.rs` -- confirmed against the compiled Rust unit tests in that
|
|
7
|
+
module (`cargo test -p alp-tan-cli --bin tan commands::build::execute::env::`,
|
|
8
|
+
all 5 passing) since the oracle binary's own `--plan-from` implies `--plan`
|
|
9
|
+
(v0.4.1 limitation, `build_cmd.py`'s own docstring) and so cannot be driven to
|
|
10
|
+
dispatch a synthetic plan end to end without a real `alp_orchestrate.py`
|
|
11
|
+
emission.
|
|
12
|
+
|
|
13
|
+
tan-cli#308: `ZEPHYR_BASE` is per ADR-0020 never carried by the plan at all --
|
|
14
|
+
it is pure consumer mechanism, always hand-derived from the west workspace
|
|
15
|
+
`tan` itself resolved -- so a stale ambient `$ZEPHYR_BASE` left over from a
|
|
16
|
+
`source zephyr-env.sh` (or an older `tan bootstrap` next-steps block, before
|
|
17
|
+
tan-cli#301) must not silently win for the spawned build child."""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from tan.core.plan_exec import apply_env_append
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def zephyr_env_overrides(
|
|
27
|
+
zephyr_base: Path | None,
|
|
28
|
+
sdk_root: Path | None,
|
|
29
|
+
slice_env: dict[str, str],
|
|
30
|
+
env_append_path: dict[str, list[str]],
|
|
31
|
+
inherited: Callable[[str], str | None],
|
|
32
|
+
toolchain_store: Path | None = None,
|
|
33
|
+
) -> list[tuple[str, str]]:
|
|
34
|
+
"""Consumer-mechanism env the plan deliberately does NOT carry, filled in
|
|
35
|
+
as a gap-filler so `tan build` runs a plan slice with no manual setup:
|
|
36
|
+
|
|
37
|
+
* `ZEPHYR_BASE` -- the resolved workspace's zephyr. Per ADR-0020 the plan
|
|
38
|
+
never emits this; it is pure consumer mechanism, always hand-derived.
|
|
39
|
+
* `EXTRA_ZEPHYR_MODULES` -- the alp-sdk checkout, so `west build -b
|
|
40
|
+
<alp-board>` finds the SDK's boards. This now comes FROM the plan's
|
|
41
|
+
`env_append_path` for an SDK-emitted plan; the hand-derived value here
|
|
42
|
+
is only a FALLBACK for a plan that carries neither the slice-env pin
|
|
43
|
+
nor the `env_append_path` entry (plan wins / CLI fills gaps).
|
|
44
|
+
* `ZEPHYR_SDK_INSTALL_DIR` (tan-cli#1209) -- CMake's `FindZephyr-sdk.cmake`
|
|
45
|
+
only finds `~/.alp/toolchains/zephyr-sdk-<v>-arm-zephyr-eabi/` by being
|
|
46
|
+
TOLD where it is: the module's own prefix scan looks one level too
|
|
47
|
+
shallow under `$HOME` to ever see it. `toolchain_store` is that
|
|
48
|
+
store's path, already verified (`tan.commands.build.toolchain.
|
|
49
|
+
verified_store_dir`) against THIS checkout's pinned manifest digest --
|
|
50
|
+
callers pass `None` when it isn't verified, and this function fills
|
|
51
|
+
nothing in that case. Precedence, per the ruling: a plan slice pin
|
|
52
|
+
wins outright (checked below); an inherited `ZEPHYR_SDK_INSTALL_DIR`
|
|
53
|
+
naming a path that EXISTS is the user's deliberate choice and is taken
|
|
54
|
+
verbatim (never normalised, never probed for anything beyond
|
|
55
|
+
existence); only when it is unset, blank, or names a path that does
|
|
56
|
+
NOT exist does tan's own stamped store fill the gap (tan-cli#1209
|
|
57
|
+
review MINOR: an earlier version skipped the existence probe here,
|
|
58
|
+
diverging from `build.toolchain.resolve_toolchain_root`'s own rule for
|
|
59
|
+
the SAME variable -- "set but naming a path that does NOT exist -- and
|
|
60
|
+
the empty string -- fall THROUGH to the scan" -- and from
|
|
61
|
+
`doctor_cmd._zephyr_sdk_detected_root`'s stricter validity check; a
|
|
62
|
+
stale pointer to a deleted SDK made `tan build` fail cmake's configure
|
|
63
|
+
while `tan doctor` reported the store present). A wrong/missing
|
|
64
|
+
`sdk_root` or an unstamped/stale store already resolves
|
|
65
|
+
`toolchain_store` to `None` upstream, so this function's own
|
|
66
|
+
contribution is exactly the precedence, nothing more.
|
|
67
|
+
|
|
68
|
+
Never overrides a key THIS slice's env pins.
|
|
69
|
+
|
|
70
|
+
`inherited` is the parent-process env lookup (the same one the caller
|
|
71
|
+
already threads through to `assemble_slice_env` for `env_append_path`
|
|
72
|
+
seeding). The `EXTRA_ZEPHYR_MODULES` gap-filler must not just return the
|
|
73
|
+
bare SDK root: the caller's own gap-filler merge OVERWRITES rather than
|
|
74
|
+
appends (see `assemble_slice_env`'s docstring), so returning only the SDK
|
|
75
|
+
root here would silently replace a developer's own
|
|
76
|
+
`export EXTRA_ZEPHYR_MODULES=/my/module` with just the SDK root on any
|
|
77
|
+
plan that doesn't itself pin the key. Seed from `inherited` and append the
|
|
78
|
+
SDK root via the same `apply_env_append` (per-key separator, de-dup) the
|
|
79
|
+
plan-driven path uses, so the two paths agree on whether an inherited
|
|
80
|
+
value survives."""
|
|
81
|
+
out: list[tuple[str, str]] = []
|
|
82
|
+
if "ZEPHYR_BASE" not in slice_env and zephyr_base is not None:
|
|
83
|
+
out.append(("ZEPHYR_BASE", str(zephyr_base)))
|
|
84
|
+
|
|
85
|
+
# Plan wins: skip the hand-derived value when the plan carries
|
|
86
|
+
# EXTRA_ZEPHYR_MODULES (as a slice-env pin or an env_append_path entry).
|
|
87
|
+
if "EXTRA_ZEPHYR_MODULES" not in slice_env and "EXTRA_ZEPHYR_MODULES" not in env_append_path:
|
|
88
|
+
if sdk_root is not None:
|
|
89
|
+
sdk = str(sdk_root)
|
|
90
|
+
base: list[tuple[str, str]] = []
|
|
91
|
+
inherited_value = inherited("EXTRA_ZEPHYR_MODULES")
|
|
92
|
+
if inherited_value:
|
|
93
|
+
base.append(("EXTRA_ZEPHYR_MODULES", inherited_value))
|
|
94
|
+
apply_env_append(base, {"EXTRA_ZEPHYR_MODULES": [sdk]})
|
|
95
|
+
if base:
|
|
96
|
+
out.append(("EXTRA_ZEPHYR_MODULES", base[0][1]))
|
|
97
|
+
|
|
98
|
+
# tan-cli#1209: plan pin > inherited (verbatim, IFF it names a path that
|
|
99
|
+
# still exists -- a stale pointer to a deleted SDK is not tan's to keep
|
|
100
|
+
# deferring to, matching resolve_toolchain_root's rule for the same
|
|
101
|
+
# variable) > tan's own verified store > nothing.
|
|
102
|
+
if (
|
|
103
|
+
"ZEPHYR_SDK_INSTALL_DIR" not in slice_env
|
|
104
|
+
and not _inherited_path_exists(inherited("ZEPHYR_SDK_INSTALL_DIR"))
|
|
105
|
+
and toolchain_store is not None
|
|
106
|
+
):
|
|
107
|
+
out.append(("ZEPHYR_SDK_INSTALL_DIR", str(toolchain_store)))
|
|
108
|
+
return out
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _inherited_path_exists(value: str | None) -> bool:
|
|
112
|
+
"""Existence probe for an inherited env value that names a path -- the
|
|
113
|
+
same rule `build.toolchain.resolve_toolchain_root` already applies to
|
|
114
|
+
this SAME `ZEPHYR_SDK_INSTALL_DIR` variable: blank is never trusted, and
|
|
115
|
+
neither is a path that does not exist. `OSError` (an unencodable or
|
|
116
|
+
too-long path on this platform) reads as "cannot confirm it exists",
|
|
117
|
+
not a crash -- matching that function's own `except OSError: exists =
|
|
118
|
+
False`."""
|
|
119
|
+
if not value:
|
|
120
|
+
return False
|
|
121
|
+
try:
|
|
122
|
+
return Path(value).exists()
|
|
123
|
+
except OSError:
|
|
124
|
+
return False
|
tan/env.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Shared environment-variable contracts, kept in one place so no second
|
|
3
|
+
hand-rolled copy can drift from it (tan-cli#288).
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def no_color_requested() -> bool:
|
|
13
|
+
"""Whether `NO_COLOR` disables colour, per the spec: PRESENCE, not
|
|
14
|
+
truthiness -- `NO_COLOR=` (set, empty) must still disable colour, same as
|
|
15
|
+
`NO_COLOR=0` or `NO_COLOR=false`. Mirrors the Rust oracle exactly
|
|
16
|
+
(`crates/tan-cli/src/style.rs:27`'s `var_os("NO_COLOR").is_none()`).
|
|
17
|
+
|
|
18
|
+
`faultdecode_cmd.py` and `size_cmd.py` each hand-rolled this check and
|
|
19
|
+
drifted: one did `if os.environ.get("NO_COLOR")` (truthy -- wrong, an
|
|
20
|
+
empty value read as unset), the other already did the correct presence
|
|
21
|
+
check. Do NOT reach for `typer.Option(..., envvar="NO_COLOR")` instead --
|
|
22
|
+
Click's bool-envvar coercion parses the STRING as a boolean (`1`/`true`/
|
|
23
|
+
`yes`/`on`, ...), so `NO_COLOR=` is "not a valid boolean" and crashes the
|
|
24
|
+
command outright, a regression neither original copy had.
|
|
25
|
+
"""
|
|
26
|
+
return os.environ.get("NO_COLOR") is not None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def stderr_is_tty() -> bool:
|
|
30
|
+
"""The one place that probes `sys.stderr.isatty()` -- `AttributeError`
|
|
31
|
+
when stderr has been replaced by something that doesn't implement it,
|
|
32
|
+
`ValueError` when it has been closed. Shared by `use_color` and
|
|
33
|
+
`wrap_width` below so neither hand-rolls its own copy of this
|
|
34
|
+
try/except (tan-cli#288, the exact drift this module's own docstring
|
|
35
|
+
exists to prevent).
|
|
36
|
+
|
|
37
|
+
tan-cli#488 round 6: promoted from `_stderr_is_tty` (module-private) to
|
|
38
|
+
a public name once `tan.core.consent.can_prompt`,
|
|
39
|
+
`tan.commands.doctor_cmd.fix_suppressed_issue` and
|
|
40
|
+
`tan.commands.build_cmd._dispatch` each turned out to hand-roll the exact
|
|
41
|
+
`X is not None and X.isatty()` shape this function already guards against
|
|
42
|
+
-- a `None` check alone stops the `AttributeError` from a detached
|
|
43
|
+
handle, but not from a handle that exists and simply has no `.isatty()`
|
|
44
|
+
at all, which is exactly what `tan.cli`'s `_TeeStderr` is under
|
|
45
|
+
`--format json`. Duplicating the try/except a fourth time is the same
|
|
46
|
+
drift this module's own docstring already names; importing the one
|
|
47
|
+
version is the fix, not writing a fifth."""
|
|
48
|
+
try:
|
|
49
|
+
return sys.stderr.isatty()
|
|
50
|
+
except (AttributeError, ValueError):
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def stdin_is_tty() -> bool:
|
|
55
|
+
"""The `stdin` counterpart of `stderr_is_tty` above, same rationale and
|
|
56
|
+
same exception pair: `AttributeError` when `sys.stdin` is `None` (a
|
|
57
|
+
detached-stdio process) or has been replaced by something with no
|
|
58
|
+
`.isatty()`, `ValueError` when it has been closed. `tan.core.consent.
|
|
59
|
+
can_prompt` and `tan.commands.doctor_cmd.fix_suppressed_issue` each used
|
|
60
|
+
to hand-roll `sys.stdin is not None and sys.stdin.isatty()` -- correct
|
|
61
|
+
for today's `None`-or-real-stream shape, but the same one-operand-short
|
|
62
|
+
pattern the stderr side was fixed for tan-cli#488 round 5/6; sharing this
|
|
63
|
+
helper closes both sides the same way instead of leaving stdin's copy to
|
|
64
|
+
rediscover the lesson on its own."""
|
|
65
|
+
try:
|
|
66
|
+
return sys.stdin.isatty()
|
|
67
|
+
except (AttributeError, ValueError):
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def use_color(no_color: bool, ci: bool) -> bool:
|
|
72
|
+
"""`Theme::from_args`: human text goes to stderr, so the TTY probe is
|
|
73
|
+
against stderr. Shared by every command's `--no-color`/`--ci` handling
|
|
74
|
+
(moved out of `size_cmd.py`, tan-cli's UX-polish sweep) so a second,
|
|
75
|
+
driftable copy never grows up beside this one -- `size_cmd._use_color`
|
|
76
|
+
now delegates here."""
|
|
77
|
+
if no_color or ci or no_color_requested():
|
|
78
|
+
return False
|
|
79
|
+
return stderr_is_tty()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
#: The floor a resolved terminal width may never drop below, moved here from
|
|
83
|
+
#: `doctor_cmd._DOCTOR_TEXT_MIN_WIDTH` (tan-cli's `doctor` wrap-seam adoption,
|
|
84
|
+
#: PR #480) so `wrap_width` below and `doctor_cmd.doctor` can share the one
|
|
85
|
+
#: number instead of drifting into two: a real terminal narrower than this
|
|
86
|
+
#: (or a redirected/piped run whose `shutil.get_terminal_size` fallback would
|
|
87
|
+
#: otherwise apply) must not be left to wrap one word per line. `doctor_cmd`
|
|
88
|
+
#: imports this name rather than keeping its own copy.
|
|
89
|
+
TEXT_WRAP_MIN_WIDTH = 60
|
|
90
|
+
|
|
91
|
+
#: Rows handed to `shutil.get_terminal_size`'s `fallback` pair by
|
|
92
|
+
#: `terminal_width` below. Never read by any caller -- every width probe in
|
|
93
|
+
#: this CLI wants columns only -- but the parameter takes a `(columns, lines)`
|
|
94
|
+
#: pair, so naming the number here keeps each call site from restating a bare
|
|
95
|
+
#: `24` it does not care about.
|
|
96
|
+
_TERMINAL_HEIGHT_FALLBACK = 24
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def terminal_width(fallback: int) -> int:
|
|
100
|
+
"""The one place that MEASURES a terminal, beside `stderr_is_tty` above
|
|
101
|
+
which is the one place that PROBES one -- the column count of the stream
|
|
102
|
+
every text surface in this CLI actually prints to, `sys.stderr`.
|
|
103
|
+
|
|
104
|
+
tan-cli#564: `wrap_width` below, `doctor_cmd.doctor`'s report width and
|
|
105
|
+
`build_cmd._heartbeat_line_width` were each armed on `stderr_is_tty()`
|
|
106
|
+
and then sized off a bare `shutil.get_terminal_size(fallback=(100, 24))`,
|
|
107
|
+
which CPython resolves against `os.get_terminal_size(sys.__stdout__.
|
|
108
|
+
fileno())`. Nothing in text mode is written to stdout, so with stdout
|
|
109
|
+
redirected that ioctl raised `OSError [Errno 25] Inappropriate ioctl for
|
|
110
|
+
device` -- swallowed by `shutil` itself -- and every probe silently took
|
|
111
|
+
the hard-coded 100 columns while stderr's own fd read 70. The #536
|
|
112
|
+
isatty-guard consolidation moved the PROBE onto one shared helper but
|
|
113
|
+
left the MEASUREMENT on the other handle; this moves it.
|
|
114
|
+
|
|
115
|
+
`COLUMNS` comes first so `shutil`'s existing precedence is preserved
|
|
116
|
+
exactly: an operator who exports `COLUMNS=137` is overriding BOTH handles
|
|
117
|
+
on purpose, and that override must not become the one thing the move
|
|
118
|
+
takes away. A non-positive, empty or non-numeric value is ignored, and so
|
|
119
|
+
is a 0-column ioctl answer (a known pty shape) -- both exactly as
|
|
120
|
+
`shutil` ignores them, and because a literal 0 would reach
|
|
121
|
+
`build_cmd._heartbeat_line_width` as a one-column line.
|
|
122
|
+
|
|
123
|
+
The last step falls through to `shutil.get_terminal_size` rather than
|
|
124
|
+
straight to `fallback`, so the mirror-image run -- stderr redirected
|
|
125
|
+
while stdout is still a terminal -- resolves exactly the number it
|
|
126
|
+
resolves today. `fallback` is a plain column count; the
|
|
127
|
+
`(columns, lines)` pair `shutil` wants is assembled here.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
columns = int(os.environ["COLUMNS"])
|
|
131
|
+
except (KeyError, ValueError):
|
|
132
|
+
pass
|
|
133
|
+
else:
|
|
134
|
+
if columns > 0:
|
|
135
|
+
return columns
|
|
136
|
+
try:
|
|
137
|
+
# `AttributeError` when stderr has been replaced by something with no
|
|
138
|
+
# `.fileno()`, `ValueError` when it has been closed, `OSError` when the
|
|
139
|
+
# fd is real but not a terminal (the redirected case above).
|
|
140
|
+
measured = os.get_terminal_size(sys.stderr.fileno()).columns
|
|
141
|
+
except (AttributeError, ValueError, OSError):
|
|
142
|
+
measured = 0
|
|
143
|
+
if measured > 0:
|
|
144
|
+
return measured
|
|
145
|
+
return shutil.get_terminal_size(fallback=(fallback, _TERMINAL_HEIGHT_FALLBACK)).columns
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def wrap_width() -> int | None:
|
|
149
|
+
"""The column budget for hard-wrapping prose to stderr, or `None` when
|
|
150
|
+
stderr is not a terminal.
|
|
151
|
+
|
|
152
|
+
`None` is not "80 columns", it is "do not wrap at all" -- a caller must
|
|
153
|
+
print each logical line as one physical line. This is the design point
|
|
154
|
+
`explain`/`sdk current` adopt this seam for: `tan explain | grep` (or any
|
|
155
|
+
pipe) wants one record per line, and inserting a hard newline into a
|
|
156
|
+
piped stream breaks that in a way a real terminal's own non-destructive
|
|
157
|
+
soft-wrap never would. So the probe here is narrower than `use_color`'s:
|
|
158
|
+
a redirected/piped stream (not a tty) always returns `None`, full stop.
|
|
159
|
+
|
|
160
|
+
Deliberately takes no `no_color`/`ci` arguments, unlike `use_color`
|
|
161
|
+
above, and must not gain them: `--no-color` asks only to drop ANSI
|
|
162
|
+
colour, and `--ci` asks only to skip interactive prompts (`can_prompt`)
|
|
163
|
+
-- neither says anything about whether stderr is a pipe, and a user who
|
|
164
|
+
passed `--no-color` on a real terminal still wants readable line breaks.
|
|
165
|
+
Folding either flag in here would silently also turn off wrapping for
|
|
166
|
+
every `--no-color` invocation, which is a second, unrelated behaviour
|
|
167
|
+
change riding on a flag that never asked for it.
|
|
168
|
+
|
|
169
|
+
`doctor` deliberately does NOT use this seam: it resolves its width
|
|
170
|
+
unconditionally, so `tan doctor > report.txt` still comes out wrapped.
|
|
171
|
+
That is a difference in what the two kinds of output ARE, not an
|
|
172
|
+
oversight, and it is the reason this function exists as a separate
|
|
173
|
+
decision rather than something `render_check_lines` could have called
|
|
174
|
+
for everybody. `doctor` prints a REPORT -- prose a human reads top to
|
|
175
|
+
bottom, whose redirected form is a file someone opens later and wants
|
|
176
|
+
wrapped exactly as the terminal would have shown it. `explain` and
|
|
177
|
+
`sdk current` print RECORDS -- `Generation targets: ...`, ` path
|
|
178
|
+
<value>` -- whose redirected form is an input to `grep`/`cut`, where a
|
|
179
|
+
hard newline in the middle of a value is corruption. Shape of the
|
|
180
|
+
output decides, so a future command adopting this seam should ask which
|
|
181
|
+
of the two it is rather than copying whichever neighbour it read first.
|
|
182
|
+
"""
|
|
183
|
+
if not stderr_is_tty():
|
|
184
|
+
return None
|
|
185
|
+
return max(terminal_width(100), TEXT_WRAP_MIN_WIDTH)
|