Adding Documentation to amalgamate.py

Adding Documentation to amalgamate.py
This commit is contained in:
Anindhiths 2025-03-19 18:11:07 +05:30 committed by GitHub
parent c502ce172f
commit ba1b6499aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -5,11 +5,16 @@ import sys
import time import time
from typing import List, Dict from typing import List, Dict
# Run the prebuild script before starting the amalgamation process
assert os.system("python prebuild.py") == 0 assert os.system("python prebuild.py") == 0
# Define the root directory containing headers
ROOT = 'include/pocketpy' ROOT = 'include/pocketpy'
# List of public headers that will be included in the final amalgamated header
PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h'] PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h']
# Copyright notice to be added at the beginning of amalgamated files
COPYRIGHT = '''/* COPYRIGHT = '''/*
* Copyright (c) 2025 blueloveTH * Copyright (c) 2025 blueloveTH
* Distributed Under The MIT License * Distributed Under The MIT License
@ -18,142 +23,205 @@ COPYRIGHT = '''/*
''' '''
def read_file(path): def read_file(path):
with open(path, 'rt', encoding='utf-8') as f: """Read the content of a file with UTF-8 encoding."""
return f.read() with open(path, 'rt', encoding='utf-8') as f:
return f.read()
def write_file(path, content): def write_file(path, content):
with open(path, 'wt', encoding='utf-8', newline='\n') as f: """Write content to a file with UTF-8 encoding and Unix line endings."""
f.write(content) with open(path, 'wt', encoding='utf-8', newline='\n') as f:
f.write(content)
# Remove existing amalgamated directory if it exists and create a new one
if os.path.exists('amalgamated'): if os.path.exists('amalgamated'):
shutil.rmtree('amalgamated') shutil.rmtree('amalgamated')
time.sleep(0.5) time.sleep(0.5) # Wait briefly to ensure directory is fully removed
os.mkdir('amalgamated') os.mkdir('amalgamated')
class Header: class Header:
path: str """
content: str # header source Class to represent and process a header file.
dependencies: List[str]
def __init__(self, path: str): Attributes:
self.path = path path: Relative path to the header file
self.dependencies = [] content: Processed content of the header file
self.content = read_file(f'{ROOT}/{path}') dependencies: List of other headers this header depends on
"""
path: str
content: str
dependencies: List[str]
# process raw content and get dependencies def __init__(self, path: str):
self.content = self.content.replace('#pragma once', '') """
def _replace(m): Initialize a Header object.
path = m.group(1)
if path.startswith('xmacros/'):
return read_file(f'{ROOT}/{path}') + '\n'
if path in PUBLIC_HEADERS:
return '' # remove include
if path != self.path:
self.dependencies.append(path)
return '' # remove include
self.content = re.sub( Args:
r'#include\s+"pocketpy/(.+)"\s*', path: Relative path to the header file
_replace, """
self.content self.path = path
) self.dependencies = []
self.content = read_file(f'{ROOT}/{path}')
def __repr__(self): # Process raw content and extract dependencies
return f'Header({self.path!r}, dependencies={self.dependencies})' self.content = self.content.replace('#pragma once', '') # Remove pragma once directives
def text(self): def _replace(m):
return f'// {self.path}\n{self.content}\n' """
Process #include directives in the header.
Returns:
- For xmacros: the content of the included file
- For public headers: empty string (removed)
- For other headers: empty string and adds the header to dependencies
"""
path = m.group(1)
if path.startswith('xmacros/'):
return read_file(f'{ROOT}/{path}') + '\n' # Inline xmacros directly
if path in PUBLIC_HEADERS:
return '' # Remove includes of public headers
if path != self.path:
self.dependencies.append(path) # Add to dependencies
return '' # Remove include directive
# Process all include directives
self.content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*',
_replace,
self.content
)
def __repr__(self):
"""Return string representation of the Header object."""
return f'Header({self.path!r}, dependencies={self.dependencies})'
def text(self):
"""Return the processed header content with a comment indicating its source."""
return f'// {self.path}\n{self.content}\n'
# Dictionary to store all header objects
headers: Dict[str, Header] = {} headers: Dict[str, Header] = {}
# Process all header files in the ROOT directory
for entry in os.listdir(ROOT): for entry in os.listdir(ROOT):
if os.path.isdir(f'{ROOT}/{entry}'): if os.path.isdir(f'{ROOT}/{entry}'):
if entry == 'xmacros' or entry in PUBLIC_HEADERS: if entry == 'xmacros' or entry in PUBLIC_HEADERS:
continue continue # Skip xmacros directory and public headers
files = os.listdir(f'{ROOT}/{entry}') files = os.listdir(f'{ROOT}/{entry}')
for file in sorted(files): for file in sorted(files):
assert file.endswith('.h') assert file.endswith('.h') # Ensure we're only processing header files
if entry in PUBLIC_HEADERS: if entry in PUBLIC_HEADERS:
continue continue
headers[f'{entry}/{file}'] = Header(f'{entry}/{file}') headers[f'{entry}/{file}'] = Header(f'{entry}/{file}')
def merge_c_files(): def merge_c_files():
c_files = [COPYRIGHT, '\n', '#include "pocketpy.h"', '\n'] """
Merge all C source files into a single file.
# merge internal headers Uses a topological sort to ensure headers are included in the correct order
internal_h = [] based on their dependencies.
while True:
for h in headers.values():
if not h.dependencies:
break
else:
if headers:
print(headers)
raise RuntimeError("Circular dependencies detected")
break
# print(h.path)
internal_h.append(h.text())
del headers[h.path]
for h2 in headers.values():
h2.dependencies = [d for d in h2.dependencies if d != h.path]
c_files.extend(internal_h) Returns:
String containing the amalgamated C source
"""
c_files = [COPYRIGHT, '\n', '#include "pocketpy.h"', '\n'] # Start with copyright and main include
def _replace(m): # Merge internal headers in dependency order
path = m.group(1) internal_h = []
if path.startswith('xmacros/'): while True:
return read_file(f'{ROOT}/{path}') + '\n' # Find a header with no dependencies
return '' # remove include for h in headers.values():
if not h.dependencies:
break
else:
# If no headers without dependencies were found and headers still remain,
# there must be a circular dependency
if headers:
print(headers)
raise RuntimeError("Circular dependencies detected")
break # No headers left, we're done
for root, _, files in os.walk('src/'): # Add this header to the amalgamated file
for file in files: internal_h.append(h.text())
if file.endswith('.c'): del headers[h.path]
path = os.path.join(root, file)
c_files.append(f'// {path}\n') # Remove this header from the dependencies of other headers
content = read_file(path) for h2 in headers.values():
content = re.sub( h2.dependencies = [d for d in h2.dependencies if d != h.path]
r'#include\s+"pocketpy/(.+)"\s*',
_replace, c_files.extend(internal_h)
content,
) def _replace(m):
c_files.append(content) """Process include directives in C files."""
c_files.append('\n') path = m.group(1)
return ''.join(c_files) if path.startswith('xmacros/'):
return read_file(f'{ROOT}/{path}') + '\n' # Inline xmacros
return '' # Remove other includes
# Process all C source files
for root, _, files in os.walk('src/'):
for file in files:
if file.endswith('.c'):
path = os.path.join(root, file)
c_files.append(f'// {path}\n') # Add comment with original file path
content = read_file(path)
# Process include directives
content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*',
_replace,
content,
)
c_files.append(content)
c_files.append('\n')
return ''.join(c_files)
def merge_h_files(): def merge_h_files():
h_files = [COPYRIGHT, '#pragma once'] """
Merge all public header files into a single header file.
def _replace(m): Returns:
path = m.group(1) String containing the amalgamated header
if path.startswith('xmacros/'): """
return read_file(f'{ROOT}/{path}') + '\n' h_files = [COPYRIGHT, '#pragma once'] # Start with copyright and pragma once
return '' # remove include
for path in PUBLIC_HEADERS: def _replace(m):
content = read_file(f'{ROOT}/{path}') """Process include directives in header files."""
content = content.replace('#pragma once', '') path = m.group(1)
content = re.sub( if path.startswith('xmacros/'):
r'#include\s+"pocketpy/(.+)"\s*', return read_file(f'{ROOT}/{path}') + '\n' # Inline xmacros
_replace, return '' # Remove other includes
content,
)
h_files.append(content)
return '\n'.join(h_files)
# Process all public headers
for path in PUBLIC_HEADERS:
content = read_file(f'{ROOT}/{path}')
content = content.replace('#pragma once', '') # Remove pragma once directives
# Process include directives
content = re.sub(
r'#include\s+"pocketpy/(.+)"\s*',
_replace,
content,
)
h_files.append(content)
return '\n'.join(h_files)
# Generate amalgamated files
write_file('amalgamated/pocketpy.c', merge_c_files()) write_file('amalgamated/pocketpy.c', merge_c_files())
write_file('amalgamated/pocketpy.h', merge_h_files()) write_file('amalgamated/pocketpy.h', merge_h_files())
# Copy the main.c file to the amalgamated directory
shutil.copy("src2/main.c", "amalgamated/main.c") shutil.copy("src2/main.c", "amalgamated/main.c")
# Test compile on Linux or macOS
if sys.platform in ['linux', 'darwin']: if sys.platform in ['linux', 'darwin']:
ok = os.system("gcc -o main amalgamated/pocketpy.c amalgamated/main.c -O1 --std=c11 -lm -ldl") ok = os.system("gcc -o main amalgamated/pocketpy.c amalgamated/main.c -O1 --std=c11 -lm -ldl")
if ok == 0: if ok == 0:
print("Test build success!") print("Test build success!")
print("amalgamated/pocketpy.h") print("amalgamated/pocketpy.h")
# Copy the amalgamated files to the Flutter plugin directory
shutil.copy("amalgamated/pocketpy.h", "plugins/flutter/pocketpy/src/pocketpy.h") shutil.copy("amalgamated/pocketpy.h", "plugins/flutter/pocketpy/src/pocketpy.h")
shutil.copy("amalgamated/pocketpy.c", "plugins/flutter/pocketpy/src/pocketpy.c") shutil.copy("amalgamated/pocketpy.c", "plugins/flutter/pocketpy/src/pocketpy.c")