mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 04:59:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			313 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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
 | |
| 
 | |
| # Determime build directory
 | |
| builddir = '_build'
 | |
| if 'BUILDDIR' in os.environ:
 | |
|     builddir = os.environ['BUILDDIR']
 | |
| 
 | |
| # 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 = os.path.join(builddir, '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)
 | |
| 
 | |
|         previous_rst_output = ''
 | |
|         if os.path.isfile(inc_file_path):
 | |
|             with open(inc_file_path, "r") as inc_file_old:
 | |
|                 previous_rst_output = inc_file_old.read()
 | |
| 
 | |
|         if previous_rst_output != rst_output:
 | |
|             with open(inc_file_path, "w") as inc_file:
 | |
|                 inc_file.write(rst_output)
 | |
| 
 | |
| 
 | |
| 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()
 | 
