Merge pull request #4669 from willmmiles/4597-usermods-not-building

Correct issues with usermods not being linked.
- Explicitly set libArchive: false in usermod library.json files
- Fix up symlink path generation on Windows
- Add validation script to report usermod linkage in resulting binary
This commit is contained in:
Will Miles 2025-05-26 22:41:45 -04:00 committed by GitHub
commit 4a3af814bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 236 additions and 97 deletions

View File

@ -1,17 +1,14 @@
Import('env') Import('env')
import os.path
from collections import deque from collections import deque
from pathlib import Path # For OS-agnostic path manipulation from pathlib import Path # For OS-agnostic path manipulation
from platformio.package.manager.library import LibraryPackageManager from click import secho
from SCons.Script import Exit
from platformio.builder.tools.piolib import LibBuilderBase
usermod_dir = Path(env["PROJECT_DIR"]) / "usermods" usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
if env['PIOENV'] == "usermods": # Utility functions
# Add all usermods def find_usermod(mod: str) -> Path:
env.GetProjectConfig().set(f"env:usermods", 'custom_usermods', " ".join([f.name for f in all_usermods]))
def find_usermod(mod: str):
"""Locate this library in the usermods folder. """Locate this library in the usermods folder.
We do this to avoid needing to rename a bunch of folders; We do this to avoid needing to rename a bunch of folders;
this could be removed later this could be removed later
@ -22,51 +19,36 @@ def find_usermod(mod: str):
return mp return mp
mp = usermod_dir / f"{mod}_v2" mp = usermod_dir / f"{mod}_v2"
if mp.exists(): if mp.exists():
return mp return mp
mp = usermod_dir / f"usermod_v2_{mod}" mp = usermod_dir / f"usermod_v2_{mod}"
if mp.exists(): if mp.exists():
return mp return mp
raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!") raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
def is_wled_module(dep: LibBuilderBase) -> bool:
"""Returns true if the specified library is a wled module
"""
return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-")
## Script starts here
# Process usermod option
usermods = env.GetProjectOption("custom_usermods","") usermods = env.GetProjectOption("custom_usermods","")
# Handle "all usermods" case
if usermods == '*':
usermods = [f.name for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
else:
usermods = usermods.split()
if usermods: if usermods:
# Inject usermods in to project lib_deps # Inject usermods in to project lib_deps
proj = env.GetProjectConfig() symlinks = [f"symlink://{find_usermod(mod).resolve()}" for mod in usermods]
deps = env.GetProjectOption('lib_deps') env.GetProjectConfig().set("env:" + env['PIOENV'], 'lib_deps', env.GetProjectOption('lib_deps') + symlinks)
src_dir = proj.get("platformio", "src_dir")
src_dir = src_dir.replace('\\','/')
mod_paths = {mod: find_usermod(mod) for mod in usermods.split()}
usermods = [f"{mod} = symlink://{path}" for mod, path in mod_paths.items()]
proj.set("env:" + env['PIOENV'], 'lib_deps', deps + usermods)
# Force usermods to be installed in to the environment build state before the LDF runs
# Otherwise we won't be able to see them until it's too late to change their paths for LDF
# Logic is largely borrowed from PlaformIO internals
not_found_specs = []
for spec in usermods:
found = False
for storage_dir in env.GetLibSourceDirs():
#print(f"Checking {storage_dir} for {spec}")
lm = LibraryPackageManager(storage_dir)
if lm.get_package(spec):
#print("Found!")
found = True
break
if not found:
#print("Missing!")
not_found_specs.append(spec)
if not_found_specs:
lm = LibraryPackageManager(
env.subst(os.path.join("$PROJECT_LIBDEPS_DIR", "$PIOENV"))
)
for spec in not_found_specs:
#print(f"LU: forcing install of {spec}")
lm.install(spec)
# Utility function for assembling usermod include paths # Utility function for assembling usermod include paths
def cached_add_includes(dep, dep_cache: set, includes: deque): def cached_add_includes(dep, dep_cache: set, includes: deque):
""" Add dep's include paths to includes if it's not in the cache """ """ Add dep's include paths to includes if it's not in the cache """
if dep not in dep_cache: if dep not in dep_cache:
dep_cache.add(dep) dep_cache.add(dep)
for include in dep.get_include_dirs(): for include in dep.get_include_dirs():
if include not in includes: if include not in includes:
@ -82,13 +64,6 @@ old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
# Our new wrapper # Our new wrapper
def wrapped_ConfigureProjectLibBuilder(xenv): def wrapped_ConfigureProjectLibBuilder(xenv):
# Update usermod properties
# Set libArchive before build actions are added
for um in (um for um in xenv.GetLibBuilders() if usermod_dir in Path(um.src_dir).parents):
build = um._manifest.get("build", {})
build["libArchive"] = False
um._manifest["build"] = build
# Call the wrapped function # Call the wrapped function
result = old_ConfigureProjectLibBuilder.clone(xenv)() result = old_ConfigureProjectLibBuilder.clone(xenv)()
@ -102,12 +77,25 @@ def wrapped_ConfigureProjectLibBuilder(xenv):
for dep in result.depbuilders: for dep in result.depbuilders:
cached_add_includes(dep, processed_deps, extra_include_dirs) cached_add_includes(dep, processed_deps, extra_include_dirs)
for um in [dep for dep in result.depbuilders if usermod_dir in Path(dep.src_dir).parents]: broken_usermods = []
# Add the wled folder to the include path for dep in result.depbuilders:
um.env.PrependUnique(CPPPATH=wled_dir) if is_wled_module(dep):
# Add WLED's own dependencies # Add the wled folder to the include path
for dir in extra_include_dirs: dep.env.PrependUnique(CPPPATH=str(wled_dir))
um.env.PrependUnique(CPPPATH=dir) # Add WLED's own dependencies
for dir in extra_include_dirs:
dep.env.PrependUnique(CPPPATH=str(dir))
# Enforce that libArchive is not set; we must link them directly to the executable
if dep.lib_archive:
broken_usermods.append(dep)
if broken_usermods:
broken_usermods = [usermod.name for usermod in broken_usermods]
secho(
f"ERROR: libArchive=false is missing on usermod(s) {' '.join(broken_usermods)} -- modules will not compile in correctly",
fg="red",
err=True)
Exit(1)
return result return result

View File

@ -0,0 +1,92 @@
import re
from pathlib import Path # For OS-agnostic path manipulation
from typing import Iterable
from click import secho
from SCons.Script import Action, Exit
from platformio.builder.tools.piolib import LibBuilderBase
def is_wled_module(env, dep: LibBuilderBase) -> bool:
"""Returns true if the specified library is a wled module
"""
usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-")
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], dirs: Iterable[str]) -> set[str]:
""" Identify which dirs contributed to the final build
Returns the (sub)set of 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 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_usermod_objects(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 modules 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)
# Identify the WLED module source directories
module_lib_builders = [builder for builder in env.GetLibBuilders() if is_wled_module(env, builder)]
if env.GetProjectOption("custom_usermods","") == "*":
# All usermods build; filter non-platform-OK modules
module_lib_builders = [builder for builder in module_lib_builders if env.IsCompatibleLibBuilder(builder)]
else:
incompatible_builders = [builder for builder in module_lib_builders if not env.IsCompatibleLibBuilder(builder)]
if incompatible_builders:
secho(
f"ERROR: Modules {[b.name for b in incompatible_builders]} are not compatible with this platform!",
fg="red",
err=True)
Exit(1)
# Extract the values we care about
modules = {Path(builder.build_dir).name: builder.name for builder in module_lib_builders}
secho(f"INFO: {len(modules)} libraries linked as WLED optional/user modules")
# Now parse the map file
map_file_contents = read_lines(map_file_path)
usermod_object_count = count_usermod_objects(map_file_contents)
secho(f"INFO: {usermod_object_count} usermod object entries")
confirmed_modules = check_map_file_objects(map_file_contents, modules.keys())
missing_modules = [modname for mdir, modname in modules.items() if mdir not in confirmed_modules]
if missing_modules:
secho(
f"ERROR: No object files from {missing_modules} found in linked output!",
fg="red",
err=True)
Exit(1)
return None
Import("env")
env.Append(LINKFLAGS=[env.subst("-Wl,--Map=${BUILD_DIR}/${PROGNAME}.map")])
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking linked optional modules (usermods) in map file'))

View File

@ -116,6 +116,7 @@ extra_scripts =
pre:pio-scripts/user_config_copy.py pre:pio-scripts/user_config_copy.py
pre:pio-scripts/load_usermods.py pre:pio-scripts/load_usermods.py
pre:pio-scripts/build_ui.py pre:pio-scripts/build_ui.py
post:pio-scripts/validate_modules.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) ; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -659,5 +660,5 @@ build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_
lib_deps = ${esp32_idf_V4.lib_deps} lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.flash_mode = dio board_build.flash_mode = dio
; custom_usermods = *every folder with library.json* -- injected by pio-scripts/load_usermods.py custom_usermods = * ; Expands to all usermods in usermods folder
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat

View File

@ -1,5 +1,6 @@
{ {
"name": "ADS1115_v2", "name": "ADS1115_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2", "Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0" "Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"

View File

@ -1,5 +1,6 @@
{ {
"name": "AHT10_v2", "name": "AHT10_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"enjoyneering/AHT10":"~1.1.0" "enjoyneering/AHT10":"~1.1.0"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "Analog_Clock" "name": "Analog_Clock",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "Animated_Staircase" "name": "Animated_Staircase",
"build": { "libArchive": false }
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "BH1750_v2", "name": "BH1750_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"claws/BH1750":"^1.2.0" "claws/BH1750":"^1.2.0"
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "BME280_v2", "name": "BME280_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"finitespace/BME280":"~3.0.0" "finitespace/BME280":"~3.0.0"
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "BME68X", "name": "BME68X",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"boschsensortec/BSEC Software Library":"^1.8.1492" "boschsensortec/BSEC Software Library":"^1.8.1492"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "Battery" "name": "Battery",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "Cronixie" "name": "Cronixie",
"build": { "libArchive": false }
} }

View File

@ -1,4 +1,5 @@
{ {
"name": "EXAMPLE", "name": "EXAMPLE",
"build": { "libArchive": false },
"dependencies": {} "dependencies": {}
} }

View File

@ -1,5 +1,6 @@
{ {
"name:": "EleksTube_IPS", "name:": "EleksTube_IPS",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"TFT_eSPI" : "2.5.33" "TFT_eSPI" : "2.5.33"
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "INA226_v2", "name": "INA226_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"wollewald/INA226_WE":"~1.2.9" "wollewald/INA226_WE":"~1.2.9"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "Internal_Temperature_v2" "name": "Internal_Temperature_v2",
"build": { "libArchive": false }
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "LD2410_v2", "name": "LD2410_v2",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"ncmreynolds/ld2410":"^0.1.3" "ncmreynolds/ld2410":"^0.1.3"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "LDR_Dusk_Dawn_v2" "name": "LDR_Dusk_Dawn_v2",
"build": { "libArchive": false }
} }

View File

@ -1,4 +1,5 @@
{ {
"name": "MY9291", "name": "MY9291",
"build": { "libArchive": false },
"platforms": ["espressif8266"] "platforms": ["espressif8266"]
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "PIR_sensor_switch" "name": "PIR_sensor_switch",
"build": { "libArchive": false }
} }

View File

@ -1,6 +1,7 @@
{ {
"name": "PWM_fan", "name": "PWM_fan",
"build": { "build": {
"libArchive": false,
"extraScript": "setup_deps.py" "extraScript": "setup_deps.py"
} }
} }

View File

@ -1,11 +1,12 @@
from platformio.package.meta import PackageSpec
Import('env') Import('env')
usermods = env.GetProjectOption("custom_usermods","").split() libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
# Check for dependencies # Check for dependencies
if "Temperature" in usermods: if "Temperature" in libs:
env.Append(CPPDEFINES=[("USERMOD_DALLASTEMPERATURE")]) env.Append(CPPDEFINES=[("USERMOD_DALLASTEMPERATURE")])
elif "sht" in usermods: elif "sht" in libs:
env.Append(CPPDEFINES=[("USERMOD_SHT")]) env.Append(CPPDEFINES=[("USERMOD_SHT")])
elif "PWM_fan" in usermods: # The script can be run if this module was previously selected elif "PWM_fan" in libs: # The script can be run if this module was previously selected
raise RuntimeError("PWM_fan usermod requires Temperature or sht to be enabled") raise RuntimeError("PWM_fan usermod requires Temperature or sht to be enabled")

View File

@ -1,3 +1,4 @@
{ {
"name": "RTC" "name": "RTC",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "SN_Photoresistor" "name": "SN_Photoresistor",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name:": "ST7789_display" "name:": "ST7789_display",
"build": { "libArchive": false }
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "Si7021_MQTT_HA", "name": "Si7021_MQTT_HA",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"finitespace/BME280":"3.0.0", "finitespace/BME280":"3.0.0",
"adafruit/Adafruit Si7021 Library" : "1.5.3" "adafruit/Adafruit Si7021 Library" : "1.5.3"

View File

@ -1,3 +1,4 @@
{ {
"name": "TetrisAI_v2" "name": "TetrisAI_v2",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "boblight" "name": "boblight",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "buzzer" "name": "buzzer",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "deep_sleep" "name": "deep_sleep",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "multi_relay" "name": "multi_relay",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "pwm_outputs" "name": "pwm_outputs",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "sd_card" "name": "sd_card",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "seven_segment_display" "name": "seven_segment_display",
"build": { "libArchive": false }
} }

View File

@ -1,6 +1,7 @@
{ {
"name": "seven_segment_display_reloaded", "name": "seven_segment_display_reloaded",
"build": { "build": {
"libArchive": false,
"extraScript": "setup_deps.py" "extraScript": "setup_deps.py"
} }
} }

View File

@ -1,9 +1,10 @@
from platformio.package.meta import PackageSpec
Import('env') Import('env')
usermods = env.GetProjectOption("custom_usermods","").split() libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
# Check for partner usermods # Check for partner usermods
if "SN_Photoresistor" in usermods: if "SN_Photoresistor" in libs:
env.Append(CPPDEFINES=[("USERMOD_SN_PHOTORESISTOR")]) env.Append(CPPDEFINES=[("USERMOD_SN_PHOTORESISTOR")])
if any(mod in ("BH1750_v2", "BH1750") for mod in usermods): if any(mod in ("BH1750_v2", "BH1750") for mod in libs):
env.Append(CPPDEFINES=[("USERMOD_BH1750")]) env.Append(CPPDEFINES=[("USERMOD_BH1750")])

View File

@ -1,5 +1,6 @@
{ {
"name": "sht", "name": "sht",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"robtillaart/SHT85": "~0.3.3" "robtillaart/SHT85": "~0.3.3"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "smartnest" "name": "smartnest",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "stairway_wipe_basic" "name": "stairway_wipe_basic",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "usermod_rotary_brightness_color" "name": "usermod_rotary_brightness_color",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "usermod_v2_HttpPullLightControl" "name": "usermod_v2_HttpPullLightControl",
"build": { "libArchive": false }
} }

View File

@ -4,6 +4,9 @@
const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl"; const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl";
const char HttpPullLightControl::_enabled[] PROGMEM = "Enable"; const char HttpPullLightControl::_enabled[] PROGMEM = "Enable";
static HttpPullLightControl http_pull_usermod;
REGISTER_USERMOD(http_pull_usermod);
void HttpPullLightControl::setup() { void HttpPullLightControl::setup() {
//Serial.begin(115200); //Serial.begin(115200);

View File

@ -1,5 +1,6 @@
{ {
"name": "usermod_v2_RF433", "name": "usermod_v2_RF433",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"sui77/rc-switch":"2.6.4" "sui77/rc-switch":"2.6.4"
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "animartrix", "name": "animartrix",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"Animartrix": "https://github.com/netmindz/animartrix.git#b172586" "Animartrix": "https://github.com/netmindz/animartrix.git#b172586"
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "auto_save" "name": "auto_save",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "brightness_follow_sun" "name": "brightness_follow_sun",
"build": { "libArchive": false }
} }

View File

@ -1,5 +1,6 @@
{ {
"name": "four_line_display_ALT", "name": "four_line_display_ALT",
"build": { "libArchive": false },
"dependencies": { "dependencies": {
"U8g2": "~2.34.4", "U8g2": "~2.34.4",
"Wire": "" "Wire": ""

View File

@ -1,3 +1,4 @@
{ {
"name": "usermod_v2_klipper_percentage" "name": "usermod_v2_klipper_percentage",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "usermod_v2_ping_pong_clock" "name": "usermod_v2_ping_pong_clock",
"build": { "libArchive": false }
} }

View File

@ -1,6 +1,7 @@
{ {
"name": "rotary_encoder_ui_ALT", "name": "rotary_encoder_ui_ALT",
"build": { "build": {
"libArchive": false,
"extraScript": "setup_deps.py" "extraScript": "setup_deps.py"
} }
} }

View File

@ -1,8 +1,8 @@
from platformio.package.meta import PackageSpec
Import('env') Import('env')
libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
usermods = env.GetProjectOption("custom_usermods","").split()
# Check for partner usermod # Check for partner usermod
# Allow both "usermod_v2" and unqualified syntax # Allow both "usermod_v2" and unqualified syntax
if any(mod in ("four_line_display_ALT", "usermod_v2_four_line_display_ALT") for mod in usermods): if any(mod in ("four_line_display_ALT", "usermod_v2_four_line_display_ALT") for mod in libs):
env.Append(CPPDEFINES=[("USERMOD_FOUR_LINE_DISPLAY")]) env.Append(CPPDEFINES=[("USERMOD_FOUR_LINE_DISPLAY")])

View File

@ -1,3 +1,4 @@
{ {
"name": "usermod_v2_word_clock" "name": "usermod_v2_word_clock",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "wizlights" "name": "wizlights",
"build": { "libArchive": false }
} }

View File

@ -1,3 +1,4 @@
{ {
"name": "word-clock-matrix" "name": "word-clock-matrix",
"build": { "libArchive": false }
} }

View File

@ -39,7 +39,13 @@ bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) {
return false; return false;
} }
void UsermodManager::addToJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToJsonState(obj); } void UsermodManager::addToJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToJsonState(obj); }
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToJsonInfo(obj); } void UsermodManager::addToJsonInfo(JsonObject& obj) {
auto um_id_list = obj.createNestedArray("um");
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
um_id_list.add((*mod)->getId());
(*mod)->addToJsonInfo(obj);
}
}
void UsermodManager::readFromJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->readFromJsonState(obj); } void UsermodManager::readFromJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->readFromJsonState(obj); }
void UsermodManager::addToConfig(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToConfig(obj); } void UsermodManager::addToConfig(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToConfig(obj); }
bool UsermodManager::readFromConfig(JsonObject& obj) { bool UsermodManager::readFromConfig(JsonObject& obj) {