diff options
Diffstat (limited to 'libmisc/wrap-cc')
-rwxr-xr-x | libmisc/wrap-cc | 141 |
1 files changed, 127 insertions, 14 deletions
diff --git a/libmisc/wrap-cc b/libmisc/wrap-cc index 5124689..e7a0b91 100755 --- a/libmisc/wrap-cc +++ b/libmisc/wrap-cc @@ -5,11 +5,70 @@ # SPDX-License-Identifier: AGPL-3.0-or-later import os +import subprocess import sys import typing +def scan_tuple( + text: str, beg: int, on_part: typing.Callable[[str], None] | None = None +) -> int: + assert text[beg] == "(" + pos = beg + 1 + arg_start = pos + parens = 1 + instring = False + while parens: + c = text[pos] + if instring: + match c: + case "\\": + pos += 1 + case '"': + instring = False + else: + match c: + case "(": + parens += 1 + case ")": + parens -= 1 + if on_part and parens == 0 and text[beg + 1 : pos].strip(): + on_part(text[arg_start:pos]) + case ",": + if on_part and parens == 1: + on_part(text[arg_start:pos]) + arg_start = pos + 1 + case '"': + instring = True + pos += 1 + assert text[pos - 1] == ")" + return pos - 1 + + +def unquote(cstr: str) -> str: + assert len(cstr) >= 2 and cstr[0] == '"' and cstr[-1] == '"' + cstr = cstr[1:-1] + out = "" + while cstr: + if cstr[0] == "\\": + match cstr[1]: + case "n": + out += "\n" + cstr = cstr[2:] + case "\\": + out += "\\" + cstr = cstr[2:] + case '"': + out += '"' + cstr = cstr[2:] + else: + out += cstr[0] + cstr = cstr[1:] + return out + + def preprocess(all_args: list[str]) -> typing.NoReturn: + # argparse ################################################################# _args = all_args def shift(n: int) -> list[str]: @@ -19,47 +78,101 @@ def preprocess(all_args: list[str]) -> typing.NoReturn: return ret arg0 = shift(1)[0] - flags: list[str] = [] + common_flags: list[str] = [] + output_flags: list[str] = [] positional: list[str] = [] while _args: if len(_args[0]) > 2 and _args[0][0] == "-" and _args[0][1] in "IDU": _args = [_args[0][:2], _args[0][2:], *_args[1:]] match _args[0]: # Mode - case "-E" | "-quiet" | "-lang-asm": - flags += shift(1) + case "-E" | "-quiet": + common_flags += shift(1) + case "-lang-asm": + os.execvp(all_args[0], all_args) # Search path case "-I" | "-imultilib" | "-isystem": - flags += shift(2) + common_flags += shift(2) # Define/Undefine case "-D" | "-U": - flags += shift(2) + common_flags += shift(2) # Optimization case "-O0" | "-O1" | "-O2" | "-O3" | "-Os" | "-Ofast" | "-Og" | "-Oz": - flags += shift(1) + common_flags += shift(1) case "-g": - flags += shift(1) + common_flags += shift(1) # Output files case "-MD" | "-MF" | "-MT" | "-dumpbase" | "-dumpbase-ext": - flags += shift(2) + output_flags += shift(2) case "-o": - flags += shift(2) + output_flags += shift(2) # Other case _: if _args[0].startswith("-"): if _args[0].startswith("-std="): - flags += shift(1) + common_flags += shift(1) elif _args[0].startswith("-m"): - flags += shift(1) + common_flags += shift(1) elif _args[0].startswith("-f"): - flags += shift(1) + common_flags += shift(1) elif _args[0].startswith("-W"): - flags += shift(1) + common_flags += shift(1) else: raise ValueError(f"unknown flag: {_args!r}") else: positional += shift(1) - os.execvp(arg0, [arg0, *flags, *positional]) + if len(positional) != 1: + raise ValueError("expected 1 input file") + infile = positional[0] + + # enhance ################################################################## + + common_flags += ["-D", "__LIBMISC_ENHANCED_CPP__"] + + text = subprocess.run( + [arg0, *common_flags, infile], + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=sys.stderr, + check=True, + text=True, + ).stdout + + macros: dict[str, str] = {} + + marker = "__xx__LM_DEFAPPEND__xx__" + pos = 0 + while (marker_beg := text.find(marker, pos)) >= 0: + args: list[str] = [] + + def add_arg(arg: str) -> None: + nonlocal args + args.append(arg) + + beg_paren = marker_beg + len(marker) + end_paren = scan_tuple(text, beg_paren, add_arg) + + before = text[:marker_beg] + # old = text[marker_beg : end_paren + 1] + after = text[end_paren + 1 :] + + assert len(args) == 2 + k = unquote(args[0].strip()) + v = unquote(args[1].strip()) + if k not in macros: + macros[k] = v + else: + macros[k] += " " + v + + text = before + after + pos = len(before) + + common_flags += ["-D", marker + "=LM_EAT"] + for k, v in macros.items(): + common_flags += ["-D", k + "=" + v] + + # Run, for-real ############################################################ + os.execvp(arg0, [arg0, *common_flags, *output_flags, infile]) def main(all_args: list[str]) -> typing.NoReturn: |