Skip to content
Snippets Groups Projects
Select Git revision
  • 7fe2191c9b72e16d271735ca24a9def7ba072217
  • wip-bootstrap default
  • dualcore
  • ch3/leds
  • ch3/time
  • master
6 results

runtime.c

Blame
  • genapi.py 5.33 KiB
    import argparse
    import contextlib
    import os
    import re
    import subprocess
    
    api_src_fmt = """\
    #define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
    #include "{header}"
    """
    
    
    """Generated Client file"""
    api_func_start_fmt = """\
    /* Autogenerated stub for {id} */
    void {cdecl}({cargs})
    {{
        const int size = {total_size};
        void*buffer;
    
        buffer = api_call_start({id}, size);
        /* TODO: Check if buffer is no NULL */
    """
    
    serialise_arg_fmt = """    *({type}*)(buffer + {offset}) = {arg};"""
    
    bother_dispatcher_fmt = """
        printf("Sending call {id}\\nBUF: ");
        for (int i = 0; i < size; i++) {{
            printf("0x%02x ", ((char*)buffer)[i]);
        }}
        printf("\\n");
    
        api_call_bother_dispatcher(buffer);
    }}
    """
    
    
    """Generated Service file"""
    api_dispatch_call_start = """\
    void __api_dispatch_call(uint32_t id, void*buffer)
    {
        switch (id) {"""
    
    dispatch_case_fmt = """\
            case {id}:
                {cdecl}("""
    
    deserialise_arg_fmt = """\
                    *({type}*)(buffer + {offset})"""
    
    insert_switch_case_break_str = """
                );
                break;"""
    
    switch_add_default_fmt = """\
            default:
                printf("Error: API function %x is unknown!!\\n", {id});
            break;
        }}
    }}"""
    
    def api_func_iter (header:str):
        # Parse the header for API definitions
        matcher = re.compile(
            r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ void (?P<decl>.+?)\((?P<args>.*?)\) \$",
            re.DOTALL | re.MULTILINE,
        )
    
        # Run the preprocessor on the header file to get the API definitions.
        #
        # For this, we first need a source to include the header which contains
        # an alternative definition of the `API` macro that marks definitions in
        # a way we can find later on.
        api_src = api_src_fmt.format(
            header=os.path.relpath(header)
        )
    
        # Evaluate the preprocessor
        source = subprocess.check_output(
            ["gcc", "-E", "-"], input=api_src.encode()
        ).decode()
    
        return matcher.finditer(source)
    
    
    def include_header (header:str, file):
        print('#include "{}"\n'.format(
            os.path.basename(header)
        ), file=file)
    
    
    def destructure_args (api_args:str):
        api_args_names = []
        api_args_types = []
        api_args_sizes = []
    
        args_matcher = re.compile(r"(?P<type>\w+(?:\*+|\s+))(?P<name>\w+),")
    
        # Destructure args
        for arg in args_matcher.finditer(api_args + ","):
            arg_type = arg.group("type").strip()
            arg_name = arg.group("name")
    
            api_args_names.append(arg_name)
            api_args_types.append(arg_type)
            api_args_sizes.append("sizeof({})".format(arg_type))
    
        return (api_args_names, api_args_types, api_args_sizes)
    
    
    def serialise_arg (ty:str, api_args_sizes:list, arg_idx:int, arg:str, file):
        print(
            serialise_arg_fmt.format(
                type=ty,
                offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
                arg=arg,
            ),
            file=file,
        )
    
    def deserialise_arg (ty:str, api_args_sizes:list, arg_idx:int, file):
        if arg_idx != 0:
            print(",", file=file)
    
        print(
            deserialise_arg_fmt.format(
                type=ty,
                offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
            ),
            file=file,
            end="",
        )
    
    
    def insert_switch_case_break (file):
        print(insert_switch_case_break_str,
                file=file,
        )
    
    
    def bother_dispatcher (api_id:str, file):
        print(bother_dispatcher_fmt.format(
                id=api_id
            ),
            file=file,
        )
    
    
    def switch_add_default (api_id:str, file):
        print(switch_add_default_fmt.format(
            id=api_id,
        ), file=file)
    
    
    
    def main():
        parser = argparse.ArgumentParser(
            description="Generate the API stubs from a header file."
        )
        parser.add_argument(
            "-H", "--header", required=True, help="The header to base the definitions on."
        )
        parser.add_argument(
            "-c", "--client", required=True, help="The output client-side c source file."
        )
        parser.add_argument(
            "-s", "--server", required=True, help="The output server-side c source file."
        )
        args = parser.parse_args()
    
        # Open output files
        cx = contextlib.ExitStack()
        f_client = cx.enter_context(open(args.client, "w"))
        f_server = cx.enter_context(open(args.server, "w"))
    
        include_header (args.header, f_client)
        include_header (args.header, f_server)
    
        print(api_dispatch_call_start, file=f_server)
    
        for api_func in api_func_iter (args.header):
            api_id = api_func.group("id")
            api_decl = api_func.group("decl")
            api_args = api_func.group("args")
    
            (api_args_names, api_args_types, api_args_sizes) = \
                    destructure_args (api_args)
    
            print(api_func_start_fmt.format(
                    id=api_id,
                    cdecl=api_decl,
                    cargs=api_args,
                    total_size=" + ".join(api_args_sizes),
                ),
                file=f_client,
            )
    
            print(dispatch_case_fmt.format(id=api_id, cdecl=api_decl),
                file=f_server,
            )
    
            for i, (arg, ty) in enumerate(zip(api_args_names, api_args_types)):
                serialise_arg (ty, api_args_sizes, i, arg, f_client)
                deserialise_arg (ty, api_args_sizes, i, f_server)
    
            insert_switch_case_break (f_server)
            bother_dispatcher (api_id, f_client)
    
        switch_add_default (api_id, f_server)
    
    
    if __name__ == "__main__":
        main()