mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
Added amalgamate.py to generate an eepp.hpp single header include (issue #172).
Added `find_missing_includes.py` that checks if all the header files of each module in the "general" module includes are included. With this I udpated the pending includes in the modules.
This commit is contained in:
112
projects/scripts/amalgamate.py
Normal file
112
projects/scripts/amalgamate.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*(?:<((?:eepp/)[^>]+)>|"([^"]+)")')
|
||||
PRAGMA_ONCE_REGEX = re.compile(r"^\s*#\s*pragma\s+once\s*")
|
||||
|
||||
visited_files = set()
|
||||
|
||||
|
||||
def amalgamate(file_path, search_paths, current_file_dir):
|
||||
# 1. Check relative to current file, 2. Check global search paths
|
||||
potential_paths = [os.path.join(current_file_dir, file_path)] + [
|
||||
os.path.join(sp, file_path) for sp in search_paths
|
||||
]
|
||||
|
||||
resolved_path = None
|
||||
for p in potential_paths:
|
||||
abs_p = os.path.abspath(p)
|
||||
if os.path.exists(abs_p):
|
||||
resolved_path = abs_p
|
||||
break
|
||||
|
||||
if not resolved_path:
|
||||
return None
|
||||
|
||||
if resolved_path in visited_files:
|
||||
return f"// [Already included: {file_path}]\n"
|
||||
|
||||
visited_files.add(resolved_path)
|
||||
print(f"Embedding: {resolved_path}")
|
||||
|
||||
output = []
|
||||
new_current_dir = os.path.dirname(resolved_path)
|
||||
|
||||
try:
|
||||
# utf-8-sig automatically strips invisible BOM characters!
|
||||
with open(resolved_path, "r", encoding="utf-8-sig") as f:
|
||||
lines = f.readlines()
|
||||
except Exception as e:
|
||||
return f"// ERROR reading {file_path}: {str(e)}\n"
|
||||
|
||||
for line in lines:
|
||||
if PRAGMA_ONCE_REGEX.match(line):
|
||||
continue # Cleanly strip pragma once
|
||||
|
||||
match = INCLUDE_REGEX.match(line)
|
||||
if match:
|
||||
included_file = match.group(1) or match.group(2)
|
||||
content = amalgamate(included_file, search_paths, new_current_dir)
|
||||
|
||||
if content is not None:
|
||||
output.append(f"\n// >>> Begin: {included_file} >>>\n")
|
||||
if not content.endswith("\n"):
|
||||
content += "\n"
|
||||
output.append(content)
|
||||
output.append(f"// <<< End: {included_file} <<<\n\n")
|
||||
else:
|
||||
output.append(line) # Keep system header
|
||||
else:
|
||||
output.append(line)
|
||||
|
||||
return "".join(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Dynamically resolve paths
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
repo_root = os.path.abspath(os.path.join(script_dir, "..", ".."))
|
||||
include_dir = os.path.join(repo_root, "include")
|
||||
|
||||
# Default output path
|
||||
default_out = os.path.join(repo_root, "eepp.hpp")
|
||||
|
||||
# Setup argparse for CLI options
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Amalgamate eepp C++ headers into a single, redistributable header file."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=str,
|
||||
default=default_out,
|
||||
help=f"Path to the output file (default: {default_out})",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
root_file = "eepp/ee.hpp"
|
||||
|
||||
print("--- Starting Amalgamation ---")
|
||||
result = amalgamate(root_file, [include_dir], include_dir)
|
||||
|
||||
if result is None:
|
||||
print(f"Error: Could not find the root file '{root_file}' in '{include_dir}'.")
|
||||
sys.exit(1)
|
||||
|
||||
final_code = "#ifndef EEPP_AMALGAMATED_HPP\n#define EEPP_AMALGAMATED_HPP\n\n"
|
||||
final_code += result
|
||||
final_code += "\n#endif // EEPP_AMALGAMATED_HPP\n"
|
||||
|
||||
# Resolve output path and ensure the directory exists
|
||||
out_file = os.path.abspath(args.output)
|
||||
out_dir = os.path.dirname(out_file)
|
||||
if out_dir:
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
with open(out_file, "w", encoding="utf-8") as out:
|
||||
out.write(final_code)
|
||||
|
||||
print(f"--- Success! Created {out_file} ---")
|
||||
98
projects/scripts/find_missing_includes.py
Normal file
98
projects/scripts/find_missing_includes.py
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
|
||||
# Catches anything inside <...> or "..."
|
||||
INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*[<"]([^>"]+)[>"]')
|
||||
|
||||
|
||||
def extract_includes_from_file(filepath):
|
||||
"""Helper function to read a file and return a set of its includes."""
|
||||
includes = set()
|
||||
if not os.path.exists(filepath):
|
||||
return includes
|
||||
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf-8-sig") as f:
|
||||
for line in f:
|
||||
match = INCLUDE_REGEX.search(line)
|
||||
if match:
|
||||
# Add the exact path inside the quotes/brackets to the set
|
||||
includes.add(match.group(1))
|
||||
except Exception as e:
|
||||
print(f"Error reading {filepath}: {e}")
|
||||
|
||||
return includes
|
||||
|
||||
|
||||
def find_missing_includes():
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
repo_root = os.path.abspath(os.path.join(script_dir, "..", ".."))
|
||||
|
||||
include_base_dir = os.path.join(repo_root, "include")
|
||||
eepp_dir = os.path.join(include_base_dir, "eepp")
|
||||
|
||||
if not os.path.exists(eepp_dir):
|
||||
print(f"Error: Could not find {eepp_dir}")
|
||||
return
|
||||
|
||||
print("Scanning eepp modules for missing includes...\n")
|
||||
|
||||
total_missing = 0
|
||||
|
||||
# 1. Find all master module headers (e.g., ui.hpp, core.hpp, scene.hpp)
|
||||
for item in sorted(os.listdir(eepp_dir)):
|
||||
if not item.endswith(".hpp") or item == "ee.hpp" or item == "version.hpp":
|
||||
continue
|
||||
|
||||
module_name = item[:-4] # strip .hpp (e.g., 'core')
|
||||
module_dir = os.path.join(eepp_dir, module_name)
|
||||
|
||||
# Only process if there is a matching directory (e.g., include/eepp/core/)
|
||||
if os.path.isdir(module_dir):
|
||||
master_header_path = os.path.join(eepp_dir, item)
|
||||
|
||||
# 2. Extract includes from the main module header
|
||||
existing_includes = extract_includes_from_file(master_header_path)
|
||||
|
||||
# --- THE CORE EXCEPTION ---
|
||||
# If this is the core module, also grab includes from eepp/core/core.hpp
|
||||
if module_name == "core":
|
||||
core_inner_path = os.path.join(module_dir, "core.hpp")
|
||||
existing_includes.update(extract_includes_from_file(core_inner_path))
|
||||
|
||||
# 3. Walk the module's directory recursively to find all .hpp files
|
||||
missing_in_module = []
|
||||
for root, dirs, files in os.walk(module_dir):
|
||||
for file in files:
|
||||
if file.endswith(".hpp"):
|
||||
full_path = os.path.join(root, file)
|
||||
|
||||
rel_path = os.path.relpath(full_path, include_base_dir)
|
||||
rel_path = rel_path.replace(os.sep, "/")
|
||||
|
||||
# Skip the inner core.hpp file itself so it doesn't get flagged
|
||||
if module_name == "core" and rel_path == "eepp/core/core.hpp":
|
||||
continue
|
||||
|
||||
# 4. Check if the file is missing from the known includes
|
||||
if rel_path not in existing_includes:
|
||||
missing_in_module.append(f"#include <{rel_path}>")
|
||||
|
||||
# 5. Print results nicely
|
||||
if missing_in_module:
|
||||
print(f"--- Missing in {item} ---")
|
||||
for missing in sorted(missing_in_module):
|
||||
print(missing)
|
||||
print("") # Spacing
|
||||
total_missing += len(missing_in_module)
|
||||
|
||||
if total_missing == 0:
|
||||
print("All module headers are perfectly up to date!")
|
||||
else:
|
||||
print(f"Found {total_missing} potentially missing includes.")
|
||||
print("Note: Some of these might be internal/detail headers you intentionally left out.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
find_missing_includes()
|
||||
Reference in New Issue
Block a user