kopia lustrzana https://github.com/Aircoookie/WLED
Validate usermods at link time
Add additional validation of the linker .map output to confirm that the correct usermods were added.pull/4669/head
rodzic
ee3864175d
commit
999637f8ad
|
@ -0,0 +1,92 @@
|
|||
import re
|
||||
import sys
|
||||
from pathlib import Path # For OS-agnostic path manipulation
|
||||
from click import secho
|
||||
from SCons.Script import Action, Exit
|
||||
from platformio import util
|
||||
|
||||
def read_lines(p: Path):
|
||||
""" Read in the contents of a file for analysis """
|
||||
with p.open("r", encoding="utf-8", errors="ignore") as f:
|
||||
return f.readlines()
|
||||
|
||||
def check_map_file_objects(map_file: list[str], usermod_dirs: list[str]) -> set[str]:
|
||||
""" Checks that an object file from each usermod_dir appears in the linked output
|
||||
|
||||
Returns the (sub)set of usermod_dirs that are found in the output ELF
|
||||
"""
|
||||
# Pattern to match symbols in object directories
|
||||
# Join directories into alternation
|
||||
usermod_dir_regex = "|".join([re.escape(dir) for dir in usermod_dirs])
|
||||
# Matches nonzero address, any size, and any path in a matching directory
|
||||
object_path_regex = re.compile(r"0x0*[1-9a-f][0-9a-f]*\s+0x[0-9a-f]+\s+\S+/(" + usermod_dir_regex + r")/\S+\.o")
|
||||
|
||||
found = set()
|
||||
for line in map_file:
|
||||
matches = object_path_regex.findall(line)
|
||||
for m in matches:
|
||||
found.add(m)
|
||||
return found
|
||||
|
||||
def count_registered_usermods(map_file: list[str]) -> int:
|
||||
""" Returns the number of usermod objects in the usermod list """
|
||||
# Count the number of entries in the usermods table section
|
||||
return len([x for x in map_file if ".dtors.tbl.usermods.1" in x])
|
||||
|
||||
|
||||
def validate_map_file(source, target, env):
|
||||
""" Validate that all usermods appear in the output build """
|
||||
build_dir = Path(env.subst("$BUILD_DIR"))
|
||||
map_file_path = build_dir / env.subst("${PROGNAME}.map")
|
||||
|
||||
if not map_file_path.exists():
|
||||
secho(f"ERROR: Map file not found: {map_file_path}", fg="red", err=True)
|
||||
Exit(1)
|
||||
|
||||
# Load project settings
|
||||
usermods = env.GetProjectOption("custom_usermods","").split()
|
||||
libdeps = env.GetProjectOption("lib_deps", [])
|
||||
lib_builders = env.GetLibBuilders()
|
||||
|
||||
secho(f"INFO: Expecting {len(usermods)} usermods: {', '.join(usermods)}")
|
||||
|
||||
# Map the usermods to libdeps; every usermod should have one
|
||||
usermod_dirs = []
|
||||
for mod in usermods:
|
||||
modstr = f"{mod} = symlink://"
|
||||
this_mod_libdeps = [libdep[len(modstr):] for libdep in libdeps if libdep.startswith(modstr)]
|
||||
if not this_mod_libdeps:
|
||||
secho(
|
||||
f"ERROR: Usermod {mod} not found in build libdeps!",
|
||||
fg="red",
|
||||
err=True)
|
||||
Exit(1)
|
||||
# Save only the final folder name
|
||||
usermod_dir = Path(this_mod_libdeps[0]).name
|
||||
# Search lib_builders
|
||||
this_mod_builders = [builder for builder in lib_builders if Path(builder.src_dir).name == usermod_dir]
|
||||
if not this_mod_builders:
|
||||
secho(
|
||||
f"ERROR: Usermod {mod} not found in library builders!",
|
||||
fg="red",
|
||||
err=True)
|
||||
Exit(1)
|
||||
usermod_dirs.append(usermod_dir)
|
||||
|
||||
# Now parse the map file
|
||||
map_file_contents = read_lines(map_file_path)
|
||||
confirmed_usermods = check_map_file_objects(map_file_contents, usermod_dirs)
|
||||
usermod_object_count = count_registered_usermods(map_file_contents)
|
||||
|
||||
secho(f"INFO: {len(usermod_dirs)}/{len(usermods)} libraries linked via custom_usermods, producing {usermod_object_count} usermod object entries")
|
||||
missing_usermods = confirmed_usermods.difference(usermod_dirs)
|
||||
if missing_usermods:
|
||||
secho(
|
||||
f"ERROR: No object files from {missing_usermods} found in linked output!",
|
||||
fg="red",
|
||||
err=True)
|
||||
Exit(1)
|
||||
return None
|
||||
|
||||
Import("env")
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking map file...'))
|
|
@ -116,6 +116,7 @@ extra_scripts =
|
|||
pre:pio-scripts/user_config_copy.py
|
||||
pre:pio-scripts/load_usermods.py
|
||||
pre:pio-scripts/build_ui.py
|
||||
post:pio-scripts/validate_usermods.py ;; double-check the build output usermods
|
||||
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Ładowanie…
Reference in New Issue