Package rosdep :: Module core
[frames] | no frames]

Source Code for Module rosdep.core

  1  #!/usr/bin/env python 
  2  # Copyright (c) 2009, Willow Garage, Inc. 
  3  # All rights reserved. 
  4  #  
  5  # Redistribution and use in source and binary forms, with or without 
  6  # modification, are permitted provided that the following conditions are met: 
  7  #  
  8  #     * Redistributions of source code must retain the above copyright 
  9  #       notice, this list of conditions and the following disclaimer. 
 10  #     * Redistributions in binary form must reproduce the above copyright 
 11  #       notice, this list of conditions and the following disclaimer in the 
 12  #       documentation and/or other materials provided with the distribution. 
 13  #     * Neither the name of the Willow Garage, Inc. nor the names of its 
 14  #       contributors may be used to endorse or promote products derived from 
 15  #       this software without specific prior written permission. 
 16  #  
 17  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 18  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 19  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 20  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 21  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 22  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 23  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 24  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 25  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 26  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 27  # POSSIBILITY OF SUCH DAMAGE. 
 28   
 29  # Author Tully Foote/tfoote@willowgarage.com 
 30   
 31  #""" 
 32  #Library and command-line tool for calculating rosdeps. 
 33  #""" 
 34   
 35  from __future__ import with_statement 
 36   
 37  import roslib.rospack 
 38  import roslib.stacks 
 39  import roslib.manifest 
 40  import roslib.os_detect 
 41  import os 
 42  import sys 
 43  import subprocess 
 44  import types 
 45  import tempfile 
 46  import yaml 
 47  import time 
 48   
 49     
 50  import rosdep.base_rosdep 
 51  import rosdep.debian as debian 
 52  import rosdep.opensuse as opensuse 
 53  import rosdep.redhat as redhat 
 54  import rosdep.gentoo as gentoo 
 55  import rosdep.macports as macports 
 56  import rosdep.arch as arch 
 57  import rosdep.cygwin as cygwin 
 58  import rosdep.freebsd as freebsd 
 59   
 60   
 61  yaml.add_constructor( 
 62      u'tag:yaml.org,2002:float', 
 63      yaml.constructor.Constructor.construct_yaml_str) 
 64   
65 -class YamlCache:
66 - def __init__(self, os_name, os_version):
67 self.os_name = os_name 68 self.os_version = os_version 69 self._yaml_cache = {} 70 self._rosstack_depends_cache = {} 71 self._expanded_rosdeps = {} 72 self.rp = roslib.packages.ROSPackages()
73
74 - def get_yaml(self, path):
75 if path in self._yaml_cache: 76 return self._yaml_cache[path] 77 78 #print "parsing path", path 79 if os.path.exists(path): 80 try: 81 f = open(path) 82 yaml_text = f.read() 83 f.close() 84 self._yaml_cache[path] = yaml.load(yaml_text) 85 return self._yaml_cache[path] 86 87 except yaml.YAMLError, exc: 88 print >> sys.stderr, "Failed parsing yaml while processing %s\n"%path, exc 89 #sys.exit(1) # not a breaking error 90 self._yaml_cache[path] = {} 91 return {}
92
93 - def get_rosstack_depends(self, stack):
94 if stack in self._rosstack_depends_cache: 95 return self._rosstack_depends_cache[stack] 96 97 self._rosstack_depends_cache[stack] = roslib.rospack.rosstack_depends(stack) 98 return self._rosstack_depends_cache[stack]
99
100 - def get_specific_rosdeps(self, path):
101 if path in self._expanded_rosdeps: 102 return self._expanded_rosdeps[path] 103 104 yaml_dict = self.get_yaml(path) 105 expanded_rosdeps = {} 106 if not yaml_dict: # prevent exception below if rosdep.yaml file was empty #2762 107 return expanded_rosdeps 108 for key in yaml_dict: 109 rosdep_entry = self.get_os_from_yaml(key, yaml_dict[key], path) 110 if not rosdep_entry: # if no match don't do anything 111 continue # matches for loop 112 expanded_rosdeps[key] = rosdep_entry 113 self._expanded_rosdeps[path] = expanded_rosdeps 114 return expanded_rosdeps
115
116 - def get_os_from_yaml(self, rosdep_name, yaml_map, source_path): #source_path is for debugging where errors come from
117 """ 118 @return The os (and version specific if required) local package name 119 """ 120 # See if the version for this OS exists 121 if self.os_name in yaml_map: 122 return self.get_version_from_yaml(rosdep_name, yaml_map[self.os_name], source_path) 123 else: 124 #print >> sys.stderr, "failed to resolve a rule for rosdep(%s) on OS(%s)"%(rosdep_name, self.os_name) 125 return False
126
127 - def get_version_from_yaml(self, rosdep_name, os_specific, source_path):
128 """ 129 @return The os (and version specific if required) local package name 130 """ 131 if type(os_specific) == type("String"): 132 return os_specific 133 else:# it must be a map of versions 134 if self.os_version in os_specific.keys(): 135 return os_specific[self.os_version] 136 #print >> sys.stderr, "failed to find definition of %s for OS(%s) Version(%s) within '''%s'''. Defined in file %s"%(rosdep_name, self.os_name, self.os_version, os_specific, source_path) 137 return False
138 139 140 141 142 143
144 -class RosdepException(Exception):
145 pass
146
147 -class RosdepLookupPackage:
148 """ 149 This is a class for interacting with rosdep.yaml files. It will 150 load all rosdep.yaml files in the current configuration at 151 startup. It has accessors to allow lookups into the rosdep.yaml 152 from rosdep name and returns the string from the yaml file for the 153 appropriate OS/version. 154 155 It uses the OSIndex class for OS detection. 156 """ 157
158 - def __init__(self, os_name, os_version, package, yaml_cache):
159 """ Read all rosdep.yaml files found at the root of stacks in 160 the current environment and build them into a map.""" 161 self.os_name = os_name 162 self.os_version = os_version 163 self.rosdep_map = {} 164 self.rosdep_source = {} 165 self.package = package 166 self.yaml_cache = yaml_cache 167 ## Find all rosdep.yamls here and load them into a map 168 169 170 if package: 171 self.load_for_package(package, yaml_cache.rp)
172 173 174
175 - def load_for_package(self, package, ros_package_proxy):
176 try: 177 rosdep_dependent_packages = ros_package_proxy.depends([package])[package] 178 #print "package", package, "needs", rosdep_dependent_packages 179 except KeyError, ex: 180 print "Depends Failed on package", ex 181 print " The errors was in ", ros_package_proxy.depends([package]) 182 rosdep_dependent_packages = [] 183 #print "Dependents of", package, rosdep_dependent_packages 184 rosdep_dependent_packages.append(package) 185 186 187 paths = set() 188 for p in rosdep_dependent_packages: 189 stack = None 190 try: 191 stack = roslib.stacks.stack_of(p) 192 except roslib.packages.InvalidROSPkgException, ex: 193 print >> sys.stderr, "Failed to find stack for package [%s]"%p 194 pass 195 if stack: 196 try: 197 paths.add( os.path.join(roslib.stacks.get_stack_dir(stack), "rosdep.yaml")) 198 if "ROSDEP_DEBUG" in os.environ: 199 print "loading rosdeps from", os.path.join(roslib.stacks.get_stack_dir(stack), "rosdep.yaml") 200 except AttributeError, ex: 201 print "Stack [%s] could not be found"%(stack) 202 for s in self.yaml_cache.get_rosstack_depends(stack): 203 try: 204 paths.add( os.path.join(roslib.stacks.get_stack_dir(s), "rosdep.yaml")) 205 except AttributeError, ex: 206 print "Stack [%s] dependency of [%s] could not be found"%(s, stack) 207 208 else: 209 try: 210 paths.add( os.path.join(roslib.packages.get_pkg_dir(p), "rosdep.yaml")) 211 if "ROSDEP_DEBUG" in os.environ: 212 print "Package fallback, no parent stack found for package %s: loading rosdeps from"%p, os.path.join(roslib.packages.get_pkg_dir(p), "rosdep.yaml") 213 except roslib.packages.InvalidROSPkgException, ex: 214 print >> sys.stderr, "Failed to load rosdep.yaml for package [%s]:%s"%(p, ex) 215 pass 216 for path in paths: 217 self.insert_map(self.parse_yaml(path), path) 218 if "ROSDEP_DEBUG" in os.environ: 219 print "rosdep loading from file: %s got"%path, self.parse_yaml(path) 220 #print "built map", self.rosdep_map 221 222 # Override with ros_home/rosdep.yaml if present 223 ros_home = roslib.rosenv.get_ros_home() 224 path = os.path.join(ros_home, "rosdep.yaml") 225 self.insert_map(self.parse_yaml(path), path, override=True)
226 227
228 - def insert_map(self, yaml_dict, source_path, override=False):
229 for key in yaml_dict: 230 rosdep_entry = yaml_dict[key] 231 if not rosdep_entry: # if no match don't do anything 232 continue # matches for loop 233 if key in self.rosdep_source: 234 235 236 if override: 237 print >>sys.stderr, "ROSDEP_OVERRIDE: %s being overridden with %s from %s"%(key, yaml_dict[key], source_path) 238 self.rosdep_source[key].append("Overriding with "+source_path) 239 self.rosdep_map[key] = rosdep_entry 240 else: 241 if self.rosdep_map[key] == rosdep_entry: 242 self.rosdep_source[key].append(source_path) 243 #print >> sys.stderr, "DEBUG: Same key found for %s: %s"%(key, self.rosdep_map[key]) 244 pass 245 else: 246 cache_p = self.yaml_cache.get_os_from_yaml(key, yaml_dict[key], source_path) 247 raise RosdepException("""QUITTING: due to conflicting rosdep definitions, please resolve this conflict. 248 Rules for %s do not match: 249 \t%s [%s] 250 \t%s [%s]"""%(key, self.rosdep_map[key], self.rosdep_source[key][0], rosdep_entry, source_path)) 251 252 else: 253 self.rosdep_source[key] = [source_path] 254 self.rosdep_map[key] = rosdep_entry
255 #print "rosdep_map[%s] = %s"%(key, self.rosdep_map[key]) 256 257
258 - def parse_yaml(self, path):
259 return self.yaml_cache.get_specific_rosdeps(path)
260 261 262
263 - def lookup_rosdep(self, rosdep):
264 """ Lookup the OS specific packages or script from the 265 prebuilt maps.""" 266 267 if rosdep in self.rosdep_map: 268 return self.rosdep_map[rosdep] 269 else: 270 print >> sys.stderr, "Failed to find rosdep %s for package %s on OS:%s version:%s"%(rosdep, self.package, self.os_name, self.os_version) 271 return False
272
273 - def get_map(self):
274 return self.rosdep_map
275 276
277 - def get_sources(self, rosdep):
278 if rosdep in self.rosdep_source: 279 return self.rosdep_source[rosdep] 280 else: 281 return []
282 283 284 285 286 287
288 -class Rosdep:
289 - def __init__(self, packages, command = "rosdep", robust = False):
290 os_list = [debian.RosdepTestOS(), debian.Debian(), debian.Ubuntu(), debian.Mint(), opensuse.OpenSuse(), redhat.Fedora(), redhat.Rhel(), arch.Arch(), macports.Macports(), gentoo.Gentoo(), cygwin.Cygwin(), freebsd.FreeBSD()] 291 # Make sure that these classes are all well formed. 292 for o in os_list: 293 if not isinstance(o, rosdep.base_rosdep.RosdepBaseOS): 294 raise RosdepException("Class [%s] not derived from RosdepBaseOS"%o.__class__.__name__) 295 # Detect the OS on which this program is running. 296 self.osi = roslib.os_detect.OSDetect(os_list) 297 self.packages = packages 298 self.rosdeps = roslib.packages.rosdeps_of(packages) 299 rp = roslib.packages.ROSPackages() 300 self.rosdeps = rp.rosdeps(packages) 301 self.robust = robust
302 303 304
305 - def get_rosdep0(self, package):
306 m = roslib.manifest.load_manifest(package) 307 return [d.name for d in m.rosdeps]
308 309
310 - def get_packages_and_scripts(self):
311 if len(self.packages) == 0: 312 return ([], []) 313 native_packages = [] 314 scripts = [] 315 failed_rosdeps = [] 316 yc = YamlCache(self.osi.get_name(), self.osi.get_version()) 317 start_time = time.time() 318 if "ROSDEP_DEBUG" in os.environ: 319 print "Generating package list and scripts for %d rosdeps. This may take a few seconds..."%len(self.packages) 320 for p in self.packages: 321 rdlp = RosdepLookupPackage(self.osi.get_name(), self.osi.get_version(), p, yc) 322 for r in self.rosdeps[p]: 323 specific = rdlp.lookup_rosdep(r) 324 if specific: 325 if len(specific.split('\n')) == 1: 326 for pk in specific.split(): 327 native_packages.append(pk) 328 else: 329 scripts.append(specific) 330 else: 331 failed_rosdeps.append(r) 332 333 if len(failed_rosdeps) > 0: 334 if not self.robust: 335 raise RosdepException("ABORTING: Rosdeps %s could not be resolved"%failed_rosdeps) 336 else: 337 print >> sys.stderr, "WARNING: Rosdeps %s could not be resolved"%failed_rosdeps 338 339 time_delta = (time.time() - start_time) 340 if "ROSDEP_DEBUG" in os.environ: 341 print "Done loading rosdeps in %f seconds, averaging %f per rosdep."%(time_delta, time_delta/len(self.packages)) 342 343 return (list(set(native_packages)), list(set(scripts)))
344
345 - def get_native_packages(self):
346 return get_packages_and_scripts()[0]
347
348 - def generate_script(self, include_duplicates=False, default_yes = False):
349 native_packages, scripts = self.get_packages_and_scripts() 350 undetected = native_packages if include_duplicates else \ 351 self.osi.get_os().strip_detected_packages(native_packages) 352 return "set -o errexit\n" + self.osi.get_os().generate_package_install_command(undetected, default_yes) + \ 353 "\n".join(["\n%s"%sc for sc in scripts])
354
355 - def check(self):
356 native_packages = [] 357 scripts = [] 358 try: 359 native_packages, scripts = self.get_packages_and_scripts() 360 except RosdepException, e: 361 print >> sys.stderr, e 362 pass 363 undetected = self.osi.get_os().strip_detected_packages(native_packages) 364 return_str = "" 365 return_str_scripts = "" 366 if len(undetected) > 0: 367 return_str += "Did not detect packages: %s\n"%undetected 368 if len(scripts) > 0: 369 return_str_scripts += "The following scripts were not tested:\n" 370 for s in scripts: 371 return_str_scripts += s + '\n' 372 return return_str, return_str_scripts
373
374 - def what_needs(self, rosdep_args):
375 packages = [] 376 for p in roslib.packages.list_pkgs(): 377 rosdeps_needed = self.get_rosdep0(p) 378 matches = [r for r in rosdep_args if r in rosdeps_needed] 379 for r in matches: 380 packages.append(p) 381 382 return packages
383
384 - def install(self, include_duplicates, default_yes):
385 with tempfile.NamedTemporaryFile() as fh: 386 script = self.generate_script(include_duplicates, default_yes) 387 fh.write(script) 388 fh.flush() 389 390 print "rosdep executing this script:\n{{{\n%s\n}}}"%script 391 p= subprocess.Popen(['bash', fh.name], stderr=subprocess.PIPE ) 392 (out, err) = p.communicate() 393 if p.returncode != 0: 394 if err: 395 return "rosdep script failed with stderr \n{{{\n%s\n}}}"%err 396 else: 397 return "rosdep script failed without stderr output" 398 else: 399 return None
400
401 - def depdb(self, packages):
402 output = "Rosdep dependencies for operating system %s version %s "%(self.osi.get_name(), self.osi.get_version()) 403 yc = YamlCache(self.osi.get_name(), self.osi.get_version()) 404 for p in packages: 405 output += "\nPACKAGE: %s\n"%p 406 rdlp = RosdepLookupPackage(self.osi.get_name(), self.osi.get_version(), p, yc) 407 map = rdlp.get_map() 408 for k in map: 409 output = output + "<<<< %s -> %s >>>>\n"%(k, map[k]) 410 return output
411
412 - def where_defined(self, rosdeps):
413 output = "" 414 locations = {} 415 rdlp = RosdepLookupPackage(self.osi.get_name(), self.osi.get_version(), None, YamlCache(self.osi.get_name(), self.osi.get_version())) 416 417 for r in rosdeps: 418 locations[r] = set() 419 420 path = os.path.join(roslib.rosenv.get_ros_home(), "rosdep.yaml") 421 rosdep_dict = rdlp.parse_yaml(path) 422 for r in rosdeps: 423 if r in rosdep_dict: 424 locations[r].add("Override:"+path) 425 426 427 for p in roslib.packages.list_pkgs(): 428 path = os.path.join(roslib.packages.get_pkg_dir(p), "rosdep.yaml") 429 rosdep_dict = rdlp.parse_yaml(path) 430 for r in rosdeps: 431 if r in rosdep_dict: 432 addendum = "" 433 if roslib.stacks.stack_of(p): 434 addendum = "<<Unused due to package '%s' being in a stack.]]"%p 435 locations[r].add(">>" + path + addendum) 436 437 438 for s in roslib.stacks.list_stacks(): 439 path = os.path.join(roslib.stacks.get_stack_dir(s), "rosdep.yaml") 440 rosdep_dict = rdlp.parse_yaml(path) 441 for r in rosdeps: 442 if r in rosdep_dict: 443 locations[r].add(path) 444 445 for rd in locations: 446 output += "%s defined in %s"%(rd, locations[rd]) 447 return output
448