cmake: Re-organize syscall generation wrt. the build system
This commit fixes https://github.com/zephyrproject-rtos/zephyr/issues/5008. It does so by splitting up gen_syscalls.py into two scripts with a json metadata file to communicate syscall metadata between them. The parsing script parses header files from include/ and writes syscall metadata to a file if the contents changed. The generation script reads from the json file and generates syscall code. The build system DAG now looks like this: always_rebuild -> json -> syscalls -> offset.o The script for generating json will do so only if the content changes, this ensures that the entire DAG does not always do a full rebuild. Signed-off-by: Sebastian Bøe <sebastian.boe@nordicsemi.no>
This commit is contained in:
parent
958acd1b4e
commit
13a6840261
5 changed files with 203 additions and 136 deletions
156
scripts/parse_syscalls.py
Normal file
156
scripts/parse_syscalls.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2017 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
import os
|
||||
import json
|
||||
|
||||
api_regex = re.compile(r'''
|
||||
__syscall\s+ # __syscall attribute, must be first
|
||||
([^(]+) # type and name of system call (split later)
|
||||
[(] # Function opening parenthesis
|
||||
([^)]*) # Arg list (split later)
|
||||
[)] # Closing parenthesis
|
||||
''', re.MULTILINE | re.VERBOSE)
|
||||
|
||||
typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$')
|
||||
|
||||
class SyscallParseException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def typename_split(item):
|
||||
if "[" in item:
|
||||
raise SyscallParseException("Please pass arrays to syscalls as pointers, unable to process '%s'"
|
||||
% item)
|
||||
|
||||
if "(" in item:
|
||||
raise SyscallParseException("Please use typedefs for function pointers")
|
||||
|
||||
mo = typename_regex.match(item)
|
||||
if not mo:
|
||||
raise SyscallParseException("Malformed system call invocation")
|
||||
|
||||
m = mo.groups()
|
||||
return (m[0].strip(), m[1])
|
||||
|
||||
|
||||
def analyze_fn(match_group, fn):
|
||||
func, args = match_group
|
||||
|
||||
try:
|
||||
if args == "void":
|
||||
args = []
|
||||
else:
|
||||
args = [typename_split(a.strip()) for a in args.split(",")]
|
||||
|
||||
func_type, func_name = typename_split(func)
|
||||
except SyscallParseException:
|
||||
sys.stderr.write("In declaration of %s\n" % func)
|
||||
raise
|
||||
|
||||
sys_id = "K_SYSCALL_" + func_name.upper()
|
||||
|
||||
if func_type == "void":
|
||||
suffix = "_VOID"
|
||||
is_void = True
|
||||
else:
|
||||
is_void = False
|
||||
if func_type in ["s64_t", "u64_t"]:
|
||||
suffix = "_RET64"
|
||||
else:
|
||||
suffix = ""
|
||||
|
||||
is_void = (func_type == "void")
|
||||
|
||||
# Get the proper system call macro invocation, which depends on the
|
||||
# number of arguments, the return type, and whether the implementation
|
||||
# is an inline function
|
||||
macro = "K_SYSCALL_DECLARE%d%s" % (len(args), suffix)
|
||||
|
||||
# Flatten the argument lists and generate a comma separated list
|
||||
# of t0, p0, t1, p1, ... tN, pN as expected by the macros
|
||||
flat_args = [i for sublist in args for i in sublist]
|
||||
if not is_void:
|
||||
flat_args = [func_type] + flat_args
|
||||
flat_args = [sys_id, func_name] + flat_args
|
||||
argslist = ", ".join(flat_args)
|
||||
|
||||
invocation = "%s(%s);" % (macro, argslist)
|
||||
|
||||
handler = "_handler_" + func_name
|
||||
|
||||
# Entry in _k_syscall_table
|
||||
table_entry = "[%s] = %s" % (sys_id, handler)
|
||||
|
||||
return (fn, handler, invocation, sys_id, table_entry)
|
||||
|
||||
|
||||
def analyze_headers(base_path):
|
||||
ret = []
|
||||
|
||||
for root, dirs, files in os.walk(base_path):
|
||||
for fn in files:
|
||||
|
||||
# toolchain/common.h has the definition of __syscall which we
|
||||
# don't want to trip over
|
||||
path = os.path.join(root, fn)
|
||||
if not fn.endswith(".h") or path.endswith("toolchain/common.h"):
|
||||
continue
|
||||
|
||||
with open(path, "r", encoding="utf-8") as fp:
|
||||
try:
|
||||
result = [analyze_fn(mo.groups(), fn)
|
||||
for mo in api_regex.finditer(fp.read())]
|
||||
except Exception:
|
||||
sys.stderr.write("While parsing %s\n" % fn)
|
||||
raise
|
||||
|
||||
ret.extend(result)
|
||||
|
||||
return ret
|
||||
|
||||
def parse_args():
|
||||
global args
|
||||
parser = argparse.ArgumentParser(description = __doc__,
|
||||
formatter_class = argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
parser.add_argument("-i", "--include", required=True,
|
||||
help="Base include directory")
|
||||
parser.add_argument("-j", "--json-file", required=True,
|
||||
help="Write system call prototype information as json to file")
|
||||
args = parser.parse_args()
|
||||
|
||||
def main():
|
||||
parse_args()
|
||||
|
||||
syscalls = analyze_headers(args.include)
|
||||
|
||||
syscalls_in_json = json.dumps(
|
||||
syscalls,
|
||||
indent=4,
|
||||
sort_keys=True
|
||||
)
|
||||
|
||||
# Check if the file already exists, and if there are no changes,
|
||||
# don't touch it since that will force an incremental rebuild
|
||||
path = args.json_file
|
||||
new = syscalls_in_json
|
||||
if os.path.exists(path):
|
||||
with open(path, 'r') as fp:
|
||||
old = fp.read()
|
||||
|
||||
if new != old:
|
||||
with open(path, 'w') as fp:
|
||||
fp.write(new)
|
||||
else:
|
||||
with open(path, 'w') as fp:
|
||||
fp.write(new)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue