blob: 2d79b535b07760c92e1ac164f6608b7232589a03 [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
Andrew Boiec78c5e62019-03-11 14:45:43 -07007"""
8Script to scan Zephyr include directories and emit system call metadata
9
10System calls require a great deal of boilerplate code in order to implement
11completely. This script is the first step in the build system's process of
12auto-generating this code by doing a text scan of directories containing
13header files, and building up a database of system calls and their
14function call prototypes. This information is emitted to a generated
15JSON file for further processing.
16
17If the output JSON file already exists, its contents are checked against
18what information this script would have outputted; if the result is that the
19file would be unchanged, it is not modified to prevent unnecessary
20incremental builds.
21"""
22
Sebastian Bøe13a68402017-11-20 13:03:55 +010023import sys
24import re
25import argparse
26import os
27import json
28
29api_regex = re.compile(r'''
30__syscall\s+ # __syscall attribute, must be first
31([^(]+) # type and name of system call (split later)
32[(] # Function opening parenthesis
33([^)]*) # Arg list (split later)
34[)] # Closing parenthesis
35''', re.MULTILINE | re.VERBOSE)
36
Sebastian Bøe13a68402017-11-20 13:03:55 +010037
Adithya Baglodye67720b2018-07-02 14:59:19 +053038def analyze_headers(multiple_directories):
Sebastian Bøe13a68402017-11-20 13:03:55 +010039 ret = []
40
Adithya Baglodye67720b2018-07-02 14:59:19 +053041 for base_path in multiple_directories:
Marc Herbertd5b28342019-02-15 18:56:57 -080042 for root, dirs, files in os.walk(base_path, topdown=True):
43 dirs.sort()
44 files.sort()
Adithya Baglodye67720b2018-07-02 14:59:19 +053045 for fn in files:
Sebastian Bøe13a68402017-11-20 13:03:55 +010046
Adithya Baglodye67720b2018-07-02 14:59:19 +053047 # toolchain/common.h has the definition of __syscall which we
48 # don't want to trip over
49 path = os.path.join(root, fn)
50 if not fn.endswith(".h") or path.endswith(os.path.join(os.sep, 'toolchain', 'common.h')):
51 continue
Sebastian Bøe13a68402017-11-20 13:03:55 +010052
Adithya Baglodye67720b2018-07-02 14:59:19 +053053 with open(path, "r", encoding="utf-8") as fp:
54 try:
Andrew Boiea698e842018-07-23 17:27:57 -070055 result = [(mo.groups(), fn)
Adithya Baglodye67720b2018-07-02 14:59:19 +053056 for mo in api_regex.finditer(fp.read())]
57 except Exception:
58 sys.stderr.write("While parsing %s\n" % fn)
59 raise
Sebastian Bøe13a68402017-11-20 13:03:55 +010060
Adithya Baglodye67720b2018-07-02 14:59:19 +053061 ret.extend(result)
Sebastian Bøe13a68402017-11-20 13:03:55 +010062
63 return ret
64
Anas Nashif72565532017-12-12 08:19:25 -050065
Sebastian Bøe13a68402017-11-20 13:03:55 +010066def parse_args():
67 global args
Anas Nashif72565532017-12-12 08:19:25 -050068 parser = argparse.ArgumentParser(
69 description=__doc__,
70 formatter_class=argparse.RawDescriptionHelpFormatter)
Sebastian Bøe13a68402017-11-20 13:03:55 +010071
Adithya Baglodye67720b2018-07-02 14:59:19 +053072 parser.add_argument("-i", "--include", required=True, action='append',
Marc Herbertd5b28342019-02-15 18:56:57 -080073 help='''include directories recursively scanned
74 for .h files. Can be specified multiple times:
75 -i topdir1 -i topdir2 ...''')
Anas Nashif72565532017-12-12 08:19:25 -050076 parser.add_argument(
77 "-j", "--json-file", required=True,
78 help="Write system call prototype information as json to file")
Sebastian Bøe13a68402017-11-20 13:03:55 +010079 args = parser.parse_args()
80
Anas Nashif72565532017-12-12 08:19:25 -050081
Sebastian Bøe13a68402017-11-20 13:03:55 +010082def main():
83 parse_args()
84
85 syscalls = analyze_headers(args.include)
86
87 syscalls_in_json = json.dumps(
88 syscalls,
89 indent=4,
90 sort_keys=True
91 )
92
93 # Check if the file already exists, and if there are no changes,
94 # don't touch it since that will force an incremental rebuild
95 path = args.json_file
96 new = syscalls_in_json
97 if os.path.exists(path):
98 with open(path, 'r') as fp:
99 old = fp.read()
100
101 if new != old:
102 with open(path, 'w') as fp:
103 fp.write(new)
104 else:
105 with open(path, 'w') as fp:
106 fp.write(new)
107
Anas Nashif72565532017-12-12 08:19:25 -0500108
Sebastian Bøe13a68402017-11-20 13:03:55 +0100109if __name__ == "__main__":
110 main()