gen-dxd.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. import sys
  9. import os
  10. import re
  11. # Script configuration
  12. header_file_path_prefix = "../components/"
  13. """string: path prefix for header files.
  14. """
  15. doxyfile_path = "Doxyfile"
  16. """string: path to a file containing header files to processs.
  17. """
  18. xml_directory_path = "xml"
  19. """string: path to directory with XML files by Doxygen.
  20. """
  21. inc_directory_path = "_build/inc"
  22. """string: path prefix for header files.
  23. """
  24. all_kinds = [
  25. ("function", "Functions"),
  26. ("union", "Unions"),
  27. ("struct", "Structures"),
  28. ("define", "Macros"),
  29. ("typedef", "Type Definitions"),
  30. ("enum", "Enumerations")
  31. ]
  32. """list of items that will be generated for a single API file
  33. """
  34. def get_doxyfile_input():
  35. """Get contents of Doxyfile's INPUT statement.
  36. Returns:
  37. Contents of Doxyfile's INPUT.
  38. """
  39. if not os.path.isfile(doxyfile_path):
  40. print "Doxyfile '%s' does not exist!" % doxyfile_path
  41. sys.exit()
  42. print "Getting Doxyfile's INPUT"
  43. input_file = open(doxyfile_path, "r")
  44. line = input_file.readline()
  45. # read contents of Doxyfile until 'INPUT' statement
  46. while line:
  47. if line.find("INPUT") == 0:
  48. break
  49. line = input_file.readline()
  50. doxyfile_INPUT = ""
  51. line = input_file.readline()
  52. # skip input_file contents until end of 'INPUT' statement
  53. while line:
  54. if line.isspace():
  55. # we have reached the end of 'INPUT' statement
  56. break
  57. # process only lines that are not comments
  58. if line.find("#") == -1:
  59. # extract header file path inside components folder
  60. m = re.search(header_file_path_prefix + "(.*\.h)", line)
  61. header_file_path = m.group(1)
  62. doxyfile_INPUT += header_file_path + "\n"
  63. # proceed reading next line
  64. line = input_file.readline()
  65. input_file.close()
  66. return doxyfile_INPUT
  67. def get_api_name(header_file_path):
  68. """Get name of API from header file path.
  69. Args:
  70. header_file_path: path to the header file.
  71. Returns:
  72. The name of API.
  73. """
  74. api_name = ""
  75. regex = r".*/(.*)\.h"
  76. m = re.search(regex, header_file_path)
  77. if m:
  78. api_name = m.group(1)
  79. return api_name
  80. def get_rst_header(header_name):
  81. """Get rst formatted code with a header.
  82. Args:
  83. header_name: name of header.
  84. Returns:
  85. Formatted rst code with the header.
  86. """
  87. rst_output = ""
  88. rst_output += header_name + "\n"
  89. rst_output += "^" * len(header_name) + "\n"
  90. rst_output += "\n"
  91. return rst_output
  92. def select_unions(innerclass_list):
  93. """Select unions from innerclass list.
  94. Args:
  95. innerclass_list: raw list with unions and structures
  96. extracted from Dogygen's xml file.
  97. Returns:
  98. Doxygen directives with unions selected from the list.
  99. """
  100. rst_output = ""
  101. for line in innerclass_list.splitlines():
  102. # union is denoted by "union" at the beginning of line
  103. if line.find("union") == 0:
  104. union_id, union_name = re.split(r"\t+", line)
  105. rst_output += ".. doxygenunion:: "
  106. rst_output += union_name
  107. rst_output += "\n"
  108. return rst_output
  109. def select_structs(innerclass_list):
  110. """Select structures from innerclass list.
  111. Args:
  112. innerclass_list: raw list with unions and structures
  113. extracted from Dogygen's xml file.
  114. Returns:
  115. Doxygen directives with structures selected from the list.
  116. Note: some structures are excluded as described on code below.
  117. """
  118. rst_output = ""
  119. for line in innerclass_list.splitlines():
  120. # structure is denoted by "struct" at the beginning of line
  121. if line.find("struct") == 0:
  122. # skip structures that are part of union
  123. # they are documented by 'doxygenunion' directive
  124. if line.find("::") > 0:
  125. continue
  126. struct_id, struct_name = re.split(r"\t+", line)
  127. rst_output += ".. doxygenstruct:: "
  128. rst_output += struct_name
  129. rst_output += "\n"
  130. rst_output += " :members:\n"
  131. rst_output += "\n"
  132. return rst_output
  133. def get_directives(tree, kind):
  134. """Get directives for specific 'kind'.
  135. Args:
  136. tree: the ElementTree 'tree' of XML by Doxygen
  137. kind: name of API "kind" to be generated
  138. Returns:
  139. Doxygen directives for selected 'kind'.
  140. Note: the header with "kind" name is included.
  141. """
  142. rst_output = ""
  143. if kind in ["union", "struct"]:
  144. innerclass_list = ""
  145. for elem in tree.iterfind('compounddef/innerclass'):
  146. innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
  147. if kind == "union":
  148. rst_output += select_unions(innerclass_list)
  149. else:
  150. rst_output += select_structs(innerclass_list)
  151. else:
  152. for elem in tree.iterfind(
  153. 'compounddef/sectiondef/memberdef[@kind="%s"]' % kind):
  154. name = elem.find('name')
  155. rst_output += ".. doxygen%s:: " % kind
  156. rst_output += name.text + "\n"
  157. if rst_output:
  158. all_kinds_dict = dict(all_kinds)
  159. rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
  160. return rst_output
  161. def generate_directives(header_file_path):
  162. """Generate API reference with Doxygen directives for a header file.
  163. Args:
  164. header_file_path: a path to the header file with API.
  165. Returns:
  166. Doxygen directives for the header file.
  167. """
  168. api_name = get_api_name(header_file_path)
  169. # in XLT file name each "_" in the api name is expanded by Doxygen to "__"
  170. xlt_api_name = api_name.replace("_", "__")
  171. xml_file_path = "%s/%s_8h.xml" % (xml_directory_path, xlt_api_name)
  172. rst_output = ""
  173. rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
  174. rst_output += "\n"
  175. rst_output += get_rst_header("Header File")
  176. rst_output += "* :component_file:`" + header_file_path + "`\n"
  177. rst_output += "\n"
  178. try:
  179. import xml.etree.cElementTree as ET
  180. except ImportError:
  181. import xml.etree.ElementTree as ET
  182. tree = ET.ElementTree(file=xml_file_path)
  183. for i in range(len(all_kinds)):
  184. kind = all_kinds[i][0]
  185. rst_output += get_directives(tree, kind)
  186. return rst_output
  187. def generate_api_inc_files():
  188. """Generate header_file.inc files
  189. with API reference made of doxygen directives
  190. for each header file
  191. specified in the 'INPUT' statement of Doxyfile.
  192. """
  193. if not os.path.isdir(xml_directory_path):
  194. print "Directory %s does not exist!" % xml_directory_path
  195. sys.exit()
  196. if not os.path.exists(inc_directory_path):
  197. os.makedirs(inc_directory_path)
  198. list_to_generate = get_doxyfile_input()
  199. print "Generating 'api_name.inc' files with Doxygen directives"
  200. for header_file_path in list_to_generate.splitlines():
  201. api_name = get_api_name(header_file_path)
  202. inc_file_path = inc_directory_path + "/" + api_name + ".inc"
  203. rst_output = generate_directives(header_file_path)
  204. inc_file = open(inc_file_path, "w")
  205. inc_file.write(rst_output)
  206. inc_file.close()
  207. if __name__ == "__main__":
  208. """The main script that generates
  209. Doxygen directives.
  210. """
  211. # Process command line arguments, if any
  212. if len(sys.argv) > 1:
  213. if not os.path.isdir(xml_directory_path):
  214. print "Directory %s does not exist!" % xml_directory_path
  215. sys.exit()
  216. header_file_path = sys.argv[1]
  217. api_name = get_api_name(header_file_path)
  218. if api_name:
  219. rst_output = generate_directives(header_file_path)
  220. print "Doxygen directives for '%s'" % header_file_path
  221. print
  222. print rst_output
  223. else:
  224. print "Options to execute 'gen-dxd.py' application:"
  225. print "1: $ python gen-dxd.py"
  226. print " Generate API 'header_file.inc' files for headers defined in '%s'" % doxyfile_path
  227. print "2: $ python gen-dxd.py header_file_path"
  228. print " Print out Doxygen directives for a single header file"
  229. print " example: $ python gen-dxd.py mdns/include/mdns.h"
  230. print " NOTE: Run Doxygen first to get XML files for the header file"
  231. sys.exit()
  232. # No command line arguments given
  233. generate_api_inc_files()