Fix idf_tools_check parser for ESP-IDF v5.x multi-line output
The v5.x check command uses multi-line blocks per tool instead of single-line "tool version: found" format. Parser now tracks "Checking tool <name>" headings and "version installed in tools directory:" lines. Old single-line format still supported.
This commit is contained in:
parent
877fb273d0
commit
edc1be4cc3
@ -133,15 +133,53 @@ def _parse_tools_check(output: str) -> dict[str, Any]:
|
||||
"""Parse ``idf_tools.py check`` output.
|
||||
|
||||
Returns dict with ``installed`` and ``missing`` lists.
|
||||
|
||||
ESP-IDF v5.x uses a multi-line format per tool::
|
||||
|
||||
Checking tool xtensa-esp-elf
|
||||
no version found in PATH
|
||||
version installed in tools directory: esp-13.2.0_20240530
|
||||
|
||||
A tool counts as *installed* when at least one ``version installed``
|
||||
line appears under its heading. It counts as *missing* when there is
|
||||
no such line (or only "no version found" lines).
|
||||
|
||||
The parser also handles the older single-line format
|
||||
``tool version: found`` / ``tool version: not found``.
|
||||
"""
|
||||
installed: list[str] = []
|
||||
missing: list[str] = []
|
||||
|
||||
current_tool: str | None = None
|
||||
has_installed_version = False
|
||||
|
||||
for line in output.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
# Lines look like: "xtensa-esp-elf 14.2.0_20241119: found" or "... not found"
|
||||
|
||||
# Multi-line format: "Checking tool <name>"
|
||||
if stripped.startswith("Checking tool "):
|
||||
# Flush previous tool
|
||||
if current_tool is not None:
|
||||
(installed if has_installed_version else missing).append(current_tool)
|
||||
current_tool = stripped[len("Checking tool "):]
|
||||
has_installed_version = False
|
||||
continue
|
||||
|
||||
# Multi-line format: indented status lines under a tool heading
|
||||
if current_tool is not None and line[0] in (" ", "\t"):
|
||||
if "version installed in tools directory:" in stripped:
|
||||
version = stripped.split(":", 1)[1].strip()
|
||||
has_installed_version = True
|
||||
# Enrich the tool name with the installed version
|
||||
current_tool_with_ver = f"{current_tool} {version}"
|
||||
# Replace plain name if this is the first version found
|
||||
if has_installed_version and " " not in current_tool:
|
||||
current_tool = current_tool_with_ver
|
||||
continue
|
||||
|
||||
# Single-line format: "xtensa-esp-elf 14.2.0: found" (older IDF)
|
||||
if ": found" in stripped:
|
||||
tool_name = stripped.split(":")[0].strip()
|
||||
installed.append(tool_name)
|
||||
@ -149,6 +187,10 @@ def _parse_tools_check(output: str) -> dict[str, Any]:
|
||||
tool_name = stripped.split(":")[0].strip()
|
||||
missing.append(tool_name)
|
||||
|
||||
# Flush final tool from multi-line parsing
|
||||
if current_tool is not None:
|
||||
(installed if has_installed_version else missing).append(current_tool)
|
||||
|
||||
return {"installed": installed, "missing": missing}
|
||||
|
||||
|
||||
|
||||
@ -170,7 +170,8 @@ class TestParseToolsList:
|
||||
class TestParseToolsCheck:
|
||||
"""Tests for _parse_tools_check parser."""
|
||||
|
||||
SAMPLE_OUTPUT = textwrap.dedent("""\
|
||||
# Old single-line format (kept for backwards compat)
|
||||
SAMPLE_OUTPUT_LEGACY = textwrap.dedent("""\
|
||||
xtensa-esp-elf 14.2.0_20241119: found
|
||||
riscv32-esp-elf 14.2.0_20241119: found
|
||||
xtensa-esp-elf-gdb 14.2_20240403: found
|
||||
@ -179,22 +180,42 @@ class TestParseToolsCheck:
|
||||
ninja 1.11.1: not found
|
||||
""")
|
||||
|
||||
# Real ESP-IDF v5.3 multi-line format
|
||||
SAMPLE_OUTPUT_V5 = textwrap.dedent("""\
|
||||
Checking for installed tools...
|
||||
Checking tool xtensa-esp-elf-gdb
|
||||
no version found in PATH
|
||||
version installed in tools directory: 14.2_20240403
|
||||
Checking tool riscv32-esp-elf-gdb
|
||||
no version found in PATH
|
||||
version installed in tools directory: 14.2_20240403
|
||||
Checking tool xtensa-esp-elf
|
||||
no version found in PATH
|
||||
version installed in tools directory: esp-13.2.0_20240530
|
||||
Checking tool cmake
|
||||
version found in PATH: 4.2.2
|
||||
version installed in tools directory: 3.24.0
|
||||
Checking tool qemu-riscv32
|
||||
no version found in PATH
|
||||
""")
|
||||
|
||||
# Legacy single-line tests
|
||||
def test_installed_tools(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||
assert "xtensa-esp-elf 14.2.0_20241119" in result["installed"]
|
||||
assert "riscv32-esp-elf 14.2.0_20241119" in result["installed"]
|
||||
|
||||
def test_missing_tools(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||
assert "cmake 3.24.0" in result["missing"]
|
||||
assert "ninja 1.11.1" in result["missing"]
|
||||
|
||||
def test_installed_count(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||
assert len(result["installed"]) == 4
|
||||
|
||||
def test_missing_count(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||
assert len(result["missing"]) == 2
|
||||
|
||||
def test_empty_output(self):
|
||||
@ -213,6 +234,34 @@ class TestParseToolsCheck:
|
||||
assert len(result["installed"]) == 0
|
||||
assert len(result["missing"]) == 2
|
||||
|
||||
# Multi-line format tests (ESP-IDF v5.x)
|
||||
def test_v5_installed_tools(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||
names = [t.split()[0] for t in result["installed"]]
|
||||
assert "xtensa-esp-elf-gdb" in names
|
||||
assert "riscv32-esp-elf-gdb" in names
|
||||
assert "xtensa-esp-elf" in names
|
||||
assert "cmake" in names
|
||||
|
||||
def test_v5_missing_tools(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||
assert "qemu-riscv32" in result["missing"]
|
||||
|
||||
def test_v5_installed_count(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||
assert len(result["installed"]) == 4
|
||||
|
||||
def test_v5_missing_count(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||
assert len(result["missing"]) == 1
|
||||
|
||||
def test_v5_version_included_in_name(self):
|
||||
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||
# Installed tools should include version from "version installed" line
|
||||
installed_str = " ".join(result["installed"])
|
||||
assert "14.2_20240403" in installed_str
|
||||
assert "esp-13.2.0_20240530" in installed_str
|
||||
|
||||
|
||||
class TestParseExportVars:
|
||||
"""Tests for _parse_export_vars parser."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user