blob: f7be6314815acacc77f5dcc77ddb5d2b94d0e294 [file] [log] [blame]
Sebastian Bøe13a68402017-11-20 13:03:55 +01001#!/usr/bin/env python3
2#
3# Copyright (c) 2017 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import sys
8import re
9import argparse
10import os
11import json
12
13api_regex = re.compile(r'''
14__syscall\s+ # __syscall attribute, must be first
15([^(]+) # type and name of system call (split later)
16[(] # Function opening parenthesis
17([^)]*) # Arg list (split later)
18[)] # Closing parenthesis
19''', re.MULTILINE | re.VERBOSE)
20
21typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$')
22
Anas Nashif72565532017-12-12 08:19:25 -050023
Sebastian Bøe13a68402017-11-20 13:03:55 +010024class SyscallParseException(Exception):
25 pass
26
27
28def typename_split(item):
29 if "[" in item:
Anas Nashif72565532017-12-12 08:19:25 -050030 raise SyscallParseException(
31 "Please pass arrays to syscalls as pointers, unable to process '%s'" %
32 item)
Sebastian Bøe13a68402017-11-20 13:03:55 +010033
34 if "(" in item:
Anas Nashif72565532017-12-12 08:19:25 -050035 raise SyscallParseException(
36 "Please use typedefs for function pointers")
Sebastian Bøe13a68402017-11-20 13:03:55 +010037
38 mo = typename_regex.match(item)
39 if not mo:
40 raise SyscallParseException("Malformed system call invocation")
41
42 m = mo.groups()
43 return (m[0].strip(), m[1])
44
45
46def analyze_fn(match_group, fn):
47 func, args = match_group
48
49 try:
50 if args == "void":
51 args = []
52 else:
53 args = [typename_split(a.strip()) for a in args.split(",")]
54
55 func_type, func_name = typename_split(func)
56 except SyscallParseException:
57 sys.stderr.write("In declaration of %s\n" % func)
58 raise
59
60 sys_id = "K_SYSCALL_" + func_name.upper()
61
62 if func_type == "void":
63 suffix = "_VOID"
64 is_void = True
65 else:
66 is_void = False
67 if func_type in ["s64_t", "u64_t"]:
68 suffix = "_RET64"
69 else:
70 suffix = ""
71
72 is_void = (func_type == "void")
73
74 # Get the proper system call macro invocation, which depends on the
75 # number of arguments, the return type, and whether the implementation
76 # is an inline function
77 macro = "K_SYSCALL_DECLARE%d%s" % (len(args), suffix)
78
79 # Flatten the argument lists and generate a comma separated list
80 # of t0, p0, t1, p1, ... tN, pN as expected by the macros
81 flat_args = [i for sublist in args for i in sublist]
82 if not is_void:
83 flat_args = [func_type] + flat_args
84 flat_args = [sys_id, func_name] + flat_args
85 argslist = ", ".join(flat_args)
86
87 invocation = "%s(%s);" % (macro, argslist)
88
89 handler = "_handler_" + func_name
90
91 # Entry in _k_syscall_table
92 table_entry = "[%s] = %s" % (sys_id, handler)
93
94 return (fn, handler, invocation, sys_id, table_entry)
95
96
97def analyze_headers(base_path):
98 ret = []
99
100 for root, dirs, files in os.walk(base_path):
101 for fn in files:
102
103 # toolchain/common.h has the definition of __syscall which we
104 # don't want to trip over
105 path = os.path.join(root, fn)
Carles Cufi907735d2018-01-11 15:14:54 +0100106 if not fn.endswith(".h") or path.endswith(os.path.join(os.sep, 'toolchain', 'common.h')):
Sebastian Bøe13a68402017-11-20 13:03:55 +0100107 continue
108
109 with open(path, "r", encoding="utf-8") as fp:
110 try:
111 result = [analyze_fn(mo.groups(), fn)
112 for mo in api_regex.finditer(fp.read())]
113 except Exception:
114 sys.stderr.write("While parsing %s\n" % fn)
115 raise
116
117 ret.extend(result)
118
119 return ret
120
Anas Nashif72565532017-12-12 08:19:25 -0500121
Sebastian Bøe13a68402017-11-20 13:03:55 +0100122def parse_args():
123 global args
Anas Nashif72565532017-12-12 08:19:25 -0500124 parser = argparse.ArgumentParser(
125 description=__doc__,
126 formatter_class=argparse.RawDescriptionHelpFormatter)
Sebastian Bøe13a68402017-11-20 13:03:55 +0100127
128 parser.add_argument("-i", "--include", required=True,
Anas Nashif72565532017-12-12 08:19:25 -0500129 help="Base include directory")
130 parser.add_argument(
131 "-j", "--json-file", required=True,
132 help="Write system call prototype information as json to file")
Sebastian Bøe13a68402017-11-20 13:03:55 +0100133 args = parser.parse_args()
134
Anas Nashif72565532017-12-12 08:19:25 -0500135
Sebastian Bøe13a68402017-11-20 13:03:55 +0100136def main():
137 parse_args()
138
139 syscalls = analyze_headers(args.include)
140
141 syscalls_in_json = json.dumps(
142 syscalls,
143 indent=4,
144 sort_keys=True
145 )
146
147 # Check if the file already exists, and if there are no changes,
148 # don't touch it since that will force an incremental rebuild
149 path = args.json_file
150 new = syscalls_in_json
151 if os.path.exists(path):
152 with open(path, 'r') as fp:
153 old = fp.read()
154
155 if new != old:
156 with open(path, 'w') as fp:
157 fp.write(new)
158 else:
159 with open(path, 'w') as fp:
160 fp.write(new)
161
Anas Nashif72565532017-12-12 08:19:25 -0500162
Sebastian Bøe13a68402017-11-20 13:03:55 +0100163if __name__ == "__main__":
164 main()