| #!/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(os.path.join(os.sep, '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() |