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