Skip to content

Commit a6d9a82

Browse files
authored
Merge pull request #4480 from willmmiles/usermod-libs
Convert usermods to static libraries
2 parents 7c23872 + 0ba80ce commit a6d9a82

File tree

171 files changed

+1054
-1579
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+1054
-1579
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jobs:
5757
cache: 'pip'
5858
- name: Install PlatformIO
5959
run: pip install -r requirements.txt
60+
6061
- name: Build firmware
6162
run: pio run -e ${{ matrix.environment }}
6263
- uses: actions/upload-artifact@v4

pio-scripts/load_usermods.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
Import('env')
2+
import os.path
3+
from collections import deque
4+
from pathlib import Path # For OS-agnostic path manipulation
5+
from platformio.package.manager.library import LibraryPackageManager
6+
7+
usermod_dir = Path(env["PROJECT_DIR"]) / "usermods"
8+
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
9+
10+
if env['PIOENV'] == "usermods":
11+
# Add all usermods
12+
env.GetProjectConfig().set(f"env:usermods", 'custom_usermods', " ".join([f.name for f in all_usermods]))
13+
14+
def find_usermod(mod: str):
15+
"""Locate this library in the usermods folder.
16+
We do this to avoid needing to rename a bunch of folders;
17+
this could be removed later
18+
"""
19+
# Check name match
20+
mp = usermod_dir / mod
21+
if mp.exists():
22+
return mp
23+
mp = usermod_dir / f"{mod}_v2"
24+
if mp.exists():
25+
return mp
26+
mp = usermod_dir / f"usermod_v2_{mod}"
27+
if mp.exists():
28+
return mp
29+
raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
30+
31+
usermods = env.GetProjectOption("custom_usermods","")
32+
if usermods:
33+
# Inject usermods in to project lib_deps
34+
proj = env.GetProjectConfig()
35+
deps = env.GetProjectOption('lib_deps')
36+
src_dir = proj.get("platformio", "src_dir")
37+
src_dir = src_dir.replace('\\','/')
38+
mod_paths = {mod: find_usermod(mod) for mod in usermods.split()}
39+
usermods = [f"{mod} = symlink://{path}" for mod, path in mod_paths.items()]
40+
proj.set("env:" + env['PIOENV'], 'lib_deps', deps + usermods)
41+
# Force usermods to be installed in to the environment build state before the LDF runs
42+
# Otherwise we won't be able to see them until it's too late to change their paths for LDF
43+
# Logic is largely borrowed from PlaformIO internals
44+
not_found_specs = []
45+
for spec in usermods:
46+
found = False
47+
for storage_dir in env.GetLibSourceDirs():
48+
#print(f"Checking {storage_dir} for {spec}")
49+
lm = LibraryPackageManager(storage_dir)
50+
if lm.get_package(spec):
51+
#print("Found!")
52+
found = True
53+
break
54+
if not found:
55+
#print("Missing!")
56+
not_found_specs.append(spec)
57+
if not_found_specs:
58+
lm = LibraryPackageManager(
59+
env.subst(os.path.join("$PROJECT_LIBDEPS_DIR", "$PIOENV"))
60+
)
61+
for spec in not_found_specs:
62+
#print(f"LU: forcing install of {spec}")
63+
lm.install(spec)
64+
65+
66+
# Utility function for assembling usermod include paths
67+
def cached_add_includes(dep, dep_cache: set, includes: deque):
68+
""" Add dep's include paths to includes if it's not in the cache """
69+
if dep not in dep_cache:
70+
dep_cache.add(dep)
71+
for include in dep.get_include_dirs():
72+
if include not in includes:
73+
includes.appendleft(include)
74+
if usermod_dir not in Path(dep.src_dir).parents:
75+
# Recurse, but only for NON-usermods
76+
for subdep in dep.depbuilders:
77+
cached_add_includes(subdep, dep_cache, includes)
78+
79+
# Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies
80+
# Save the old value
81+
old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
82+
83+
# Our new wrapper
84+
def wrapped_ConfigureProjectLibBuilder(xenv):
85+
# Update usermod properties
86+
# Set libArchive before build actions are added
87+
for um in (um for um in xenv.GetLibBuilders() if usermod_dir in Path(um.src_dir).parents):
88+
build = um._manifest.get("build", {})
89+
build["libArchive"] = False
90+
um._manifest["build"] = build
91+
92+
# Call the wrapped function
93+
result = old_ConfigureProjectLibBuilder.clone(xenv)()
94+
95+
# Fix up include paths
96+
# In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder
97+
wled_dir = xenv["PROJECT_SRC_DIR"]
98+
# Build a list of dependency include dirs
99+
# TODO: Find out if this is the order that PlatformIO/SCons puts them in??
100+
processed_deps = set()
101+
extra_include_dirs = deque() # Deque used for fast prepend
102+
for dep in result.depbuilders:
103+
cached_add_includes(dep, processed_deps, extra_include_dirs)
104+
105+
for um in [dep for dep in result.depbuilders if usermod_dir in Path(dep.src_dir).parents]:
106+
# Add the wled folder to the include path
107+
um.env.PrependUnique(CPPPATH=wled_dir)
108+
# Add WLED's own dependencies
109+
for dir in extra_include_dirs:
110+
um.env.PrependUnique(CPPPATH=dir)
111+
112+
return result
113+
114+
# Apply the wrapper
115+
env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder")

0 commit comments

Comments
 (0)