1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8Script to scan Zephyr include directories and emit system call and subsystem 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 13C or header 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 17This script also scans for struct definitions such as __subsystem and 18__net_socket, emitting a JSON dictionary mapping tags to all the struct 19declarations found that were tagged with them. 20 21If the output JSON file already exists, its contents are checked against 22what information this script would have outputted; if the result is that the 23file would be unchanged, it is not modified to prevent unnecessary 24incremental builds. 25""" 26 27import sys 28import re 29import argparse 30import os 31import json 32 33regex_flags = re.MULTILINE | re.VERBOSE 34 35syscall_regex = re.compile(r''' 36__syscall\s+ # __syscall attribute, must be first 37([^(]+) # type and name of system call (split later) 38[(] # Function opening parenthesis 39([^)]*) # Arg list (split later) 40[)] # Closing parenthesis 41''', regex_flags) 42 43struct_tags = ["__subsystem", "__net_socket"] 44 45tagged_struct_decl_template = r''' 46%s\s+ # tag, must be first 47struct\s+ # struct keyword is next 48([^{]+) # name of subsystem 49[{] # Open curly bracket 50''' 51 52def tagged_struct_update(target_list, tag, contents): 53 regex = re.compile(tagged_struct_decl_template % tag, regex_flags) 54 items = [mo.groups()[0].strip() for mo in regex.finditer(contents)] 55 target_list.extend(items) 56 57 58def analyze_headers(multiple_directories): 59 syscall_ret = [] 60 tagged_ret = {} 61 62 for tag in struct_tags: 63 tagged_ret[tag] = [] 64 65 for base_path in multiple_directories: 66 for root, dirs, files in os.walk(base_path, topdown=True): 67 dirs.sort() 68 files.sort() 69 for fn in files: 70 71 # toolchain/common.h has the definitions of these tagswhich we 72 # don't want to trip over 73 path = os.path.join(root, fn) 74 if (not (path.endswith(".h") or path.endswith(".c")) or 75 path.endswith(os.path.join(os.sep, 'toolchain', 76 'common.h'))): 77 continue 78 79 with open(path, "r", encoding="utf-8") as fp: 80 contents = fp.read() 81 82 try: 83 syscall_result = [(mo.groups(), fn) 84 for mo in syscall_regex.finditer(contents)] 85 for tag in struct_tags: 86 tagged_struct_update(tagged_ret[tag], tag, contents) 87 except Exception: 88 sys.stderr.write("While parsing %s\n" % fn) 89 raise 90 91 syscall_ret.extend(syscall_result) 92 93 return syscall_ret, tagged_ret 94 95 96def update_file_if_changed(path, new): 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 108 109def parse_args(): 110 global args 111 parser = argparse.ArgumentParser( 112 description=__doc__, 113 formatter_class=argparse.RawDescriptionHelpFormatter) 114 115 parser.add_argument("-i", "--include", required=True, action='append', 116 help='''include directories recursively scanned 117 for .h files. Can be specified multiple times: 118 -i topdir1 -i topdir2 ...''') 119 parser.add_argument( 120 "-j", "--json-file", required=True, 121 help="Write system call prototype information as json to file") 122 parser.add_argument( 123 "-t", "--tag-struct-file", required=True, 124 help="Write tagged struct name information as json to file") 125 126 args = parser.parse_args() 127 128 129def main(): 130 parse_args() 131 132 syscalls, tagged = analyze_headers(args.include) 133 134 # Only write json files if they don't exist or have changes since 135 # they will force an incremental rebuild. 136 137 syscalls_in_json = json.dumps( 138 syscalls, 139 indent=4, 140 sort_keys=True 141 ) 142 update_file_if_changed(args.json_file, syscalls_in_json) 143 144 tagged_struct_in_json = json.dumps( 145 tagged, 146 indent=4, 147 sort_keys=True 148 ) 149 update_file_if_changed(args.tag_struct_file, tagged_struct_in_json) 150 151 152if __name__ == "__main__": 153 main() 154