jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
6.6KB

  1. #! /usr/bin/env python
  2. # encoding: UTF-8
  3. # Thomas Nagy 2008-2010 (ita)
  4. """
  5. Doxygen support
  6. Variables passed to bld():
  7. * doxyfile -- the Doxyfile to use
  8. * doxy_tar -- destination archive for generated documentation (if desired)
  9. * install_path -- where to install the documentation
  10. * pars -- dictionary overriding doxygen configuration settings
  11. When using this tool, the wscript will look like:
  12. def options(opt):
  13. opt.load('doxygen')
  14. def configure(conf):
  15. conf.load('doxygen')
  16. # check conf.env.DOXYGEN, if it is mandatory
  17. def build(bld):
  18. if bld.env.DOXYGEN:
  19. bld(features="doxygen", doxyfile='Doxyfile', ...)
  20. """
  21. from fnmatch import fnmatchcase
  22. import os, os.path, re, stat
  23. from waflib import Task, Utils, Node, Logs, Errors
  24. from waflib.TaskGen import feature
  25. DOXY_STR = '"${DOXYGEN}" - '
  26. DOXY_FMTS = 'html latex man rft xml'.split()
  27. DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
  28. c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
  29. inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
  30. '''.split())
  31. re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
  32. re_nl = re.compile('\r*\n', re.M)
  33. def parse_doxy(txt):
  34. tbl = {}
  35. txt = re_rl.sub('', txt)
  36. lines = re_nl.split(txt)
  37. for x in lines:
  38. x = x.strip()
  39. if not x or x.startswith('#') or x.find('=') < 0:
  40. continue
  41. if x.find('+=') >= 0:
  42. tmp = x.split('+=')
  43. key = tmp[0].strip()
  44. if key in tbl:
  45. tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
  46. else:
  47. tbl[key] = '+='.join(tmp[1:]).strip()
  48. else:
  49. tmp = x.split('=')
  50. tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
  51. return tbl
  52. class doxygen(Task.Task):
  53. vars = ['DOXYGEN', 'DOXYFLAGS']
  54. color = 'BLUE'
  55. def runnable_status(self):
  56. '''
  57. self.pars are populated in runnable_status - because this function is being
  58. run *before* both self.pars "consumers" - scan() and run()
  59. set output_dir (node) for the output
  60. '''
  61. for x in self.run_after:
  62. if not x.hasrun:
  63. return Task.ASK_LATER
  64. if not getattr(self, 'pars', None):
  65. txt = self.inputs[0].read()
  66. self.pars = parse_doxy(txt)
  67. if self.pars.get('OUTPUT_DIRECTORY'):
  68. # Use the path parsed from the Doxyfile as an absolute path
  69. output_node = self.inputs[0].parent.get_bld().make_node(self.pars['OUTPUT_DIRECTORY'])
  70. else:
  71. # If no OUTPUT_PATH was specified in the Doxyfile, build path from the Doxyfile name + '.doxy'
  72. output_node = self.inputs[0].parent.get_bld().make_node(self.inputs[0].name + '.doxy')
  73. output_node.mkdir()
  74. self.pars['OUTPUT_DIRECTORY'] = output_node.abspath()
  75. # Override with any parameters passed to the task generator
  76. if getattr(self.generator, 'pars', None):
  77. for k, v in self.generator.pars.iteritems():
  78. self.pars[k] = v
  79. self.doxy_inputs = getattr(self, 'doxy_inputs', [])
  80. if not self.pars.get('INPUT'):
  81. self.doxy_inputs.append(self.inputs[0].parent)
  82. else:
  83. for i in self.pars.get('INPUT').split():
  84. if os.path.isabs(i):
  85. node = self.generator.bld.root.find_node(i)
  86. else:
  87. node = self.inputs[0].parent.find_node(i)
  88. if not node:
  89. self.generator.bld.fatal('Could not find the doxygen input %r' % i)
  90. self.doxy_inputs.append(node)
  91. if not getattr(self, 'output_dir', None):
  92. bld = self.generator.bld
  93. # Output path is always an absolute path as it was transformed above.
  94. self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
  95. self.signature()
  96. return Task.Task.runnable_status(self)
  97. def scan(self):
  98. exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
  99. file_patterns = self.pars.get('FILE_PATTERNS','').split()
  100. if not file_patterns:
  101. file_patterns = DOXY_FILE_PATTERNS
  102. if self.pars.get('RECURSIVE') == 'YES':
  103. file_patterns = ["**/%s" % pattern for pattern in file_patterns]
  104. nodes = []
  105. names = []
  106. for node in self.doxy_inputs:
  107. if os.path.isdir(node.abspath()):
  108. for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
  109. nodes.append(m)
  110. else:
  111. nodes.append(node)
  112. return (nodes, names)
  113. def run(self):
  114. dct = self.pars.copy()
  115. code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
  116. code = code.encode() # for python 3
  117. #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
  118. cmd = Utils.subst_vars(DOXY_STR, self.env)
  119. env = self.env.env or None
  120. proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.inputs[0].parent.abspath())
  121. proc.communicate(code)
  122. return proc.returncode
  123. def post_run(self):
  124. nodes = self.output_dir.ant_glob('**/*', quiet=True)
  125. for x in nodes:
  126. x.sig = Utils.h_file(x.abspath())
  127. self.outputs += nodes
  128. if getattr(self.generator, 'install_path', None):
  129. if not getattr(self.generator, 'doxy_tar', None):
  130. self.generator.bld.install_files(self.generator.install_path,
  131. self.outputs,
  132. postpone=False,
  133. cwd=self.output_dir,
  134. relative_trick=True)
  135. return Task.Task.post_run(self)
  136. class tar(Task.Task):
  137. "quick tar creation"
  138. run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
  139. color = 'RED'
  140. after = ['doxygen']
  141. def runnable_status(self):
  142. for x in getattr(self, 'input_tasks', []):
  143. if not x.hasrun:
  144. return Task.ASK_LATER
  145. if not getattr(self, 'tar_done_adding', None):
  146. # execute this only once
  147. self.tar_done_adding = True
  148. for x in getattr(self, 'input_tasks', []):
  149. self.set_inputs(x.outputs)
  150. if not self.inputs:
  151. return Task.SKIP_ME
  152. return Task.Task.runnable_status(self)
  153. def __str__(self):
  154. tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
  155. return '%s: %s\n' % (self.__class__.__name__, tgt_str)
  156. @feature('doxygen')
  157. def process_doxy(self):
  158. if not getattr(self, 'doxyfile', None):
  159. self.generator.bld.fatal('no doxyfile??')
  160. node = self.doxyfile
  161. if not isinstance(node, Node.Node):
  162. node = self.path.find_resource(node)
  163. if not node:
  164. raise ValueError('doxygen file not found')
  165. # the task instance
  166. dsk = self.create_task('doxygen', node)
  167. if getattr(self, 'doxy_tar', None):
  168. tsk = self.create_task('tar')
  169. tsk.input_tasks = [dsk]
  170. tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
  171. if self.doxy_tar.endswith('bz2'):
  172. tsk.env['TAROPTS'] = ['cjf']
  173. elif self.doxy_tar.endswith('gz'):
  174. tsk.env['TAROPTS'] = ['czf']
  175. else:
  176. tsk.env['TAROPTS'] = ['cf']
  177. if getattr(self, 'install_path', None):
  178. self.bld.install_files(self.install_path, tsk.outputs)
  179. def configure(conf):
  180. '''
  181. Check if doxygen and tar commands are present in the system
  182. If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
  183. variables will be set. Detection can be controlled by setting DOXYGEN and
  184. TAR environmental variables.
  185. '''
  186. conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
  187. conf.find_program('tar', var='TAR', mandatory=False)