]> granicus.if.org Git - esp-idf/blob - docs/gen-dxd.py
Merge branch 'feature/python_future' into 'master'
[esp-idf] / docs / gen-dxd.py
1 # gen-dxd.py - Generate Doxygen Directives
2 #
3 # This code is in the Public Domain (or CC0 licensed, at your option.)
4 # Unless required by applicable law or agreed to in writing, this
5 # software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6 # CONDITIONS OF ANY KIND, either express or implied.
7 #
8
9 import sys
10 import os
11 import re
12
13 # Determime build directory
14 builddir = '_build'
15 if 'BUILDDIR' in os.environ:
16     builddir = os.environ['BUILDDIR']
17
18 # Script configuration
19 header_file_path_prefix = "../../components/"
20 """string: path prefix for header files.
21 """
22 doxyfile_path = "../Doxyfile"
23 """string: path to a file containing header files to processs.
24 """
25 xml_directory_path = "xml"
26 """string: path to directory with XML files by Doxygen.
27 """
28 inc_directory_path = os.path.join(builddir, 'inc')
29 """string: path prefix for header files.
30 """
31 all_kinds = [
32     ("function", "Functions"),
33     ("union", "Unions"),
34     ("struct", "Structures"),
35     ("define", "Macros"),
36     ("typedef", "Type Definitions"),
37     ("enum", "Enumerations")
38     ]
39 """list of items that will be generated for a single API file
40 """
41
42
43 def get_doxyfile_input():
44     """Get contents of Doxyfile's INPUT statement.
45
46     Returns:
47         Contents of Doxyfile's INPUT.
48
49     """
50     if not os.path.isfile(doxyfile_path):
51         print "Doxyfile '%s' does not exist!" % doxyfile_path
52         sys.exit()
53
54     print "Getting Doxyfile's INPUT"
55
56     input_file = open(doxyfile_path, "r")
57
58     line = input_file.readline()
59     # read contents of Doxyfile until 'INPUT' statement
60     while line:
61         if line.find("INPUT") == 0:
62             break
63         line = input_file.readline()
64
65     doxyfile_INPUT = ""
66     line = input_file.readline()
67     # skip input_file contents until end of 'INPUT' statement
68     while line:
69         if line.isspace():
70             # we have reached the end of 'INPUT' statement
71             break
72         # process only lines that are not comments
73         if line.find("#") == -1:
74             # extract header file path inside components folder
75             m = re.search(header_file_path_prefix + "(.*\.h)", line)
76             header_file_path = m.group(1)
77             doxyfile_INPUT += header_file_path + "\n"
78         # proceed reading next line
79         line = input_file.readline()
80
81     input_file.close()
82     return doxyfile_INPUT
83
84
85 def get_api_name(header_file_path):
86     """Get name of API from header file path.
87
88     Args:
89         header_file_path: path to the header file.
90
91     Returns:
92         The name of API.
93
94     """
95     api_name = ""
96     regex = r".*/(.*)\.h"
97     m = re.search(regex, header_file_path)
98     if m:
99         api_name = m.group(1)
100
101     return api_name
102
103
104 def get_rst_header(header_name):
105     """Get rst formatted code with a header.
106
107     Args:
108         header_name: name of header.
109
110     Returns:
111         Formatted rst code with the header.
112
113     """
114
115     rst_output = ""
116     rst_output += header_name + "\n"
117     rst_output += "^" * len(header_name) + "\n"
118     rst_output += "\n"
119
120     return rst_output
121
122
123 def select_unions(innerclass_list):
124     """Select unions from innerclass list.
125
126     Args:
127         innerclass_list: raw list with unions and structures
128                          extracted from Dogygen's xml file.
129
130     Returns:
131         Doxygen directives with unions selected from the list.
132
133     """
134
135     rst_output = ""
136     for line in innerclass_list.splitlines():
137         # union is denoted by "union" at the beginning of line
138         if line.find("union") == 0:
139             union_id, union_name = re.split(r"\t+", line)
140             rst_output += ".. doxygenunion:: "
141             rst_output += union_name
142             rst_output += "\n"
143
144     return rst_output
145
146
147 def select_structs(innerclass_list):
148     """Select structures from innerclass list.
149
150     Args:
151         innerclass_list: raw list with unions and structures
152                          extracted from Dogygen's xml file.
153
154     Returns:
155         Doxygen directives with structures selected from the list.
156         Note: some structures are excluded as described on code below.
157
158     """
159
160     rst_output = ""
161     for line in innerclass_list.splitlines():
162         # structure is denoted by "struct" at the beginning of line
163         if line.find("struct") == 0:
164             # skip structures that are part of union
165             # they are documented by 'doxygenunion' directive
166             if line.find("::") > 0:
167                 continue
168             struct_id, struct_name = re.split(r"\t+", line)
169             rst_output += ".. doxygenstruct:: "
170             rst_output += struct_name
171             rst_output += "\n"
172             rst_output += "    :members:\n"
173             rst_output += "\n"
174
175     return rst_output
176
177
178 def get_directives(tree, kind):
179     """Get directives for specific 'kind'.
180
181     Args:
182         tree: the ElementTree 'tree' of XML by Doxygen
183         kind: name of API "kind" to be generated
184
185     Returns:
186         Doxygen directives for selected 'kind'.
187         Note: the header with "kind" name is included.
188
189     """
190
191     rst_output = ""
192     if kind in ["union", "struct"]:
193         innerclass_list = ""
194         for elem in tree.iterfind('compounddef/innerclass'):
195             innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
196         if kind == "union":
197             rst_output += select_unions(innerclass_list)
198         else:
199             rst_output += select_structs(innerclass_list)
200     else:
201         for elem in tree.iterfind(
202                 'compounddef/sectiondef/memberdef[@kind="%s"]' % kind):
203             name = elem.find('name')
204             rst_output += ".. doxygen%s:: " % kind
205             rst_output += name.text + "\n"
206     if rst_output:
207         all_kinds_dict = dict(all_kinds)
208         rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
209
210     return rst_output
211
212
213 def generate_directives(header_file_path):
214     """Generate API reference with Doxygen directives for a header file.
215
216     Args:
217         header_file_path: a path to the header file with API.
218
219     Returns:
220         Doxygen directives for the header file.
221
222     """
223
224     api_name = get_api_name(header_file_path)
225
226     # in XLT file name each "_" in the api name is expanded by Doxygen to "__"
227     xlt_api_name = api_name.replace("_", "__")
228     xml_file_path = "%s/%s_8h.xml" % (xml_directory_path, xlt_api_name)
229
230     rst_output = ""
231     rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
232     rst_output += "\n"
233     rst_output += get_rst_header("Header File")
234     rst_output += "* :component_file:`" + header_file_path + "`\n"
235     rst_output += "\n"
236
237     try:
238         import xml.etree.cElementTree as ET
239     except ImportError:
240         import xml.etree.ElementTree as ET
241
242     tree = ET.ElementTree(file=xml_file_path)
243     for i in range(len(all_kinds)):
244         kind = all_kinds[i][0]
245         rst_output += get_directives(tree, kind)
246
247     return rst_output
248
249
250 def generate_api_inc_files():
251     """Generate header_file.inc files
252     with API reference made of doxygen directives
253     for each header file
254     specified in the 'INPUT' statement of Doxyfile.
255
256     """
257
258     if not os.path.isdir(xml_directory_path):
259         print "Directory %s does not exist!" % xml_directory_path
260         sys.exit()
261
262     if not os.path.exists(inc_directory_path):
263         os.makedirs(inc_directory_path)
264
265     list_to_generate = get_doxyfile_input()
266     print "Generating 'api_name.inc' files with Doxygen directives"
267     for header_file_path in list_to_generate.splitlines():
268         api_name = get_api_name(header_file_path)
269         inc_file_path = inc_directory_path + "/" + api_name + ".inc"
270         rst_output = generate_directives(header_file_path)
271
272         previous_rst_output = ''
273         if os.path.isfile(inc_file_path):
274             with open(inc_file_path, "r") as inc_file_old:
275                 previous_rst_output = inc_file_old.read()
276
277         if previous_rst_output != rst_output:
278             with open(inc_file_path, "w") as inc_file:
279                 inc_file.write(rst_output)
280
281
282 if __name__ == "__main__":
283     """The main script that generates
284     Doxygen directives.
285
286     """
287
288     # Process command line arguments, if any
289     if len(sys.argv) > 1:
290         if not os.path.isdir(xml_directory_path):
291             print "Directory %s does not exist!" % xml_directory_path
292             sys.exit()
293         header_file_path = sys.argv[1]
294         api_name = get_api_name(header_file_path)
295         if api_name:
296             rst_output = generate_directives(header_file_path)
297             print "Doxygen directives for '%s'" % header_file_path
298             print
299             print rst_output
300         else:
301             print "Options to execute 'gen-dxd.py' application:"
302             print "1: $ python gen-dxd.py"
303             print "   Generate API 'header_file.inc' files for headers defined in '%s'" % doxyfile_path
304             print "2: $ python gen-dxd.py header_file_path"
305             print "   Print out Doxygen directives for a single header file"
306             print "   example: $ python gen-dxd.py mdns/include/mdns.h"
307             print "   NOTE: Run Doxygen first to get XML files for the header file"
308
309         sys.exit()
310
311     # No command line arguments given
312     generate_api_inc_files()