Fix usermod platformio integration

Should now work for new *and* old versions of PlatformIO!
This commit is contained in:
Will Miles 2025-01-16 01:05:12 +00:00
parent 8487dd7cfd
commit 15edfcd088

View File

@ -1,5 +1,7 @@
Import('env')
import os.path
from pathlib import Path # For OS-agnostic path manipulation
from platformio.package.manager.library import LibraryPackageManager
usermod_dir = Path(env["PROJECT_DIR"]) / "usermods"
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
@ -24,38 +26,70 @@ def find_usermod(mod: str):
usermods = env.GetProjectOption("custom_usermods","")
if usermods:
# Inject usermods in to project lib_deps
proj = env.GetProjectConfig()
deps = env.GetProjectOption('lib_deps')
deps = env.GetProjectOption('lib_deps')
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)
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)
# Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies
# Save the old value
cpl = env.ConfigureProjectLibBuilder
old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
# Our new wrapper
def cpl_wrapper(env):
def wrapped_ConfigureProjectLibBuilder(xenv):
# Update usermod properties
lib_builders = env.GetLibBuilders()
um_deps = [dep for dep in lib_builders if usermod_dir in Path(dep.src_dir).parents]
other_deps = [dep for dep in lib_builders if usermod_dir not in Path(dep.src_dir).parents]
for um in um_deps:
# Add include paths for all non-usermod dependencies
for dep in other_deps:
for dir in dep.get_include_dirs():
um.env.PrependUnique(CPPPATH=dir)
# Add the wled folder to the include path
um.env.PrependUnique(CPPPATH=env["PROJECT_SRC_DIR"])
# Make sure we link directly, not through an archive
# Archives drop the .dtor table section we need
# 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
return cpl.clone(env)()
# Call the wrapped function
result = old_ConfigureProjectLibBuilder.clone(xenv)()
# Replace the old one with ours
env.AddMethod(cpl_wrapper, "ConfigureProjectLibBuilder")
# Fix up include paths
# In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder
wled_dir = xenv["PROJECT_SRC_DIR"]
lib_builders = xenv.GetLibBuilders()
um_deps = [dep for dep in lib_builders if usermod_dir in Path(dep.src_dir).parents]
other_deps = [dep for dep in lib_builders if usermod_dir not in Path(dep.src_dir).parents]
for um in um_deps:
# Add the wled folder to the include path
um.env.PrependUnique(CPPPATH=wled_dir)
# Add WLED's own dependencies
for dep in other_deps:
for dir in dep.get_include_dirs():
um.env.PrependUnique(CPPPATH=dir)
return result
# Apply the wrapper
env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder")