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