#!/usr/bin/env python3 # trunk-ignore-all(ruff/F821) # trunk-ignore-all(flake8/F821): For SConstruct imports # # Registers optional modules dropped into src/modules/optional/. # # A module is a directory / holding .h, which declares `void setup();`. This # writes $BUILD_DIR/OptionalModules.h with an include and a setup call for each one found; # Modules.cpp picks that header up through __has_include and calls OPTIONAL_MODULES_SETUP(). # # src/modules/optional/ does not exist in a stock checkout, so a stock build generates a header that # defines nothing and registers nothing. Sources under the directory are compiled by the default # recursive build_src_filter, so dropping a module in needs no platformio.ini edit. import os import re Import("env") # The directory name becomes a C++ call, so it has to be a usable identifier. identifier = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") optionalDir = os.path.join(env["PROJECT_DIR"], "src", "modules", "optional") names = [] if os.path.isdir(optionalDir): for entry in sorted(os.listdir(optionalDir)): entryDir = os.path.join(optionalDir, entry) if not os.path.isdir(entryDir): continue if not identifier.match(entry): print(f"optional-modules: skipping {entry}/, setup{entry}() is not a valid identifier") elif os.path.isfile(os.path.join(entryDir, entry + ".h")): names.append(entry) else: print(f"optional-modules: skipping {entry}/, no {entry}.h") lines = ["// Generated by bin/optional-modules.py. Do not edit.", "#pragma once", ""] for name in names: lines.append(f'#include "modules/optional/{name}/{name}.h"') if names: lines.append("") lines.append("#define OPTIONAL_MODULES_SETUP() \\") lines.append(" do { \\") for name in names: lines.append(f" setup{name}(); \\") lines.append(" } while (0)") lines.append("") content = "\n".join(lines) buildDir = env.subst("$BUILD_DIR") os.makedirs(buildDir, exist_ok=True) header = os.path.join(buildDir, "OptionalModules.h") # Rewrite only on a change, so an unchanged set of modules does not keep rebuilding Modules.cpp. previous = None if os.path.isfile(header): with open(header, encoding="utf-8") as f: previous = f.read() if previous != content: with open(header, "w", encoding="utf-8") as f: f.write(content) env.Append(CPPPATH=[buildDir]) if names: print("optional-modules: " + ", ".join(names))