mdoc.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 # file:        mdoc.py
00003 # author:      Brian Fulkerson and Andrea Vedaldi
00004 # description: MDoc main
00005 
00006 # Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
00007 # All rights reserved.
00008 #
00009 # This file is part of the VLFeat library and is made available under
00010 # the terms of the BSD license (see the COPYING file).
00011 
00012 import sys, os, re, shutil
00013 import subprocess, signal
00014 
00015 from wikidoc import wikidoc
00016 from formatter import Formatter
00017 from optparse import OptionParser
00018 
00019 excludeRegexList = []
00020 format           = 'html'
00021 verb             = 0
00022 sitexml          = ""
00023 
00024 usage = """usage: %prog [options] <basedir> <docdir>
00025 
00026 Takes all .m files in basedir and its subdirectories and converts
00027 them to html documentation, placing the results in docdir."""
00028 
00029 parser = OptionParser(usage=usage)
00030 
00031 parser.add_option(
00032     "-f", "--format",
00033     dest    = "format",
00034     default = "html",
00035     action  = "store",
00036     help    = "specify the output format (html, wiki, web)",
00037     metavar = "STRING")
00038 
00039 parser.add_option(
00040     "-x", "--exclude",
00041     dest    = "excludeList",
00042     action  = "append",
00043     type    = "string",
00044     help    = "exclude files matching the specified regexp")
00045 
00046 parser.add_option(
00047     "-v", "--verbose",
00048     dest    = "verb",
00049     default = False,
00050     action  = "store_true",
00051     help    = "print debug information")
00052 
00053 parser.add_option(
00054     "-t", "--helptoc",
00055     dest    = "helptoc",
00056     default = False,
00057     action  = "store_true",
00058     help    = "create helptoc.xml")
00059 
00060 parser.add_option(
00061     "", "--helptoc-toolbox-name",
00062     dest  = "helptoc_toolbox_name",
00063     default = "Example",
00064     action  = "store",
00065     type = "string",
00066     help    = "helptoc.xml: Toolbox Name")
00067 
00068 # --------------------------------------------------------------------
00069 def runcmd(cmd):
00070 # --------------------------------------------------------------------
00071     """
00072     runcmd(CMD) runs the command CMD. The function waits for the
00073     command to complete and correctly react to Ctrl-C by stopping the
00074     process and raising an exception.
00075     """
00076     try:
00077         p = subprocess.Popen(cmd, shell=True)
00078         sts = os.waitpid(p.pid, 0)
00079     except (KeyboardInterrupt, SystemExit):
00080         os.kill(p.pid, signal.SIGKILL)
00081         raise
00082 
00083 # --------------------------------------------------------------------
00084 class MFile:
00085 # --------------------------------------------------------------------
00086     """
00087     MFile('sub/file.m') represents a MATLAB M-File.
00088     """
00089     def __init__(self, basedir, dirname, name):
00090         funcname = os.path.splitext(name)[0]
00091 
00092         self.funcname = funcname #.upper()
00093         self.path     = os.path.join(basedir, dirname, name)
00094         self.mdocname = funcname.replace(os.path.sep, '_')
00095         self.webname  = funcname.replace(os.path.sep, '.')
00096         self.htmlname = self.mdocname + '.html'
00097         self.wikiname = 'MDoc_' + (os.path.join(dirname, funcname)
00098                                    .upper().replace(os.path.sep, '_'))
00099 
00100         self.prev = None
00101         self.next = None
00102         self.node = None
00103 
00104     def getId (self, format='html'):
00105         if format == 'html':
00106             return self.htmlname
00107         elif format == 'web':
00108             return self.webname
00109         elif format == 'wiki':
00110             return self.wikiname
00111 
00112     def getRef (self, format='html'):
00113         if format == 'html':
00114             return self.htmlname
00115         elif format == 'web':
00116             return '%pathto:' + self.webname + ';'
00117         elif format == 'wiki':
00118             return self.wikiname
00119 
00120     def __cmp__(self, other):
00121         return cmp(self.webname, other.webname)
00122 
00123     def __str__(self):
00124         str  = "MFile: %s\n" % (self.funcname)
00125         str += "  path    : %s\n" % (self.path)
00126         str += "  mdocname: %s\n" % (self.mdocname)
00127         str += "  htmlname: %s\n" % (self.htmlname)
00128         str += "  wikiname: %s\n" % (self.wikiname)
00129         return str
00130 
00131 # --------------------------------------------------------------------
00132 class Node:
00133 # --------------------------------------------------------------------
00134     """
00135     A Node N represents a node in the toolbox hierechy. A node is a
00136     directory in the toolbox hierarchy and contains both M-files and
00137     other sub-directories.
00138     """
00139     def __init__(self, dirname):
00140         self.dirname = dirname
00141         self.children = []
00142         self.mfiles   = []
00143 
00144     def addChildNode(self, node):
00145         "Add a child node (toolbox subdirectory) to this node"
00146         self.children.append(node)
00147 
00148     def addMFile(self, mfile):
00149         "Add a MATLAB M-File to this node"
00150         self.mfiles.append(mfile)
00151         mfile.node = self
00152 
00153     def toIndexPage(self, format='html', depth=1):
00154         "Converts the node hierarchy rooted here into an index."
00155         page = ""
00156         if format == 'html' or format == 'web':
00157             if len(self.mfiles) > 0:
00158                 page += "<b>%s</b>" % (self.dirname.upper())
00159                 page += "<ul>\n"
00160                 for m in self.mfiles:
00161                     page += "<li>"
00162                     page += "<b><a href='%s'>%s</a></b>" % (m.getRef(format),
00163                                                             m.funcname)
00164                     page += " %s" % (m.brief)
00165                     page += "</li>"
00166                 page += "</ul>\n"
00167         elif format == 'wiki':
00168             if len(self.mfiles) > 0:
00169                 if depth > 1:
00170                     page += "=== %s ===\n" % (self.dirname.upper())
00171                 for m in self.mfiles:
00172                     page += "* [[%s|%s]]" % (m.getRef(format), m.funcname)
00173                     page += " %s\n" % (m.brief)
00174         elif format == 'helptoc':
00175             for m in self.mfiles:
00176                 page += "<tocitem target='%s'>%s</tocitem>\n" % (m.getRef('html'),
00177                                                                  m.funcname)
00178         else:
00179             assert False
00180         for n in self.children:
00181             page += n.toIndexPage(format, depth+1)
00182         return page
00183 
00184     def toIndexXML(self):
00185         xml = ""
00186         for m in self.mfiles:
00187             dirname = m.node.dirname.upper()
00188             if len(dirname) > 0:
00189                 xml += \
00190                     "<page id='%s' name='%s' title='%s - %s' hide='yes'>" \
00191                     "<div class='mdoc'>" \
00192                     "<include src='%s'/></div></page>\n" % (m.getId('web'), m.funcname,
00193                                                             dirname,
00194                                                             m.funcname, m.htmlname)
00195             else:
00196                 xml += \
00197                     "<page id='%s' name='%s' title='%s' hide='yes'>" \
00198                     "<div class='mdoc'>" \
00199                     "<include src='%s'/></div></page>\n" % (m.getId('web'), m.funcname,
00200                                                             m.funcname, m.htmlname)
00201 
00202         for n in self.children:
00203             xml += n.toIndexXML() ;
00204         return xml
00205 
00206     def __str__(self):
00207         s = "Node: %s\n" % self.dirname
00208         for m in self.mfiles:
00209             s += m.__str__()
00210         for n in self.children:
00211             s += n.__str__()
00212         return s
00213 
00214 # --------------------------------------------------------------------
00215 def depth_first(node):
00216 # --------------------------------------------------------------------
00217     """
00218     depth_first(NODE) is a generator that implements a depth first
00219     visit of the node hierarchy rooted at NODE.
00220     """
00221     yield node
00222     for n in node.children:
00223         for m in depth_first(n):
00224             yield m
00225     return
00226 
00227 # --------------------------------------------------------------------
00228 def extract(path):
00229 # --------------------------------------------------------------------
00230     """
00231     (BODY, FUNC, BRIEF) = extract(PATH) extracts the comment BODY, the
00232     function name FUNC and the brief description BRIEF from the MATLAB
00233     M-file located at PATH.
00234     """
00235     body         = []
00236     func         = ""
00237     brief        = ""
00238     seenfunction = False
00239     seenpercent  = False
00240 
00241     for l in open(path):
00242 
00243         # Remove whitespace and newline
00244         line = l.strip().lstrip()
00245 
00246         if line.startswith('%'): seenpercent = True
00247         if line.startswith('function'):
00248             seenfunction = True
00249             continue
00250         if not line.startswith('%'):
00251             if (seenfunction and seenpercent) or not seenfunction:
00252                 break
00253             else:
00254                 continue
00255 
00256         # remove leading `%' character
00257         line = line[1:] #
00258         body.append('%s\n' % line)
00259 
00260     # Extract header from body
00261     if len(body) > 0:
00262         head  = body[0]
00263         body  = body[1:]
00264         match = re.match(r"^\s*(\w+)\s*(\S.*)\n$", head)
00265         func  = match.group(1)
00266         brief = match.group(2)
00267 
00268     return (body, func, brief)
00269 
00270 
00271 # --------------------------------------------------------------------
00272 def xscan(baseDir, subDir=''):
00273 # --------------------------------------------------------------------
00274     """
00275     NODE = xscan(BASEDIR) recusrively scans the directory BASEDIR and
00276     construct the toolbox hierarchy rooted at NODE.
00277     """
00278 
00279     node = Node(subDir)
00280     dir = os.listdir(os.path.join(baseDir, subDir))
00281     fileNames  = [f for f in dir if os.path.isfile(
00282                   os.path.join(baseDir, subDir, f))]
00283     subSubDirs = [s for s in dir if os.path.isdir (
00284                   os.path.join(baseDir, subDir, s))]
00285     fileNames.sort()
00286 
00287     # Scan M-FileNames
00288     for fileName in fileNames:
00289         # only m-files
00290         if not os.path.splitext(fileName)[1] == '.m':
00291             continue
00292 
00293         # skip if in the exclude list
00294         exclude = False
00295         for rx in excludeRegexList:
00296             fileRelPath = os.path.join(subDir, fileName)
00297             mo = rx.match(fileRelPath)
00298             if mo and (mo.end() - mo.start() == len(fileRelPath)):
00299                 if verb:
00300                     print "mdoc: excluding ''%s''." % fileRelPath
00301                     exclude = True
00302         if exclude: continue
00303 
00304         node.addMFile(MFile(baseDir, subDir, fileName))
00305 
00306     # Scan sub-directories
00307     for s in subSubDirs:
00308         node.addChildNode(xscan(basedir, os.path.join(subDir, s)))
00309 
00310     return node
00311 
00312 # --------------------------------------------------------------------
00313 def breadCrumb(m):
00314 # --------------------------------------------------------------------
00315     breadcrumb = "<ul class='breadcrumb'>"
00316     if format == 'web':
00317         breadcrumb += "<li><a href='%pathto:matlab;'>Index</a></li>"
00318     else:
00319         breadcrumb += "<li><a href='index.html'>Index</a></li>"
00320     if m.prev: breadcrumb += "<li><a href='%s'>Prev</a></li>" % m.prev.getRef(format)
00321     if m.next: breadcrumb += "<li><a href='%s'>Next</a></li>" % m.next.getRef(format)
00322     breadcrumb += "</ul>"
00323     #breadcrumb += "<span class='path'>%s</span>" % m.node.dirname.upper()
00324 
00325     return breadcrumb
00326 
00327 # --------------------------------------------------------------------
00328 if __name__ == '__main__':
00329 # --------------------------------------------------------------------
00330 
00331     #
00332     # Parse comand line options
00333     #
00334 
00335     (options, args) = parser.parse_args()
00336 
00337     if options.verb: verb = 1
00338     format = options.format
00339     helptoc = options.helptoc
00340 
00341     print options.excludeList
00342     for ex in options.excludeList:
00343         rx = re.compile(ex)
00344         excludeRegexList.append(rx)
00345 
00346     if len(args) != 2:
00347         parser.print_help()
00348         sys.exit(2)
00349 
00350     basedir = args[0]
00351     docdir  = args[1]
00352 
00353     if not basedir.endswith('/'): basedir = basedir + "/"
00354     if not basedir.endswith('/'): docdir  = docdir + "/"
00355 
00356     if verb:
00357         print "mdoc: search path: %s" % basedir
00358         print "mdoc: output path: %s" % docdir
00359         print "mdoc: output format: %s" % format
00360 
00361     #
00362     # Search for mfiles
00363     #
00364 
00365     toolbox = xscan(basedir)
00366 
00367     #
00368     # Extract dictionaries of links and M-Files
00369     #
00370 
00371     linkdict = {}
00372     mfiles   = {}
00373     prev     = None
00374     next     = None
00375     for n in depth_first(toolbox):
00376         for m in n.mfiles:
00377             if prev:
00378                 prev.next = m
00379                 m.prev = prev
00380             prev = m
00381             func = m.funcname.upper()
00382             mfiles[func] = m
00383             linkdict[func] = m.getRef(format)
00384     if verb:
00385         print "mdoc: num mfiles: %d" % (len(mfiles))
00386 
00387     # Create output directory
00388     if not os.access(docdir, os.F_OK):
00389         os.makedirs(docdir)
00390 
00391     # ----------------------------------------------------------------
00392     #                          Extract comment block and run formatter
00393     # ----------------------------------------------------------------
00394     for (func, m) in mfiles.items():
00395 
00396         if format == 'wiki':
00397             outname = m.wikiname
00398         elif format == 'html':
00399             outname = m.htmlname
00400         elif format == 'web':
00401             outname = m.htmlname
00402 
00403         if verb:
00404             print "mdoc: generating %s from %s" % (outname, m.path)
00405 
00406         # extract comment block from file
00407         (lines, func, brief) = extract(m.path)
00408 
00409         m.brief = brief
00410 
00411         # Run formatter
00412         content = ""
00413         if len(lines) > 0:
00414             if format == 'wiki' :
00415                 formatter = Formatter(lines, linkdict, 'wiki')
00416             else:
00417                 formatter = Formatter(lines, linkdict, 'a')
00418 
00419             content = formatter.toDOM().toxml("UTF-8")
00420             content = content[content.find('?>')+2:]
00421 
00422         # add decorations
00423         if not format == 'wiki':
00424             content = breadCrumb(m) + content
00425 
00426         if format == 'web':
00427             content = "<group>\n" + content + "</group>\n"
00428 
00429         # save the result to an html file
00430         if format == 'wiki':
00431             f = open(os.path.join(docdir, m.wikiname), 'w')
00432         else:
00433             f = open(os.path.join(docdir, m.htmlname), 'w')
00434         f.write(content)
00435         f.close()
00436 
00437     # ----------------------------------------------------------------
00438     #                                                  Make index page
00439     # ----------------------------------------------------------------
00440 
00441     page = ""
00442     if format == 'html':
00443         pagename = 'index.html'
00444         page += toolbox.toIndexPage('html')
00445     elif format == 'web':
00446         pagename = 'mdoc.html'
00447         page += '<group>\n' + toolbox.toIndexPage('web') + '</group>\n'
00448     elif format =='wiki' :
00449         pagename = 'MDoc'
00450         page = "== Documentation ==\n"
00451         page += toolbox.toIndexPage('wiki')
00452 
00453     f = open(os.path.join(docdir, pagename), 'w')
00454     f.write(page)
00455     f.close()
00456 
00457     if format == 'web':
00458         f = open(os.path.join(docdir, "mdoc.xml"), 'w')
00459         f.write("<group>"+toolbox.toIndexXML()+"</group>\n")
00460         f.close()
00461 
00462     # ----------------------------------------------------------------
00463     #                                                 Make helptoc.xml
00464     # ----------------------------------------------------------------
00465 
00466     if helptoc:
00467         page = """<?xml version='1.0' encoding="utf-8"?>
00468 <toc version="2.0">
00469     <tocitem target="../index.html">%s
00470         <tocitem target="%s" image="HelpIcon.FUNCTION">Functions
00471 """ % (options.helptoc_toolbox_name, pagename)
00472         page += toolbox.toIndexPage('helptoc')
00473         page += """
00474    </tocitem>
00475  </tocitem>
00476 </toc>
00477 """
00478         f = open(os.path.join(docdir, "helptoc.xml"), 'w')
00479         f.write(page)
00480         f.close()
00481 
00482     # ----------------------------------------------------------------
00483     #                                            Checkin files to wiki
00484     # ----------------------------------------------------------------
00485     def towiki(docdir, pagename):
00486         pagenamewiki = pagename + '.wiki'
00487         runcmd("cd %s ; mvs update %s" % (docdir, pagenamewiki))
00488         if verb:
00489             print "mdoc: converting", pagename, "to", pagenamewiki
00490         wikidoc(os.path.join(docdir, pagenamewiki),
00491                 os.path.join(docdir, pagename))
00492         runcmd("cd %s ; mvs commit -M -m 'Documentation update' %s" % (docdir, pagenamewiki))
00493 
00494     if format == 'wiki' :
00495         try:
00496             towiki(docdir, pagename)
00497         except (KeyboardInterrupt, SystemExit):
00498             sys.exit(1)
00499 
00500         for (func, m) in mfiles.items():
00501             try:
00502                 towiki(docdir, m.wikiname)
00503             except (KeyboardInterrupt, SystemExit):
00504                 sys.exit(1)


libvlfeat
Author(s): Andrea Vedaldi
autogenerated on Thu Jun 6 2019 20:25:51