waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

doxygen.py (7471B)


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