1c8c342a6SMilanka Ringwald#!/usr/bin/env python3 2c8c342a6SMilanka Ringwaldimport os, sys, getopt, re, pickle 3c8c342a6SMilanka Ringwaldimport subprocess 4c8c342a6SMilanka Ringwald 5c8c342a6SMilanka Ringwaldclass State: 6d7ef0676SMilanka Ringwald SearchTitle = 0 7d7ef0676SMilanka Ringwald SearchEndTitle = 1 8d7ef0676SMilanka Ringwald SearchStartAPI = 2 9d7ef0676SMilanka Ringwald SearchEndAPI = 3 10d7ef0676SMilanka Ringwald DoneAPI = 4 11c8c342a6SMilanka Ringwald 124a400a05SMilanka Ringwaldheader_files = {} 13c8c342a6SMilanka Ringwaldfunctions = {} 14c8c342a6SMilanka Ringwaldtypedefs = {} 15c8c342a6SMilanka Ringwald 164a400a05SMilanka Ringwaldlinenr = 0 174a400a05SMilanka RingwaldtypedefFound = 0 184a400a05SMilanka Ringwaldmultiline_function_def = 0 194a400a05SMilanka Ringwaldstate = State.SearchStartAPI 20c8c342a6SMilanka Ringwald 214a400a05SMilanka Ringwald# if dash is used in api_header, the windmill theme will repeat the same API_TITLE twice in the menu (i.e: APIs/API_TITLE/API_TITLE) 224a400a05SMilanka Ringwald# if <h2> is used, this is avoided (i.e: APIs/API_TITLE), but reference {...} is not translated to HTML 234a400a05SMilanka Ringwaldapi_header = """ 244a400a05SMilanka Ringwald# API_TITLE API {#sec:API_LABEL_api} 25c8c342a6SMilanka Ringwald 26c8c342a6SMilanka Ringwald""" 272ea56b0cSMilanka Ringwald 2878fab72eSMilanka Ringwaldapi_subheader = """ 2978fab72eSMilanka Ringwald## API_TITLE API {#sec:API_LABEL_api} 3078fab72eSMilanka Ringwald 3178fab72eSMilanka Ringwald""" 32c8c342a6SMilanka Ringwald 332ea56b0cSMilanka Ringwaldapi_description = """ 342ea56b0cSMilanka Ringwald**FILENAME** DESCRIPTION 352ea56b0cSMilanka Ringwald 36c8c342a6SMilanka Ringwald""" 37c8c342a6SMilanka Ringwald 38*2d21ec19SMilanka Ringwaldcode_ref = """GITHUB/FPATH#LLINENR""" 39c8c342a6SMilanka Ringwald 40d7ef0676SMilanka Ringwalddef isEndOfComment(line): 41d7ef0676SMilanka Ringwald return re.match('\s*\*/.*', line) 42d7ef0676SMilanka Ringwald 43d7ef0676SMilanka Ringwalddef isStartOfComment(line): 44d7ef0676SMilanka Ringwald return re.match('\s*\/\*/.*', line) 45d7ef0676SMilanka Ringwald 46d7ef0676SMilanka Ringwalddef isTypedefStart(line): 47d7ef0676SMilanka Ringwald return re.match('.*typedef\s+struct.*', line) 48d7ef0676SMilanka Ringwald 49*2d21ec19SMilanka Ringwalddef codeReference(fname, githuburl, filename_without_extension, filepath, linenr): 50c8c342a6SMilanka Ringwald global code_ref 51c8c342a6SMilanka Ringwald ref = code_ref.replace("GITHUB", githuburl) 52*2d21ec19SMilanka Ringwald ref = ref.replace("FPATH", filename_without_extension) 53c8c342a6SMilanka Ringwald ref = ref.replace("LINENR", str(linenr)) 54c8c342a6SMilanka Ringwald return ref 55c8c342a6SMilanka Ringwald 564a400a05SMilanka Ringwalddef isTagAPI(line): 574a400a05SMilanka Ringwald return re.match('(.*)(-\s*\')(APIs).*',line) 584a400a05SMilanka Ringwald 594a400a05SMilanka Ringwalddef getSecondLevelIdentation(line): 604a400a05SMilanka Ringwald indentation = "" 614a400a05SMilanka Ringwald parts = re.match('(.*)(-\s*\')(APIs).*',line) 624a400a05SMilanka Ringwald if parts: 634a400a05SMilanka Ringwald # return double identation for the submenu 644a400a05SMilanka Ringwald indentation = parts.group(1) + parts.group(1) + "- " 654a400a05SMilanka Ringwald return indentation 664a400a05SMilanka Ringwald 674a400a05SMilanka Ringwalddef filename_stem(filepath): 684a400a05SMilanka Ringwald return os.path.splitext(os.path.basename(filepath))[0] 69c8c342a6SMilanka Ringwald 70d7ef0676SMilanka Ringwalddef writeAPI(fout, fin, mk_codeidentation): 71c8c342a6SMilanka Ringwald state = State.SearchStartAPI 72d7ef0676SMilanka Ringwald 73c8c342a6SMilanka Ringwald for line in fin: 74c8c342a6SMilanka Ringwald if state == State.SearchStartAPI: 75c8c342a6SMilanka Ringwald parts = re.match('.*API_START.*',line) 76c8c342a6SMilanka Ringwald if parts: 77c8c342a6SMilanka Ringwald state = State.SearchEndAPI 78c8c342a6SMilanka Ringwald continue 79c8c342a6SMilanka Ringwald 80c8c342a6SMilanka Ringwald if state == State.SearchEndAPI: 81c8c342a6SMilanka Ringwald parts = re.match('.*API_END.*',line) 82c8c342a6SMilanka Ringwald if parts: 83c8c342a6SMilanka Ringwald state = State.DoneAPI 84c8c342a6SMilanka Ringwald continue 85c8c342a6SMilanka Ringwald fout.write(mk_codeidentation + line) 86c8c342a6SMilanka Ringwald continue 87c8c342a6SMilanka Ringwald 88c8c342a6SMilanka Ringwald 89c8c342a6SMilanka Ringwald 90*2d21ec19SMilanka Ringwalddef createIndex(fin, filename, api_filepath, api_title, api_label, githuburl): 91d7ef0676SMilanka Ringwald global typedefs, functions 92d7ef0676SMilanka Ringwald global linenr, multiline_function_def, typedefFound, state 93d7ef0676SMilanka Ringwald 94d7ef0676SMilanka Ringwald 95c8c342a6SMilanka Ringwald for line in fin: 96c8c342a6SMilanka Ringwald if state == State.DoneAPI: 97c8c342a6SMilanka Ringwald continue 98c8c342a6SMilanka Ringwald 99c8c342a6SMilanka Ringwald linenr = linenr + 1 100c8c342a6SMilanka Ringwald 101c8c342a6SMilanka Ringwald if state == State.SearchStartAPI: 102c8c342a6SMilanka Ringwald parts = re.match('.*API_START.*',line) 103c8c342a6SMilanka Ringwald if parts: 104c8c342a6SMilanka Ringwald state = State.SearchEndAPI 105c8c342a6SMilanka Ringwald continue 106c8c342a6SMilanka Ringwald 107c8c342a6SMilanka Ringwald if state == State.SearchEndAPI: 108c8c342a6SMilanka Ringwald parts = re.match('.*API_END.*',line) 109c8c342a6SMilanka Ringwald if parts: 110c8c342a6SMilanka Ringwald state = State.DoneAPI 111c8c342a6SMilanka Ringwald continue 112c8c342a6SMilanka Ringwald 113c8c342a6SMilanka Ringwald if multiline_function_def: 114c8c342a6SMilanka Ringwald function_end = re.match('.*;\n', line) 115c8c342a6SMilanka Ringwald if function_end: 116c8c342a6SMilanka Ringwald multiline_function_def = 0 117c8c342a6SMilanka Ringwald continue 118c8c342a6SMilanka Ringwald 119c8c342a6SMilanka Ringwald param = re.match(".*@brief.*", line) 120c8c342a6SMilanka Ringwald if param: 121c8c342a6SMilanka Ringwald continue 122c8c342a6SMilanka Ringwald param = re.match(".*@param.*", line) 123c8c342a6SMilanka Ringwald if param: 124c8c342a6SMilanka Ringwald continue 125c8c342a6SMilanka Ringwald param = re.match(".*@return.*", line) 126c8c342a6SMilanka Ringwald if param: 127c8c342a6SMilanka Ringwald continue 128c8c342a6SMilanka Ringwald 129c8c342a6SMilanka Ringwald # search typedef struct begin 130d7ef0676SMilanka Ringwald if isTypedefStart(line): 131c8c342a6SMilanka Ringwald typedefFound = 1 132c8c342a6SMilanka Ringwald 133c8c342a6SMilanka Ringwald # search typedef struct end 134c8c342a6SMilanka Ringwald if typedefFound: 135c8c342a6SMilanka Ringwald typedef = re.match('}\s*(.*);\n', line) 136c8c342a6SMilanka Ringwald if typedef: 137c8c342a6SMilanka Ringwald typedefFound = 0 138*2d21ec19SMilanka Ringwald typedefs[typedef.group(1)] = codeReference(typedef.group(1), githuburl, filename, api_filepath, linenr) 139c8c342a6SMilanka Ringwald continue 140c8c342a6SMilanka Ringwald 141c8c342a6SMilanka Ringwald ref_function = re.match('.*typedef\s+void\s+\(\s*\*\s*(.*?)\)\(.*', line) 142c8c342a6SMilanka Ringwald if ref_function: 143*2d21ec19SMilanka Ringwald functions[ref_function.group(1)] = codeReference(ref_function.group(1), githuburl, filename, api_filepath, linenr) 144c8c342a6SMilanka Ringwald continue 145c8c342a6SMilanka Ringwald 146d7ef0676SMilanka Ringwald 147d7ef0676SMilanka Ringwald one_line_function_definition = re.match('(.*?)\s*\(.*\(*.*;\n', line) 148d7ef0676SMilanka Ringwald if one_line_function_definition: 149d7ef0676SMilanka Ringwald parts = one_line_function_definition.group(1).split(" "); 150c8c342a6SMilanka Ringwald name = parts[len(parts)-1] 151c8c342a6SMilanka Ringwald if len(name) == 0: 152c8c342a6SMilanka Ringwald print(parts); 153c8c342a6SMilanka Ringwald sys.exit(10) 154*2d21ec19SMilanka Ringwald functions[name] = codeReference( name, githuburl, filename, api_filepath, linenr) 155c8c342a6SMilanka Ringwald continue 156c8c342a6SMilanka Ringwald 157d7ef0676SMilanka Ringwald multi_line_function_definition = re.match('.(.*?)\s*\(.*\(*.*', line) 158d7ef0676SMilanka Ringwald if multi_line_function_definition: 159d7ef0676SMilanka Ringwald parts = multi_line_function_definition.group(1).split(" "); 160d7ef0676SMilanka Ringwald 161d7ef0676SMilanka Ringwald name = parts[len(parts)-1] 162c8c342a6SMilanka Ringwald if len(name) == 0: 163c8c342a6SMilanka Ringwald print(parts); 164c8c342a6SMilanka Ringwald sys.exit(10) 165c8c342a6SMilanka Ringwald multiline_function_def = 1 166*2d21ec19SMilanka Ringwald functions[name] = codeReference(name, githuburl, filename, api_filepath, linenr) 167c8c342a6SMilanka Ringwald 168c8c342a6SMilanka Ringwald 169d7ef0676SMilanka Ringwalddef findTitle(fin): 170d7ef0676SMilanka Ringwald title = None 171d7ef0676SMilanka Ringwald desc = "" 172d7ef0676SMilanka Ringwald state = State.SearchTitle 173d7ef0676SMilanka Ringwald 174d7ef0676SMilanka Ringwald for line in fin: 175d7ef0676SMilanka Ringwald if state == State.SearchTitle: 176d7ef0676SMilanka Ringwald if isStartOfComment(line): 177d7ef0676SMilanka Ringwald continue 178d7ef0676SMilanka Ringwald 179d7ef0676SMilanka Ringwald parts = re.match('.*(@title)(.*)', line) 180d7ef0676SMilanka Ringwald if parts: 181d7ef0676SMilanka Ringwald title = parts.group(2).strip() 182d7ef0676SMilanka Ringwald state = State.SearchEndTitle 183d7ef0676SMilanka Ringwald continue 184d7ef0676SMilanka Ringwald 185d7ef0676SMilanka Ringwald if state == State.SearchEndTitle: 186d7ef0676SMilanka Ringwald if (isEndOfComment(line)): 187d7ef0676SMilanka Ringwald state = State.DoneAPI 188d7ef0676SMilanka Ringwald break 189d7ef0676SMilanka Ringwald 190d7ef0676SMilanka Ringwald parts = re.match('(\s*\*\s*)(.*\n)',line) 191d7ef0676SMilanka Ringwald if parts: 192d7ef0676SMilanka Ringwald desc = desc + parts.group(2) 1934a400a05SMilanka Ringwald return [title, desc] 194d7ef0676SMilanka Ringwald 195c8c342a6SMilanka Ringwalddef main(argv): 196d7ef0676SMilanka Ringwald global linenr, multiline_function_def, typedefFound, state 197d7ef0676SMilanka Ringwald 198c8c342a6SMilanka Ringwald mk_codeidentation = " " 199c8c342a6SMilanka Ringwald git_branch_name = "master" 200c8c342a6SMilanka Ringwald btstackfolder = "../../" 201*2d21ec19SMilanka Ringwald githuburl_template = "https://github.com/bluekitchen/btstack/blob/" 202*2d21ec19SMilanka Ringwald githuburl = "master/src/" 203c8c342a6SMilanka Ringwald markdownfolder = "docs-markdown/" 204c8c342a6SMilanka Ringwald 205c8c342a6SMilanka Ringwald cmd = 'markdown_create_apis.py [-r <root_btstackfolder>] [-g <githuburl>] [-o <output_markdownfolder>]' 206c8c342a6SMilanka Ringwald try: 207c8c342a6SMilanka Ringwald opts, args = getopt.getopt(argv,"r:g:o:",["rfolder=","github=","ofolder="]) 208c8c342a6SMilanka Ringwald except getopt.GetoptError: 209c8c342a6SMilanka Ringwald print (cmd) 210c8c342a6SMilanka Ringwald sys.exit(2) 211c8c342a6SMilanka Ringwald for opt, arg in opts: 212c8c342a6SMilanka Ringwald if opt == '-h': 213c8c342a6SMilanka Ringwald print (cmd) 214c8c342a6SMilanka Ringwald sys.exit() 215c8c342a6SMilanka Ringwald elif opt in ("-r", "--rfolder"): 216c8c342a6SMilanka Ringwald btstackfolder = arg 217c8c342a6SMilanka Ringwald elif opt in ("-g", "--github"): 218c8c342a6SMilanka Ringwald githuburl = arg 219c8c342a6SMilanka Ringwald elif opt in ("-o", "--ofolder"): 220c8c342a6SMilanka Ringwald markdownfolder = arg 221c8c342a6SMilanka Ringwald 222c8c342a6SMilanka Ringwald apifile = markdownfolder + "appendix/apis.md" 223c8c342a6SMilanka Ringwald # indexfile = markdownfolder + "api_index.md" 224d7ef0676SMilanka Ringwald btstack_srcfolder = btstackfolder + "src/" 225c8c342a6SMilanka Ringwald 226c8c342a6SMilanka Ringwald try: 227c8c342a6SMilanka Ringwald output = subprocess.check_output("git symbolic-ref --short HEAD", stderr=subprocess.STDOUT, timeout=3, shell=True) 228c8c342a6SMilanka Ringwald git_branch_name = output.decode().rstrip() 229c8c342a6SMilanka Ringwald except subprocess.CalledProcessError as exc: 230c8c342a6SMilanka Ringwald print('GIT branch name: failed to get, use default value \"%s\"" ', git_branch_name, exc.returncode, exc.output) 231c8c342a6SMilanka Ringwald else: 232c8c342a6SMilanka Ringwald print ('GIT branch name : %s' % git_branch_name) 233c8c342a6SMilanka Ringwald 234*2d21ec19SMilanka Ringwald githuburl = githuburl_template + git_branch_name 235c8c342a6SMilanka Ringwald 236d7ef0676SMilanka Ringwald print ('BTstack src folder is : ' + btstack_srcfolder) 237c8c342a6SMilanka Ringwald print ('API file is : ' + apifile) 238c8c342a6SMilanka Ringwald print ('Github URL is : ' + githuburl) 239c8c342a6SMilanka Ringwald 2404a400a05SMilanka Ringwald # create a dictionary of header files {file_path : [title, description]} 2414a400a05SMilanka Ringwald # title and desctiption are extracted from the file 242d7ef0676SMilanka Ringwald for root, dirs, files in os.walk(btstack_srcfolder, topdown=True): 243d7ef0676SMilanka Ringwald for f in files: 244d7ef0676SMilanka Ringwald if not f.endswith(".h"): 245d7ef0676SMilanka Ringwald continue 246d7ef0676SMilanka Ringwald 2474a400a05SMilanka Ringwald if not root.endswith("/"): 2484a400a05SMilanka Ringwald root = root + "/" 249d7ef0676SMilanka Ringwald 2504a400a05SMilanka Ringwald header_filepath = root + f 251d7ef0676SMilanka Ringwald 2524a400a05SMilanka Ringwald with open(header_filepath, 'rt') as fin: 2534a400a05SMilanka Ringwald [header_title, header_desc] = findTitle(fin) 254d7ef0676SMilanka Ringwald 2554a400a05SMilanka Ringwald if header_title: 2564a400a05SMilanka Ringwald header_files[header_filepath] = [header_title, header_desc] 2574a400a05SMilanka Ringwald else: 2584a400a05SMilanka Ringwald print("No @title flag found. Skip %s" % header_filepath) 259d7ef0676SMilanka Ringwald 2602ea56b0cSMilanka Ringwald # create an >md file, for each header file in header_files dictionary 2614a400a05SMilanka Ringwald for header_filepath in sorted(header_files.keys()): 262*2d21ec19SMilanka Ringwald filename = str(header_filepath) 263*2d21ec19SMilanka Ringwald filename = filename.split("../../")[1] 264*2d21ec19SMilanka Ringwald 2652ea56b0cSMilanka Ringwald filename_without_extension = filename_stem(header_filepath) # file name without .h 2662ea56b0cSMilanka Ringwald filetitle = header_files[header_filepath][0] 2672ea56b0cSMilanka Ringwald description = header_files[header_filepath][1] 2682ea56b0cSMilanka Ringwald 2692ea56b0cSMilanka Ringwald if len(description) > 1: 2702ea56b0cSMilanka Ringwald description = ": " + description 2712ea56b0cSMilanka Ringwald 2722ea56b0cSMilanka Ringwald header_description = api_description.replace("FILENAME", filename).replace("DESCRIPTION", description) 2732ea56b0cSMilanka Ringwald 2742ea56b0cSMilanka Ringwald header_title = api_header.replace("API_TITLE", filetitle).replace("API_LABEL", filename_without_extension) 2752ea56b0cSMilanka Ringwald markdown_filepath = markdownfolder + "appendix/" + filename_without_extension + ".md" 276d7ef0676SMilanka Ringwald 2774a400a05SMilanka Ringwald with open(header_filepath, 'rt') as fin: 2784a400a05SMilanka Ringwald with open(markdown_filepath, 'wt') as fout: 2794a400a05SMilanka Ringwald fout.write(header_title) 2804a400a05SMilanka Ringwald fout.write(header_description) 281d7ef0676SMilanka Ringwald writeAPI(fout, fin, mk_codeidentation) 282d7ef0676SMilanka Ringwald 2834a400a05SMilanka Ringwald with open(header_filepath, 'rt') as fin: 284d7ef0676SMilanka Ringwald linenr = 0 285d7ef0676SMilanka Ringwald typedefFound = 0 286d7ef0676SMilanka Ringwald multiline_function_def = 0 287d7ef0676SMilanka Ringwald state = State.SearchStartAPI 288*2d21ec19SMilanka Ringwald createIndex(fin, filename, markdown_filepath, header_title, filename_without_extension, githuburl) 289d7ef0676SMilanka Ringwald 2904a400a05SMilanka Ringwald # add API list to the navigation menu 2914a400a05SMilanka Ringwald with open("mkdocs-temp.yml", 'rt') as fin: 2924a400a05SMilanka Ringwald with open("mkdocs.yml", 'wt') as fout: 2934a400a05SMilanka Ringwald for line in fin: 2944a400a05SMilanka Ringwald fout.write(line) 2954a400a05SMilanka Ringwald 2964a400a05SMilanka Ringwald if not isTagAPI(line): 2974a400a05SMilanka Ringwald continue 2984a400a05SMilanka Ringwald 2994a400a05SMilanka Ringwald identation = getSecondLevelIdentation(line) 3004a400a05SMilanka Ringwald 3014a400a05SMilanka Ringwald for header_filepath in sorted(header_files.keys()): 3022ea56b0cSMilanka Ringwald header_title = os.path.basename(header_filepath) 3034a400a05SMilanka Ringwald markdown_reference = "appendix/" + filename_stem(header_filepath) + ".md" 3044a400a05SMilanka Ringwald 3054a400a05SMilanka Ringwald fout.write(identation + "'" + header_title + "': " + markdown_reference + "\n") 306c8c342a6SMilanka Ringwald 30778fab72eSMilanka Ringwald 3082ea56b0cSMilanka Ringwald # create mkdocs-latex.yml with single appendix/apis.md reference for pdf generation 30978fab72eSMilanka Ringwald with open("mkdocs-temp.yml", 'rt') as fin: 31078fab72eSMilanka Ringwald with open("mkdocs-latex.yml", 'wt') as fout: 31178fab72eSMilanka Ringwald for line in fin: 31278fab72eSMilanka Ringwald if not isTagAPI(line): 31378fab72eSMilanka Ringwald fout.write(line) 31478fab72eSMilanka Ringwald continue 31578fab72eSMilanka Ringwald 31678fab72eSMilanka Ringwald fout.write(" - 'APIs': appendix/apis.md\n") 31778fab72eSMilanka Ringwald 3182ea56b0cSMilanka Ringwald # create single appendix/apis.md file for pdf generation 31978fab72eSMilanka Ringwald markdown_filepath = markdownfolder + "appendix/apis.md" 32078fab72eSMilanka Ringwald with open(markdown_filepath, 'wt') as fout: 32178fab72eSMilanka Ringwald fout.write("\n# APIs\n\n") 32278fab72eSMilanka Ringwald for header_filepath in sorted(header_files.keys()): 3232ea56b0cSMilanka Ringwald filename = os.path.basename(header_filepath) 3242ea56b0cSMilanka Ringwald filename_without_extension = filename_stem(header_filepath) # file name without 3252ea56b0cSMilanka Ringwald filetitle = header_files[header_filepath][0] 32678fab72eSMilanka Ringwald 3272ea56b0cSMilanka Ringwald description = header_files[header_filepath][1] 3282ea56b0cSMilanka Ringwald 3292ea56b0cSMilanka Ringwald if len(description) > 1: 3302ea56b0cSMilanka Ringwald description = ": " + description 3312ea56b0cSMilanka Ringwald 3322ea56b0cSMilanka Ringwald header_description = api_description.replace("FILENAME", filename).replace("DESCRIPTION", description) 3332ea56b0cSMilanka Ringwald 3342ea56b0cSMilanka Ringwald subheader_title = api_subheader.replace("API_TITLE", filetitle).replace("API_LABEL", filename_without_extension) 33578fab72eSMilanka Ringwald with open(header_filepath, 'rt') as fin: 33678fab72eSMilanka Ringwald fout.write(subheader_title) 33778fab72eSMilanka Ringwald fout.write(header_description) 33878fab72eSMilanka Ringwald writeAPI(fout, fin, mk_codeidentation) 33978fab72eSMilanka Ringwald 340c8c342a6SMilanka Ringwald 341c8c342a6SMilanka Ringwald references = functions.copy() 342c8c342a6SMilanka Ringwald references.update(typedefs) 343c8c342a6SMilanka Ringwald 344c8c342a6SMilanka Ringwald # with open(indexfile, 'w') as fout: 345c8c342a6SMilanka Ringwald # for function, reference in references.items(): 346c8c342a6SMilanka Ringwald # fout.write("[" + function + "](" + reference + ")\n") 347c8c342a6SMilanka Ringwald 348c8c342a6SMilanka Ringwald pickle.dump(references, open("references.p", "wb" ) ) 349c8c342a6SMilanka Ringwald 350c8c342a6SMilanka Ringwaldif __name__ == "__main__": 351c8c342a6SMilanka Ringwald main(sys.argv[1:]) 352