btstack/tool/btstack_parse_header.py

234 lines
7.2 KiB
Python
Executable File

#!/usr/bin/env python3
import os, sys, getopt, re
class State:
SearchTitle = 0
SearchEndTitle = 1
SearchStartAPI = 2
SearchEndAPI = 3
DoneAPI = 4
def isEndOfComment(line):
parts = re.match(r'\s*\*\/.*', line)
if parts:
return True
return False
def isStartOfComment(line):
parts = re.match(r'\s*\/\*\*.*', line)
if parts:
return True
return False
def isComment(line):
parts = re.match(r'\s*\/\/.*', line)
if parts:
return True
parts = re.match(r'\s*\*.*', line)
if parts:
return True
return isStartOfComment(line) or isEndOfComment(line)
def isTypedefStart(line):
return re.match(r'.*typedef\s+struct.*', line)
def filename_stem(filepath):
return os.path.splitext(os.path.basename(filepath))[0]
def clean_text(text):
return re.sub(r'\s+', ' ', text).strip()
def validate_function(name, text):
if text.startswith('#define'):
print("Error: function name with #define: ", name)
return False
if name in ['PBAP_FEATURES_NOT_PRESENT', 'PROFILE_FEATURES_NOT_PRESENT', 'Q7_TO_FLOAT', '_attribute__']:
print("Error: function name with reserved name: ", name)
return False
if '\t' in name:
print("Error: function name with tabs: ", name)
return False
if "PGM_" in text:
return False
return True
def parse_header(header_filepath):
'''
Parses a header file to extract function definitions and typedefs.
:param path:
:return: (title, functions, typedefs)
This function reads a header file, identifies function definitions and typedefs.
It returns the file @title if found and two dictionaries:
- functions: A dictionary where keys are function names and values are (function signature, line number, is_api).
- typedefs: A dictionary where keys are typedef names and values are their definitions.
'''
functions = {}
typedefs = {}
title = None
state = State.SearchStartAPI
multiline_function_name = None
multiline_function_text = ''
typedefFound = False
typedef_text = ''
multiline_function_terminator = ''
is_api = False
with open(header_filepath, 'rt') as fin:
for line_num, line in enumerate(fin, start=1):
if multiline_function_name:
multiline_function_text += "\n" + clean_text(line)
if multiline_function_terminator in line:
if validate_function(multiline_function_name, multiline_function_text):
functions[multiline_function_name] = (multiline_function_text, line_num, is_api)
multiline_function_name = None
continue
if line.startswith('#define'):
continue
if state == State.SearchStartAPI:
parts = re.match(r'.*API_START.*', line)
if parts:
state = State.SearchEndAPI
is_api = True
continue
if state == State.SearchEndAPI:
parts = re.match(r'.*API_END.*', line)
if parts:
state = State.DoneAPI
is_api = False
continue
if isComment(line):
continue
param = re.match(r".*@brief.*", line)
if param:
continue
param = re.match(r".*@param.*", line)
if param:
continue
param = re.match(r".*@return.*", line)
if param:
continue
param = re.match(r".*@result.*", line)
if param:
continue
param = re.match(r".*@note.*", line)
if param:
continue
param = re.match(r".*return.*", line)
if param:
continue
# search typedef struct begin
if isTypedefStart(line):
typedefFound = True
# search typedef struct end
if typedefFound:
typedef = re.match(r'}\s*(.*);\n', line)
if typedef:
typedefFound = False
typdefef_name = typedef.group(1)
typedefs[typdefef_name] = typedef_text
typedef_text = ''
continue
# filter callback
callback_function_definition = re.match(r'(.*?)\s*\(\s*\*.*\(*.*;\n', line)
if callback_function_definition:
continue
one_line_function_definition = re.match(r'(.*?)\s*\(.*\(*.*;\n', line)
if one_line_function_definition:
parts = one_line_function_definition.group(1).split(" ")
name = parts[len(parts) - 1].replace('*', '')
if len(name) == 0:
print(parts)
sys.exit(10)
# ignore typedef for callbacks
if parts[0] == 'typedef':
continue
text = clean_text(line)
if validate_function(name, text):
functions[name] = (text, line_num, is_api)
continue
multi_line_function_definition = re.match(r'.(.*?)\s*\(.*\(*.*', line)
if multi_line_function_definition:
parts = multi_line_function_definition.group(1).split(" ")
name = parts[len(parts) - 1].replace('*', '')
if len(name) == 0:
print(parts)
sys.exit(10)
multiline_function_name = name
multiline_function_text = clean_text(line)
if line.startswith('static inline'):
multiline_function_terminator = '};'
else:
multiline_function_terminator = ';'
return (title, functions, typedefs)
def dump_typedefs(typedefs):
for typedef in sorted(typedefs.keys()):
print(typedef, "--", typedefs[typedef])
def dump_functions(functions):
for function in sorted(functions.keys()):
(text, linenr, is_api) = functions[function]
print('--', function, "--")
print(linenr, text, '\n')
files_to_ignore = []
def main(btstack_root):
btstack_srcfolder = btstack_root + "/src/"
print('BTstack src: ' + btstack_srcfolder)
# create a dictionary of header files {file_path : []}
header_files = {}
for root, dirs, files in os.walk(btstack_srcfolder, topdown=True):
for f in files:
if not f.endswith(".h"):
continue
if not root.endswith("/"):
root = root + "/"
if f in files_to_ignore:
continue
header_filepath = root + f
with open(header_filepath, 'rt') as fin:
header_files[header_filepath] = []
for header_filepath in sorted(header_files.keys()):
# get filename without path
(title, functions, typedefs) = parse_header(header_filepath)
print("\n\n==== Parsing file: ", header_filepath)
print("Title: ", title)
dump_functions(functions)
# dump_typedefs(typedefs)
if __name__ == "__main__":
btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/../..')
main(btstack_root)