1 #-------------------------------------------------------------------------------
3 # | ____| _| |_ / __ \ /\ | \/ |
4 # | |__ _ __ ___ ___ / \| | | | / \ | \ / |
5 # | __| '__/ _ \/ _ ( (| |) ) | | |/ /\ \ | |\/| |
6 # | | | | | __/ __/\_ _/| |__| / ____ \| | | |
7 # |_| |_| \___|\___| |_| \____/_/ \_\_| |_|
9 # FreeFOAM: The Cross-Platform CFD Toolkit
11 # Copyright (C) 2008-2012 Michael Wild <themiwi@users.sf.net>
12 # Gerber van der Graaf <gerber_graaf@users.sf.net>
13 #-------------------------------------------------------------------------------
15 # This file is part of FreeFOAM.
17 # FreeFOAM is free software: you can redistribute it and/or modify it
18 # under the terms of the GNU General Public License as published by the
19 # Free Software Foundation, either version 3 of the License, or (at your
20 # option) any later version.
22 # FreeFOAM is distributed in the hope that it will be useful, but WITHOUT
23 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 # You should have received a copy of the GNU General Public License
28 # along with FreeFOAM. If not, see <http://www.gnu.org/licenses/>.
34 # pass-through filter for doxygen
36 # Special treatment for applications/{solvers,utilities}/*.C
37 # - only keep the first comment block of the C source file
38 # use @cond / @endcond to suppress documenting all classes/variables
40 # Otherwise converts cocoon style sentinel strings into doxygen style
41 # strings. Assumes comment strings are formatted as follows
42 # //- general description
44 # // and even more information
45 # This should be re-formatted as the following
46 # //! general description
49 # and even more information
51 # The intermediate "/*! ... */" block is left-justified to handle
52 # possible verbatim text
54 #-------------------------------------------------------------------------------
56 """Pass through filter module for Doxygen"""
58 from FreeFOAM
.compat
import *
60 class DoxyFiltError(Exception):
61 """Thrown if an error occurs"""
62 def __init__(self
, msg
):
63 Exception.__init
__(self
, msg
)
67 def filter(fileName
, libName
=None, baseDir
=None, topOnly
=False,
69 """Run the filter on `fileName`.
73 fileName The file to filter.
74 libName If the file is part of a library, its name.
75 baseDir Directory to which all paths are made relative. Defaults
77 topOnly Only output first comment block, suppress documentation
79 incWrapperDir Directory containing include-wrappers.
92 if not os
.path
.isfile(fileName
):
93 echo('ERROR: "%s" does not exist or is not a file'%fileName
, file=sys
.stderr
)
96 relFileName
= os
.path
.relpath(fileName
, baseDir
)
98 lines
= open(fileName
, 'rt').readlines()
100 # first transform comment blocks and insert conditionals for applications
102 for i
, l
in enumerate(lines
):
105 # /* starts a comment
106 if re
.match(r
'\s*/\*', l
):
109 output
.append('//! @cond FOAM_IGNORE\n')
113 # */ ends a comment block
114 if re
.search(r
'\*/', l
):
115 output
.extend((l
, '//! @cond FOAM_IGNORE\n'))
118 # print everyting in the first comment block
122 # //- starts a comment block
123 if re
.match(r
'\s*//-', l
):
124 output
.append(re
.sub(r
'//-', '//!', l
))
127 elif re
.match(r
'\s*//', l
):
129 output
.append('/*! ')
132 output
.append(re
.sub(r
'^\s*//(?: )?', '', l
))
141 if topOnly
and state
==2:
142 output
.append('//! @endcond\n')
144 # now process special sections and markers in header comment block
145 iter = enumerate(output
)
146 foundFileCommand
=False
148 # remove License block
149 if re
.match('License', l
):
153 for ll
in output
[i
:]:
159 if re
.search(r
'MA 0211.-130. USA|<http://www.gnu.org/licenses/>', ll
):
161 foundFileCommand
= True
162 output
.insert(i
, ('*/\n/*! @file %s\n'%relFileName
)
165 if re
.search(r
'\*/', ll
):
169 # remove Application and Global blocks
170 if re
.match(r
'(?:Application|Global)\b\s*$', l
):
173 # transform Class, Namespace, Typedef and Primitive blocks
174 m
= re
.match(r
'(?P<type>Class|Namespace|Typedef|Primitive)\b\s*$', l
)
179 r
= r
'@%s \1'%t
.lower()
181 f
= os
.path
.basename(fileName
)
182 if libName
is not None:
183 incName
= "<%s/%s>"%(libName
, f
)
185 incName
= "<FOAM_LOCAL/%s>"%(f)
186 r
+= '\n'+' '.join(('@headerfile', f
, incName
))
188 # unwrap following indented lines
190 for j
, ll
in enumerate(output
[i
:]):
191 if re
.match(r
'\S|^\s*$', ll
):
193 sl
.append(ll
.strip())
194 del output
[i
:i
+len(sl
)-1]
195 output
[i
] = re
.sub(r
'^ {4}(.+)', r
, ' '+''.join(sl
))
196 output
[i
] += len(sl
)*'\n'
198 # transform special headings
199 m
= re
.match(r
'(?P<heading>'+
201 'Description', 'Usage', r
'See\s*Also', 'Author', 'Note', 'To[Dd]o',
202 'Warning', 'Deprecated')) + r
')\b\s*$', l
)
204 t
= m
.group('heading')
205 if t
== 'Description':
208 '<a class="anchor" name="Description"></a> @brief\n')
210 output
[i
] = '@par Usage\n'
211 elif re
.match(r
'See\s*Also', t
):
214 output
[i
] = '@'+t
.lower()+'\n'
216 output
[j
] = re
.sub(r
'\s{4}', '', ll
)
217 if re
.match(r
'\S', output
[j
+1]):
221 # treat SourceFiles list
222 if re
.match(r
'SourceFiles\s*$', l
):
223 output
[i
] = '@par Source files\n<ul><li>%s</li>\n'%relFileName
225 if re
.match(r
'\s*$', ll
):
226 output
[j
-1] = output
[j
-1][:-1] + '</ul>\n'
229 output
[j
] = re
.sub(r
'\s*(\w+\.\w+)',
230 r
' <li>%s/\1</li>'%os.path
.dirname(relFileName
), ll
)
233 # fix #include "partial/path/someFile.H" to read
234 # #include "path/to/partial/path/someFile.H"
235 m
= re
.match(r
'\s*#\s*include\s+"([^"]+\.[CH])"', l
)
238 fullIncFile
= os
.path
.join(os
.path
.dirname(fileName
), incFile
)
239 if os
.path
.isfile(fullIncFile
):
240 output
[i
] = '#include "%s"\n'%os.path
.relpath(fullIncFile
, baseDir
)
242 echo('ERROR: cannot resolve relative include "%s"\n'%incFile
,
246 # fix #include <someLib/someFile.H> to read
247 # #include "relative/path/to/someFile.H" incWrapperDir is not empty.
250 m
= re
.match(r
'\s*#\s*include\s+<(([^>]+)/(\w+\.[CH]))>', l
)
252 incWrapper
= os
.path
.normpath(os
.path
.join(incWrapperDir
, m
.group(1)))
253 if os
.path
.isfile(incWrapper
):
254 # parse the file for the first #include statement
256 for ll
in open(incWrapper
, 'rt'):
257 mm
= re
.match(r
'#include "(.*)"', ll
)
259 realInclude
= mm
.group(1)
263 'ERROR: Failed to parse include wrapper "%s"\n'%
265 realInclude
= os
.path
.normpath(os
.path
.join(
266 incWrapperDir
, m
.group(2), realInclude
))
267 if not os
.path
.isfile(realInclude
):
269 'ERROR: Wrapped include file "%s" does not exist\n'%
271 realInclude
= os
.path
.relpath(realInclude
, baseDir
)
272 output
[i
] = '#include "%s"\n'%realInclude
273 except DoxyFiltError
, e
:
274 echo(str(e
), file=sys
.stderr
)
275 # append @file command if not already present
276 if not foundFileCommand
:
277 output
.append('/*! @file %s */\n'%relFileName
)
278 # finally write to stdout
279 echo(''.join(output
))
281 # ------------------------- vim: set sw=3 sts=3 et: --------------- end-of-file