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