# SPDX-License-Identifier: LGPL-3.0-or-other # Copyright (C) 2021 Contributors to the SLS Detector Package """ This file is used to auto generate Python bindings for the sls::Detector class. The tool needs the libclang bindings to be installed. When the Detector API is updated this file should be run manually. """ from clang import cindex import subprocess import argparse import sys import time from pathlib import Path from parse import system_include_paths, clang_format_version REDC = '\033[91m' GREENC = '\033[92m' ENDC = '\033[0m' def red(msg): return f'{REDC}{msg}{ENDC}' def green(msg): return f'{GREENC}{msg}{ENDC}' def check_clang_format_version(required_version): if (ver := clang_format_version()) != required_version: msg = red(f'Clang format version {required_version} required, detected: {ver}. Bye!') print(msg) sys.exit(1) else: msg = green(f'Found clang-format version {ver}') print(msg) def check_for_compile_commands_json(path): # print(f"Looking for compile data base in: {path}") compile_data_base_file = path/'compile_commands.json' if not compile_data_base_file.exists(): msg = red(f"No compile_commands.json file found in {path}. Bye!") print(msg) sys.exit(1) else: msg = green(f'Found: {compile_data_base_file}') print(msg) default_build_path = "/home/l_frojdh/sls/build/" fpath = "../../slsDetectorSoftware/src/Detector.cpp" m = [] ag = [] lines = [] ag2 = [] cn = [] def get_arguments(node): args = [a.type.spelling for a in node.get_arguments()] args = [ "py::arg() = Positions{}" if item == "sls::Positions" else "py::arg()" for item in args ] args = ", ".join(args) if args: args = f", {args}" return args def get_arguments_with_default(node): args = [] for arg in node.get_arguments(): tokens = [t.spelling for t in arg.get_tokens()] # print(tokens) if '=' in tokens: if arg.type.spelling == "sls::Positions": #TODO! automate args.append("py::arg() = Positions{}") else: args.append('py::arg()' + ''.join(tokens[tokens.index('='):])) else: args.append('py::arg()') args = ", ".join(args) if args: args = f", {args}" return args def get_fdec(node): args = [a.type.spelling for a in node.get_arguments()] if node.result_type.spelling: return_type = node.result_type.spelling else: return_type = 'void' if node.is_const_method(): const = 'const' else: const = '' args = ", ".join(args) args = f'({return_type}(Detector::*)({args}){const})' return args def visit(node): if node.kind == cindex.CursorKind.CLASS_DECL: if node.displayname == "Detector": for child in node.get_children(): if ( child.kind == cindex.CursorKind.CXX_METHOD and child.access_specifier == cindex.AccessSpecifier.PUBLIC ): m.append(child) # args = get_arguments(child) args = get_arguments_with_default(child) fs = get_fdec(child) lines.append( f'.def("{child.spelling}",{fs} &Detector::{child.spelling}{args})' ) if cargs.verbose: print(f'&Detector::{child.spelling}{args})') cn.append(child) for child in node.get_children(): visit(child) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "-p", "--build_path", help="Path to the build database", type=Path, default=default_build_path, ) parser.add_argument( "-v", "--verbose", help="more output", action='store_true', ) cargs = parser.parse_args() check_clang_format_version(12) check_for_compile_commands_json(cargs.build_path) print("Parsing functions in Detector.h - ", end = "", flush = True) t0 = time.perf_counter() #parse functions db = cindex.CompilationDatabase.fromDirectory(cargs.build_path) index = cindex.Index.create() args = db.getCompileCommands(fpath) args = list(iter(args).__next__().arguments)[0:-1] args = args + "-x c++ --std=c++11".split() syspath = system_include_paths("clang++") incargs = ["-I" + inc for inc in syspath] args = args + incargs tu = index.parse(fpath, args=args) visit(tu.cursor) print(green('OK')) print(f'Parsing took {time.perf_counter()-t0:.3f}s') print("Read detector_in.cpp - ", end = "") with open("../src/detector_in.cpp") as f: data = f.read() s = "".join(lines) s += ";" text = data.replace("[[FUNCTIONS]]", s) warning = "/* WARINING This file is auto generated any edits might be overwritten without warning */\n\n" print(green("OK")) print("Writing to detector.cpp - ", end = "") with open("../src/detector.cpp", "w") as f: f.write(warning) f.write(text) print(green('OK')) # run clang format on the output print('Running clang format on generated source -', end = "") subprocess.run(["clang-format", "../src/detector.cpp", "-i"]) print(green(" OK")) print("Changes since last commit:") subprocess.run(['git', 'diff', '../src/detector.cpp'])