1
0
mirror of https://github.com/facebook/zstd.git synced 2025-08-07 06:23:00 +03:00

Work-in-progress; annotated types, added docs, parsed and resolved excluded files

This commit is contained in:
Carl Woffenden
2022-01-18 11:43:01 +01:00
parent 7e50d1e8c1
commit 829ac2e9ce

View File

@@ -4,20 +4,27 @@
# #
# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain) # Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
import argparse, os, re, sys import argparse, io, os, re, sys
from pathlib import Path from pathlib import Path
# File roots when searching (equivalent to -I paths for the compiler). # Set of file roots when searching (equivalent to -I paths for the compiler).
roots = set() roots = set()
# File Path objects previously inlined. # Set of (canonical) file Path objects to exclude from inlining (and not only
# exclude but to add a compiler error directive when they're encountered).
excludes = set()
# Set of (canonical) file Path objects to keep as include directives.
keeps = set()
# Set of file Path objects previously inlined.
found = set() found = set()
# Destination file object (or stdout if no output file was supplied). # Destination file TextIOBase object (or stdout if no output file was supplied).
destn = None destn = None
# Regex to handle the following type of file includes: # Compiled regex Patern to handle the following type of file includes:
# #
# #include "file" # #include "file"
# #include "file" # #include "file"
@@ -41,7 +48,7 @@ include_regex = re.compile(r'^\s*#\s*include\s*"(.+?)"')
# Simple tests to prove include_regex's cases. # Simple tests to prove include_regex's cases.
# #
def test_match_include(): def test_match_include() -> bool:
if (include_regex.match('#include "file"') and if (include_regex.match('#include "file"') and
include_regex.match(' #include "file"') and include_regex.match(' #include "file"') and
include_regex.match('# include "file"') and include_regex.match('# include "file"') and
@@ -56,7 +63,7 @@ def test_match_include():
return True return True
return False return False
# Regex to handle "#pragma once" in various formats: # Compiled regex Patern to handle "#pragma once" in various formats:
# #
# #pragma once # #pragma once
# #pragma once # #pragma once
@@ -70,7 +77,7 @@ pragma_regex = re.compile(r'^\s*#\s*pragma\s*once\s*')
# Simple tests to prove pragma_regex's cases. # Simple tests to prove pragma_regex's cases.
# #
def text_match_pragma(): def text_match_pragma() -> bool:
if (pragma_regex.match('#pragma once') and if (pragma_regex.match('#pragma once') and
pragma_regex.match(' #pragma once') and pragma_regex.match(' #pragma once') and
pragma_regex.match('# pragma once') and pragma_regex.match('# pragma once') and
@@ -83,10 +90,10 @@ def text_match_pragma():
return False return False
# Finds 'file'. First the currently processing file's 'parent' path is looked at # Finds 'file'. First the currently processing file's 'parent' path is looked at
# for a match, followed by the list of 'root', returning a valid Path in # for a match, followed by the list of 'root' paths, returning a valid Path in
# canonical form. If no match is found None is returned. # canonical form. If no match is found None is returned.
# #
def resolve_include(parent: Path, file: str): def resolve_include(parent: Path, file: str) -> Path:
found = parent.joinpath(file).resolve(); found = parent.joinpath(file).resolve();
if (found.is_file()): if (found.is_file()):
return found return found
@@ -98,15 +105,17 @@ def resolve_include(parent: Path, file: str):
# Writes 'line' to the open file 'destn' (or stdout). # Writes 'line' to the open file 'destn' (or stdout).
# #
def write_line(line): def write_line(line: str) -> None:
print(line, file=destn) print(line, file=destn)
# Logs 'line' to stderr. # Logs 'line' to stderr.
# #
def log_line(line): def log_line(line: str) -> None:
print(line, file=sys.stderr) print(line, file=sys.stderr)
def add_file(file): # Adds the contents of 'file' with any of its includes inlined.
#
def add_file(file: Path) -> None:
if (isinstance(file, Path) and file.is_file()): if (isinstance(file, Path) and file.is_file()):
log_line(f'Processing: {file}') log_line(f'Processing: {file}')
with file.open('r') as opened: with file.open('r') as opened:
@@ -130,7 +139,7 @@ def add_file(file):
else: else:
log_line(f'Error: Unable to find: {file}') log_line(f'Error: Unable to find: {file}')
# Start here
parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c') parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c')
parser.add_argument('-r', '--root', action='append', type=Path, help='file root search path') parser.add_argument('-r', '--root', action='append', type=Path, help='file root search path')
parser.add_argument('-x', '--exclude', action='append', help='file to completely exclude from inlining') parser.add_argument('-x', '--exclude', action='append', help='file to completely exclude from inlining')
@@ -140,17 +149,40 @@ parser.add_argument('-o', '--output', type=argparse.FileType('w'), help='output
parser.add_argument('input', type=Path, help='input file') parser.add_argument('input', type=Path, help='input file')
args = parser.parse_args() args = parser.parse_args()
# Fail early on an invalid input (and store it so we don't recurse)
args.input = args.input.resolve(strict=True)
found.add(args.input)
# Resolve all of the root paths upfront (we'll halt here on invalid roots) # Resolve all of the root paths upfront (we'll halt here on invalid roots)
if (args.root is not None): if (args.root):
for path in args.root: for path in args.root:
roots.add(path.resolve(strict=True)) roots.add(path.resolve(strict=True))
# Resolve the excluded files
if (args.exclude):
for filename in args.exclude:
resolved = resolve_include(args.input.parent, filename)
if (resolved):
excludes.add(resolved)
else:
log_line(f'Warning: excluded file not found: {filename}')
# And the files to keep
if (args.keep):
for filename in args.keep:
resolved = resolve_include(args.input.parent, filename)
if (resolved):
keeps.add(resolved)
else:
log_line(f'Warning: kept #include not found: {filename}')
# Then recursively process the input file
try: try:
if (args.output is None): if (not args.output):
destn = sys.stdout destn = sys.stdout
else: else:
destn = args.output destn = args.output
add_file(args.input) add_file(args.input)
finally: finally:
if (destn is not None): if (not destn):
destn.close() destn.close()