gen-dxd.py 9.0 KB

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