00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 import os
00036 import traceback
00037 import sys
00038 from subprocess import Popen, PIPE
00039 import yaml
00040 
00041 import roslib.packages
00042 import roslib.manifest 
00043 import roslib.rospack 
00044 import roslib.stacks
00045 import roslib.stack_manifest
00046 
00047 class RosdocContext(object):
00048     
00049     def __init__(self, name, docdir, package_filters=None, path_filters=None):
00050         self.name = name
00051         self.package_filters = package_filters
00052         self.path_filters = []
00053         if path_filters:
00054             for p in path_filters.split(os.pathsep):
00055                 if not p:
00056                     continue
00057                 if not p.endswith(os.sep):
00058                     p = p + os.sep
00059                 self.path_filters.append(p)
00060         self.docdir = docdir
00061 
00062         
00063         self.packages = {}
00064         self.stacks = {}        
00065         self.external_docs = {}
00066         self.manifests = {}
00067         self.stack_manifests = {}
00068 
00069         
00070         self.quiet = False
00071         
00072         self.timings = {}
00073 
00074         
00075         self.rd_configs = {} 
00076 
00077         self.template_dir = None
00078 
00079     def has_builder(self, package, builder):
00080         """
00081         @return: True if package is configured to use builder. NOTE:
00082         if there is no config, package is assumed to define a doxygen
00083         builder
00084         @rtype: bool
00085         """
00086         rd_config = self.rd_configs.get(package, None)
00087         if not rd_config:
00088             return builder == 'doxygen'
00089         if type(rd_config) != list:
00090             sys.stderr.write("WARNING: package [%s] has an invalid rosdoc config\n"%(package))
00091             return False            
00092         try:
00093             return len([d for d in rd_config if d['builder'] == builder]) > 0
00094         except KeyError:
00095             sys.stderr.write("config file for [%s] is invalid, missing required 'builder' key\n"%(package))
00096             return False
00097         except:
00098             sys.stderr.write("config file for [%s] is invalid\n"%(package))
00099             return False
00100             
00101     def should_document(self, package):
00102         """
00103         @return: True if package should be documented
00104         @rtype: bool
00105         """
00106         if not package in self.packages:
00107             return False
00108         
00109         if self.package_filters:
00110             return package in self.package_filters
00111         
00112         if self.path_filters:
00113             package_path = self.packages[package]
00114             if not [p for p in self.path_filters if package_path.startswith(p)]:
00115                 return False
00116         
00117         return True
00118 
00119     def init(self):
00120         if not self.quiet:
00121             print "initializing rosdoc context:\n\tpackage filters: %s\n\tpath filters: %s"%(self.package_filters, self.path_filters)
00122         
00123         rosdoc_dir = roslib.packages.get_pkg_dir('rosdoc')
00124         self.template_dir = os.path.join(rosdoc_dir, 'templates')
00125 
00126         
00127         rospack_list = roslib.rospack.rospackexec(['list']).split('\n')
00128         rospack_list = [x.split(' ') for x in rospack_list if ' ' in x]
00129 
00130         
00131         
00132         
00133         
00134         packages = self.packages
00135         for package, path in rospack_list:
00136             packages[package] = path
00137 
00138         
00139         stack_manifests = self.stack_manifests
00140         rosstack_list = roslib.rospack.rosstackexec(['list']).split('\n')
00141         rosstack_list = [x.split(' ') for x in rosstack_list if ' ' in x]
00142         for stack, path in rosstack_list:
00143 
00144             f = os.path.join(path, roslib.stack_manifest.STACK_FILE)
00145             try:
00146                 stack_manifests[stack] = roslib.stack_manifest.parse_file(f)
00147             except:
00148                 traceback.print_exc()
00149                 print >> sys.stderr, "WARN: stack '%s' does not have a valid stack.xml file, manifest information will not be included in docs"%stack
00150 
00151         self.doc_packages = [p for p in packages if self.should_document(p)]
00152         self._crawl_deps()
00153         
00154     def _crawl_deps(self):
00155         """
00156         Crawl manifest.xml dependencies
00157         """
00158         external_docs = self.external_docs
00159         manifests = self.manifests
00160         rd_configs = self.rd_configs
00161 
00162         stacks = self.stacks = {}
00163 
00164         
00165         bad = []
00166         for package, path in self.packages.iteritems():
00167 
00168             
00169             if self.should_document(package):
00170                 if not self.quiet:
00171                     print "+package[%s]"%(package)
00172                 stack = roslib.stacks.stack_of(package) or ''
00173                 if stack and stack not in stacks:
00174                     
00175                     try:
00176                         p = roslib.stacks.get_stack_dir(stack)
00177                         stacks[stack] = p
00178                     except:
00179                         sys.stderr.write("cannot locate directory of stack [%s]\n"%(stack))
00180                 
00181             f = os.path.join(path, roslib.manifest.MANIFEST_FILE)
00182             try:
00183                 manifests[package] = m = roslib.manifest.parse_file(f)
00184 
00185                 if self.should_document(package):
00186                     
00187                     
00188 
00189                     
00190                     
00191                     
00192                     for e in m.get_export('doxymaker', 'external'):
00193                         external_docs[package] = e
00194                     for e in m.get_export('rosdoc', 'external'):
00195                         external_docs[package] = e
00196 
00197                     
00198                     
00199                     for e in m.get_export('rosdoc', 'config'):
00200                         try:
00201                             e = e.replace('${prefix}', path)
00202                             config_p = os.path.join(path, e)
00203                             with open(config_p, 'r') as config_f:
00204                                 rd_configs[package] = yaml.load(config_f)
00205                         except Exception as e:
00206                             sys.stderr.write("ERROR: unable to load rosdoc config file [%s]: %s\n"%(config_p, str(e)))
00207                     
00208             except:
00209                 if self.should_document(package):
00210                     sys.stderr.write("WARN: Package '%s' does not have a valid manifest.xml file, manifest information will not be included in docs\n"%(package))
00211                 bad.append(package)
00212 
00213         for b in bad:
00214             if b in self.packages:
00215                 del self.packages[b]
00216         stack_manifests = self.stack_manifests
00217         for stack, path in stacks.iteritems():
00218             if not self.quiet:
00219                 print "+stack[%s]"%(stack)
00220 
00221 def compute_relative(src, target):
00222     s1, s2 = [p.split(os.sep) for p in [src, target]]
00223     
00224     s1, s2 = filter(lambda x: x, s1), filter(lambda x: x, s2)
00225     i = 0
00226     while i < min(len(s1), len(s2)):
00227         if s1[i] != s2[i]:
00228             break
00229         i+=1
00230     rel = ['..' for d in s1[i:]] + s2[i:]
00231     return os.sep.join(rel)
00232 
00233 def html_path(package, docdir):
00234     return os.path.join(docdir, package, 'html')
00235 
00236 
00237 
00238 
00239 
00240 _TEMPLATES_DIR = 'templates'
00241 
00242 def load_tmpl(filename):
00243     filename = os.path.join(roslib.packages.get_pkg_dir('rosdoc'), _TEMPLATES_DIR, filename)
00244     if not os.path.isfile(filename):
00245         sys.stderr.write("Cannot locate template file '%s'\n"%(filename))
00246         sys.exit(1)
00247     with open(filename, 'r') as f:
00248         str = f.read()
00249         if not str:
00250             sys.stderr.write("Template file '%s' is empty\n"%(filename))
00251             sys.exit(1)
00252         return str
00253 
00254 def instantiate_template(tmpl, vars):
00255     for k, v in vars.iteritems():
00256         tmpl = tmpl.replace(k, str(v).encode('utf-8'))
00257     return tmpl
00258