Skip to content
Snippets Groups Projects
Verified Commit 06df747a authored by rahix's avatar rahix
Browse files

refactor(api): Restructure API generation script


Signed-off-by: default avatarRahix <rahix@rahix.de>
parent 15bb6ac6
Branches
No related tags found
No related merge requests found
import argparse import argparse
import contextlib
import os import os
import re import re
import subprocess import subprocess
MATCH_DECLARATION = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<type>\w+(?:\*+|\s+))(?P<name>.+?)\((?P<args>.*?)\) \$",
re.DOTALL | re.MULTILINE,
)
MATCH_ARGS = re.compile(r"(?P<type>\w+(?:\*+|\s+))(?P<name>\w+),")
def parse_declarations(source):
"""Parse all declarations in the given source."""
declarations = []
for decl in MATCH_DECLARATION.finditer(source):
id = decl.group("id")
return_type = decl.group("type").strip()
name = decl.group("name")
args = []
args_str = decl.group("args")
# Parse arguments
for arg in MATCH_ARGS.finditer(args_str + ","):
arg_type = arg.group("type").strip()
arg_name = arg.group("name")
args.append({
"type": arg_type,
"name": arg_name,
"sizeof": "sizeof({})".format(arg_type),
})
declarations.append({
"id": id,
"return_type": return_type,
"name": name,
"args": args,
"args_str": args_str,
})
return declarations
def sizeof(args):
"""Return a string that describes the size of a list of arguments."""
return " + ".join(a["sizeof"] for a in args) if args != [] else "0"
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
...@@ -20,169 +63,125 @@ def main(): ...@@ -20,169 +63,125 @@ def main():
) )
args = parser.parse_args() args = parser.parse_args()
with contextlib.ExitStack() as cx: # Run the preprocessor on the header file to get the API definitions.
# 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
# For this, we first need a source to include the header which contains # an alternative definition of the `API` macro that marks definitions in
# an alternative definition of the `API` macro that marks definitions in # a way we can find later on.
# a way we can find later on. api_src = """\
api_src = """\
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $ #define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
#include "{header}" #include "{header}"
""".format( """.format(
header=os.path.relpath(args.header) header=os.path.relpath(args.header)
) )
# Evaluate the preprocessor
source = subprocess.check_output(
["gcc", "-E", "-"], input=api_src.encode()
).decode()
# Parse the header for API definitions
matcher = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<type>\w+(?:\*+|\s+))(?P<decl>.+?)\((?P<args>.*?)\) \$",
re.DOTALL | re.MULTILINE,
)
args_matcher = re.compile(r"(?P<type>(?:const )?\w+(?:\*+|\s+))(?P<name>\w+),") # Evaluate the preprocessor
source = subprocess.check_output(
["gcc", "-E", "-"], input=api_src.encode()
).decode()
# Open output files declarations = parse_declarations(source)
f_client = cx.enter_context(open(args.client, "w")) fmt_header = {
f_server = cx.enter_context(open(args.server, "w")) "header": os.path.basename(args.header)
}
print("""\ # Generate Client {{{
with open(args.client, "w") as f_client:
tmp = """\
#include <stdio.h> #include <stdio.h>
#include "{}" #include "{header}"
#include "api/caller.h" #include "api/caller.h"
""".format( """
os.path.basename(args.header) f_client.write(tmp.format(**fmt_header))
), file=f_client)
print("""\ for decl in declarations:
#include <stdio.h> decl["total_size"] = sizeof(decl["args"])
#include "{}" tmp = """\
void __api_dispatch_call(uint32_t id, void*buffer)
{{
switch (id) {{""".format(
os.path.basename(args.header)
), file=f_server)
for match in matcher.finditer(source):
api_id = match.group("id")
api_return = match.group("type").strip()
api_decl = match.group("decl")
api_args = match.group("args")
api_args_names = []
api_args_types = []
api_args_sizes = []
# Destructure args
for match in args_matcher.finditer(api_args + ","):
arg_type = match.group("type").strip()
arg_name = match.group("name")
api_args_names.append(arg_name)
api_args_types.append(arg_type)
api_args_sizes.append("sizeof({})".format(arg_type))
print(
"""\
/* Autogenerated stub for {id} */ /* Autogenerated stub for {id} */
{ret} {cdecl}({cargs}) {return_type} {name}({args_str})
{{ {{
const int size = {total_size}; const int size = {total_size};
void*buffer; void*buffer;
buffer = _api_call_start({id}, size); buffer = _api_call_start({id}, size);
/* TODO: Check if buffer is no NULL */ /* TODO: Check if buffer is not NULL */
""".format(
id=api_id, """
ret=api_return, f_client.write(tmp.format(**decl))
cdecl=api_decl,
cargs=api_args, for i, arg in enumerate(decl["args"]):
total_size=" + ".join(api_args_sizes) if api_args_sizes != [] else "0", arg["offset"] = sizeof(decl["args"][:i])
), tmp = """\
file=f_client, *({type}*)(buffer + {offset}) = {name};
) """
f_client.write(tmp.format(**arg))
if api_return != "void":
print("""\ if decl["return_type"] == "void":
case {id}: # Don't return if return type is void
*(({ret}*)buffer) = {cdecl}(""".format(id=api_id, ret=api_return, cdecl=api_decl), tmp = """\
file=f_server,
) _api_call_transact(buffer);
}}
"""
f_client.write(tmp.format(**decl))
else: else:
print("""\ tmp = """\
case {id}:
{cdecl}(""".format(id=api_id, ret=api_return, cdecl=api_decl), return *({return_type}*)_api_call_transact(buffer);
file=f_server,
)
for i, (arg, ty) in enumerate(zip(api_args_names, api_args_types)):
print(
""" *({type}*)(buffer + {offset}) = {arg};""".format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
arg=arg,
),
file=f_client,
)
if i != 0:
print(",", file=f_server)
print(
"""\
*({type}*)(buffer + {offset})""".format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
),
file=f_server,
end="",
)
print("""
);
break;""".format(
cdecl=api_decl,
args=", ".join(api_args_names),
),
file=f_server,
)
if api_return != "void":
print(
"""
return *({ret}*)_api_call_transact(buffer);
}} }}
""".format( """
id=api_id, f_client.write(tmp.format(**decl))
ret=api_return, # END: Generate Client }}}
),
file=f_client, # Generate Dispatcher {{{
) with open(args.server, "w") as f_dispatcher:
tmp = """\
#include <stdio.h>
#include "{header}"
void __api_dispatch_call(uint32_t id, void*buffer)
{{
switch (id) {{
"""
f_dispatcher.write(tmp.format(**fmt_header))
for decl in declarations:
if decl["return_type"] == "void":
tmp = """\
case {id}:
{name}("""
f_dispatcher.write(tmp.format(**decl))
else: else:
print( tmp = """\
""" case {id}:
_api_call_transact(buffer); *(({return_type}*)buffer) = {name}("""
f_dispatcher.write(tmp.format(**decl))
for i, arg in enumerate(decl["args"]):
arg["comma"] = "" if i == 0 else ","
arg["offset"] = sizeof(decl["args"][:i])
tmp = """{comma}
*({type}*)(buffer + {offset})"""
f_dispatcher.write(tmp.format(**arg))
tmp = """
);
break;
"""
f_dispatcher.write(tmp.format(**decl))
tmp = """\
default:
/* TODO: Better error handling */
printf("Error: API function %x is unknown!!\\n", id);
break;
}}
}} }}
""".format( """
id=api_id, f_dispatcher.write(tmp.format(**fmt_header))
), # END: Generate Dispatcher }}}
file=f_client,
)
print("""\
default:
printf("Error: API function %x is unknown!!\\n", {id});
break;
}}
}}""".format(
id=api_id,
), file=f_server)
if __name__ == "__main__": if __name__ == "__main__":
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment