1 # gen-dxd.py - Generate Doxygen Directives
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.
13 # Determime build directory
15 if 'BUILDDIR' in os.environ:
16 builddir = os.environ['BUILDDIR']
18 # Script configuration
19 header_file_path_prefix = "../../components/"
20 """string: path prefix for header files.
22 doxyfile_path = "../Doxyfile"
23 """string: path to a file containing header files to processs.
25 xml_directory_path = "xml"
26 """string: path to directory with XML files by Doxygen.
28 inc_directory_path = os.path.join(builddir, 'inc')
29 """string: path prefix for header files.
32 ("function", "Functions"),
34 ("struct", "Structures"),
36 ("typedef", "Type Definitions"),
37 ("enum", "Enumerations")
39 """list of items that will be generated for a single API file
43 def get_doxyfile_input():
44 """Get contents of Doxyfile's INPUT statement.
47 Contents of Doxyfile's INPUT.
50 if not os.path.isfile(doxyfile_path):
51 print "Doxyfile '%s' does not exist!" % doxyfile_path
54 print "Getting Doxyfile's INPUT"
56 input_file = open(doxyfile_path, "r")
58 line = input_file.readline()
59 # read contents of Doxyfile until 'INPUT' statement
61 if line.find("INPUT") == 0:
63 line = input_file.readline()
66 line = input_file.readline()
67 # skip input_file contents until end of 'INPUT' statement
70 # we have reached the end of 'INPUT' statement
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()
85 def get_api_name(header_file_path):
86 """Get name of API from header file path.
89 header_file_path: path to the header file.
97 m = re.search(regex, header_file_path)
104 def get_rst_header(header_name):
105 """Get rst formatted code with a header.
108 header_name: name of header.
111 Formatted rst code with the header.
116 rst_output += header_name + "\n"
117 rst_output += "^" * len(header_name) + "\n"
123 def select_unions(innerclass_list):
124 """Select unions from innerclass list.
127 innerclass_list: raw list with unions and structures
128 extracted from Dogygen's xml file.
131 Doxygen directives with unions selected from the list.
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
147 def select_structs(innerclass_list):
148 """Select structures from innerclass list.
151 innerclass_list: raw list with unions and structures
152 extracted from Dogygen's xml file.
155 Doxygen directives with structures selected from the list.
156 Note: some structures are excluded as described on code below.
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:
168 struct_id, struct_name = re.split(r"\t+", line)
169 rst_output += ".. doxygenstruct:: "
170 rst_output += struct_name
172 rst_output += " :members:\n"
178 def get_directives(tree, kind):
179 """Get directives for specific 'kind'.
182 tree: the ElementTree 'tree' of XML by Doxygen
183 kind: name of API "kind" to be generated
186 Doxygen directives for selected 'kind'.
187 Note: the header with "kind" name is included.
192 if kind in ["union", "struct"]:
194 for elem in tree.iterfind('compounddef/innerclass'):
195 innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
197 rst_output += select_unions(innerclass_list)
199 rst_output += select_structs(innerclass_list)
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"
207 all_kinds_dict = dict(all_kinds)
208 rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
213 def generate_directives(header_file_path):
214 """Generate API reference with Doxygen directives for a header file.
217 header_file_path: a path to the header file with API.
220 Doxygen directives for the header file.
224 api_name = get_api_name(header_file_path)
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)
231 rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
233 rst_output += get_rst_header("Header File")
234 rst_output += "* :component_file:`" + header_file_path + "`\n"
238 import xml.etree.cElementTree as ET
240 import xml.etree.ElementTree as ET
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)
250 def generate_api_inc_files():
251 """Generate header_file.inc files
252 with API reference made of doxygen directives
254 specified in the 'INPUT' statement of Doxyfile.
258 if not os.path.isdir(xml_directory_path):
259 print "Directory %s does not exist!" % xml_directory_path
262 if not os.path.exists(inc_directory_path):
263 os.makedirs(inc_directory_path)
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)
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()
277 if previous_rst_output != rst_output:
278 with open(inc_file_path, "w") as inc_file:
279 inc_file.write(rst_output)
282 if __name__ == "__main__":
283 """The main script that generates
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
293 header_file_path = sys.argv[1]
294 api_name = get_api_name(header_file_path)
296 rst_output = generate_directives(header_file_path)
297 print "Doxygen directives for '%s'" % header_file_path
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"
311 # No command line arguments given
312 generate_api_inc_files()