summaryrefslogtreecommitdiff
path: root/libmisc/wrap-cc
diff options
context:
space:
mode:
Diffstat (limited to 'libmisc/wrap-cc')
-rwxr-xr-xlibmisc/wrap-cc141
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: