mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
Merge branch 'esp8266-native-ninja-emission' into esp8266-arduino-toolchain
This commit is contained in:
+19
-19
@@ -99,14 +99,18 @@ def _resolve_src_dir(name: str, read_path: Path, build: dict) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _reject_unsupported_link_fields(name: str, data: dict) -> None:
|
def _reject_unsupported_link_fields(name: str, data: dict) -> None:
|
||||||
for dropped_key in ("precompiled", "ldflags"):
|
# PIO's Arduino lib builder honors these; building without them would
|
||||||
if data.get(dropped_key):
|
# fail at link with no stated cause. library.properties values are
|
||||||
# PIO's Arduino lib builder honors these; building without them
|
# strings, so "false" (the spec's explicit opt-out) is not a declaration.
|
||||||
# would fail at link with no stated cause
|
precompiled = data.get("precompiled")
|
||||||
raise EsphomeError(
|
if precompiled and str(precompiled).strip().lower() != "false":
|
||||||
f"Library {name} declares {dropped_key}, which this backend "
|
raise EsphomeError(
|
||||||
"does not support"
|
f"Library {name} declares precompiled, which this backend does not support"
|
||||||
)
|
)
|
||||||
|
if data.get("ldflags"):
|
||||||
|
raise EsphomeError(
|
||||||
|
f"Library {name} declares ldflags, which this backend does not support"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_lib_archive(name: str, data: dict, build: dict) -> bool:
|
def _resolve_lib_archive(name: str, data: dict, build: dict) -> bool:
|
||||||
@@ -218,18 +222,14 @@ def _collect_lib_sources(
|
|||||||
len(dropped),
|
len(dropped),
|
||||||
", ".join(sorted(dropped)),
|
", ".join(sorted(dropped)),
|
||||||
)
|
)
|
||||||
if (
|
if not lib.sources and not any(
|
||||||
not lib.sources
|
Path(f).suffix.lower() in LIBRARY_HEADER_SUFFIXES for f in matched
|
||||||
and ("srcFilter" in build or "srcDir" in build)
|
|
||||||
and not any(Path(f).suffix.lower() in LIBRARY_HEADER_SUFFIXES for f in matched)
|
|
||||||
):
|
):
|
||||||
# Matched headers mean a header-only library; a declared filter
|
# Matched headers mean a header-only library; a filter matching
|
||||||
# matching nothing (or only inert files) is a manifest/tree problem.
|
# nothing (or only inert files) is a manifest/tree problem whether
|
||||||
# The truly empty tree raises via _assert_tree_has_code.
|
# or not it was declared. The truly empty tree raises via
|
||||||
_LOGGER.warning(
|
# _assert_tree_has_code.
|
||||||
"Library %s declares srcFilter/srcDir but no source files matched",
|
_LOGGER.warning("Library %s: no source files matched", name)
|
||||||
name,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
|
def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
|
||||||
|
|||||||
@@ -67,9 +67,11 @@ def _run_ar(ar: str, archive: str, rspfile: str) -> int:
|
|||||||
def _run_copy(src: str, dst: str) -> int:
|
def _run_copy(src: str, dst: str) -> int:
|
||||||
try:
|
try:
|
||||||
shutil.copyfile(src, dst)
|
shutil.copyfile(src, dst)
|
||||||
except OSError:
|
except OSError as err:
|
||||||
# Never leave a partially written output (e.g. a firmware image)
|
# Never leave a partially written output (e.g. a firmware image);
|
||||||
Path(dst).unlink(missing_ok=True)
|
# SameFileError means dst IS src, where unlinking destroys the input
|
||||||
|
if not isinstance(err, shutil.SameFileError):
|
||||||
|
Path(dst).unlink(missing_ok=True)
|
||||||
raise
|
raise
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
@@ -188,6 +189,20 @@ def test_surplus_arguments_error(capsys: pytest.CaptureFixture[str]) -> None:
|
|||||||
assert "expected 2 arguments, got 3" in capsys.readouterr().err
|
assert "expected 2 arguments, got 3" in capsys.readouterr().err
|
||||||
|
|
||||||
|
|
||||||
|
def test_copy_same_file_keeps_the_input(tmp_path: Path) -> None:
|
||||||
|
"""A same-file copy (dst IS src) must not unlink the input."""
|
||||||
|
src = tmp_path / "firmware.bin"
|
||||||
|
src.write_bytes(b"image")
|
||||||
|
with (
|
||||||
|
patch.object(
|
||||||
|
build_tool.sys, "argv", ["build_tool", "copy", str(src), str(src)]
|
||||||
|
),
|
||||||
|
pytest.raises(shutil.SameFileError),
|
||||||
|
):
|
||||||
|
build_tool.main()
|
||||||
|
assert src.read_bytes() == b"image"
|
||||||
|
|
||||||
|
|
||||||
def test_copy_failure_leaves_no_partial_output(tmp_path: Path) -> None:
|
def test_copy_failure_leaves_no_partial_output(tmp_path: Path) -> None:
|
||||||
"""A failed copy unlinks the destination; a partial firmware image must
|
"""A failed copy unlinks the destination; a partial firmware image must
|
||||||
never be left on disk."""
|
never be left on disk."""
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ def test_library_info_declared_filter_matches_nothing_warns(
|
|||||||
data = {"build": {"srcFilter": ["+<nothing/*>"]}}
|
data = {"build": {"srcFilter": ["+<nothing/*>"]}}
|
||||||
lib = component._library_info("x", read_path, data)
|
lib = component._library_info("x", read_path, data)
|
||||||
assert not lib.sources
|
assert not lib.sources
|
||||||
assert "declares srcFilter/srcDir but no source files matched" in caplog.text
|
assert "no source files matched" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_empty_converted_tree_raises_at_emit(tmp_path: Path) -> None:
|
def test_empty_converted_tree_raises_at_emit(tmp_path: Path) -> None:
|
||||||
@@ -606,6 +606,44 @@ def test_library_info_unsupported_link_fields_raise(tmp_path: Path) -> None:
|
|||||||
component._library_info("x", read_path, {"ldflags": "-lfoo", "build": {}})
|
component._library_info("x", read_path, {"ldflags": "-lfoo", "build": {}})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ["false", "False", " false ", "", False, None])
|
||||||
|
def test_library_info_precompiled_opt_out_accepted(
|
||||||
|
tmp_path: Path, value: object
|
||||||
|
) -> None:
|
||||||
|
"""Manifest values are strings; precompiled=false is the spec's
|
||||||
|
explicit opt-out, not a declaration."""
|
||||||
|
read_path = tmp_path / "lib"
|
||||||
|
(read_path / "src").mkdir(parents=True)
|
||||||
|
(read_path / "src" / "stub.cpp").write_text("")
|
||||||
|
data = {"build": {}}
|
||||||
|
if value is not None:
|
||||||
|
data["precompiled"] = value
|
||||||
|
component._library_info("x", read_path, data)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ["full", True, "weird"])
|
||||||
|
def test_library_info_precompiled_set_raises(tmp_path: Path, value: object) -> None:
|
||||||
|
"""Both full (Arduino's other legal value) and unknown spellings fail safe."""
|
||||||
|
read_path = tmp_path / "lib"
|
||||||
|
(read_path / "src").mkdir(parents=True)
|
||||||
|
(read_path / "src" / "stub.cpp").write_text("")
|
||||||
|
with pytest.raises(EsphomeError, match="declares precompiled"):
|
||||||
|
component._library_info("x", read_path, {"precompiled": value, "build": {}})
|
||||||
|
|
||||||
|
|
||||||
|
def test_library_info_default_filter_matching_nothing_warns(
|
||||||
|
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
|
"""The empty-match warning is not gated on a declared srcFilter/srcDir;
|
||||||
|
a default-filter src/ holding only inert files warns too."""
|
||||||
|
read_path = tmp_path / "lib"
|
||||||
|
(read_path / "src").mkdir(parents=True)
|
||||||
|
(read_path / "src" / "keywords.txt").write_text("")
|
||||||
|
lib = component._library_info("x", read_path, {"build": {}})
|
||||||
|
assert not lib.sources
|
||||||
|
assert "no source files matched" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_library_info_unmapped_sources_warn(
|
def test_library_info_unmapped_sources_warn(
|
||||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user