[nrf52] Add nRF Connect SDK 3.4.0 / Zephyr 4.x support (#19863)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
tomaszduda23
2026-10-01 07:41:18 -05:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent 7e32770c5a
commit 5a3e95b161
7 changed files with 346 additions and 34 deletions
+9 -2
View File
@@ -20,6 +20,13 @@ __attribute__((weak)) void print_coredump() {}
namespace esphome::logger {
// Zephyr 3.7 renamed z_arch_esf_t to struct arch_esf; the old name was later removed.
#if KERNEL_VERSION_NUMBER >= 0x030700
using FatalErrorEsf = ::arch_esf;
#else
using FatalErrorEsf = z_arch_esf_t;
#endif
__attribute__((section(".noinit"))) struct {
uint32_t magic;
uint32_t reason;
@@ -168,7 +175,7 @@ void Logger::dump_crash_() {
}
}
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
void k_sys_fatal_error_handler(unsigned int reason, const FatalErrorEsf *esf) {
crash_buf.magic = App.get_config_hash();
crash_buf.reason = reason;
if (esf) {
@@ -192,7 +199,7 @@ void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
extern "C" {
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
void k_sys_fatal_error_handler(unsigned int reason, const esphome::logger::FatalErrorEsf *esf) {
esphome::logger::k_sys_fatal_error_handler(reason, esf);
}
}
+21 -13
View File
@@ -438,7 +438,8 @@ async def to_code(config: ConfigType) -> None:
zephyr_add_prj_conf("SERIAL", False, False)
# disable stuff to make image smaller by default
zephyr_add_prj_conf("NCS_BOOT_BANNER", False, False)
if framework_ver >= cv.Version(2, 9, 2):
zephyr_add_prj_conf("NCS_BOOT_BANNER", False, False)
zephyr_add_prj_conf("BOOT_BANNER", False, False)
zephyr_add_prj_conf("PRINTK", False, False)
zephyr_add_prj_conf("CONSOLE", False, False)
@@ -495,8 +496,8 @@ def get_download_types(storage_json: StorageJSON) -> list[dict[str, str]]:
types = []
UF2_PATH = "zephyr/zephyr.uf2"
DFU_PATH = "firmware.zip"
HEX_PATH = "zephyr/zephyr.hex" # SDK 2.6.1, only generated when OTA is disabled
HEX_MERGED_PATH = "zephyr/merged.hex" # SDK 2.9.2, always generated
HEX_PATH = "zephyr/zephyr.hex" # SDK 2.6.1 without OTA, SDK 3.4.0+
HEX_MERGED_PATH = "zephyr/merged.hex" # SDK 2.9.2 to 3.3.x, always generated
APP_IMAGE_PATH = "zephyr/app_update.bin"
build_dir = Path(storage_json.firmware_bin_path).parent
if (build_dir / UF2_PATH).is_file():
@@ -968,6 +969,11 @@ def run_compile(args, config: ConfigType) -> bool:
_LOGGER.info("Build inputs changed, cleaning %s", build_dir)
rmtree(build_dir)
# SDK 3.4.0+ no longer generates merged.hex; drop one left by an older SDK
# build so it is never packaged or offered for download.
for stale_hex in (build_dir / "merged.hex", build_dir / "zephyr" / "merged.hex"):
stale_hex.unlink(missing_ok=True)
if pch_on:
pch.log_pch_in_use()
# Zephyr turns ccache on by itself when it is installed
@@ -1012,13 +1018,18 @@ def run_compile(args, config: ConfigType) -> bool:
west_out = zephyr_dir / "zephyr"
_copy_if_exists(west_out / "zephyr.uf2", zephyr_dir / "zephyr.uf2")
_copy_if_exists(west_out / "zephyr.signed.bin", zephyr_dir / "app_update.bin")
_copy_if_exists(west_out / "zephyr.hex", zephyr_dir / "zephyr.hex")
_copy_if_exists(build_dir / "merged.hex", zephyr_dir / "merged.hex")
# For Adafruit bootloader builds, regenerate the UF2 from merged.hex,
# whose records carry the correct flash addresses. The build's own
# zephyr.uf2 uses the board's default offset, which is wrong in some cases.
merged_hex = zephyr_dir / "merged.hex"
if bootloader in _UF2_FAMILY_IDS and merged_hex.is_file():
# For Adafruit bootloader builds, regenerate the UF2 from a hex file.
# merged.hex carries the correct flash addresses; SDK 3.4.0+ no longer
# generates it, so use zephyr.hex there. Chosen by version so a merged.hex
# left by an older SDK build is never picked.
if framework_ver >= cv.Version(3, 4, 0):
hex_file = zephyr_dir / "zephyr.hex"
else:
hex_file = zephyr_dir / "merged.hex"
if bootloader in _UF2_FAMILY_IDS and hex_file.is_file():
# Drop the build's own wrong-offset UF2 so it isn't shipped alongside.
app_uf2 = west_out / "zephyr.uf2"
if app_uf2.is_file():
@@ -1035,12 +1046,12 @@ def run_compile(args, config: ConfigType) -> bool:
"-c",
"-o",
str(zephyr_dir / "zephyr.uf2"),
str(merged_hex),
str(hex_file),
],
env=env,
stream_output=True,
):
raise EsphomeError("Failed to generate UF2 from merged hex")
raise EsphomeError(f"Failed to generate UF2 from {hex_file.name}")
if bootloader in (
BOOTLOADER_ADAFRUIT,
@@ -1048,9 +1059,6 @@ def run_compile(args, config: ConfigType) -> bool:
BOOTLOADER_ADAFRUIT_NRF52_SD140_V6,
BOOTLOADER_ADAFRUIT_NRF52_SD140_V7,
):
# no fallback is needed for adafruit case. merged merged.hex is always generated.
# get_download_types needs fallback for mcuboot (non adafruit)
hex_file = zephyr_dir / "merged.hex"
dfu_package = build_dir / "firmware.zip"
genpkg_cmd = [
str(paths["python_executable"]),
+72 -19
View File
@@ -28,6 +28,26 @@ _LOGGER = logging.getLogger(__name__)
_REQUIREMENTS = Path(__file__).parent / "requirements.txt"
TOOLCHAIN_VERSION = "0.17.4"
# Zephyr SDK used by nRF Connect SDK 3.4.0 and newer.
_TOOLCHAIN_VERSION_NCS_3_4_0 = "1.0.1"
_TOOLCHAIN_VERSIONS = (TOOLCHAIN_VERSION, _TOOLCHAIN_VERSION_NCS_3_4_0)
def _uses_sdk_ng_1_toolchain() -> bool:
"""True when the framework needs Zephyr SDK 1.0+.
SDK 1.0 moved the GNU toolchain under gnu/ and renamed its archive
to toolchain_gnu_*.
"""
return CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(3, 4, 0)
def _get_toolchain_version() -> str:
"""Return the Zephyr SDK toolchain version for the current framework."""
if _uses_sdk_ng_1_toolchain():
return _TOOLCHAIN_VERSION_NCS_3_4_0
return TOOLCHAIN_VERSION
# Packages the PlatformIO toolchain's Zephyr build script needs beyond west
# (which comes from requirements.txt). Keep the pin in sync with
@@ -40,6 +60,20 @@ SDK_NG_TOOLCHAIN_MIRRORS = str_to_lst_of_str(
"https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v{VERSION}/toolchain_{sysname}-{machine}_arm-zephyr-eabi.{extension}",
)
)
_SDK_NG_TOOLCHAIN_GNU_MIRRORS = str_to_lst_of_str(
os.environ.get(
"ESPHOME_SDK_NG_TOOLCHAIN_GNU_MIRRORS",
"https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v{VERSION}/toolchain_gnu_{sysname}-{machine}_arm-zephyr-eabi.{extension}",
)
)
def _get_sdk_ng_toolchain_mirrors() -> list[str]:
"""Return toolchain mirror URLs for the current framework version."""
if _uses_sdk_ng_1_toolchain():
return _SDK_NG_TOOLCHAIN_GNU_MIRRORS
return SDK_NG_TOOLCHAIN_MIRRORS
# Minimal SDK provides cmake discovery files (Zephyr-sdkConfig.cmake) and
# host tools (dtc etc.) required by the Zephyr cmake build system.
@@ -85,15 +119,22 @@ def _get_toolchain_path(version: str) -> Path:
return get_sdk_nrf_tools_path() / "toolchains" / version
def toolchain_tool(name: str) -> Path:
"""Path to one of the pinned Zephyr SDK's tools (objdump, readelf, ...).
def _get_arm_toolchain_path() -> Path:
"""The arm-zephyr-eabi directory inside the pinned Zephyr SDK.
The single owner of the ``arm-zephyr-eabi/bin/arm-zephyr-eabi-<name>``
layout and the Windows suffix.
The single owner of the SDK 0.x (arm-zephyr-eabi/) and SDK 1.0+
(gnu/arm-zephyr-eabi/) layouts.
"""
toolchain_root = _get_toolchain_path(_get_toolchain_version())
if _uses_sdk_ng_1_toolchain():
return toolchain_root / "gnu" / "arm-zephyr-eabi"
return toolchain_root / "arm-zephyr-eabi"
def toolchain_tool(name: str) -> Path:
"""Path to one of the pinned Zephyr SDK's tools (objdump, readelf, ...)."""
suffix = ".exe" if os.name == "nt" else ""
bin_path = _get_toolchain_path(TOOLCHAIN_VERSION) / "arm-zephyr-eabi" / "bin"
return bin_path / f"arm-zephyr-eabi-{name}{suffix}"
return _get_arm_toolchain_path() / "bin" / f"arm-zephyr-eabi-{name}{suffix}"
_SITECUSTOMIZE = """\
@@ -169,7 +210,7 @@ def get_build_env() -> dict:
# "Zephyr-sdk_DIR" environment hint proved unreliable here: containerized
# non-root builds failed to locate the SDK with it, while
# ZEPHYR_SDK_INSTALL_DIR fixed the same invocation.
env["ZEPHYR_SDK_INSTALL_DIR"] = str(_get_toolchain_path(TOOLCHAIN_VERSION))
env["ZEPHYR_SDK_INSTALL_DIR"] = str(_get_toolchain_path(_get_toolchain_version()))
return env
@@ -561,36 +602,41 @@ def _check_and_install(version: str) -> None:
raise EsphomeError(f"Install Zephyr requirements for {version} failure")
zephyr_sentinel.touch()
# Shared by every SDK version; locked only while missing
if not (_get_toolchain_path(TOOLCHAIN_VERSION) / ".ready").exists():
with _install_lock(f"toolchain-{TOOLCHAIN_VERSION}"):
# Shared by every SDK version that uses the same toolchain; locked only
# while missing
toolchain_version = _get_toolchain_version()
if not (_get_toolchain_path(toolchain_version) / ".ready").exists():
with _install_lock(f"toolchain-{toolchain_version}"):
_install_toolchain()
def _install_toolchain() -> None:
toolchains_dir = _get_toolchain_path(TOOLCHAIN_VERSION)
toolchain_version = _get_toolchain_version()
toolchains_dir = _get_toolchain_path(toolchain_version)
sentinel = toolchains_dir / ".ready"
if not sentinel.exists():
rmdir(toolchains_dir, msg=f"Clean up {TOOLCHAIN_VERSION} toolchain environment")
rmdir(toolchains_dir, msg=f"Clean up {toolchain_version} toolchain environment")
sysname, machine, extension = _get_toolchain_platform_info()
substitutions = {
"VERSION": TOOLCHAIN_VERSION,
"VERSION": toolchain_version,
"sysname": sysname,
"machine": machine,
"extension": extension,
}
# Downloaded next to the destination (not a temp file) so an
# interrupted download's .part file resumes on the next run.
# SDK 1.0+ Zephyr-sdkConfig.cmake looks for the toolchain in
# gnu/arm-zephyr-eabi/; extraction strips the archive's single root.
for mirrors, extract_dir, what, slug in (
(SDK_NG_MINIMAL_MIRRORS, toolchains_dir, "Zephyr SDK minimal", "minimal"),
(
SDK_NG_TOOLCHAIN_MIRRORS,
toolchains_dir / "arm-zephyr-eabi",
_get_sdk_ng_toolchain_mirrors(),
_get_arm_toolchain_path(),
"toolchain",
"toolchain",
),
):
_LOGGER.info("Downloading %s %s ...", TOOLCHAIN_VERSION, what)
_LOGGER.info("Downloading %s %s ...", toolchain_version, what)
download_and_extract(
mirrors,
substitutions,
@@ -598,10 +644,17 @@ def _install_toolchain() -> None:
extract_dir,
progress_header="Extracting",
)
# Best-effort prune of resume leftovers, including a previous
# TOOLCHAIN_VERSION's orphans; the SDK archives are hundreds of MB.
# A locked file must not discard the just-completed install.
# Best-effort prune of resume leftovers, including orphans of retired
# toolchain versions; the SDK archives are hundreds of MB. The other
# toolchain still in use may be downloading under its own lock, so its
# leftovers are kept. A locked file must not discard the just-completed
# install.
other_versions = tuple(
f"{v}." for v in _TOOLCHAIN_VERSIONS if v != toolchain_version
)
for leftover in toolchains_dir.parent.glob("*.archive.part*"):
if leftover.name.startswith(other_versions):
continue
try:
leftover.unlink()
except OSError as err:
@@ -7,6 +7,7 @@ import pytest
from esphome.components import nrf52
from esphome.components.zephyr.const import KEY_BOARD
import esphome.config_validation as cv
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION, Toolchain
from esphome.core import CORE, EsphomeError
@@ -110,6 +111,7 @@ def run_cmd(tmp_path: Path) -> Mock:
CORE.build_path = tmp_path / "build"
CORE.name = "livingroom"
CORE.toolchain = Toolchain.SDK_NRF
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: cv.Version(3, 2, 0)}
with (
patch.object(nrf52, "check_and_install"),
patch.object(nrf52, "_generate_cmake_lists", return_value=False),
@@ -0,0 +1,130 @@
"""nrf52 sdk-nrf build: the hex file used for the Adafruit UF2 and DFU package."""
from collections.abc import Iterator
from pathlib import Path
from unittest.mock import Mock, patch
import pytest
from esphome.components import nrf52
from esphome.components.nrf52.const import BOOTLOADER_ADAFRUIT_NRF52_SD140_V7
from esphome.components.zephyr.const import KEY_BOARD, KEY_BOOTLOADER
import esphome.config_validation as cv
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION, Toolchain
from esphome.core import CORE
@pytest.fixture
def build_dir(tmp_path: Path) -> Path:
CORE.config_path = tmp_path / "test.yaml"
CORE.build_path = tmp_path / "build"
CORE.name = "livingroom"
CORE.toolchain = Toolchain.SDK_NRF
return CORE.relative_pioenvs_path(CORE.name)
@pytest.fixture
def run_cmd(tmp_path: Path) -> Iterator[Mock]:
with (
patch.object(nrf52, "check_and_install"),
patch.object(nrf52, "_generate_cmake_lists", return_value=False),
patch.object(
nrf52,
"get_build_paths",
return_value={"python_executable": "python3", "framework_path": tmp_path},
),
patch.object(nrf52, "get_build_env", return_value={}),
patch.object(
nrf52,
"zephyr_data",
return_value={
KEY_BOARD: "board",
KEY_BOOTLOADER: BOOTLOADER_ADAFRUIT_NRF52_SD140_V7,
},
),
patch.object(nrf52.pch, "pch_enabled", return_value=False),
patch.object(nrf52, "run_command_ok", return_value=True) as run,
):
yield run
def _uf2_hex(run_cmd: Mock) -> str:
cmd = next(c.args[0] for c in run_cmd.call_args_list if "-f" in c.args[0])
return cmd[-1]
def _dfu_hex(run_cmd: Mock) -> str:
cmd = next(c.args[0] for c in run_cmd.call_args_list if "genpkg" in c.args[0])
return cmd[cmd.index("--application") + 1]
def _build(
build_dir: Path,
version: cv.Version,
outputs: list[str],
run_cmd: Mock,
leftovers: tuple[str, ...] = (),
) -> None:
"""Run run_compile; *outputs* appear when west builds, *leftovers* before."""
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: version}
(build_dir / "zephyr" / "zephyr").mkdir(parents=True)
# Without a CMake cache run_compile wipes the build dir first
for name in ["CMakeCache.txt", *leftovers]:
(build_dir / name).write_text("")
def west_build(cmd: list[str], **kwargs: object) -> bool:
if "west" in cmd:
for name in outputs:
(build_dir / name).write_text("")
return True
run_cmd.side_effect = west_build
nrf52.run_compile(None, {})
def test_uf2_and_dfu_use_merged_hex(build_dir: Path, run_cmd: Mock) -> None:
_build(
build_dir,
cv.Version(2, 9, 2),
["merged.hex", "zephyr/zephyr/zephyr.hex"],
run_cmd,
)
merged = str(build_dir / "zephyr" / "merged.hex")
assert _uf2_hex(run_cmd) == merged
assert _dfu_hex(run_cmd) == merged
def test_uf2_and_dfu_fall_back_to_zephyr_hex(build_dir: Path, run_cmd: Mock) -> None:
"""The nRF Connect SDK 3.4.0 build produces no merged.hex."""
_build(build_dir, cv.Version(3, 4, 0), ["zephyr/zephyr/zephyr.hex"], run_cmd)
app_hex = build_dir / "zephyr" / "zephyr.hex"
assert app_hex.is_file()
assert _uf2_hex(run_cmd) == str(app_hex)
assert _dfu_hex(run_cmd) == str(app_hex)
def test_older_sdk_does_not_fall_back_to_zephyr_hex(
build_dir: Path, run_cmd: Mock
) -> None:
"""Before SDK 3.4.0 a missing merged.hex must not be replaced by zephyr.hex."""
_build(build_dir, cv.Version(3, 2, 0), ["zephyr/zephyr/zephyr.hex"], run_cmd)
assert not any("-f" in c.args[0] for c in run_cmd.call_args_list)
assert _dfu_hex(run_cmd) == str(build_dir / "zephyr" / "merged.hex")
def test_stale_merged_hex_is_removed_before_building(
build_dir: Path, run_cmd: Mock
) -> None:
"""A merged.hex left by an older SDK build must not be packaged."""
_build(
build_dir,
cv.Version(3, 4, 0),
["zephyr/zephyr/zephyr.hex"],
run_cmd,
leftovers=("merged.hex", "zephyr/merged.hex"),
)
assert not (build_dir / "merged.hex").exists()
assert not (build_dir / "zephyr" / "merged.hex").exists()
app_hex = str(build_dir / "zephyr" / "zephyr.hex")
assert _uf2_hex(run_cmd) == app_hex
assert _dfu_hex(run_cmd) == app_hex
+96
View File
@@ -20,6 +20,7 @@ from esphome.components.nrf52.framework import (
_get_penv_site_packages,
_get_platformio_penv_path,
_get_toolchain_platform_info,
_install_toolchain,
_needs_venv_rebuild,
_wanted_west_projects,
check_and_install,
@@ -808,6 +809,63 @@ class TestCheckAndInstall:
assert substitutions["machine"] == "x86_64"
assert substitutions["extension"] == "tar.xz"
def test_toolchain_download_uses_gnu_url_for_sdk_3_4_0(
self,
tmp_path: Path,
mock_nrf52_ops: SimpleNamespace,
) -> None:
"""For nRF Connect SDK >= 3.4.0 the toolchain archive name includes 'toolchain_gnu_'."""
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: Version.parse("3.4.0")}
sdk_version = "3.4.0"
tools = get_sdk_nrf_tools_path()
python_env = tools / "penvs" / f"v{sdk_version}"
framework = tools / "frameworks" / f"v{sdk_version}"
toolchain_dir = tools / "toolchains" / "1.0.1"
for d in (python_env, framework, toolchain_dir):
d.mkdir(parents=True, exist_ok=True)
(framework / "zephyr" / "scripts").mkdir(parents=True, exist_ok=True)
(framework / "zephyr" / "scripts" / "requirements.txt").touch()
_mark_venv_ready(python_env)
(framework / ".ready").touch()
check_and_install()
# Two download calls: minimal SDK first, toolchain second
toolchain_call = mock_nrf52_ops.download_from_mirrors.call_args_list[1]
mirrors = toolchain_call.args[0]
assert all("toolchain_gnu_" in m for m in mirrors)
def test_toolchain_extracts_under_gnu_for_sdk_3_4_0(
self,
tmp_path: Path,
mock_nrf52_ops: SimpleNamespace,
) -> None:
"""SDK 1.0+ toolchain archive must land in gnu/arm-zephyr-eabi/.
Zephyr-sdkConfig.cmake validates the toolchain at gnu/arm-zephyr-eabi/
in SDK 1.0+; if the archive is extracted to arm-zephyr-eabi/ instead,
cmake reports the package as not found.
"""
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: Version.parse("3.4.0")}
sdk_version = "3.4.0"
tools = get_sdk_nrf_tools_path()
python_env = tools / "penvs" / f"v{sdk_version}"
framework = tools / "frameworks" / f"v{sdk_version}"
toolchain_dir = tools / "toolchains" / "1.0.1"
for d in (python_env, framework, toolchain_dir):
d.mkdir(parents=True, exist_ok=True)
(framework / "zephyr" / "scripts").mkdir(parents=True, exist_ok=True)
(framework / "zephyr" / "scripts" / "requirements.txt").touch()
_mark_venv_ready(python_env)
(framework / ".ready").touch()
check_and_install()
# Two extract calls: minimal SDK first (to toolchain root), toolchain second
extract_calls = mock_nrf52_ops.archive_extract_all.call_args_list
_, toolchain_extract_dir = extract_calls[1].args[:2]
assert toolchain_extract_dir == toolchain_dir / "gnu" / "arm-zephyr-eabi"
# ---------------------------------------------------------------------------
# setup_platformio_python_env tests
@@ -1040,6 +1098,44 @@ def test_get_build_env(
assert env["SOME_PREEXISTING_VAR"] == "kept"
def test_get_build_env_sdk_3_4_0_uses_toolchain_root(
setup_core: Path,
) -> None:
"""For NCS >= 3.4.0, ZEPHYR_SDK_INSTALL_DIR still points at the toolchain root."""
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: Version.parse("3.4.0")}
env = get_build_env()
tools = get_sdk_nrf_tools_path()
assert env["ZEPHYR_SDK_INSTALL_DIR"] == str(tools / "toolchains" / "1.0.1")
assert "Zephyr-sdk_DIR" not in env
def test_install_toolchain_keeps_other_live_toolchain_leftovers(
setup_core: Path,
) -> None:
"""Pruning must not delete a partial download of the other toolchain in use."""
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: Version.parse("3.4.0")}
toolchains = get_sdk_nrf_tools_path() / "toolchains"
toolchains.mkdir(parents=True)
own = toolchains / "1.0.1.toolchain.archive.part"
other = toolchains / f"{TOOLCHAIN_VERSION}.toolchain.archive.part"
retired = toolchains / "0.16.8.toolchain.archive.part"
for leftover in (own, other, retired):
leftover.write_text("")
with patch(
"esphome.components.nrf52.framework.download_and_extract",
side_effect=lambda *args, **kwargs: args[3].mkdir(parents=True),
):
_install_toolchain()
assert not own.exists()
assert other.exists()
assert not retired.exists()
assert (toolchains / "1.0.1" / ".ready").exists()
# ---------------------------------------------------------------------------
# get_sdk_nrf_tools_path tests
# ---------------------------------------------------------------------------
+16
View File
@@ -24,6 +24,7 @@ def _set_sdk_version(version: cv.Version) -> None:
def test_binutils_come_from_the_pinned_zephyr_sdk() -> None:
_set_sdk_version(cv.Version(2, 9, 0))
bin_path = (
get_sdk_nrf_tools_path()
/ "toolchains"
@@ -37,6 +38,21 @@ def test_binutils_come_from_the_pinned_zephyr_sdk() -> None:
assert toolchain.get_readelf_path() == bin_path / f"arm-zephyr-eabi-readelf{suffix}"
def test_binutils_use_new_toolchain_for_sdk_3_4_0() -> None:
_set_sdk_version(cv.Version(3, 4, 0))
bin_path = (
get_sdk_nrf_tools_path()
/ "toolchains"
/ "1.0.1"
/ "gnu"
/ "arm-zephyr-eabi"
/ "bin"
)
suffix = ".exe" if os.name == "nt" else ""
assert toolchain.get_objdump_path() == bin_path / f"arm-zephyr-eabi-objdump{suffix}"
assert toolchain.get_readelf_path() == bin_path / f"arm-zephyr-eabi-readelf{suffix}"
def test_elf_uses_the_nested_layout_from_sdk_2_9_2(nrf52_build: Path) -> None:
_set_sdk_version(cv.Version(2, 9, 2))
# A flat image left by an older SDK must not be picked over the current