Skip to content
Snippets Groups Projects
Commit f49d850f authored by rahix's avatar rahix
Browse files

Merge branch 'rahix/genapi-v2' into 'master'

Refactor genapi.py script

See merge request card10/firmware!9
parents 15bb6ac6 b38163f2
No related branches found
No related tags found
1 merge request!9Refactor genapi.py script
Pipeline #646 passed
import argparse import argparse
import contextlib
import os import os
import re import re
import subprocess import subprocess
import sys
MATCH_EXPANSION = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<decl>.*?) \$",
re.DOTALL | re.MULTILINE,
)
MATCH_DECLARATION = re.compile(
r"^(?P<typename>.*?)\s*\((?P<args>.*)\)$",
re.DOTALL,
)
MATCH_TYPENAME = re.compile(
r"^(?P<type>(?:const )?(?:struct )?\w+(?:\s+|\*+))(?P<name>\w+)$",
)
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 bailout(message, *args, **kwargs):
fmt = "\x1B[31;1mError\x1B[0m: {}"
print(fmt.format(message.format(*args, **kwargs)), file=sys.stderr)
sys.exit(1)
def parse_declarations(source):
"""Parse all declarations in the given source."""
declarations = []
for exp in MATCH_EXPANSION.finditer(source):
id = exp.group("id")
decl = MATCH_DECLARATION.match(exp.group("decl"))
if decl is None:
bailout("Error in declaration '{}'", exp.group("decl"))
typename = MATCH_TYPENAME.match(decl.group("typename"))
args = []
args_str = decl.group("args")
if typename is None:
bailout("Error in declaration '{}'", exp.group("decl"))
# Parse arguments
for arg_str in map(str.strip, args_str.split(",")):
if arg_str in ["void", ""]:
continue
arg = MATCH_TYPENAME.match(arg_str.strip())
if arg is None:
bailout("Failed to parse argument '{}'", arg_str.strip())
args.append({
"type": arg.group("type").strip(),
"name": arg.group("name"),
"sizeof": "sizeof({})".format(arg.group("type").strip()),
"offset": sizeof(args),
})
declarations.append({
"id": id,
"return_type": typename.group("type").strip(),
"name": typename.group("name"),
"args": args,
"args_str": args_str,
})
return declarations
def main(): def main():
...@@ -20,169 +91,123 @@ def main(): ...@@ -20,169 +91,123 @@ 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", tmp = """\
), *({type}*)(buffer + {offset}) = {name};
file=f_client, """
) f_client.write(tmp.format(**arg))
if api_return != "void": if decl["return_type"] == "void":
print("""\ # Don't return if return type is void
case {id}: tmp = """\
*(({ret}*)buffer) = {cdecl}(""".format(id=api_id, ret=api_return, cdecl=api_decl),
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 ","
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.
Finish editing this message first!
Please register or to comment