1818
1919from spack import *
2020import spack .spec
21+ import spack .error
2122import packages
2223import tty
2324import attr
2425import validate
2526import url
2627
27- from spec import Compiler
2828from version import *
2929from multi_function import platform
3030from stage import Stage
@@ -249,7 +249,7 @@ class SomePackage(Package):
249249 # These variables are per-package metadata will be defined by subclasses.
250250 #
251251 """By default a package has no dependencies."""
252- dependencies = []
252+ dependencies = {}
253253
254254 #
255255 # These are default values for instance variables.
@@ -371,21 +371,51 @@ def dependents(self):
371371 return tuple (self ._dependents )
372372
373373
374- def sanity_check (self ):
375- """Ensure that this package and its dependencies don't have conflicting
376- requirements."""
377- deps = sorted (self .all_dependencies , key = lambda d : d .name )
374+ def preorder_traversal (self , visited = None ):
375+ if visited is None :
376+ visited = set ()
377+
378+ if self .name in visited :
379+ return
380+ visited .add (self .name )
381+
382+ yield self
383+ for name , spec in self .dependencies .iteritems ():
384+ for pkg in packages .get (name ).preorder_traversal (visited ):
385+ yield pkg
378386
379387
388+ def validate_dependencies (self ):
389+ """Ensure that this package and its dependencies all have consistent
390+ constraints on them.
391+ """
392+ # This algorithm just attempts to merge all the constraints on the same
393+ # package together, loses information about the source of the conflict.
394+ # What we'd really like to know is exactly which two constraints
395+ # conflict, but that algorithm is more expensive, so we'll do it
396+ # the simple, less informative way for now.
397+ merged = spack .spec .DependencyMap ()
398+
399+ try :
400+ for pkg in self .preorder_traversal ():
401+ for name , spec in pkg .dependencies .iteritems ():
402+ if name not in merged :
403+ merged [name ] = spec .copy ()
404+ else :
405+ merged [name ].constrain (spec )
406+
407+ except spack .spec .UnsatisfiableSpecError , e :
408+ raise InvalidPackageDependencyError (
409+ "Package %s has inconsistent dependency constraints: %s"
410+ % (self .name , e .message ))
411+
380412
381413 @property
382414 @memoized
383415 def all_dependencies (self ):
384416 """Dict(str -> Package) of all transitive dependencies of this package."""
385- all_deps = set (self .dependencies )
386- for dep in self .dependencies :
387- dep_pkg = packages .get (dep .name )
388- all_deps = all_deps .union (dep_pkg .all_dependencies )
417+ all_deps = {name : dep for dep in self .preorder_traversal }
418+ del all_deps [self .name ]
389419 return all_deps
390420
391421
@@ -533,7 +563,7 @@ def setup_install_environment(self):
533563
534564 # Pass along prefixes of dependencies here
535565 path_set (SPACK_DEPENDENCIES ,
536- [dep .package .prefix for dep in self .dependencies ])
566+ [dep .package .prefix for dep in self .dependencies . values () ])
537567
538568 # Install location
539569 os .environ [SPACK_PREFIX ] = self .prefix
@@ -544,7 +574,7 @@ def setup_install_environment(self):
544574
545575 def do_install_dependencies (self ):
546576 # Pass along paths of dependencies here
547- for dep in self .dependencies :
577+ for dep in self .dependencies . values () :
548578 dep .package .do_install ()
549579
550580
@@ -607,7 +637,7 @@ def do_clean_dist(self):
607637 @property
608638 def available_versions (self ):
609639 if not self ._available_versions :
610- self ._available_versions = VersionList ( )
640+ self ._available_versions = ver ([ self . version ] )
611641 try :
612642 # Run curl but grab the mime type from the http headers
613643 listing = spack .curl ('-s' , '-L' , self .list_url , return_output = True )
@@ -617,18 +647,18 @@ def available_versions(self):
617647 for s in strings :
618648 match = re .search (wildcard , s )
619649 if match :
620- self ._available_versions .add (ver (match .group (0 )))
650+ self ._available_versions .add (Version (match .group (0 )))
651+
652+ if not self ._available_versions :
653+ tty .warn ("Found no versions for %s" % self .name ,
654+ "Packate.available_versions may require adding the list_url attribute" ,
655+ "to the package to tell Spack where to look for versions." )
621656
622- except CalledProcessError :
657+ except subprocess . CalledProcessError :
623658 tty .warn ("Fetching %s failed." % self .list_url ,
624659 "Package.available_versions requires an internet connection." ,
625660 "Version list may be incomplete." )
626661
627- if not self ._available_versions :
628- tty .warn ("Found no versions for %s" % self .name ,
629- "Packate.available_versions may require adding the list_url attribute" ,
630- "to the package to tell Spack where to look for versions." )
631- self ._available_versions = [self .version ]
632662 return self ._available_versions
633663
634664
@@ -654,3 +684,10 @@ def __call__(self, *args, **kwargs):
654684 args = (jobs ,) + args
655685
656686 super (MakeExecutable , self ).__call__ (* args , ** kwargs )
687+
688+
689+ class InvalidPackageDependencyError (spack .error .SpackError ):
690+ """Raised when package specification is inconsistent with requirements of
691+ its dependencies."""
692+ def __init__ (self , message ):
693+ super (InvalidPackageDependencyError , self ).__init__ (message )
0 commit comments