Alexander Afanasyev | a1ae0a1 | 2014-01-28 15:21:02 -0800 | [diff] [blame^] | 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 | |
| 12 | ported from waf 1.5 (incomplete) |
| 13 | """ |
| 14 | |
| 15 | from fnmatch import fnmatchcase |
| 16 | import os, os.path, re, stat |
| 17 | from waflib import Task, Utils, Node, Logs |
| 18 | from waflib.TaskGen import feature |
| 19 | |
| 20 | DOXY_STR = '${DOXYGEN} - ' |
| 21 | DOXY_FMTS = 'html latex man rft xml'.split() |
| 22 | DOXY_FILE_PATTERNS = '*.' + ' *.'.join(''' |
| 23 | c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3 |
| 24 | inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx |
| 25 | '''.split()) |
| 26 | |
| 27 | re_rl = re.compile('\\\\\r*\n', re.MULTILINE) |
| 28 | re_nl = re.compile('\r*\n', re.M) |
| 29 | def parse_doxy(txt): |
| 30 | tbl = {} |
| 31 | txt = re_rl.sub('', txt) |
| 32 | lines = re_nl.split(txt) |
| 33 | for x in lines: |
| 34 | x = x.strip() |
| 35 | if not x or x.startswith('#') or x.find('=') < 0: |
| 36 | continue |
| 37 | if x.find('+=') >= 0: |
| 38 | tmp = x.split('+=') |
| 39 | key = tmp[0].strip() |
| 40 | if key in tbl: |
| 41 | tbl[key] += ' ' + '+='.join(tmp[1:]).strip() |
| 42 | else: |
| 43 | tbl[key] = '+='.join(tmp[1:]).strip() |
| 44 | else: |
| 45 | tmp = x.split('=') |
| 46 | tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip() |
| 47 | return tbl |
| 48 | |
| 49 | class doxygen(Task.Task): |
| 50 | vars = ['DOXYGEN', 'DOXYFLAGS'] |
| 51 | color = 'BLUE' |
| 52 | |
| 53 | def runnable_status(self): |
| 54 | ''' |
| 55 | self.pars are populated in runnable_status - because this function is being |
| 56 | run *before* both self.pars "consumers" - scan() and run() |
| 57 | |
| 58 | set output_dir (node) for the output |
| 59 | ''' |
| 60 | |
| 61 | for x in self.run_after: |
| 62 | if not x.hasrun: |
| 63 | return Task.ASK_LATER |
| 64 | |
| 65 | if not getattr(self, 'pars', None): |
| 66 | txt = self.inputs[0].read() |
| 67 | self.pars = parse_doxy(txt) |
| 68 | if not self.pars.get('OUTPUT_DIRECTORY'): |
| 69 | self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath() |
| 70 | |
| 71 | self.doxy_inputs = getattr(self, 'doxy_inputs', []) |
| 72 | if not self.pars.get('INPUT'): |
| 73 | self.doxy_inputs.append(self.inputs[0].parent) |
| 74 | else: |
| 75 | for i in self.pars.get('INPUT').split(): |
| 76 | if os.path.isabs(i): |
| 77 | node = self.generator.bld.root.find_node(i) |
| 78 | else: |
| 79 | node = self.generator.path.find_node(i) |
| 80 | if not node: |
| 81 | self.generator.bld.fatal('Could not find the doxygen input %r' % i) |
| 82 | self.doxy_inputs.append(node) |
| 83 | |
| 84 | if not getattr(self, 'output_dir', None): |
| 85 | bld = self.generator.bld |
| 86 | # First try to find an absolute path, then find or declare a relative path |
| 87 | self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY']) |
| 88 | if not self.output_dir: |
| 89 | self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY']) |
| 90 | |
| 91 | self.signature() |
| 92 | return Task.Task.runnable_status(self) |
| 93 | |
| 94 | def scan(self): |
| 95 | if self.pars.get('RECURSIVE') == 'YES': |
| 96 | Logs.warn("Doxygen RECURSIVE dependencies are not supported") |
| 97 | |
| 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 | |
| 103 | nodes = [] |
| 104 | names = [] |
| 105 | for node in self.doxy_inputs: |
| 106 | if os.path.isdir(node.abspath()): |
| 107 | for m in node.ant_glob(file_patterns): |
| 108 | nodes.append(m) |
| 109 | else: |
| 110 | nodes.append(node) |
| 111 | return (nodes, names) |
| 112 | |
| 113 | def run(self): |
| 114 | dct = self.pars.copy() |
| 115 | # TODO will break if paths have spaces |
| 116 | dct['INPUT'] = ' '.join([x.abspath() for x in self.doxy_inputs]) |
| 117 | code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars]) |
| 118 | code = code.encode() # for python 3 |
| 119 | #fmt = DOXY_STR % (self.inputs[0].parent.abspath()) |
| 120 | cmd = Utils.subst_vars(DOXY_STR, self.env) |
| 121 | env = self.env.env or None |
| 122 | proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath()) |
| 123 | proc.communicate(code) |
| 124 | return proc.returncode |
| 125 | |
| 126 | def post_run(self): |
| 127 | nodes = self.output_dir.ant_glob('**/*', quiet=True) |
| 128 | for x in nodes: |
| 129 | x.sig = Utils.h_file(x.abspath()) |
| 130 | self.outputs += nodes |
| 131 | return Task.Task.post_run(self) |
| 132 | |
| 133 | class tar(Task.Task): |
| 134 | "quick tar creation" |
| 135 | run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}' |
| 136 | color = 'RED' |
| 137 | after = ['doxygen'] |
| 138 | def runnable_status(self): |
| 139 | for x in getattr(self, 'input_tasks', []): |
| 140 | if not x.hasrun: |
| 141 | return Task.ASK_LATER |
| 142 | |
| 143 | if not getattr(self, 'tar_done_adding', None): |
| 144 | # execute this only once |
| 145 | self.tar_done_adding = True |
| 146 | for x in getattr(self, 'input_tasks', []): |
| 147 | self.set_inputs(x.outputs) |
| 148 | if not self.inputs: |
| 149 | return Task.SKIP_ME |
| 150 | return Task.Task.runnable_status(self) |
| 151 | |
| 152 | def __str__(self): |
| 153 | tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) |
| 154 | return '%s: %s\n' % (self.__class__.__name__, tgt_str) |
| 155 | |
| 156 | @feature('doxygen') |
| 157 | def process_doxy(self): |
| 158 | if not getattr(self, 'doxyfile', None): |
| 159 | self.generator.bld.fatal('no doxyfile??') |
| 160 | |
| 161 | node = self.doxyfile |
| 162 | if not isinstance(node, Node.Node): |
| 163 | node = self.path.find_resource(node) |
| 164 | if not node: |
| 165 | raise ValueError('doxygen file not found') |
| 166 | |
| 167 | # the task instance |
| 168 | dsk = self.create_task('doxygen', node) |
| 169 | |
| 170 | if getattr(self, 'doxy_tar', None): |
| 171 | tsk = self.create_task('tar') |
| 172 | tsk.input_tasks = [dsk] |
| 173 | tsk.set_outputs(self.path.find_or_declare(self.doxy_tar)) |
| 174 | if self.doxy_tar.endswith('bz2'): |
| 175 | tsk.env['TAROPTS'] = ['cjf'] |
| 176 | elif self.doxy_tar.endswith('gz'): |
| 177 | tsk.env['TAROPTS'] = ['czf'] |
| 178 | else: |
| 179 | tsk.env['TAROPTS'] = ['cf'] |
| 180 | |
| 181 | def configure(conf): |
| 182 | conf.find_program('doxygen', var='DOXYGEN') |
| 183 | conf.find_program('tar', var='TAR') |
| 184 | |