1.12, 1.13, 1.14, 1.15.2

This commit is contained in:
Carl Pearson
2024-11-07 10:08:55 -07:00
commit 0942e89158
20 changed files with 102368 additions and 0 deletions

168
fuse/__main__.py Normal file
View File

@@ -0,0 +1,168 @@
import os
import re
from pathlib import Path
from typing import Set, Dict, List
from dataclasses import dataclass
from enum import Enum, auto
from sys import stderr
class FileType(Enum):
HEADER = auto()
SOURCE = auto()
UNKNOWN = auto()
@dataclass
class ProcessedFile:
path: Path
content: str
type: FileType
def get_file_type(path: Path) -> FileType:
"""Determine if file is header or source based on extension."""
ext = path.suffix.lower()
if ext in ['.h', '.hpp', '.hxx', '.hh']:
return FileType.HEADER
elif ext in ['.c', '.cpp', '.cxx', '.cc']:
return FileType.SOURCE
return FileType.UNKNOWN
class CppFusioner:
def __init__(self, include_paths: List[str], inline_headers=False, inline_sources=False):
self.include_paths = [Path(p) for p in include_paths]
self.processing_stack: Set[Path] = set() # For circular dependency detection
self.inline_headers = inline_headers
self.inline_sources = inline_sources
self.already_inlined: Set[Path] = set()
def find_include_file(self, include_name: str, current_dir: Path) -> Path | None:
"""Search for included file in current directory and include paths."""
# First check relative to current file
local_path = current_dir / include_name
if local_path.exists():
return local_path
# Then check include paths
for include_path in self.include_paths:
full_path = include_path / include_name
if full_path.exists():
return full_path
return None
def process_file(self, file_path: Path) -> ProcessedFile:
"""Process a single file and its includes recursively."""
abs_path = file_path.resolve()
# Check for circular dependencies
if abs_path in self.processing_stack:
raise Exception(f"Circular dependency detected: {abs_path}")
self.processing_stack.add(abs_path)
try:
with open(abs_path, 'r') as f:
content = f.read()
file_type = get_file_type(abs_path)
processed_lines = []
# Process line by line
for line in content.split('\n'):
include_match = re.match(r'#include\s*[<"]([^>"]+)[>"]', line)
if include_match:
include_name = include_match.group(1)
# Keep system includes
if '<' in line:
print(f"preserve system include {include_name} in {file_path}", file=stderr)
processed_lines.append(line)
continue
# Find and process local include
include_path = self.find_include_file(include_name, abs_path.parent)
if include_path in self.already_inlined:
print(f"already inlined {include_path} when processing {file_path}", file=stderr)
continue
if include_path:
included_file_type = get_file_type(include_path)
if self.inline_headers and included_file_type == FileType.HEADER:
included_file = self.process_file(include_path)
print(f"inline header {include_name}", file=stderr)
processed_lines.append(f"// Inlined from: {include_path}")
processed_lines.append(included_file.content)
self.already_inlined.add(include_path)
elif self.inline_sources and (included_file_type == FileType.SOURCE or include_name == "src/gtest-internal-inl.h" or include_name == "gtest/gtest-spi.h"):
included_file = self.process_file(include_path)
print(f"inline source {include_name}", file=stderr)
processed_lines.append(f"// Inlined from: {include_path}")
processed_lines.append(included_file.content)
self.already_inlined.add(include_path)
elif include_name.startswith("gtest/") and include_name != "gtest/gtest.h":
print(f"skipping {include_name} when processing {file_path}", file=stderr)
processed_lines.append("//" + line)
else:
print(f"not inlining {include_name} when processing {file_path}", file=stderr)
processed_lines.append(line)
else:
# Include not found, keep original line
print(f"could not find {line}", file=stderr)
processed_lines.append(line)
else:
processed_lines.append(line)
processed = ProcessedFile(
path=abs_path,
content='\n'.join(processed_lines),
type=file_type
)
return processed
finally:
self.processing_stack.remove(abs_path)
def fuse_file(self, input_file: str, output_file: str):
"""Process input file and write fused output."""
input_path = Path(input_file)
processed = self.process_file(input_path)
output_content = []
if processed.type == FileType.HEADER:
# Add header guard
guard_name = f"FUSED_{input_path.stem.upper()}_H"
output_content.extend([
f"#ifndef {guard_name}",
f"#define {guard_name}",
"",
processed.content,
"",
f"#endif // {guard_name}"
])
else:
output_content.append(processed.content)
with open(output_file, 'w') as f:
f.write('\n'.join(output_content))
if __name__ == '__main__':
import sys
if len(sys.argv) < 3:
print("Usage: python fusioner.py <input_file> <output_file> [include_path1] [include_path2] ...")
sys.exit(1)
input_file = Path(sys.argv[1])
output_file = sys.argv[2]
include_paths = sys.argv[3:] if len(sys.argv) > 3 else ['.']
file_type = get_file_type(input_file)
fusioner = CppFusioner(include_paths, inline_headers=(file_type == FileType.HEADER), inline_sources=(file_type == FileType.SOURCE))
fusioner.fuse_file(input_file, output_file)