Replace shell implementation with python script. More flexible solution that adjusts indentation of included content to offset of directive in host file. This allows greater reusability. Change-Id: Ic048f2ad950b686127710dee983ef56f688eda65 Signed-off-by: Ron Stone <ronald.stone@windriver.com>
126 lines
3.8 KiB
Python
126 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Detect a pre-include directive
|
|
PRE_INCLUDE_RE = re.compile(r'^(\s*)\.\.\s+pre-include::\s+(.+)$')
|
|
# Detect options following the directive
|
|
OPTION_RE = re.compile(r'^\s*:(\w[\w-]*):\s*(.*)$')
|
|
|
|
def read_included_content(filepath, options):
|
|
"""Read included file and slice per start-after/end-before, literal, and tab-width."""
|
|
try:
|
|
with open(filepath, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
except FileNotFoundError:
|
|
return [f".. (error: file not found: {filepath})\n"]
|
|
|
|
# Apply start-after
|
|
if "start-after" in options:
|
|
marker = options["start-after"]
|
|
for i, line in enumerate(lines):
|
|
if marker in line:
|
|
lines = lines[i + 1 :]
|
|
break
|
|
|
|
# Apply end-before
|
|
if "end-before" in options:
|
|
marker = options["end-before"]
|
|
for i, line in enumerate(lines):
|
|
if marker in line:
|
|
lines = lines[:i]
|
|
break
|
|
|
|
# Handle tab-width (default 8 if not provided)
|
|
if "tab-width" in options:
|
|
try:
|
|
width = int(options["tab-width"]) if options["tab-width"] else 8
|
|
lines = [l.expandtabs(width) for l in lines]
|
|
except ValueError:
|
|
pass # ignore malformed value
|
|
|
|
return lines
|
|
|
|
|
|
def process_file(path):
|
|
"""Process one file and expand all pre-include directives."""
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
lines = f.readlines()
|
|
|
|
output_lines = []
|
|
i = 0
|
|
changed = False
|
|
|
|
while i < len(lines):
|
|
line = lines[i]
|
|
m = PRE_INCLUDE_RE.match(line)
|
|
if not m:
|
|
output_lines.append(line)
|
|
i += 1
|
|
continue
|
|
|
|
indent, filename = m.groups()
|
|
filename = filename.strip()
|
|
options = {}
|
|
|
|
# Gather options (start-after, end-before, literal, tab-width, etc.)
|
|
j = i + 1
|
|
while j < len(lines):
|
|
opt_match = OPTION_RE.match(lines[j])
|
|
if not opt_match:
|
|
break
|
|
opt_name, opt_value = opt_match.groups()
|
|
options[opt_name] = opt_value
|
|
j += 1
|
|
|
|
include_path = (Path(path).parent / filename).resolve()
|
|
included_lines = read_included_content(include_path, options)
|
|
|
|
# Apply literal or standard indentation
|
|
if "literal" in options:
|
|
# For literal, ensure a blank line before and after if not present
|
|
if output_lines and output_lines[-1].strip():
|
|
output_lines.append("\n")
|
|
output_lines.extend([indent + l for l in included_lines])
|
|
if included_lines and included_lines[-1].strip():
|
|
output_lines.append("\n")
|
|
else:
|
|
# Normal include: indent non-empty lines, preserve blank ones
|
|
indented = [indent + l if l.strip() else l for l in included_lines]
|
|
output_lines.extend(indented)
|
|
|
|
changed = True
|
|
i = j # Skip over directive and its options
|
|
|
|
if changed:
|
|
# backup_path = path + ".bak"
|
|
# os.rename(path, backup_path)
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.writelines(output_lines)
|
|
# print(f"Processed {path} (backup saved as {backup_path})")
|
|
print(f"\u2705 Processed {path})")
|
|
else:
|
|
print(f"No pre-include directives in {path}")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) != 2:
|
|
print("Usage: normalize_includes.py <directory>")
|
|
sys.exit(1)
|
|
|
|
root = Path(sys.argv[1]).resolve()
|
|
if not root.is_dir():
|
|
print(f"⚠️ Error: {root} is not a directory")
|
|
sys.exit(1)
|
|
|
|
for ext in ("*.rst", "*.rest"):
|
|
for path in root.rglob(ext):
|
|
process_file(path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|