Fix usermod validation portability

Use proper path analysis instead of bare text matching.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Will Miles
2026-03-10 09:56:39 -04:00
parent 53d6c4b059
commit 3bfdb736e3

View File

@@ -49,25 +49,36 @@ def check_elf_modules(elf_path: Path, env, module_lib_builders) -> set[str]:
secho(f"WARNING: nm failed ({e}); skipping per-module validation", fg="yellow", err=True)
return {Path(b.build_dir).name for b in module_lib_builders} # conservative pass
# Build a filtered set of lines that have a nonzero address.
# Collect source file Paths from placed symbols (nonzero address only).
# nm --defined-only still includes debugging symbols (type 'N') such as the
# per-CU markers GCC emits in .debug_info (e.g. "usermod_example_cpp_6734d48d").
# These live at address 0x00000000 in their debug section — not in any load
# segment — so filtering them out leaves only genuinely placed symbols.
placed_lines = [
line for line in nm_output.splitlines()
if (parts := line.split(None, 1)) and parts[0].lstrip('0')
]
placed_output = "\n".join(placed_lines)
# nm -l appends a tab-separated "file:lineno" location to each symbol line.
placed_paths: set[Path] = set()
for line in nm_output.splitlines():
parts = line.split(None, 1)
if not (parts and parts[0].lstrip('0')):
continue # zero address — skip debug-section marker
if '\t' in line:
loc = line.rsplit('\t', 1)[1]
# Strip trailing :lineno (e.g. "/path/to/foo.cpp:42" → "/path/to/foo.cpp")
file_part = loc.rsplit(':', 1)[0]
placed_paths.add(Path(file_part))
found = set()
for builder in module_lib_builders:
# builder.src_dir is the library source directory (used by is_wled_module() too)
src_dir = str(builder.src_dir).rstrip("/\\")
# Guard against prefix collisions (e.g. /path/to/mod vs /path/to/mod-extra)
# by requiring a path separator immediately after the directory name.
if re.search(re.escape(src_dir) + r'[/\\]', placed_output):
found.add(Path(builder.build_dir).name)
src_dir = Path(str(builder.src_dir))
# Path.is_relative_to() / relative_to() handles OS-specific separators
# correctly without any regex, avoiding Windows path escaping issues.
for p in placed_paths:
try:
p.relative_to(src_dir)
found.add(Path(builder.build_dir).name)
break
except ValueError:
pass
return found