|
|
@@ -0,0 +1,301 @@
|
|
|
+# gen-dxd.py - Generate Doxygen Directives
|
|
|
+#
|
|
|
+# This code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
+# Unless required by applicable law or agreed to in writing, this
|
|
|
+# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
+# CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+#
|
|
|
+
|
|
|
+import sys
|
|
|
+import os
|
|
|
+import re
|
|
|
+
|
|
|
+# Script configuration
|
|
|
+header_file_path_prefix = "../components/"
|
|
|
+"""string: path prefix for header files.
|
|
|
+"""
|
|
|
+doxyfile_path = "Doxyfile"
|
|
|
+"""string: path to a file containing header files to processs.
|
|
|
+"""
|
|
|
+xml_directory_path = "xml"
|
|
|
+"""string: path to directory with XML files by Doxygen.
|
|
|
+"""
|
|
|
+inc_directory_path = "_build/inc"
|
|
|
+"""string: path prefix for header files.
|
|
|
+"""
|
|
|
+all_kinds = [
|
|
|
+ ("function", "Functions"),
|
|
|
+ ("union", "Unions"),
|
|
|
+ ("struct", "Structures"),
|
|
|
+ ("define", "Macros"),
|
|
|
+ ("typedef", "Type Definitions"),
|
|
|
+ ("enum", "Enumerations")
|
|
|
+ ]
|
|
|
+"""list of items that will be generated for a single API file
|
|
|
+"""
|
|
|
+
|
|
|
+
|
|
|
+def get_doxyfile_input():
|
|
|
+ """Get contents of Doxyfile's INPUT statement.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Contents of Doxyfile's INPUT.
|
|
|
+
|
|
|
+ """
|
|
|
+ if not os.path.isfile(doxyfile_path):
|
|
|
+ print "Doxyfile '%s' does not exist!" % doxyfile_path
|
|
|
+ sys.exit()
|
|
|
+
|
|
|
+ print "Getting Doxyfile's INPUT"
|
|
|
+
|
|
|
+ input_file = open(doxyfile_path, "r")
|
|
|
+
|
|
|
+ line = input_file.readline()
|
|
|
+ # read contents of Doxyfile until 'INPUT' statement
|
|
|
+ while line:
|
|
|
+ if line.find("INPUT") == 0:
|
|
|
+ break
|
|
|
+ line = input_file.readline()
|
|
|
+
|
|
|
+ doxyfile_INPUT = ""
|
|
|
+ line = input_file.readline()
|
|
|
+ # skip input_file contents until end of 'INPUT' statement
|
|
|
+ while line:
|
|
|
+ if line.isspace():
|
|
|
+ # we have reached the end of 'INPUT' statement
|
|
|
+ break
|
|
|
+ # process only lines that are not comments
|
|
|
+ if line.find("#") == -1:
|
|
|
+ # extract header file path inside components folder
|
|
|
+ m = re.search(header_file_path_prefix + "(.*\.h)", line)
|
|
|
+ header_file_path = m.group(1)
|
|
|
+ doxyfile_INPUT += header_file_path + "\n"
|
|
|
+ # proceed reading next line
|
|
|
+ line = input_file.readline()
|
|
|
+
|
|
|
+ input_file.close()
|
|
|
+ return doxyfile_INPUT
|
|
|
+
|
|
|
+
|
|
|
+def get_api_name(header_file_path):
|
|
|
+ """Get name of API from header file path.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ header_file_path: path to the header file.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ The name of API.
|
|
|
+
|
|
|
+ """
|
|
|
+ api_name = ""
|
|
|
+ regex = r".*/(.*)\.h"
|
|
|
+ m = re.search(regex, header_file_path)
|
|
|
+ if m:
|
|
|
+ api_name = m.group(1)
|
|
|
+
|
|
|
+ return api_name
|
|
|
+
|
|
|
+
|
|
|
+def get_rst_header(header_name):
|
|
|
+ """Get rst formatted code with a header.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ header_name: name of header.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Formatted rst code with the header.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ rst_output = ""
|
|
|
+ rst_output += header_name + "\n"
|
|
|
+ rst_output += "^" * len(header_name) + "\n"
|
|
|
+ rst_output += "\n"
|
|
|
+
|
|
|
+ return rst_output
|
|
|
+
|
|
|
+
|
|
|
+def select_unions(innerclass_list):
|
|
|
+ """Select unions from innerclass list.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ innerclass_list: raw list with unions and structures
|
|
|
+ extracted from Dogygen's xml file.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Doxygen directives with unions selected from the list.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ rst_output = ""
|
|
|
+ for line in innerclass_list.splitlines():
|
|
|
+ # union is denoted by "union" at the beginning of line
|
|
|
+ if line.find("union") == 0:
|
|
|
+ union_id, union_name = re.split(r"\t+", line)
|
|
|
+ rst_output += ".. doxygenunion:: "
|
|
|
+ rst_output += union_name
|
|
|
+ rst_output += "\n"
|
|
|
+
|
|
|
+ return rst_output
|
|
|
+
|
|
|
+
|
|
|
+def select_structs(innerclass_list):
|
|
|
+ """Select structures from innerclass list.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ innerclass_list: raw list with unions and structures
|
|
|
+ extracted from Dogygen's xml file.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Doxygen directives with structures selected from the list.
|
|
|
+ Note: some structures are excluded as described on code below.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ rst_output = ""
|
|
|
+ for line in innerclass_list.splitlines():
|
|
|
+ # structure is denoted by "struct" at the beginning of line
|
|
|
+ if line.find("struct") == 0:
|
|
|
+ # skip structures that are part of union
|
|
|
+ # they are documented by 'doxygenunion' directive
|
|
|
+ if line.find("::") > 0:
|
|
|
+ continue
|
|
|
+ struct_id, struct_name = re.split(r"\t+", line)
|
|
|
+ rst_output += ".. doxygenstruct:: "
|
|
|
+ rst_output += struct_name
|
|
|
+ rst_output += "\n"
|
|
|
+ rst_output += " :members:\n"
|
|
|
+ rst_output += "\n"
|
|
|
+
|
|
|
+ return rst_output
|
|
|
+
|
|
|
+
|
|
|
+def get_directives(tree, kind):
|
|
|
+ """Get directives for specific 'kind'.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ tree: the ElementTree 'tree' of XML by Doxygen
|
|
|
+ kind: name of API "kind" to be generated
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Doxygen directives for selected 'kind'.
|
|
|
+ Note: the header with "kind" name is included.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ rst_output = ""
|
|
|
+ if kind in ["union", "struct"]:
|
|
|
+ innerclass_list = ""
|
|
|
+ for elem in tree.iterfind('compounddef/innerclass'):
|
|
|
+ innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
|
|
|
+ if kind == "union":
|
|
|
+ rst_output += select_unions(innerclass_list)
|
|
|
+ else:
|
|
|
+ rst_output += select_structs(innerclass_list)
|
|
|
+ else:
|
|
|
+ for elem in tree.iterfind(
|
|
|
+ 'compounddef/sectiondef/memberdef[@kind="%s"]' % kind):
|
|
|
+ name = elem.find('name')
|
|
|
+ rst_output += ".. doxygen%s:: " % kind
|
|
|
+ rst_output += name.text + "\n"
|
|
|
+ if rst_output:
|
|
|
+ all_kinds_dict = dict(all_kinds)
|
|
|
+ rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
|
|
|
+
|
|
|
+ return rst_output
|
|
|
+
|
|
|
+
|
|
|
+def generate_directives(header_file_path):
|
|
|
+ """Generate API reference with Doxygen directives for a header file.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ header_file_path: a path to the header file with API.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Doxygen directives for the header file.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ api_name = get_api_name(header_file_path)
|
|
|
+
|
|
|
+ # in XLT file name each "_" in the api name is expanded by Doxygen to "__"
|
|
|
+ xlt_api_name = api_name.replace("_", "__")
|
|
|
+ xml_file_path = "%s/%s_8h.xml" % (xml_directory_path, xlt_api_name)
|
|
|
+
|
|
|
+ rst_output = ""
|
|
|
+ rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
|
|
|
+ rst_output += "\n"
|
|
|
+ rst_output += get_rst_header("Header File")
|
|
|
+ rst_output += "* :component_file:`" + header_file_path + "`\n"
|
|
|
+ rst_output += "\n"
|
|
|
+
|
|
|
+ try:
|
|
|
+ import xml.etree.cElementTree as ET
|
|
|
+ except ImportError:
|
|
|
+ import xml.etree.ElementTree as ET
|
|
|
+
|
|
|
+ tree = ET.ElementTree(file=xml_file_path)
|
|
|
+ for i in range(len(all_kinds)):
|
|
|
+ kind = all_kinds[i][0]
|
|
|
+ rst_output += get_directives(tree, kind)
|
|
|
+
|
|
|
+ return rst_output
|
|
|
+
|
|
|
+
|
|
|
+def generate_api_inc_files():
|
|
|
+ """Generate header_file.inc files
|
|
|
+ with API reference made of doxygen directives
|
|
|
+ for each header file
|
|
|
+ specified in the 'INPUT' statement of Doxyfile.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ if not os.path.isdir(xml_directory_path):
|
|
|
+ print "Directory %s does not exist!" % xml_directory_path
|
|
|
+ sys.exit()
|
|
|
+
|
|
|
+ if not os.path.exists(inc_directory_path):
|
|
|
+ os.makedirs(inc_directory_path)
|
|
|
+
|
|
|
+ list_to_generate = get_doxyfile_input()
|
|
|
+ print "Generating 'api_name.inc' files with Doxygen directives"
|
|
|
+ for header_file_path in list_to_generate.splitlines():
|
|
|
+ api_name = get_api_name(header_file_path)
|
|
|
+ inc_file_path = inc_directory_path + "/" + api_name + ".inc"
|
|
|
+ rst_output = generate_directives(header_file_path)
|
|
|
+ inc_file = open(inc_file_path, "w")
|
|
|
+ inc_file.write(rst_output)
|
|
|
+ inc_file.close()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ """The main script that generates
|
|
|
+ Doxygen directives.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ # Process command line arguments, if any
|
|
|
+ if len(sys.argv) > 1:
|
|
|
+ if not os.path.isdir(xml_directory_path):
|
|
|
+ print "Directory %s does not exist!" % xml_directory_path
|
|
|
+ sys.exit()
|
|
|
+ header_file_path = sys.argv[1]
|
|
|
+ api_name = get_api_name(header_file_path)
|
|
|
+ if api_name:
|
|
|
+ rst_output = generate_directives(header_file_path)
|
|
|
+ print "Doxygen directives for '%s'" % header_file_path
|
|
|
+ print
|
|
|
+ print rst_output
|
|
|
+ else:
|
|
|
+ print "Options to execute 'gen-dxd.py' application:"
|
|
|
+ print "1: $ python gen-dxd.py"
|
|
|
+ print " Generate API 'header_file.inc' files for headers defined in '%s'" % doxyfile_path
|
|
|
+ print "2: $ python gen-dxd.py header_file_path"
|
|
|
+ print " Print out Doxygen directives for a single header file"
|
|
|
+ print " example: $ python gen-dxd.py mdns/include/mdns.h"
|
|
|
+ print " NOTE: Run Doxygen first to get XML files for the header file"
|
|
|
+
|
|
|
+ sys.exit()
|
|
|
+
|
|
|
+ # No command line arguments given
|
|
|
+ generate_api_inc_files()
|