BASIS  version 1.2.3 (revision 2104)
doxyfilter-cmake.py
00001 ##############################################################################
00002 # @file  doxyfilter-cmake.py
00003 # @brief Doxygen filter for CMake and CTest scripts.
00004 #
00005 # Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
00006 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007 #
00008 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009 #
00010 # @ingroup Tools
00011 ##############################################################################
00012 
00013 import sys
00014 import re
00015 
00016 if __name__ == "__main__":
00017     # parse arguments
00018     if len (sys.argv) != 2:
00019         sys.stderr.write ("No file specified to process!\n")
00020         sys.exit (1)
00021     fileName = sys.argv [1]
00022     # package name
00023     packageName = '__Pkg__'
00024     m = re.search ('(^|/)(?P<pkg>\w+)Config(Version)?\.cmake', fileName)
00025     if m: packageName = m.group('pkg')
00026     else:
00027         m = re.search ('(^|/)(?P<pkg>\w+)Use\.cmake', fileName)
00028         if m: packageName = m.group('pkg')
00029     # open input file
00030     f = open (fileName, 'r')
00031     if not f:
00032         sys.stderr.write ("Failed to open file " + fileName + " for reading!\n")
00033         sys.exit (1)
00034     # compile regular expressions
00035     reInclude       = re.compile (r"include\s*\((?P<module>.+)\)\s*$")
00036     reFunctionStart = re.compile (r"function\s*\((?P<name>\w+)(?P<args>.*)\)\s*$")
00037     reFunctionEnd   = re.compile (r"endfunction\s*\(.*\)\s*$")
00038     reMacroStart    = re.compile (r"macro\s*\((?P<name>\w+)(?P<args>.*)\)\s*$")
00039     reMacroEnd      = re.compile (r"endmacro\s*\(.*\)\s*$")
00040     reSetStart      = re.compile (r"(?P<cmd>set|basis_set_if_empty|basis_set_script_path|basis_set_config)\s*(\((?P<name>\w*)|$)")
00041     reSetVarName    = re.compile (r"(?P<name>\w+)")
00042     reSetEnd        = re.compile (r".*\)\s*$")
00043     reOptionStart   = re.compile (r"option\s*\((?P<name>\w*)|option\s*$")
00044     reOptionName    = re.compile (r"(?P<name>\w+)")
00045     reOptionEnd     = re.compile (r".*\)\s*$")
00046     reArgs          = re.compile (r"\W+")
00047     reCommentStart  = re.compile (r"##+(?P<comment>.*)")
00048     reCommentLine   = re.compile (r"#+(?P<comment>.*)")
00049     reOptParamDoc   = re.compile (r"[\@\\]param\s*(\[[inout,\s]+\])?\s+(?P<param>ARG(N|V[0-9]?))")
00050     reIfClauseStart = re.compile (r"if\s*\(")
00051     reIfClauseEnd   = re.compile (r"else\s*\(|elseif\s*\(|endif\s*\(")
00052 
00053     # parse line-by-line and output pseudo C++ code to stdout
00054     ifClauseDepth = 0     # current depth of if-clauses
00055     commentDepth  = 0     # if-clause depth where comment was encountered
00056     previousBlock = ''    # name of previous CMake code block
00057     currentBlock  = ''    # name of current CMake code block
00058     currentCmd    = ''    # used in particular to distinguish set() commands
00059     optParams     = []    # documented optional function/macro parameters
00060     for line in f:
00061         line = line.strip ()
00062         # next comment line,
00063         if currentBlock == 'comment':
00064             m = reCommentLine.match (line)
00065             if m is not None:
00066                 comment = m.group ('comment')
00067                 sys.stdout.write ("///" + comment + "\n")
00068                 m = reOptParamDoc.search (line)
00069                 if m is not None:
00070                     optParams.append (m.group ('param'))
00071                 continue
00072             else:
00073                 previousBlock = currentBlock
00074                 currentBlock = ''
00075                 # continue processing of this (yet unhandled) line
00076         # inside function definition
00077         if currentBlock == 'function':
00078             m = reFunctionEnd.match (line)
00079             if m is not None:
00080                 previousBlock = currentBlock
00081                 currentBlock = ''
00082             sys.stdout.write ("\n")
00083             continue
00084         # inside macro definition
00085         if currentBlock == 'macro':
00086             m = reMacroEnd.match (line)
00087             if m is not None:
00088                 previousBlock = currentBlock
00089                 currentBlock = ''
00090             sys.stdout.write ("\n")
00091             continue
00092         # inside brackets of multi-line set() command
00093         if currentBlock == 'set':
00094             m = reSetEnd.match (line)
00095             if m is not None:
00096                 previousBlock = currentBlock
00097                 currentBlock = ''
00098             sys.stdout.write ("\n")
00099             continue
00100         if currentBlock == 'set-no-name':
00101             m = reSetVarName.match (line)
00102             if m is not None:
00103                 name = m.group ('name')
00104                 if currentCmd == 'basis_set_config':
00105                     name = '_'.join([packageName, name])
00106                 sys.stdout.write (name + ";\n")
00107                 currentBlock = 'set'
00108                 m = reSetEnd.match (line)
00109                 if m is not None:
00110                     previousBlock = currentBlock
00111                     currentBlock = ''
00112             else:
00113                 sys.stdout.wirte ("\n")
00114             continue
00115         # inside brackets of multi-line options command
00116         if currentBlock == 'option':
00117             m = reOptionEnd.match (line)
00118             if m is not None:
00119                 previousBlock = currentBlock
00120                 currentBlock = ''
00121             sys.stdout.write ("\n")
00122             continue
00123         if currentBlock == 'option-no-name':
00124             m = reOptionName.match (line)
00125             if m is not None:
00126                 name = m.group ('name')
00127                 sys.stdout.write ("option " + name + ";\n")
00128                 currentBlock = 'option'
00129                 m = reOptionEnd.match (line)
00130                 if m is not None:
00131                     previousBlock = currentBlock
00132                     currentBlock = ''
00133             else:
00134                 sys.stdout.write ("\n")
00135             continue
00136         # look for new comment block or block following a comment
00137         if currentBlock == '':
00138             currentCmd = ''
00139             # include
00140             m = reInclude.match (line)
00141             if m is not None:
00142                 module = m.group ('module')
00143                 module = module.replace ("\"", "")
00144                 module = module.replace ("${CMAKE_CURRENT_LIST_DIR}/", "")
00145                 module = module.replace ("${BASIS_MODULE_PATH}/", "")
00146                 module = module.replace ("@BASIS_MODULE_PATH@/", "")
00147                 module = module.replace ("${${NS}MODULE_PATH}/", "")
00148                 module = module.replace (" OPTIONAL", "")
00149                 module = module.replace (" NO_POLICY_SCOPE", "")
00150                 sys.stdout.write ("#include \"" + module + "\"\n")
00151                 continue
00152             # enter if-clause
00153             m = reIfClauseStart.match (line)
00154             if m is not None:
00155                 ifClauseDepth = ifClauseDepth + 1
00156                 sys.stdout.write ("\n")
00157                 continue
00158             # leave if-clause
00159             if ifClauseDepth > 0:
00160                 m = reIfClauseEnd.match (line)
00161                 if m is not None:
00162                     ifClauseDepth = ifClauseDepth - 1
00163                     if commentDepth > ifClauseDepth:
00164                         previousBlock = ''
00165                         currentBlock  = ''
00166                     sys.stdout.write ("\n")
00167                     continue
00168             # Doxygen comment
00169             m = reCommentStart.match (line)
00170             if m is not None:
00171                 comment = m.group ('comment')
00172                 sys.stdout.write ("///" + comment + "\n")
00173                 currentBlock = 'comment'
00174                 commentDepth = ifClauseDepth
00175                 m = reOptParamDoc.search (line)
00176                 if m is not None:
00177                     optParams.append (m.group ('param'))
00178                 continue
00179             # if previous block was a Doxygen comment process
00180             # supported following blocks such as variable setting
00181             # and function definition optionally only the one
00182             # inside the if-case of an if-else-clause
00183             if previousBlock == 'comment':
00184                 # function
00185                 m = reFunctionStart.match (line)
00186                 if m is not None:
00187                     name = m.group ('name')
00188                     args = m.group ('args').strip ()
00189                     if args != '':
00190                         argv = reArgs.split (args)
00191                     else:
00192                         argv = []
00193                     sys.stdout.write ("function " + name + " (")
00194                     for i in range (0, len (argv)):
00195                         if i > 0:
00196                             sys.stdout.write (", ")
00197                         sys.stdout.write ("in " + argv [i])
00198                     for i in range (0, len (optParams)):
00199                         if i > 0 or len (argv) > 0:
00200                             sys.stdout.write (", ")
00201                         sys.stdout.write ("in " + optParams [i])
00202                     sys.stdout.write (");\n")
00203                     currentBlock = 'function'
00204                     optParams = []
00205                     continue
00206                 # macro
00207                 m = reMacroStart.match (line)
00208                 if m is not None:
00209                     name = m.group ('name')
00210                     args = m.group ('args').strip ()
00211                     if args != '':
00212                         argv = reArgs.split (args)
00213                     else:
00214                         argv = []
00215                     sys.stdout.write ("macro " + name + " (")
00216                     for i in range (0, len (argv)):
00217                         if i > 0:
00218                             sys.stdout.write (", ")
00219                         sys.stdout.write ("in " + argv [i])
00220                     for i in range (0, len (optParams)):
00221                         if i > 0 or len (argv) > 0:
00222                             sys.stdout.write (", ")
00223                         sys.stdout.write ("in " + optParams [i])
00224                     sys.stdout.write (");\n")
00225                     currentBlock = 'macro'
00226                     optParams = []
00227                     continue
00228                 # setting of global variable/constant
00229                 m = reSetStart.match (line)
00230                 if m is not None:
00231                     currentCmd = m.group ('cmd')
00232                     name       = m.group ('name')
00233                     if name == '':
00234                         currentBlock = 'set-no-name'
00235                         sys.stdout.write ("\n")
00236                         continue
00237                     if currentCmd == 'basis_set_config':
00238                         name = '_'.join([packageName, name])
00239                     sys.stdout.write (name + ";\n")
00240                     m = reSetEnd.match (line)
00241                     if m is None:
00242                         currentBlock = 'set'
00243                     else:
00244                         previousBlock = 'set'
00245                     continue
00246                 # option
00247                 m = reOptionStart.match (line)
00248                 if m is not None:
00249                     name = m.group ('name')
00250                     if name == '':
00251                         currentBlock = 'option-no-name'
00252                         sys.stdout.write ("\n")
00253                         continue
00254                     sys.stdout.write ("option " + name + ";\n")
00255                     m = reOptionEnd.match (line)
00256                     if m is None:
00257                         currentBlock = 'option'
00258                     else:
00259                         previousBlock = 'option'
00260                     continue
00261             if line != '':
00262                 if previousBlock == 'comment':
00263                     # prevent comments that are not associated with any
00264                     # processed block to be merged with subsequent comments
00265                     sys.stdout.write ("class COMMENT_DUMPED_BY_DOXYGEN_FILTER;\n")
00266                 else:
00267                     sys.stdout.write ("\n")
00268                 previousBlock = ''
00269             else:
00270                 sys.stdout.write ("\n")
00271         else:
00272             sys.stdout.write ("\n")
00273     # close input file
00274     f.close ()
00275     # done
00276     sys.exit (0)