Skip to content
File

Blob: scripts/project.py

python72 lines
1"""Shared paths and the pinned firmware toolchain; never modifies the shell profile."""
2import os
3from pathlib import Path
4import subprocess
5 
6ROOT = Path(__file__).resolve().parents[1]
7IDF_REVISION = "2c211b236707889e8400c4dc5644dd5c4ee071e0"
8DSP_REVISION = "3c8ac0fdfec83740b783e200862c8d0c056de0ad"
9CODEC_REVISION = "da256e5f462a8e010667d35314a5ba5cdc4a8d9a"
10VENDOR = ROOT / ".tools/vendor"
11RUST_RELEASE = "1.97.0.0"
12RUST_TOOLCHAIN = "esp-radio"
13ESPUP_VERSION = "0.17.1"
14ESPUP_SHA256 = "dbe54e9907b687809dbe1b955731569ed6df2b525362710d676256c5c8cf9ccd"
15CLANG_FORMAT_VERSION = "23.1.1"
16 
17 
18def sdk_root():
19 configured = os.environ.get("RADIO_SDK_ROOT")
20 if configured:
21 return Path(configured).expanduser().resolve()
22 existing = ROOT / ".tools/validation-sdk"
23 return (existing if existing.exists() else ROOT / ".tools/sdk").resolve()
24 
25 
26def build_dir():
27 return sdk_root() / "rust-radio-build"
28 
29 
30def verify_checkout(path, revision):
31 actual = subprocess.check_output(["git", "-C", str(path), "rev-parse", "HEAD"], text=True).strip()
32 if actual != revision:
33 raise SystemExit(f"{path.name} revision differs from the pinned version; use a separate checkout")
34 changes = subprocess.check_output(["git", "-C", str(path), "status", "--porcelain", "--untracked-files=no"], text=True)
35 if changes:
36 raise SystemExit(f"{path.name} has tracked modifications; use a clean pinned checkout")
37 
38 
39def rust_libclang():
40 """Find libclang bundled with the named Xtensa Rust toolchain."""
41 compiler = subprocess.run(
42 ["rustup", "which", "--toolchain", RUST_TOOLCHAIN, "rustc"],
43 capture_output=True, text=True,
44 )
45 if not compiler.returncode:
46 toolchain = Path(compiler.stdout.strip()).parent.parent
47 libraries = sorted(toolchain.glob("xtensa-esp32-elf-clang/*/esp-clang/lib/libclang.so"))
48 for library in reversed(libraries):
49 if library.is_file():
50 return library.parent
51 raise SystemExit("Xtensa libclang is missing. Run make setup first.")
52 
53 
54def idf_environment():
55 """Return ESP-IDF's Python and the build environment, including Xtensa libclang."""
56 sdk = sdk_root()
57 tools = sdk / "tools"
58 try:
59 python = next((tools / "python_env").glob("idf5.5_py*_env/bin/python"))
60 gcc = next((tools / "tools/xtensa-esp-elf").rglob("xtensa-esp32s3-elf-gcc"))
61 ninja = next((tools / "tools/ninja").rglob("ninja"))
62 rom = next((tools / "tools/esp-rom-elfs").glob("*"))
63 except StopIteration:
64 raise SystemExit("ESP-IDF tools are missing. Run make setup first.") from None
65 env = os.environ.copy()
66 env.update(IDF_PATH=str(sdk / "esp-idf"), IDF_TOOLS_PATH=str(tools), IDF_TARGET="esp32s3",
67 IDF_PYTHON_ENV_PATH=str(python.parent.parent), PIP_CACHE_DIR=str(sdk / "pip-cache"),
68 ESP_ROM_ELF_DIR=str(rom), LIBCLANG_PATH=str(rust_libclang()),
69 ESP_IDF_ESPUP_CLANG_SYMLINK="ignore")
70 env["PATH"] = os.pathsep.join([str(python.parent), str(gcc.parent), str(ninja.parent), env["PATH"]])
71 return python, env