예제 #1
0
def generate_globals_bindings(name, cpp_globals):
    includes = []
    bindings_functions = []
    objects = []
    for global_name, cpp_global in cpp_globals.items():
        if cpp_global.flags.nobind:
            continue
        export_name = format_name(cpp_global.name)
        log.info(f"  Generating bindings for global {global_name}")
        includes.append(get_include_file(cpp_global))
        objects.append({
            "bindings": f"Global{export_name}",
            "identifier": f"{cpp_global.namespace}::{cpp_global.name}",
            "load_priority": cpp_global.flags.load_priority,
        })
        state_view = flavour.STATE_VIEW
        binding_function_signature = f"void LoadGlobal{export_name}({state_view} state)"
        namespace_access = fetch_table(name) + "\n"
        binding_function_body = namespace_access + flavour.GLOBAL_BODY.format(
            namespace=name.split("::")[-1],
            global_name=cpp_global.name,
            global_ptr=global_name,
        )
        bindings_functions.append(f"{binding_function_signature}\n{{\n"
                                  f"{binding_function_body}\n}}")
    return {
        "includes": includes,
        "bindings_functions": bindings_functions,
        "objects": objects,
    }
예제 #2
0
def parse_doxygen_files(path_to_doc, cpp_db):
    doxygen_index = parse_doxygen_index(
        os.path.join(path_to_doc, "docbuild", "xml", "index.xml"))
    log.info("Loading classes info...")
    for currentDir, _, files in os.walk(
            os.path.join(path_to_doc, "docbuild/xml/")):
        for f in sorted(files):
            if any((f.startswith(f"class{item['namespace']}")
                    or f.startswith(f"struct{item['namespace']}"))
                   for item in SOURCE_DIRECTORIES):
                class_filepath = os.path.join(currentDir, f)
                log.debug(f"  Parsing class {class_filepath}")
                tree = etree.parse(class_filepath)
                class_model = parse_class_from_xml(
                    tree.xpath("/doxygen/compounddef")[0], doxygen_index)
                cpp_db.classes["::".join(
                    [class_model.namespace, class_model.name])] = class_model
            elif any(
                    f.startswith(f"namespace{item['namespace']}")
                    for item in SOURCE_DIRECTORIES):
                namespace_filepath = os.path.join(currentDir, f)
                log.debug(f"  Parsing namespace {namespace_filepath}")
                parse_namespace_from_xml(namespace_filepath, cpp_db,
                                         doxygen_index)
            else:
                log.warning(f"Ignoring file {f}")
예제 #3
0
def generate_enums_bindings(name: str, enums: List[EnumModel]):
    includes = []
    bindings_functions = []
    objects = []
    for enum_name, enum in enums.items():
        log.info(f"  Generating bindings for enum {enum_name}")
        enum_path = strip_include(enum.location.file).replace(os.path.sep, "/")
        includes.append(f"#include <{enum_path}>")
        state_view = flavour.STATE_VIEW
        export_name = format_name(enum.name)
        binding_function_signature = f"void LoadEnum{export_name}({state_view} state)"
        table_access = fetch_table(name) + "\n"
        binding_function_body = table_access + flavour.ENUM_BODY.format(
            namespace=name.split("::")[-1],
            enum_type=enum_name,
            enum_name=enum.name,
            enum_fields=generate_enum_fields(enum_name, enum),
        )
        bindings_functions.append(f"{binding_function_signature}\n{{\n"
                                  f"{binding_function_body}\n}}")
        objects.append({
            "bindings": f"Enum{export_name}",
            "identifier": f"{enum.namespace}::{enum.name}",
            "load_priority": enum.flags.load_priority,
        })
    return {
        "includes": includes,
        "bindings_functions": bindings_functions,
        "objects": objects,
    }
예제 #4
0
def generate_bindings_for_namespace(namespace_name, namespace):
    log.info(f"Generating bindings for namespace {namespace_name}")
    split_name = "/".join(namespace_name.split("::"))
    source = match_namespace_with_source(namespace_name)
    location = LOCATIONS[source["output_location"]]
    os.makedirs(
        os.path.join(OUTPUT_DIRECTORY, location["headers"], split_name),
        exist_ok=True,
    )
    os.makedirs(
        os.path.join(OUTPUT_DIRECTORY, location["sources"], split_name),
        exist_ok=True,
    )

    class_bindings = generate_classes_bindings(namespace.classes)
    enum_bindings = generate_enums_bindings(namespace_name, namespace.enums)
    functions_bindings = generate_functions_bindings(namespace.functions)
    globals_bindings = generate_globals_bindings(namespace_name,
                                                 namespace.globals)

    generated_objects = (class_bindings["objects"] + enum_bindings["objects"] +
                         functions_bindings["objects"] +
                         globals_bindings["objects"])

    bindings_header = os.path.join(
        split_name,
        f"{namespace_name.split('::')[-1]}.hpp").replace(os.path.sep, "/")
    if GENERATE_BINDINGS:
        make_bindings_header(bindings_header, namespace_name,
                             generated_objects)
    namespace_data = {
        "includes":
        namespace.namespaces.flags.additional_includes
        if namespace.namespaces.flags.additional_includes else [],
        "bindings_functions": [],
    }
    bindings_source = os.path.join(
        split_name,
        f"{namespace_name.split('::')[-1]}.cpp").replace(os.path.sep, "/")
    FILES_TO_FORMAT.append(os.path.join(location["headers"], bindings_header))
    FILES_TO_FORMAT.append(os.path.join(location["sources"], bindings_source))
    if GENERATE_BINDINGS:
        bindings_header_include_path = strip_include(
            os.path.join(location["headers"],
                         bindings_header)).replace("\\", "/")
        make_bindings_sources(
            namespace_name,
            bindings_source,
            bindings_header_include_path,
            enum_bindings,
            class_bindings,
            functions_bindings,
            globals_bindings,
            namespace_data,
        )
    return generated_objects, bindings_header, bindings_source
예제 #5
0
def check_git_directory():
    global PATH_TO_OBENGINE
    if PATH_TO_OBENGINE is not None:
        log.debug(f"Found existing ÖbEngine repository in {PATH_TO_OBENGINE}")
    else:
        log.debug("Cloning ÖbEngine repository...")
        PATH_TO_OBENGINE = set_obengine_git_directory(clone_obengine_repo())
    log.debug("Checking ÖbEngine repository validity...")
    if not check_obengine_repo(PATH_TO_OBENGINE):
        raise InvalidObEngineGitRepositoryException(PATH_TO_OBENGINE)
    log.info(f"Using ÖbEngine repository in {PATH_TO_OBENGINE}")
    return PATH_TO_OBENGINE
예제 #6
0
def generate_bindings(cpp_db: CppDatabase, write_files: bool = True):
    global GENERATE_BINDINGS
    GENERATE_BINDINGS = write_files
    log.info("===== Generating bindings for ÖbEngine ====")
    discard_placeholders(cpp_db)
    inject_ref_in_function_parameters(cpp_db)
    patch_const_ref_return_type(cpp_db)
    namespaces = group_bindings_by_namespace(cpp_db)
    generated_objects = {}
    for namespace_name, namespace in namespaces.items():
        copy_parent_bindings(cpp_db, namespace.classes)
        copy_parent_bases(cpp_db, namespace.classes)
        flag_abstract_classes(cpp_db, namespace.classes)
        apply_proxies(cpp_db, namespace.functions)
        generation_results = generate_bindings_for_namespace(
            namespace_name, namespace)
        generated_objects[namespace_name] = {
            "objects": generation_results[0],
            "header": generation_results[1],
            "source": generation_results[2],
        }
    if GENERATE_BINDINGS:
        for location, source_namespaces in SOURCE_DIRECTORIES_BY_OUTPUT.items(
        ):
            source_generated_objects = {
                namespace: generated_object
                for namespace, generated_object in generated_objects.items()
                if any(
                    namespace.startswith(source_namespace)
                    for source_namespace in source_namespaces)
            }
            source_path = LOCATIONS[location]["sources"]
            with open(
                    os.path.join(OUTPUT_DIRECTORY, f"{source_path}/index.cpp"),
                    "w",
            ) as bindings_index:
                bindings_index.write(
                    generated_bindings_index(location,
                                             source_generated_objects))
            FILES_TO_FORMAT.append(f"{source_path}/index.cpp")
            if GENERATE_BINDINGS:
                clang_format_files(FILES_TO_FORMAT)
    return generated_objects
예제 #7
0
def generate_functions_bindings(functions):
    objects = []
    includes = []
    bindings_functions = []
    for function_name, function_value in functions.items():
        log.info(f"  Generating bindings for function {function_name}")
        func_type, short_name = get_real_function_name(
            function_name.split("::")[-1], function_value)
        real_function_name = format_name(short_name)
        if isinstance(function_value, FunctionOverloadModel):
            identifier = (
                f"{function_value.overloads[0].namespace}::{function_value.name}"
            )
        else:
            identifier = f"{function_value.namespace}::{function_value.name}"

        objects.append({
            "bindings": f"{func_type}{real_function_name}",
            "identifier": identifier,
            "load_priority": function_value.flags.load_priority,
        })
        if isinstance(function_value, FunctionOverloadModel):
            for overload in function_value.overloads:
                includes.append(get_include_file(overload))
        else:
            includes.append(get_include_file(function_value))

        state_view = flavour.STATE_VIEW
        binding_function_signature = (
            f"void LoadFunction{real_function_name}({state_view} state)")

        binding_function = (
            f"{binding_function_signature}\n{{\n"
            f"{generate_function_bindings(function_name, function_value)}}}")
        bindings_functions.append(binding_function)
    return {
        "includes": includes,
        "objects": objects,
        "bindings_functions": bindings_functions,
    }
예제 #8
0
def generate_classes_bindings(classes):
    objects = []
    includes = []
    bindings_functions = []
    for class_name, class_value in classes.items():
        includes_for_class = []
        if class_value.flags.nobind:
            continue
        log.info(f"  Generating bindings for class {class_name}")
        real_class_name = class_name.split("::")[-1]
        real_class_name = format_name(real_class_name)
        objects.append({
            "bindings": f"Class{real_class_name}",
            "identifier": f"{class_value.namespace}::{class_value.name}",
            "load_priority": class_value.flags.load_priority,
        })
        class_path = strip_include(class_value.location.file)
        class_path = class_path.replace(os.path.sep, "/")
        class_path = f"#include <{class_path}>"
        includes_for_class.append(class_path)

        state_view = flavour.STATE_VIEW
        binding_function_signature = (
            f"void LoadClass{real_class_name}({state_view} state)")
        binding_function = (f"{binding_function_signature}\n{{\n"
                            f"{generate_class_bindings(class_value)}\n}}")
        if "_fs" in binding_function:
            includes_for_class.append("#include <System/Path.hpp>")
        if class_value.flags.additional_includes:
            includes_for_class += class_value.flags.additional_includes
        bindings_functions.append(binding_function)
        includes.append("\n".join(includes_for_class))
    return {
        "includes": includes,
        "objects": objects,
        "bindings_functions": bindings_functions,
    }
예제 #9
0
파일: hints.py 프로젝트: ObEngine/Obidog
def generate_hints(cpp_db: CppDatabase, path_to_doc: str):
    log.info("Discarding placeholders")
    discard_placeholders(cpp_db)

    log.info("Converting all types")
    convert_all_types(cpp_db)

    cpp_db.classes |= _build_table_for_events(cpp_db.classes)
    cpp_db.classes |= _generate_dynamic_types()
    all_elements = [
        item for item_type in cpp_db.__dict__.keys()
        for item in getattr(cpp_db, item_type).values()
        if not item.flags.nobind
    ]

    _add_return_type_to_constructors(cpp_db)
    _remove_operators(cpp_db)

    _fix_bind_as(all_elements)
    _setup_methods_as_attributes(cpp_db.classes)

    log.info("Generating hints")
    write_hints(_get_namespace_tables(all_elements), all_elements)
예제 #10
0
파일: main.py 프로젝트: ObEngine/Obidog
def main():
    parser = argparse.ArgumentParser()
    # Starting Obidog
    log.info("Obidog starting...")

    # Creating databases
    cpp_db = CppDatabase()

    # Checking OBENGINE_GIT_DIRECTORY
    path_to_obengine = check_git_directory()

    # Generating Doxygen documentation
    log.info("Building Doxygen XML documentation...")
    path_to_doc = build_doxygen_documentation(path_to_obengine)

    # Processing all files in Doxygen documentation
    parse_doxygen_files(path_to_doc, cpp_db)

    cwd = tempfile.mkdtemp()
    log.info(f"Working directory : {cwd}")

    parser.add_argument(
        "mode",
        help="Resource you want to generate",
        choices=["documentation", "bindings", "hints"],
    )
    args = parser.parse_args()

    if args.mode == "documentation":
        generate_documentation(cpp_db, path_to_doc)

    elif args.mode == "bindings":
        generate_bindings(cpp_db)

    if args.mode == "hints":
        generate_hints(cpp_db, path_to_doc)
예제 #11
0
def generate_documentation(cpp_db: CppDatabase, path_to_doc: str):
    doxygen_index = parse_doxygen_index(
        os.path.join(path_to_doc, "docbuild", "xml", "index.xml"))
    log.info("Preparing database")
    bindings_results = generate_bindings(
        cpp_db, False)  # TODO: Don't forget to put this to false !

    log.info("Converting all types")
    convert_all_types(cpp_db)

    all_elements = ([
        item for item_type in cpp_db.__dict__.keys()
        for item in getattr(cpp_db, item_type).values()
        if not item.flags.nobind
    ] + [
        method for class_value in cpp_db.classes.values()
        for method in class_value.methods.values() if not method.flags.nobind
    ] + [
        attribute for class_value in cpp_db.classes.values()
        for attribute in class_value.attributes.values()
        if not attribute.flags.nobind
    ])
    log.info("Retrieving urls for all elements")
    for element in all_elements:
        fill_element_urls(
            element,
            doxygen_index=doxygen_index,
            bindings_results=bindings_results,
        )

    log.info("Grouping namespace")
    namespaces = group_bindings_by_namespace(cpp_db)
    log.info("Generate namespaces documentation")
    for namespace_value in namespaces.values():
        if not namespace_value.flags.nobind:
            document_item(namespace_value)

    log.info("Generate classes documentation")
    for class_value in cpp_db.classes.values():
        if not class_value.flags.nobind:
            document_item(class_value)
    """log.info("Generate full database")
    with open(
        os.path.join("export", "db.json"), "w", encoding="utf-8"
    ) as db_export:
        json.dump(
            cpp_db.__dict__,
            db_export,
            indent=4,
            ensure_ascii=False,
            cls=DefaultEncoder,
        )
    """

    log.info("Generate search database")
    generate_search_db(cpp_db)