xref: /btstack/doc/manual/markdown_create_apis.py (revision 78fab72ea47061cabc1101e9880ba35e76a12158)
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"""
27*78fab72eSMilanka Ringwaldapi_subheader = """
28*78fab72eSMilanka Ringwald## API_TITLE API {#sec:API_LABEL_api}
29*78fab72eSMilanka Ringwald
30*78fab72eSMilanka Ringwald"""
31c8c342a6SMilanka Ringwald
32c8c342a6SMilanka Ringwaldapi_ending = """
33c8c342a6SMilanka Ringwald"""
34c8c342a6SMilanka Ringwald
35c8c342a6SMilanka Ringwaldcode_ref = """GITHUBFPATH#LLINENR"""
36c8c342a6SMilanka Ringwald
37c8c342a6SMilanka Ringwald
38d7ef0676SMilanka Ringwalddef isEndOfComment(line):
39d7ef0676SMilanka Ringwald    return re.match('\s*\*/.*', line)
40d7ef0676SMilanka Ringwald
41d7ef0676SMilanka Ringwalddef isStartOfComment(line):
42d7ef0676SMilanka Ringwald    return re.match('\s*\/\*/.*', line)
43d7ef0676SMilanka Ringwald
44d7ef0676SMilanka Ringwalddef isTypedefStart(line):
45d7ef0676SMilanka Ringwald    return re.match('.*typedef\s+struct.*', line)
46d7ef0676SMilanka Ringwald
47c8c342a6SMilanka Ringwalddef codeReference(fname, githuburl, filepath, linenr):
48c8c342a6SMilanka Ringwald    global code_ref
49c8c342a6SMilanka Ringwald    ref = code_ref.replace("GITHUB", githuburl)
50c8c342a6SMilanka Ringwald    ref = ref.replace("FPATH", filepath)
51c8c342a6SMilanka Ringwald    ref = ref.replace("LINENR", str(linenr))
52c8c342a6SMilanka Ringwald    return ref
53c8c342a6SMilanka Ringwald
544a400a05SMilanka Ringwalddef isTagAPI(line):
554a400a05SMilanka Ringwald    return re.match('(.*)(-\s*\')(APIs).*',line)
564a400a05SMilanka Ringwald
574a400a05SMilanka Ringwalddef getSecondLevelIdentation(line):
584a400a05SMilanka Ringwald    indentation = ""
594a400a05SMilanka Ringwald    parts = re.match('(.*)(-\s*\')(APIs).*',line)
604a400a05SMilanka Ringwald    if parts:
614a400a05SMilanka Ringwald        # return double identation for the submenu
624a400a05SMilanka Ringwald        indentation = parts.group(1) + parts.group(1) + "- "
634a400a05SMilanka Ringwald    return indentation
644a400a05SMilanka Ringwald
654a400a05SMilanka Ringwalddef filename_stem(filepath):
664a400a05SMilanka Ringwald    return os.path.splitext(os.path.basename(filepath))[0]
67c8c342a6SMilanka Ringwald
68d7ef0676SMilanka Ringwalddef writeAPI(fout, fin, mk_codeidentation):
69c8c342a6SMilanka Ringwald    state = State.SearchStartAPI
70d7ef0676SMilanka Ringwald
71c8c342a6SMilanka Ringwald    for line in fin:
72c8c342a6SMilanka Ringwald        if state == State.SearchStartAPI:
73c8c342a6SMilanka Ringwald            parts = re.match('.*API_START.*',line)
74c8c342a6SMilanka Ringwald            if parts:
75c8c342a6SMilanka Ringwald                state = State.SearchEndAPI
76c8c342a6SMilanka Ringwald            continue
77c8c342a6SMilanka Ringwald
78c8c342a6SMilanka Ringwald        if state == State.SearchEndAPI:
79c8c342a6SMilanka Ringwald            parts = re.match('.*API_END.*',line)
80c8c342a6SMilanka Ringwald            if parts:
81c8c342a6SMilanka Ringwald                state = State.DoneAPI
82c8c342a6SMilanka Ringwald                continue
83c8c342a6SMilanka Ringwald            fout.write(mk_codeidentation + line)
84c8c342a6SMilanka Ringwald            continue
85c8c342a6SMilanka Ringwald
86c8c342a6SMilanka Ringwald
87c8c342a6SMilanka Ringwald
884a400a05SMilanka Ringwalddef createIndex(fin, api_filepath, api_title, api_label, githuburl):
89d7ef0676SMilanka Ringwald    global typedefs, functions
90d7ef0676SMilanka Ringwald    global linenr, multiline_function_def, typedefFound, state
91d7ef0676SMilanka Ringwald
92d7ef0676SMilanka Ringwald
93c8c342a6SMilanka Ringwald    for line in fin:
94c8c342a6SMilanka Ringwald        if state == State.DoneAPI:
95c8c342a6SMilanka Ringwald            continue
96c8c342a6SMilanka Ringwald
97c8c342a6SMilanka Ringwald        linenr = linenr + 1
98c8c342a6SMilanka Ringwald
99c8c342a6SMilanka Ringwald        if state == State.SearchStartAPI:
100c8c342a6SMilanka Ringwald            parts = re.match('.*API_START.*',line)
101c8c342a6SMilanka Ringwald            if parts:
102c8c342a6SMilanka Ringwald                state = State.SearchEndAPI
103c8c342a6SMilanka Ringwald            continue
104c8c342a6SMilanka Ringwald
105c8c342a6SMilanka Ringwald        if state == State.SearchEndAPI:
106c8c342a6SMilanka Ringwald            parts = re.match('.*API_END.*',line)
107c8c342a6SMilanka Ringwald            if parts:
108c8c342a6SMilanka Ringwald                state = State.DoneAPI
109c8c342a6SMilanka Ringwald                continue
110c8c342a6SMilanka Ringwald
111c8c342a6SMilanka Ringwald        if multiline_function_def:
112c8c342a6SMilanka Ringwald            function_end = re.match('.*;\n', line)
113c8c342a6SMilanka Ringwald            if function_end:
114c8c342a6SMilanka Ringwald                multiline_function_def = 0
115c8c342a6SMilanka Ringwald            continue
116c8c342a6SMilanka Ringwald
117c8c342a6SMilanka Ringwald        param = re.match(".*@brief.*", line)
118c8c342a6SMilanka Ringwald        if param:
119c8c342a6SMilanka Ringwald            continue
120c8c342a6SMilanka Ringwald        param = re.match(".*@param.*", line)
121c8c342a6SMilanka Ringwald        if param:
122c8c342a6SMilanka Ringwald            continue
123c8c342a6SMilanka Ringwald        param = re.match(".*@return.*", line)
124c8c342a6SMilanka Ringwald        if param:
125c8c342a6SMilanka Ringwald            continue
126c8c342a6SMilanka Ringwald
127c8c342a6SMilanka Ringwald        # search typedef struct begin
128d7ef0676SMilanka Ringwald        if isTypedefStart(line):
129c8c342a6SMilanka Ringwald            typedefFound = 1
130c8c342a6SMilanka Ringwald
131c8c342a6SMilanka Ringwald        # search typedef struct end
132c8c342a6SMilanka Ringwald        if typedefFound:
133c8c342a6SMilanka Ringwald            typedef = re.match('}\s*(.*);\n', line)
134c8c342a6SMilanka Ringwald            if typedef:
135c8c342a6SMilanka Ringwald                typedefFound = 0
1364a400a05SMilanka Ringwald                typedefs[typedef.group(1)] = codeReference(typedef.group(1), githuburl, api_filepath, linenr)
137c8c342a6SMilanka Ringwald            continue
138c8c342a6SMilanka Ringwald
139c8c342a6SMilanka Ringwald        ref_function =  re.match('.*typedef\s+void\s+\(\s*\*\s*(.*?)\)\(.*', line)
140c8c342a6SMilanka Ringwald        if ref_function:
1414a400a05SMilanka Ringwald            functions[ref_function.group(1)] = codeReference(ref_function.group(1), githuburl, api_filepath, linenr)
142c8c342a6SMilanka Ringwald            continue
143c8c342a6SMilanka Ringwald
144d7ef0676SMilanka Ringwald
145d7ef0676SMilanka Ringwald        one_line_function_definition = re.match('(.*?)\s*\(.*\(*.*;\n', line)
146d7ef0676SMilanka Ringwald        if one_line_function_definition:
147d7ef0676SMilanka Ringwald            parts = one_line_function_definition.group(1).split(" ");
148c8c342a6SMilanka Ringwald            name = parts[len(parts)-1]
149c8c342a6SMilanka Ringwald            if len(name) == 0:
150c8c342a6SMilanka Ringwald                print(parts);
151c8c342a6SMilanka Ringwald                sys.exit(10)
1524a400a05SMilanka Ringwald            functions[name] = codeReference( name, githuburl, api_filepath, linenr)
153c8c342a6SMilanka Ringwald            continue
154c8c342a6SMilanka Ringwald
155d7ef0676SMilanka Ringwald        multi_line_function_definition = re.match('.(.*?)\s*\(.*\(*.*', line)
156d7ef0676SMilanka Ringwald        if multi_line_function_definition:
157d7ef0676SMilanka Ringwald            parts = multi_line_function_definition.group(1).split(" ");
158d7ef0676SMilanka Ringwald
159d7ef0676SMilanka Ringwald            name = parts[len(parts)-1]
160c8c342a6SMilanka Ringwald            if len(name) == 0:
161c8c342a6SMilanka Ringwald                print(parts);
162c8c342a6SMilanka Ringwald                sys.exit(10)
163c8c342a6SMilanka Ringwald            multiline_function_def = 1
1644a400a05SMilanka Ringwald            functions[name] = codeReference(name, githuburl, api_filepath, linenr)
165c8c342a6SMilanka Ringwald
166c8c342a6SMilanka Ringwald
167d7ef0676SMilanka Ringwalddef findTitle(fin):
168d7ef0676SMilanka Ringwald    title = None
169d7ef0676SMilanka Ringwald    desc = ""
170d7ef0676SMilanka Ringwald    state = State.SearchTitle
171d7ef0676SMilanka Ringwald
172d7ef0676SMilanka Ringwald    for line in fin:
173d7ef0676SMilanka Ringwald        if state == State.SearchTitle:
174d7ef0676SMilanka Ringwald            if isStartOfComment(line):
175d7ef0676SMilanka Ringwald                continue
176d7ef0676SMilanka Ringwald
177d7ef0676SMilanka Ringwald            parts = re.match('.*(@title)(.*)', line)
178d7ef0676SMilanka Ringwald            if parts:
179d7ef0676SMilanka Ringwald                title = parts.group(2).strip()
180d7ef0676SMilanka Ringwald                state = State.SearchEndTitle
181d7ef0676SMilanka Ringwald                continue
182d7ef0676SMilanka Ringwald
183d7ef0676SMilanka Ringwald        if state == State.SearchEndTitle:
184d7ef0676SMilanka Ringwald            if (isEndOfComment(line)):
185d7ef0676SMilanka Ringwald                state = State.DoneAPI
186d7ef0676SMilanka Ringwald                break
187d7ef0676SMilanka Ringwald
188d7ef0676SMilanka Ringwald            parts = re.match('(\s*\*\s*)(.*\n)',line)
189d7ef0676SMilanka Ringwald            if parts:
190d7ef0676SMilanka Ringwald                desc = desc + parts.group(2)
1914a400a05SMilanka Ringwald    return [title, desc]
192d7ef0676SMilanka Ringwald
193c8c342a6SMilanka Ringwalddef main(argv):
194d7ef0676SMilanka Ringwald    global linenr, multiline_function_def, typedefFound, state
195d7ef0676SMilanka Ringwald
196c8c342a6SMilanka Ringwald    mk_codeidentation = "    "
197c8c342a6SMilanka Ringwald    git_branch_name = "master"
198c8c342a6SMilanka Ringwald    btstackfolder = "../../"
199c8c342a6SMilanka Ringwald    githuburl  = "https://github.com/bluekitchen/btstack/blob/master/"
200c8c342a6SMilanka Ringwald    markdownfolder = "docs-markdown/"
201c8c342a6SMilanka Ringwald
202c8c342a6SMilanka Ringwald    cmd = 'markdown_create_apis.py [-r <root_btstackfolder>] [-g <githuburl>] [-o <output_markdownfolder>]'
203c8c342a6SMilanka Ringwald    try:
204c8c342a6SMilanka Ringwald        opts, args = getopt.getopt(argv,"r:g:o:",["rfolder=","github=","ofolder="])
205c8c342a6SMilanka Ringwald    except getopt.GetoptError:
206c8c342a6SMilanka Ringwald        print (cmd)
207c8c342a6SMilanka Ringwald        sys.exit(2)
208c8c342a6SMilanka Ringwald    for opt, arg in opts:
209c8c342a6SMilanka Ringwald        if opt == '-h':
210c8c342a6SMilanka Ringwald            print (cmd)
211c8c342a6SMilanka Ringwald            sys.exit()
212c8c342a6SMilanka Ringwald        elif opt in ("-r", "--rfolder"):
213c8c342a6SMilanka Ringwald            btstackfolder = arg
214c8c342a6SMilanka Ringwald        elif opt in ("-g", "--github"):
215c8c342a6SMilanka Ringwald            githuburl = arg
216c8c342a6SMilanka Ringwald        elif opt in ("-o", "--ofolder"):
217c8c342a6SMilanka Ringwald            markdownfolder = arg
218c8c342a6SMilanka Ringwald
219c8c342a6SMilanka Ringwald    apifile   = markdownfolder + "appendix/apis.md"
220c8c342a6SMilanka Ringwald    # indexfile = markdownfolder + "api_index.md"
221d7ef0676SMilanka Ringwald    btstack_srcfolder = btstackfolder + "src/"
222c8c342a6SMilanka Ringwald
223c8c342a6SMilanka Ringwald    try:
224c8c342a6SMilanka Ringwald        output = subprocess.check_output("git symbolic-ref --short HEAD", stderr=subprocess.STDOUT, timeout=3, shell=True)
225c8c342a6SMilanka Ringwald        git_branch_name = output.decode().rstrip()
226c8c342a6SMilanka Ringwald    except subprocess.CalledProcessError as exc:
227c8c342a6SMilanka Ringwald        print('GIT branch name: failed to get, use default value \"%s\""  ', git_branch_name, exc.returncode, exc.output)
228c8c342a6SMilanka Ringwald    else:
229c8c342a6SMilanka Ringwald        print ('GIT branch name :  %s' % git_branch_name)
230c8c342a6SMilanka Ringwald
231c8c342a6SMilanka Ringwald    githuburl = githuburl + git_branch_name
232c8c342a6SMilanka Ringwald
233d7ef0676SMilanka Ringwald    print ('BTstack src folder is : ' + btstack_srcfolder)
234c8c342a6SMilanka Ringwald    print ('API file is       : ' + apifile)
235c8c342a6SMilanka Ringwald    print ('Github URL is    : ' +  githuburl)
236c8c342a6SMilanka Ringwald
2374a400a05SMilanka Ringwald    # create a dictionary of header files {file_path : [title, description]}
2384a400a05SMilanka Ringwald    # title and desctiption are extracted from the file
239d7ef0676SMilanka Ringwald    for root, dirs, files in os.walk(btstack_srcfolder, topdown=True):
240d7ef0676SMilanka Ringwald        for f in files:
241d7ef0676SMilanka Ringwald            if not f.endswith(".h"):
242d7ef0676SMilanka Ringwald                continue
243d7ef0676SMilanka Ringwald
2444a400a05SMilanka Ringwald            if not root.endswith("/"):
2454a400a05SMilanka Ringwald                root = root + "/"
246d7ef0676SMilanka Ringwald
2474a400a05SMilanka Ringwald            header_filepath = root + f
248d7ef0676SMilanka Ringwald
2494a400a05SMilanka Ringwald            with open(header_filepath, 'rt') as fin:
2504a400a05SMilanka Ringwald                [header_title, header_desc] = findTitle(fin)
251d7ef0676SMilanka Ringwald
2524a400a05SMilanka Ringwald            if header_title:
2534a400a05SMilanka Ringwald                header_files[header_filepath] = [header_title, header_desc]
2544a400a05SMilanka Ringwald            else:
2554a400a05SMilanka Ringwald                print("No @title flag found. Skip %s" % header_filepath)
256d7ef0676SMilanka Ringwald
257d7ef0676SMilanka Ringwald
2584a400a05SMilanka Ringwald    for header_filepath in sorted(header_files.keys()):
2594a400a05SMilanka Ringwald        header_label = filename_stem(header_filepath) # file name without
2604a400a05SMilanka Ringwald        header_description = header_files[header_filepath][1]
2614a400a05SMilanka Ringwald        header_title = api_header.replace("API_TITLE", header_files[header_filepath][0]).replace("API_LABEL", header_label)
2624a400a05SMilanka Ringwald        markdown_filepath = markdownfolder + "appendix/" + header_label + ".md"
263d7ef0676SMilanka Ringwald
2644a400a05SMilanka Ringwald        with open(header_filepath, 'rt') as fin:
2654a400a05SMilanka Ringwald            with open(markdown_filepath, 'wt') as fout:
2664a400a05SMilanka Ringwald                fout.write(header_title)
2674a400a05SMilanka Ringwald                fout.write(header_description)
268d7ef0676SMilanka Ringwald                writeAPI(fout, fin, mk_codeidentation)
269d7ef0676SMilanka Ringwald
2704a400a05SMilanka Ringwald        with open(header_filepath, 'rt') as fin:
271d7ef0676SMilanka Ringwald            linenr = 0
272d7ef0676SMilanka Ringwald            typedefFound = 0
273d7ef0676SMilanka Ringwald            multiline_function_def = 0
274d7ef0676SMilanka Ringwald            state = State.SearchStartAPI
2754a400a05SMilanka Ringwald            createIndex(fin, markdown_filepath, header_title, header_label, githuburl)
276d7ef0676SMilanka Ringwald
2774a400a05SMilanka Ringwald    # add API list to the navigation menu
2784a400a05SMilanka Ringwald    with open("mkdocs-temp.yml", 'rt') as fin:
2794a400a05SMilanka Ringwald        with open("mkdocs.yml", 'wt') as fout:
2804a400a05SMilanka Ringwald            for line in fin:
2814a400a05SMilanka Ringwald                fout.write(line)
2824a400a05SMilanka Ringwald
2834a400a05SMilanka Ringwald                if not isTagAPI(line):
2844a400a05SMilanka Ringwald                    continue
2854a400a05SMilanka Ringwald
2864a400a05SMilanka Ringwald                identation = getSecondLevelIdentation(line)
2874a400a05SMilanka Ringwald
2884a400a05SMilanka Ringwald                for header_filepath in sorted(header_files.keys()):
2894a400a05SMilanka Ringwald                    header_title = header_files[header_filepath][0]
2904a400a05SMilanka Ringwald                    markdown_reference = "appendix/" + filename_stem(header_filepath) + ".md"
2914a400a05SMilanka Ringwald
2924a400a05SMilanka Ringwald                    fout.write(identation + "'" + header_title + "': " + markdown_reference + "\n")
293c8c342a6SMilanka Ringwald
294*78fab72eSMilanka Ringwald
295*78fab72eSMilanka Ringwald    # create singe appendix/apis.md for latex
296*78fab72eSMilanka Ringwald    with open("mkdocs-temp.yml", 'rt') as fin:
297*78fab72eSMilanka Ringwald        with open("mkdocs-latex.yml", 'wt') as fout:
298*78fab72eSMilanka Ringwald            for line in fin:
299*78fab72eSMilanka Ringwald                if not isTagAPI(line):
300*78fab72eSMilanka Ringwald                    fout.write(line)
301*78fab72eSMilanka Ringwald                    continue
302*78fab72eSMilanka Ringwald
303*78fab72eSMilanka Ringwald                fout.write("  - 'APIs': appendix/apis.md\n")
304*78fab72eSMilanka Ringwald
305*78fab72eSMilanka Ringwald
306*78fab72eSMilanka Ringwald    markdown_filepath = markdownfolder + "appendix/apis.md"
307*78fab72eSMilanka Ringwald    with open(markdown_filepath, 'wt') as fout:
308*78fab72eSMilanka Ringwald        fout.write("\n# APIs\n\n")
309*78fab72eSMilanka Ringwald        for header_filepath in sorted(header_files.keys()):
310*78fab72eSMilanka Ringwald            header_label = filename_stem(header_filepath) # file name without
311*78fab72eSMilanka Ringwald            header_description = header_files[header_filepath][1]
312*78fab72eSMilanka Ringwald            subheader_title = api_subheader.replace("API_TITLE", header_files[header_filepath][0]).replace("API_LABEL", header_label)
313*78fab72eSMilanka Ringwald
314*78fab72eSMilanka Ringwald            with open(header_filepath, 'rt') as fin:
315*78fab72eSMilanka Ringwald                    fout.write(subheader_title)
316*78fab72eSMilanka Ringwald                    fout.write(header_description)
317*78fab72eSMilanka Ringwald                    writeAPI(fout, fin, mk_codeidentation)
318*78fab72eSMilanka Ringwald
319c8c342a6SMilanka Ringwald
320c8c342a6SMilanka Ringwald    references = functions.copy()
321c8c342a6SMilanka Ringwald    references.update(typedefs)
322c8c342a6SMilanka Ringwald
323c8c342a6SMilanka Ringwald    # with open(indexfile, 'w') as fout:
324c8c342a6SMilanka Ringwald    #     for function, reference in references.items():
325c8c342a6SMilanka Ringwald    #         fout.write("[" + function + "](" + reference + ")\n")
326c8c342a6SMilanka Ringwald
327c8c342a6SMilanka Ringwald    pickle.dump(references, open("references.p", "wb" ) )
328c8c342a6SMilanka Ringwald
329c8c342a6SMilanka Ringwaldif __name__ == "__main__":
330c8c342a6SMilanka Ringwald   main(sys.argv[1:])
331