Add initial board, HAL, and executor modules
This commit is contained in:
1 parent
3b9e129c88
commit
0b2eebe76d
82 files changed
+20611
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user