diff options
Diffstat (limited to 'lib/spack/spack/spec.py')
-rw-r--r-- | lib/spack/spack/spec.py | 100 |
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([ |