root/branches/fcptr_stable_jun07/doxygen.py

Revision 560, 7.2 kB (checked in by dirk, 2 years ago)

Added doxygen support (first round)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 # SCons Doxygen Bilder
2 #
3 # Copyright (C) 2007  Dirk Reiners
4 #
5 # based on the version from http://www.scons.org/wiki/DoxygenBuilder?highlight=%28doxygen%29
6 #
7 # Astxx, the Asterisk C++ API and Utility Library.
8 # Copyright (C) 2005, 2006  Matthew A. Nicholson
9 # Copyright (C) 2006  Tim Blechmann
10 #
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Lesser General Public
13 # License version 2.1 as published by the Free Software Foundation.
14 #
15 # This library is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 # Lesser General Public License for more details.
19 #
20 # You should have received a copy of the GNU Lesser General Public
21 # License along with this library; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 import os
25 import os.path
26 import glob
27 from fnmatch import fnmatch
28 import subprocess
29
30 def DoxyfileParse(file_contents, file_dir, env):
31    """
32    Parse a Doxygen source file and return a dictionary of all the values.
33    Values will be strings and lists of strings.
34    """
35    data = {}
36
37    import shlex
38    
39    lex = shlex.shlex(instream = file_contents, posix = True)
40    lex.wordchars += "*+./-:@$()"
41    lex.whitespace = lex.whitespace.replace("\n", "")
42    lex.escape = "" 
43      
44    lineno = lex.lineno
45    token = lex.get_token()
46    key = token   # the first token should be a key
47    last_token = ""
48    key_token = True
49    new_data = True
50    
51    def append_data(data, key, new_data, token):
52       if token[:2] == "$(":
53          try:
54             token = env[token[2:-1]]
55          except KeyError:
56             print "ERROR: Variable %s used in Doxygen file is not in environment!" % token
57             token = ""
58          # Convert space-separated list to actual list
59          token = token.split()
60          if len(token):
61             append_data(data, key, new_data, token[0])
62             for i in token[1:]:
63                append_data(data, key, True, i)
64          return
65          
66       if new_data or len(data[key]) == 0:
67          data[key].append(token)
68       else:
69          data[key][-1] += token
70
71    while token:
72       if token in ['\n']:
73          if last_token not in ['\\']:
74             key_token = True
75       elif token in ['\\']:
76          pass
77       elif key_token:
78          key = token
79          key_token = False
80       else:
81          if token == "+=":
82             if not data.has_key(key):
83                data[key] = list()
84          elif token == "=":
85             data[key] = list()
86          elif key == "@INCLUDE":
87          
88             filename = token
89             if not os.path.isabs(filename):
90                filename = os.path.join(file_dir, filename)
91
92             lex.push_source(open(filename), filename)
93          else:
94             append_data( data, key, new_data, token )
95             new_data = True
96
97       last_token = token
98       token = lex.get_token()
99
100       if last_token == '\\' and token != '\n':
101          new_data = False
102          append_data( data, key, new_data, '\\' )
103      
104
105    # compress lists of len 1 into single strings
106    for (k, v) in data.items():
107       if len(v) == 0:
108          data.pop(k)
109
110       # items in the following list will be kept as lists and not converted to strings
111       if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]:
112          continue
113
114       if len(v) == 1:
115          data[k] = v[0]
116
117    return data
118
119 def DoxySourceScan(node, env, path):
120    """
121    Doxygen Doxyfile source scanner.  This should scan the Doxygen file and add
122    any files used to generate docs to the list of source files.
123    """
124    default_file_patterns = [
125       '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx',
126       '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++',
127       '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm',
128       '*.py',
129    ]
130
131    default_exclude_patterns = [
132       '*~',
133    ]
134
135    sources = []
136
137    conf_dir = os.path.dirname(str(node))
138
139    data = DoxyfileParse(node.get_contents(), conf_dir, env)
140
141    if data.get("RECURSIVE", "NO") == "YES":
142       recursive = True
143    else:
144       recursive = False
145
146    file_patterns = data.get("FILE_PATTERNS", default_file_patterns)
147    exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns)
148
149    for node in data.get("INPUT", []):
150       if not os.path.isabs(node):
151          node = os.path.join(conf_dir, node)
152       if os.path.isfile(node):
153          sources.append(node)
154       elif os.path.isdir(node):
155          if recursive:
156             for root, dirs, files in os.walk(node):
157                for f in files:
158                   filename = os.path.join(root, f)
159
160                   pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False)
161                   exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True)
162
163                   if pattern_check and not exclude_check:
164                      sources.append(filename)
165          else:
166             for pattern in file_patterns:
167                sources.extend(glob.glob("/".join([node, pattern])))
168
169    sources = map( lambda path: env.File(path), sources )
170    return sources
171
172
173 def DoxySourceScanCheck(node, env):
174    """Check if we should scan this file"""
175    return os.path.isfile(node.path)
176
177 def DoxyEmitter(source, target, env):
178    """Doxygen Doxyfile emitter"""
179    # possible output formats and their default values and output locations
180    output_formats = {
181       "HTML": ("YES", "html"),
182       "LATEX": ("YES", "latex"),
183       "RTF": ("NO", "rtf"),
184       "MAN": ("YES", "man"),
185       "XML": ("NO", "xml"),
186    }
187
188    data = DoxyfileParse(source[0].get_contents(), os.path.dirname(str(source[0])), env)
189
190    targets = []
191    out_dir = data.get("OUTPUT_DIRECTORY", ".")
192
193    # add our output locations
194    for (k, v) in output_formats.items():
195       if data.get("GENERATE_" + k, v[0]) == "YES":
196          print os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))
197          targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) )
198
199    # don't clobber targets
200    for node in targets:
201       env.Precious(node)
202
203    # set up cleaning stuff
204    for node in targets:
205       env.Clean(node, node)
206
207    return (targets, source)
208
209
210 def DoxyAction(source, target, env):
211    """Doxygen action"""
212    e={}
213    for k,v in env.Dictionary().iteritems():
214       e[k] = str(v)
215    p = subprocess.Popen("cd %s && %s %s" %
216       (os.path.dirname(str(source[0])), env["DOXYGEN"], os.path.basename(str(source[0]))),
217       shell=True, env=e)
218    sts = os.waitpid(p.pid, 0)
219
220 def generate(env):
221    """
222    Add builders and construction variables for the
223    Doxygen tool.  This is currently for Doxygen 1.4.6.
224    """
225    doxyfile_scanner = env.Scanner(
226       DoxySourceScan,
227       "DoxySourceScan",
228       scan_check = DoxySourceScanCheck,
229    )
230
231    doxyfile_builder = env.Builder(
232       action = DoxyAction,
233       emitter = DoxyEmitter,
234       target_factory = env.fs.Entry,
235       single_source = True,
236       source_scanner =  doxyfile_scanner,
237    )
238
239    env.Append(BUILDERS = {
240       'Doxygen': doxyfile_builder,
241    })
242
243    env.AppendUnique(
244       DOXYGEN = 'doxygen',
245    )
246
247 def exists(env):
248    """
249    Make sure doxygen exists.
250    """
251    return env.Detect("doxygen")
Note: See TracBrowser for help on using the browser.