summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew LeGendre <legendre1@llnl.gov>2015-05-29 12:19:57 -0700
committerMatthew LeGendre <legendre1@llnl.gov>2015-10-05 11:36:05 -0700
commitb5c597b31864826050162b358998e240761c5d7e (patch)
tree07b67e7ca06acabf52deee2c78a1484f9c9597fc
parent59f89dd3bee0ee2b668554c537d1d18404108ec4 (diff)
downloadspack-b5c597b31864826050162b358998e240761c5d7e.tar.gz
spack-b5c597b31864826050162b358998e240761c5d7e.tar.bz2
spack-b5c597b31864826050162b358998e240761c5d7e.tar.xz
spack-b5c597b31864826050162b358998e240761c5d7e.zip
Allow specs to be sorted based on preferred packages, versions, compilers, variants and dependencies.
-rw-r--r--lib/spack/spack/__init__.py8
-rw-r--r--lib/spack/spack/config.py12
-rw-r--r--lib/spack/spack/preferred_packages.py172
-rw-r--r--lib/spack/spack/spec.py34
4 files changed, 222 insertions, 4 deletions
diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py
index caa09eb6e0..bd8478fb98 100644
--- a/lib/spack/spack/__init__.py
+++ b/lib/spack/spack/__init__.py
@@ -70,6 +70,14 @@ from spack.directory_layout import YamlDirectoryLayout
install_layout = YamlDirectoryLayout(install_path)
#
+# This controls how packages are sorted when trying to choose
+# the most preferred package. More preferred packages are sorted
+# first.
+#
+from spack.preferred_packages import PreferredPackages
+pkgsort = PreferredPackages()
+
+#
# This controls how things are concretized in spack.
# Replace it with a subclass if you want different
# policies.
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
index 3e91958c2c..dbe225960a 100644
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -89,8 +89,8 @@ off the top-levels of the tree and return subtrees.
import os
import exceptions
import sys
+import copy
-from external.ordereddict import OrderedDict
from llnl.util.lang import memoized
import spack.error
@@ -114,8 +114,7 @@ class _ConfigCategory:
_ConfigCategory('compilers', 'compilers.yaml', True)
_ConfigCategory('mirrors', 'mirrors.yaml', True)
-_ConfigCategory('view', 'views.yaml', True)
-_ConfigCategory('order', 'orders.yaml', True)
+_ConfigCategory('preferred', 'preferred.yaml', True)
"""Names of scopes and their corresponding configuration files."""
config_scopes = [('site', os.path.join(spack.etc_path, 'spack')),
@@ -156,7 +155,7 @@ def _merge_dicts(d1, d2):
"""Recursively merges two configuration trees, with entries
in d2 taking precedence over d1"""
if not d1:
- return d2.copy()
+ return copy.copy(d2)
if not d2:
return d1
@@ -230,6 +229,11 @@ def get_mirror_config():
return get_config('mirrors')
+def get_preferred_config():
+ """Get the preferred configuration from config files"""
+ return get_config('preferred')
+
+
def get_config_scope_dirname(scope):
"""For a scope return the config directory"""
global config_scopes
diff --git a/lib/spack/spack/preferred_packages.py b/lib/spack/spack/preferred_packages.py
new file mode 100644
index 0000000000..248508fe80
--- /dev/null
+++ b/lib/spack/spack/preferred_packages.py
@@ -0,0 +1,172 @@
+##############################################################################
+# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://scalability-llnl.github.io/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License (as published by
+# the Free Software Foundation) version 2.1 dated February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+
+import spack
+from spack.version import *
+
+class PreferredPackages(object):
+ _default_order = {'compiler' : [ 'gcc', 'intel', 'clang', 'pgi', 'xlc' ] }, #Arbitrary, but consistent
+
+ def __init__(self):
+ self.preferred = spack.config.get_preferred_config()
+ self._spec_for_pkgname_cache = {}
+
+ #Given a package name, sort component (e.g, version, compiler, ...), and
+ # a second_key (used by providers), return the list
+ def _order_for_package(self, pkgname, component, second_key):
+ pkglist = [pkgname]
+ pkglist.append('all')
+ for pkg in pkglist:
+ if not pkg in self.preferred:
+ continue
+ orders = self.preferred[pkg]
+ if not type(orders) is dict:
+ continue
+ if not component in orders:
+ continue
+ order = orders[component]
+ if type(order) is dict:
+ if not second_key in order:
+ continue;
+ order = order[second_key]
+ if not type(order) is str:
+ tty.die('Expected version list in preferred config, but got %s' % str(order))
+ order_list = order.split(',')
+ return [s.strip() for s in order_list]
+ return []
+
+
+ # A generic sorting function. Given a package name and sort
+ # component, return less-than-0, 0, or greater-than-0 if
+ # a is respectively less-than, equal to, or greater than b.
+ def _component_compare(self, pkgname, component, a, b, reverse_natural_compare, second_key):
+ orderlist = self._order_for_package(pkgname, component, second_key)
+ a_in_list = str(a) in orderlist
+ b_in_list = str(b) in orderlist
+ if a_in_list and not b_in_list:
+ return -1
+ elif b_in_list and not a_in_list:
+ return 1
+
+ cmp_a = None
+ cmp_b = None
+ reverse = None
+ if not a_in_list and not b_in_list:
+ cmp_a = a
+ cmp_b = b
+ reverse = -1 if reverse_natural_compare else 1
+ else:
+ cmp_a = orderlist.index(str(a))
+ cmp_b = orderlist.index(str(b))
+ reverse = 1
+
+ if cmp_a < cmp_b:
+ return -1 * reverse
+ elif cmp_a > cmp_b:
+ return 1 * reverse
+ else:
+ return 0
+
+
+ # A sorting function for specs. Similar to component_compare, but
+ # a and b are considered to match entries in the sorting list if they
+ # satisfy the list component.
+ def _spec_compare(self, pkgname, component, a, b, reverse_natural_compare, second_key):
+ specs = self._spec_for_pkgname(pkgname, component, second_key)
+ a_index = None
+ b_index = None
+ reverse = -1 if reverse_natural_compare else 1
+ for i, cspec in enumerate(specs):
+ if a_index == None and cspec.satisfies(a):
+ a_index = i
+ if b_index:
+ break
+ if b_index == None and cspec.satisfies(b):
+ b_index = i
+ if a_index:
+ break
+
+ if a_index != None and b_index == None: return -1
+ elif a_index == None and b_index != None: return 1
+ elif a_index != None and b_index == a_index: return -1 * cmp(a, b)
+ elif a_index != None and b_index != None and a_index != b_index: return cmp(a_index, b_index)
+ elif a < b: return 1 * reverse
+ elif b < a: return -1 * reverse
+ else: return 0
+
+
+ # Given a sort order specified by the pkgname/component/second_key, return
+ # a list of CompilerSpecs, VersionLists, or Specs for that sorting list.
+ def _spec_for_pkgname(self, pkgname, component, second_key):
+ key = (pkgname, component, second_key)
+ if not key in self._spec_for_pkgname_cache:
+ pkglist = self._order_for_package(pkgname, component, second_key)
+ if not pkglist:
+ if component in self._default_order:
+ pkglist = self._default_order[component]
+ if component == 'compiler':
+ self._spec_for_pkgname_cache[key] = [spack.spec.CompilerSpec(s) for s in pkglist]
+ elif component == 'version':
+ self._spec_for_pkgname_cache[key] = [VersionList(s) for s in pkglist]
+ else:
+ self._spec_for_pkgname_cache[key] = [spack.spec.Spec(s) for s in pkglist]
+ return self._spec_for_pkgname_cache[key]
+
+
+ def provider_compare(self, pkgname, provider_str, a, b):
+ """Return less-than-0, 0, or greater than 0 if a is respecively less-than, equal-to, or
+ greater-than b. A and b are possible implementations of provider_str.
+ One provider is less-than another if it is preferred over the other.
+ For example, provider_compare('scorep', 'mpi', 'mvapich', 'openmpi') would return -1 if
+ mvapich should be preferred over openmpi for scorep."""
+ return self._spec_compare(pkgname, 'providers', a, b, False, provider_str)
+
+
+ def version_compare(self, pkgname, a, b):
+ """Return less-than-0, 0, or greater than 0 if version a of pkgname is
+ respecively less-than, equal-to, or greater-than version b of pkgname.
+ One version is less-than another if it is preferred over the other."""
+ return self._spec_compare(pkgname, 'version', a, b, False, None)
+
+
+ def variant_compare(self, pkgname, a, b):
+ """Return less-than-0, 0, or greater than 0 if variant a of pkgname is
+ respecively less-than, equal-to, or greater-than variant b of pkgname.
+ One variant is less-than another if it is preferred over the other."""
+ return self._component_compare(pkgname, 'variant', a, b, False, None)
+
+
+ def architecture_compare(self, pkgname, a, b):
+ """Return less-than-0, 0, or greater than 0 if architecture a of pkgname is
+ respecively less-than, equal-to, or greater-than architecture b of pkgname.
+ One architecture is less-than another if it is preferred over the other."""
+ return self._component_compare(pkgname, 'architecture', a, b, False, None)
+
+
+ def compiler_compare(self, pkgname, a, b):
+ """Return less-than-0, 0, or greater than 0 if compiler a of pkgname is
+ respecively less-than, equal-to, or greater-than compiler b of pkgname.
+ One compiler is less-than another if it is preferred over the other."""
+ return self._spec_compare(pkgname, 'compiler', a, b, False, None)
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 3b5d16c7a7..83b1416e36 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1653,6 +1653,40 @@ class Spec(object):
return ''.join("^" + dep.format() for dep in self.sorted_deps())
+ def __cmp__(self, other):
+ #Package name sort order is not configurable, always goes alphabetical
+ if self.name != other.name:
+ return cmp(self.name, other.name)
+
+ #Package version is second in compare order
+ pkgname = self.name
+ if self.versions != other.versions:
+ return spack.pkgsort.version_compare(pkgname,
+ self.versions, other.versions)
+
+ #Compiler is third
+ if self.compiler != other.compiler:
+ return spack.pkgsort.compiler_compare(pkgname,
+ self.compiler, other.compiler)
+
+ #Variants
+ if self.variants != other.variants:
+ return spack.pkgsort.variant_compare(pkgname,
+ self.variants, other.variants)
+
+ #Architecture
+ if self.architecture != other.architecture:
+ return spack.pkgsort.architecture_compare(pkgname,
+ self.architecture, other.architecture)
+
+ #Dependency is not configurable
+ if self.dep_hash() != other.dep_hash():
+ return -1 if self.dep_hash() < other.dep_hash() else 1
+
+ #Equal specs
+ return 0
+
+
def __str__(self):
return self.format() + self.dep_string()