|
|
|
|
@ -73,11 +73,22 @@ def load_compile_flags(compile_commands_path, src_path):
|
|
|
|
|
if skip_next:
|
|
|
|
|
skip_next = False
|
|
|
|
|
continue
|
|
|
|
|
if tok.endswith('g++') or tok.endswith('clang++') or tok.endswith('clang') or tok.endswith('g++') or tok.endswith('cc'):
|
|
|
|
|
# skip the compiler binary itself
|
|
|
|
|
if (
|
|
|
|
|
tok.endswith("g++")
|
|
|
|
|
or tok.endswith("clang++")
|
|
|
|
|
or tok.endswith("clang")
|
|
|
|
|
or tok.endswith("cc")
|
|
|
|
|
or tok.endswith("c++")
|
|
|
|
|
):
|
|
|
|
|
continue
|
|
|
|
|
if tok == "-c":
|
|
|
|
|
skip_next = True
|
|
|
|
|
continue
|
|
|
|
|
# drop output file flag: -o <file>
|
|
|
|
|
if tok == "-o":
|
|
|
|
|
skip_next = True
|
|
|
|
|
continue
|
|
|
|
|
filtered.append(tok)
|
|
|
|
|
return filtered
|
|
|
|
|
return []
|
|
|
|
|
@ -183,7 +194,13 @@ def main():
|
|
|
|
|
p = argparse.ArgumentParser()
|
|
|
|
|
p.add_argument("--out-dir", "-o", required=True)
|
|
|
|
|
p.add_argument("--compile-commands", "-c", default=None)
|
|
|
|
|
p.add_argument("inputs", nargs="+")
|
|
|
|
|
p.add_argument("--header", required=True, help="C++ header file to scan for exported RPC classes")
|
|
|
|
|
p.add_argument("--source", required=True, help="C++ source file to use for resolving compile flags (from compile_commands.json)")
|
|
|
|
|
p.add_argument(
|
|
|
|
|
"--out-base",
|
|
|
|
|
required=True,
|
|
|
|
|
help="Base name for generated files: <out-base>.proxy.[h|cpp], <out-base>.skeleton.[h|cpp]",
|
|
|
|
|
)
|
|
|
|
|
p.add_argument("--templates", "-t", default=os.path.join(os.path.dirname(__file__), "templates"))
|
|
|
|
|
args = p.parse_args()
|
|
|
|
|
|
|
|
|
|
@ -199,28 +216,68 @@ def main():
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print("WARNING: cannot set libclang path:", e)
|
|
|
|
|
|
|
|
|
|
# determine compile flags from the source file (which is what appears in compile_commands.json)
|
|
|
|
|
if not os.path.exists(args.header):
|
|
|
|
|
print("ERROR: header file not found:", args.header)
|
|
|
|
|
return 1
|
|
|
|
|
if not os.path.exists(args.source):
|
|
|
|
|
print("ERROR: source file not found:", args.source)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
compile_args = load_compile_flags(args.compile_commands, args.source)
|
|
|
|
|
if not any(a.startswith("-x") for a in compile_args):
|
|
|
|
|
compile_args = ["-x", "c++", "-std=c++17"] + compile_args
|
|
|
|
|
|
|
|
|
|
print("Parsing", args.header, "with args:", compile_args)
|
|
|
|
|
|
|
|
|
|
index = Index.create()
|
|
|
|
|
all_classes = []
|
|
|
|
|
|
|
|
|
|
for inp in args.inputs:
|
|
|
|
|
if not os.path.exists(inp):
|
|
|
|
|
print("WARN: input file not found:", inp)
|
|
|
|
|
continue
|
|
|
|
|
compile_args = load_compile_flags(args.compile_commands, inp)
|
|
|
|
|
# ensure -x c++ if missing
|
|
|
|
|
if not any(a.startswith("-x") for a in compile_args):
|
|
|
|
|
compile_args = ["-x", "c++", "-std=c++17"] + compile_args
|
|
|
|
|
print("Parsing", inp, "with args:", compile_args)
|
|
|
|
|
classes = parse_file(index, inp, compile_args)
|
|
|
|
|
all_classes.extend(classes)
|
|
|
|
|
|
|
|
|
|
if not all_classes:
|
|
|
|
|
print("No exported classes/methods found. Nothing to generate.")
|
|
|
|
|
classes = parse_file(index, args.header, compile_args)
|
|
|
|
|
|
|
|
|
|
if not classes:
|
|
|
|
|
print("No exported classes/methods found in header. Nothing to generate.")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
# render templates
|
|
|
|
|
render_templates(all_classes, out_dir, args.templates)
|
|
|
|
|
print("Generated files for classes:", ", ".join(c.name for c in all_classes))
|
|
|
|
|
# For this PoC we expect a single exported service per header.
|
|
|
|
|
# If there are multiple, refuse to guess which one should define the filenames.
|
|
|
|
|
if len(classes) > 1:
|
|
|
|
|
print(
|
|
|
|
|
"ERROR: multiple exported classes found in header; "
|
|
|
|
|
"current generator expects exactly one when using --out-base."
|
|
|
|
|
)
|
|
|
|
|
for c in classes:
|
|
|
|
|
print(" -", c.name)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# render templates using the single discovered class but the user‑provided base name
|
|
|
|
|
cls = classes[0]
|
|
|
|
|
|
|
|
|
|
env = Environment(
|
|
|
|
|
loader=FileSystemLoader(args.templates),
|
|
|
|
|
autoescape=False,
|
|
|
|
|
trim_blocks=True,
|
|
|
|
|
lstrip_blocks=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
proxy_h = env.get_template("proxy.h.j2")
|
|
|
|
|
proxy_cpp = env.get_template("proxy.cpp.j2")
|
|
|
|
|
skeleton_h = env.get_template("skeleton.h.j2")
|
|
|
|
|
skeleton_cpp = env.get_template("skeleton.cpp.j2")
|
|
|
|
|
|
|
|
|
|
base = args.out_base
|
|
|
|
|
|
|
|
|
|
with open(f"{out_dir}/{base}.proxy.h", "w") as f:
|
|
|
|
|
f.write(proxy_h.render(cls=cls))
|
|
|
|
|
|
|
|
|
|
with open(f"{out_dir}/{base}.proxy.cpp", "w") as f:
|
|
|
|
|
f.write(proxy_cpp.render(cls=cls))
|
|
|
|
|
|
|
|
|
|
with open(f"{out_dir}/{base}.skeleton.h", "w") as f:
|
|
|
|
|
f.write(skeleton_h.render(cls=cls))
|
|
|
|
|
|
|
|
|
|
with open(f"{out_dir}/{base}.skeleton.cpp", "w") as f:
|
|
|
|
|
f.write(skeleton_cpp.render(cls=cls))
|
|
|
|
|
|
|
|
|
|
print("Generated files for class", cls.name, "into base", base)
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|