Add initial board, HAL, and executor modules

This commit is contained in:
wang committed 2026-05-07 17:24:10 +08:00
1 parent 3b9e129c88
commit 0b2eebe76d
82 files changed
+20611

No files matched your search

+247
View File
@@ -0,0 +1,247 @@
import os
import re
# The directories to scan for Kconfig files
SUBSYSTEM_DIRS = ["04_hal", "05_executors", "05_service", "06_behaviors", "tests"]
TEMPLATE_SCAN_DIRS = SUBSYSTEM_DIRS + ["sweekar_demos"]
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT_FILE = os.path.join(BASE_DIR, "Kconfig.modules")
SOURCE_EXTENSIONS = (".c", ".cc", ".cpp")
HEADER_EXTENSIONS = (".h", ".hpp")
SKIP_DIR_NAMES = {"build", "__pycache__"}
def iter_child_dirs(parent_dir):
if not os.path.exists(parent_dir):
return []
return sorted(
[
name
for name in os.listdir(parent_dir)
if os.path.isdir(os.path.join(parent_dir, name)) and not name.startswith(".")
]
)
def compute_default_config_name(subsystem_dir, module_name):
if subsystem_dir == "tests":
prefix = "TEST"
else:
prefix = subsystem_dir.split("_")[1].upper()
if prefix == "EXECUTORS":
prefix = "EXEC"
elif prefix == "BEHAVIORS":
prefix = "BEHAV"
elif prefix == "SERVICE":
prefix = "SVC"
return f"CONFIG_ENABLE_{prefix}_{module_name.upper()}"
def contains_module_files(module_dir):
for _, _, files in os.walk(module_dir):
for filename in files:
if filename.endswith(SOURCE_EXTENSIONS) or filename.endswith(HEADER_EXTENSIONS):
return True
return False
def collect_existing_module_cmake_files():
module_files = []
for root, _, files in os.walk(BASE_DIR):
if "module.cmake" in files:
module_files.append(os.path.join(root, "module.cmake"))
return sorted(module_files)
def collect_module_files(module_dir):
srcs = []
include_dirs = {"."}
for root, dirs, files in os.walk(module_dir):
dirs[:] = [
d for d in dirs
if d not in SKIP_DIR_NAMES and not d.startswith(".")
]
rel_root = os.path.relpath(root, module_dir).replace(os.sep, "/")
rel_root = "." if rel_root == "." else rel_root
has_header = False
for filename in files:
if filename.endswith(SOURCE_EXTENSIONS):
rel_path = os.path.normpath(os.path.join(rel_root, filename)).replace(os.sep, "/")
if rel_path.startswith("./"):
rel_path = rel_path[2:]
srcs.append(rel_path)
elif filename.endswith(HEADER_EXTENSIONS):
has_header = True
if has_header:
include_dirs.add(rel_root)
srcs = sorted(set(srcs))
include_dirs = sorted(set(include_dirs))
return srcs, include_dirs
def is_already_aggregated(rel_module_dir, existing_module_files):
rel_module_prefix = f'"{rel_module_dir}/'
for module_file in existing_module_files:
try:
with open(module_file, "r", encoding="utf-8") as f:
content = f.read()
except OSError:
continue
if rel_module_prefix in content:
return True
return False
def render_module_template(subsystem_dir, module_name, module_dir):
srcs, include_dirs = collect_module_files(module_dir)
rel_module_dir = os.path.relpath(module_dir, BASE_DIR).replace(os.sep, "/")
lines = [
f"# main/{subsystem_dir}/{module_name}/module.cmake",
"# 自动生成的模块脚手架,可直接手工修改。",
"# 如果后续添加了新文件,可以手工在此处添加,或者删除本文件让脚本重新生成。",
"",
]
if subsystem_dir != "sweekar_demos":
config_name = compute_default_config_name(subsystem_dir, module_name)
lines.extend(
[
f"if(NOT {config_name} AND NOT EARLY_EXPANSION)",
f' message(STATUS " [Skip] {module_name} module disabled by menuconfig.")',
" return()",
"endif()",
"",
]
)
lines.append("list(APPEND MODULE_SRCS")
for src in srcs:
lines.append(f' "{rel_module_dir}/{src}"')
lines.append(")")
lines.append("")
lines.append("list(APPEND MODULE_INCLUDE_DIRS")
for inc in include_dirs:
if inc == ".":
lines.append(f' "{rel_module_dir}"')
else:
lines.append(f' "{rel_module_dir}/{inc}"')
lines.append(")")
lines.append("")
lines.extend(
[
"# 需要公开依赖时,在这里手工补充。例如:",
"# list(APPEND MODULE_REQUIRES",
"# esp_timer",
"# driver",
"# )",
"",
"# 需要私有依赖时,在这里手工补充。例如:",
"# list(APPEND MODULE_PRIV_REQUIRES",
"# nvs_flash",
"# )",
"",
]
)
return "\n".join(lines)
def ensure_module_templates():
created_files = []
existing_module_files = collect_existing_module_cmake_files()
for subsystem_dir in TEMPLATE_SCAN_DIRS:
dir_path = os.path.join(BASE_DIR, subsystem_dir)
if not os.path.exists(dir_path):
continue
for module_name in iter_child_dirs(dir_path):
if subsystem_dir == "sweekar_demos" and not module_name.startswith("sweekar_demo"):
continue
module_dir = os.path.join(dir_path, module_name)
module_cmake = os.path.join(module_dir, "module.cmake")
if os.path.exists(module_cmake):
continue
if not contains_module_files(module_dir):
continue
rel_module_dir = os.path.relpath(module_dir, BASE_DIR).replace(os.sep, "/")
if subsystem_dir != "sweekar_demos" and is_already_aggregated(rel_module_dir, existing_module_files):
continue
template_content = render_module_template(subsystem_dir, module_name, module_dir)
with open(module_cmake, "w", encoding="utf-8") as f:
f.write(template_content)
created_files.append(module_cmake)
return created_files
def generate_kconfig_modules():
created_templates = ensure_module_templates()
kconfig_content = 'menu "Sweekar Subsystems Configuration"\n\n'
for d in SUBSYSTEM_DIRS:
dir_path = os.path.join(BASE_DIR, d)
if not os.path.exists(dir_path):
continue
# Create a pretty menu name from the directory name
# e.g., "05_executors" -> "05 Executors Modules"
parts = d.split('_')
menu_name = parts[0] + " " + " ".join([p.capitalize() for p in parts[1:]]) + " Modules"
kconfig_content += f' menu "{menu_name}"\n'
# Sort subdirectories to ensure consistent ordering
subdirs = iter_child_dirs(dir_path)
for module_name in subdirs:
module_dir = os.path.join(dir_path, module_name)
kconfig_file = os.path.join(module_dir, "Kconfig")
if os.path.exists(kconfig_file):
# If the module has its own Kconfig, just source it
# Make sure the source path is relative to the project root or uses proper env vars
# Since Kconfig.projbuild is processed relative to project root usually
rel_path = os.path.join("main", d, module_name, "Kconfig")
kconfig_content += f' source "{rel_path}"\n'
else:
# If no Kconfig exists, generate a default toggle for it
config_name = compute_default_config_name(d, module_name)
pretty_name = " ".join([word.capitalize() for word in module_name.split('_')])
# Default to 'n' for tests, 'y' for everything else
default_val = 'n' if d == 'tests' else 'y'
kconfig_content += f''' config {config_name.replace("CONFIG_", "")}
bool "Enable {pretty_name}"
default {default_val}
help
Enable the {module_name} module in {d}.
'''
kconfig_content += ' endmenu\n\n'
kconfig_content += 'endmenu\n'
with open(OUTPUT_FILE, 'w') as f:
f.write(kconfig_content)
print(f"Generated {OUTPUT_FILE} successfully.")
if created_templates:
print("Generated module.cmake templates:")
for path in created_templates:
print(f" - {path}")
if __name__ == "__main__":
generate_kconfig_modules()