summaryrefslogtreecommitdiff
path: root/lib/spack/spack/spec.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/spec.py')
-rw-r--r--lib/spack/spack/spec.py100
1 files changed, 75 insertions, 25 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index a424dc60c9..f0ae6a1431 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -359,13 +359,11 @@ class ArchSpec(object):
return False
# Check target
- return self._satisfies_target(other.target, strict=strict)
+ return self.target_satisfies(other, strict=strict)
- def _satisfies_target(self, other_target, strict):
- self_target = self.target
-
- need_to_check = bool(other_target) if strict or self.concrete \
- else bool(other_target and self_target)
+ def target_satisfies(self, other, strict):
+ need_to_check = bool(other.target) if strict or self.concrete \
+ else bool(other.target and self.target)
# If there's no need to check we are fine
if not need_to_check:
@@ -375,24 +373,68 @@ class ArchSpec(object):
if self.target is None:
return False
- for target_range in str(other_target).split(','):
- t_min, sep, t_max = target_range.partition(':')
-
- # Checking against a single specific target
- if not sep and self_target == t_min:
- return True
-
- if not sep and self_target != t_min:
- return False
+ return bool(self.target_intersection(other))
- # Check against a range
- min_ok = self_target.microarchitecture >= t_min if t_min else True
- max_ok = self_target.microarchitecture <= t_max if t_max else True
+ def target_constrain(self, other):
+ if not other.target_satisfies(self, strict=False):
+ raise UnsatisfiableArchitectureSpecError(self, other)
- if min_ok and max_ok:
- return True
+ if self.target_concrete:
+ return False
+ elif other.target_concrete:
+ self.target = other.target
+ return True
- return False
+ # Compute the intersection of every combination of ranges in the lists
+ results = self.target_intersection(other)
+ # Do we need to dedupe here?
+ self.target = ','.join(results)
+
+ def target_intersection(self, other):
+ results = []
+
+ if not self.target or not other.target:
+ return results
+
+ for s_target_range in str(self.target).split(','):
+ s_min, s_sep, s_max = s_target_range.partition(':')
+ for o_target_range in str(other.target).split(','):
+ o_min, o_sep, o_max = o_target_range.partition(':')
+
+ if not s_sep:
+ # s_target_range is a concrete target
+ # get a microarchitecture reference for at least one side
+ # of each comparison so we can use archspec comparators
+ s_comp = spack.architecture.Target(s_min).microarchitecture
+ if not o_sep:
+ if s_min == o_min:
+ results.append(s_min)
+ elif (not o_min or s_comp >= o_min) and (
+ not o_max or s_comp <= o_max):
+ results.append(s_min)
+ elif not o_sep:
+ # "cast" to microarchitecture
+ o_comp = spack.architecture.Target(o_min).microarchitecture
+ if (not s_min or o_comp >= s_min) and (
+ not s_max or o_comp <= s_max):
+ results.append(o_min)
+ else:
+ # Take intersection of two ranges
+ # Lots of comparisons needed
+ _s_min = spack.architecture.Target(s_min).microarchitecture
+ _s_max = spack.architecture.Target(s_max).microarchitecture
+ _o_min = spack.architecture.Target(o_min).microarchitecture
+ _o_max = spack.architecture.Target(o_max).microarchitecture
+
+ n_min = s_min if _s_min >= _o_min else o_min
+ n_max = s_max if _s_max <= _o_max else o_max
+ _n_min = spack.architecture.Target(n_min).microarchitecture
+ _n_max = spack.architecture.Target(n_max).microarchitecture
+ if _n_min == _n_max:
+ results.append(n_min)
+ elif not n_min or not n_max or _n_min < _n_max:
+ results.append('%s:%s' % (n_min, n_max))
+ return results
def constrain(self, other):
"""Projects all architecture fields that are specified in the given
@@ -409,16 +451,18 @@ class ArchSpec(object):
"""
other = self._autospec(other)
- if not self.satisfies(other):
- raise UnsatisfiableArchitectureSpecError(self, other)
+ if not other.satisfies(self):
+ raise UnsatisfiableArchitectureSpecError(other, self)
constrained = False
- for attr in ('platform', 'os', 'target'):
+ for attr in ('platform', 'os'):
svalue, ovalue = getattr(self, attr), getattr(other, attr)
if svalue is None and ovalue is not None:
setattr(self, attr, ovalue)
constrained = True
+ self.target_constrain(other)
+
return constrained
def copy(self):
@@ -431,7 +475,13 @@ class ArchSpec(object):
def concrete(self):
"""True if the spec is concrete, False otherwise"""
# return all(v for k, v in six.iteritems(self.to_cmp_dict()))
- return self.platform and self.os and self.target
+ return (self.platform and self.os and self.target and
+ self.target_concrete)
+
+ @property
+ def target_concrete(self):
+ """True if the target is not a range or list."""
+ return ':' not in str(self.target) and ',' not in str(self.target)
def to_dict(self):
d = syaml.syaml_dict([