1#!/usr/bin/env python3 2import os, sys, getopt, re, pickle 3import subprocess 4 5class State: 6 SearchTitle = 0 7 SearchEndTitle = 1 8 SearchStartAPI = 2 9 SearchEndAPI = 3 10 DoneAPI = 4 11 12header_files = {} 13functions = {} 14typedefs = {} 15 16linenr = 0 17typedefFound = 0 18multiline_function_def = 0 19state = State.SearchStartAPI 20 21# 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) 22# if <h2> is used, this is avoided (i.e: APIs/API_TITLE), but reference {...} is not translated to HTML 23api_header = """ 24# API_TITLE API {#sec:API_LABEL_api} 25 26""" 27 28api_ending = """ 29""" 30 31code_ref = """GITHUBFPATH#LLINENR""" 32 33 34def isEndOfComment(line): 35 return re.match('\s*\*/.*', line) 36 37def isStartOfComment(line): 38 return re.match('\s*\/\*/.*', line) 39 40def isTypedefStart(line): 41 return re.match('.*typedef\s+struct.*', line) 42 43def codeReference(fname, githuburl, filepath, linenr): 44 global code_ref 45 ref = code_ref.replace("GITHUB", githuburl) 46 ref = ref.replace("FPATH", filepath) 47 ref = ref.replace("LINENR", str(linenr)) 48 return ref 49 50def isTagAPI(line): 51 return re.match('(.*)(-\s*\')(APIs).*',line) 52 53def getSecondLevelIdentation(line): 54 indentation = "" 55 parts = re.match('(.*)(-\s*\')(APIs).*',line) 56 if parts: 57 # return double identation for the submenu 58 indentation = parts.group(1) + parts.group(1) + "- " 59 return indentation 60 61def filename_stem(filepath): 62 return os.path.splitext(os.path.basename(filepath))[0] 63 64def writeAPI(fout, fin, mk_codeidentation): 65 state = State.SearchStartAPI 66 67 for line in fin: 68 if state == State.SearchStartAPI: 69 parts = re.match('.*API_START.*',line) 70 if parts: 71 state = State.SearchEndAPI 72 continue 73 74 if state == State.SearchEndAPI: 75 parts = re.match('.*API_END.*',line) 76 if parts: 77 state = State.DoneAPI 78 continue 79 fout.write(mk_codeidentation + line) 80 continue 81 82 83 84def createIndex(fin, api_filepath, api_title, api_label, githuburl): 85 global typedefs, functions 86 global linenr, multiline_function_def, typedefFound, state 87 88 89 for line in fin: 90 if state == State.DoneAPI: 91 continue 92 93 linenr = linenr + 1 94 95 if state == State.SearchStartAPI: 96 parts = re.match('.*API_START.*',line) 97 if parts: 98 state = State.SearchEndAPI 99 continue 100 101 if state == State.SearchEndAPI: 102 parts = re.match('.*API_END.*',line) 103 if parts: 104 state = State.DoneAPI 105 continue 106 107 if multiline_function_def: 108 function_end = re.match('.*;\n', line) 109 if function_end: 110 multiline_function_def = 0 111 continue 112 113 param = re.match(".*@brief.*", line) 114 if param: 115 continue 116 param = re.match(".*@param.*", line) 117 if param: 118 continue 119 param = re.match(".*@return.*", line) 120 if param: 121 continue 122 123 # search typedef struct begin 124 if isTypedefStart(line): 125 typedefFound = 1 126 127 # search typedef struct end 128 if typedefFound: 129 typedef = re.match('}\s*(.*);\n', line) 130 if typedef: 131 typedefFound = 0 132 typedefs[typedef.group(1)] = codeReference(typedef.group(1), githuburl, api_filepath, linenr) 133 continue 134 135 ref_function = re.match('.*typedef\s+void\s+\(\s*\*\s*(.*?)\)\(.*', line) 136 if ref_function: 137 functions[ref_function.group(1)] = codeReference(ref_function.group(1), githuburl, api_filepath, linenr) 138 continue 139 140 141 one_line_function_definition = re.match('(.*?)\s*\(.*\(*.*;\n', line) 142 if one_line_function_definition: 143 parts = one_line_function_definition.group(1).split(" "); 144 name = parts[len(parts)-1] 145 if len(name) == 0: 146 print(parts); 147 sys.exit(10) 148 functions[name] = codeReference( name, githuburl, api_filepath, linenr) 149 continue 150 151 multi_line_function_definition = re.match('.(.*?)\s*\(.*\(*.*', line) 152 if multi_line_function_definition: 153 parts = multi_line_function_definition.group(1).split(" "); 154 155 name = parts[len(parts)-1] 156 if len(name) == 0: 157 print(parts); 158 sys.exit(10) 159 multiline_function_def = 1 160 functions[name] = codeReference(name, githuburl, api_filepath, linenr) 161 162 163def findTitle(fin): 164 title = None 165 desc = "" 166 state = State.SearchTitle 167 168 for line in fin: 169 if state == State.SearchTitle: 170 if isStartOfComment(line): 171 continue 172 173 parts = re.match('.*(@title)(.*)', line) 174 if parts: 175 title = parts.group(2).strip() 176 state = State.SearchEndTitle 177 continue 178 179 if state == State.SearchEndTitle: 180 if (isEndOfComment(line)): 181 state = State.DoneAPI 182 break 183 184 parts = re.match('(\s*\*\s*)(.*\n)',line) 185 if parts: 186 desc = desc + parts.group(2) 187 return [title, desc] 188 189def main(argv): 190 global linenr, multiline_function_def, typedefFound, state 191 192 mk_codeidentation = " " 193 git_branch_name = "master" 194 btstackfolder = "../../" 195 githuburl = "https://github.com/bluekitchen/btstack/blob/master/" 196 markdownfolder = "docs-markdown/" 197 198 cmd = 'markdown_create_apis.py [-r <root_btstackfolder>] [-g <githuburl>] [-o <output_markdownfolder>]' 199 try: 200 opts, args = getopt.getopt(argv,"r:g:o:",["rfolder=","github=","ofolder="]) 201 except getopt.GetoptError: 202 print (cmd) 203 sys.exit(2) 204 for opt, arg in opts: 205 if opt == '-h': 206 print (cmd) 207 sys.exit() 208 elif opt in ("-r", "--rfolder"): 209 btstackfolder = arg 210 elif opt in ("-g", "--github"): 211 githuburl = arg 212 elif opt in ("-o", "--ofolder"): 213 markdownfolder = arg 214 215 apifile = markdownfolder + "appendix/apis.md" 216 # indexfile = markdownfolder + "api_index.md" 217 btstack_srcfolder = btstackfolder + "src/" 218 219 try: 220 output = subprocess.check_output("git symbolic-ref --short HEAD", stderr=subprocess.STDOUT, timeout=3, shell=True) 221 git_branch_name = output.decode().rstrip() 222 except subprocess.CalledProcessError as exc: 223 print('GIT branch name: failed to get, use default value \"%s\"" ', git_branch_name, exc.returncode, exc.output) 224 else: 225 print ('GIT branch name : %s' % git_branch_name) 226 227 githuburl = githuburl + git_branch_name 228 229 print ('BTstack src folder is : ' + btstack_srcfolder) 230 print ('API file is : ' + apifile) 231 print ('Github URL is : ' + githuburl) 232 233 # create a dictionary of header files {file_path : [title, description]} 234 # title and desctiption are extracted from the file 235 for root, dirs, files in os.walk(btstack_srcfolder, topdown=True): 236 for f in files: 237 if not f.endswith(".h"): 238 continue 239 240 if not root.endswith("/"): 241 root = root + "/" 242 243 header_filepath = root + f 244 245 with open(header_filepath, 'rt') as fin: 246 [header_title, header_desc] = findTitle(fin) 247 248 if header_title: 249 header_files[header_filepath] = [header_title, header_desc] 250 else: 251 print("No @title flag found. Skip %s" % header_filepath) 252 253 254 for header_filepath in sorted(header_files.keys()): 255 header_label = filename_stem(header_filepath) # file name without 256 header_description = header_files[header_filepath][1] 257 header_title = api_header.replace("API_TITLE", header_files[header_filepath][0]).replace("API_LABEL", header_label) 258 markdown_filepath = markdownfolder + "appendix/" + header_label + ".md" 259 260 with open(header_filepath, 'rt') as fin: 261 with open(markdown_filepath, 'wt') as fout: 262 fout.write(header_title) 263 fout.write(header_description) 264 writeAPI(fout, fin, mk_codeidentation) 265 266 with open(header_filepath, 'rt') as fin: 267 linenr = 0 268 typedefFound = 0 269 multiline_function_def = 0 270 state = State.SearchStartAPI 271 createIndex(fin, markdown_filepath, header_title, header_label, githuburl) 272 273 # add API list to the navigation menu 274 with open("mkdocs-temp.yml", 'rt') as fin: 275 with open("mkdocs.yml", 'wt') as fout: 276 for line in fin: 277 fout.write(line) 278 279 if not isTagAPI(line): 280 continue 281 282 identation = getSecondLevelIdentation(line) 283 284 for header_filepath in sorted(header_files.keys()): 285 header_title = header_files[header_filepath][0] 286 markdown_reference = "appendix/" + filename_stem(header_filepath) + ".md" 287 288 fout.write(identation + "'" + header_title + "': " + markdown_reference + "\n") 289 290 for function in functions: 291 parts = function.split(' ') 292 if (len(parts) > 1): 293 print (parts) 294 295 references = functions.copy() 296 references.update(typedefs) 297 298 # with open(indexfile, 'w') as fout: 299 # for function, reference in references.items(): 300 # fout.write("[" + function + "](" + reference + ")\n") 301 302 pickle.dump(references, open("references.p", "wb" ) ) 303 304if __name__ == "__main__": 305 main(sys.argv[1:]) 306