refactor: Index conflicts by when spec
				
					
				
			Part 2 of reworking all package metadata to key by `when` conditions.
Changes conflict dictionary structure from this:
    { conflict_spec: [(when_spec, msg), ...] }
to this:
    { when_spec: [(conflict_spec, msg), ...] }
Also attempts to consistently name the variables used to iterate over conflict
dictionaries.
			
			
This commit is contained in:
		| @@ -667,8 +667,8 @@ def _unknown_variants_in_directives(pkgs, error_cls): | ||||
|         pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) | ||||
| 
 | ||||
|         # Check "conflicts" directive | ||||
|         for conflict, triggers in pkg_cls.conflicts.items(): | ||||
|             for trigger, _ in triggers: | ||||
|         for trigger, conflicts in pkg_cls.conflicts.items(): | ||||
|             for conflict, _ in conflicts: | ||||
|                 vrn = spack.spec.Spec(conflict) | ||||
|                 try: | ||||
|                     vrn.constrain(trigger) | ||||
|   | ||||
| @@ -525,7 +525,7 @@ def _depends_on( | ||||
| 
 | ||||
| 
 | ||||
| @directive("conflicts") | ||||
| def conflicts(conflict_spec, when=None, msg=None): | ||||
| def conflicts(conflict_spec: SpecType, when: WhenType = None, msg: Optional[str] = None): | ||||
|     """Allows a package to define a conflict. | ||||
| 
 | ||||
|     Currently, a "conflict" is a concretized configuration that is known | ||||
| @@ -545,16 +545,16 @@ def conflicts(conflict_spec, when=None, msg=None): | ||||
|         msg (str): optional user defined message | ||||
|     """ | ||||
| 
 | ||||
|     def _execute_conflicts(pkg): | ||||
|     def _execute_conflicts(pkg: "spack.package_base.PackageBase"): | ||||
|         # If when is not specified the conflict always holds | ||||
|         when_spec = make_when_spec(when) | ||||
|         if not when_spec: | ||||
|             return | ||||
| 
 | ||||
|         # Save in a list the conflicts and the associated custom messages | ||||
|         when_spec_list = pkg.conflicts.setdefault(conflict_spec, []) | ||||
|         conflict_spec_list = pkg.conflicts.setdefault(when_spec, []) | ||||
|         msg_with_name = f"{pkg.name}: {msg}" if msg is not None else msg | ||||
|         when_spec_list.append((when_spec, msg_with_name)) | ||||
|         conflict_spec_list.append((spack.spec.Spec(conflict_spec), msg_with_name)) | ||||
| 
 | ||||
|     return _execute_conflicts | ||||
| 
 | ||||
|   | ||||
| @@ -560,11 +560,8 @@ class PackageBase(WindowsRPath, PackageViewMixin, metaclass=PackageMeta): | ||||
|     # Declare versions dictionary as placeholder for values. | ||||
|     # This allows analysis tools to correctly interpret the class attributes. | ||||
|     versions: dict | ||||
| 
 | ||||
|     # Same for dependencies | ||||
|     dependencies: Dict["spack.spec.Spec", Dict[str, "spack.dependency.Dependency"]] | ||||
| 
 | ||||
|     # and patches | ||||
|     conflicts: Dict["spack.spec.Spec", List[Tuple["spack.spec.Spec", Optional[str]]]] | ||||
|     patches: Dict["spack.spec.Spec", List["spack.patch.Patch"]] | ||||
| 
 | ||||
|     #: By default, packages are not virtual | ||||
|   | ||||
| @@ -1234,29 +1234,30 @@ def target_ranges(self, spec, single_target_fn): | ||||
|         return [fn.attr("node_target_satisfies", spec.name, target)] | ||||
| 
 | ||||
|     def conflict_rules(self, pkg): | ||||
|         default_msg = "{0}: '{1}' conflicts with '{2}'" | ||||
|         no_constraint_msg = "{0}: conflicts with '{1}'" | ||||
|         for trigger, constraints in pkg.conflicts.items(): | ||||
|             trigger_msg = f"conflict is triggered when {str(trigger)}" | ||||
|             trigger_spec = spack.spec.Spec(trigger) | ||||
|             trigger_id = self.condition( | ||||
|                 trigger_spec, name=trigger_spec.name or pkg.name, msg=trigger_msg | ||||
|             ) | ||||
|         for when_spec, conflict_specs in pkg.conflicts.items(): | ||||
|             when_spec_msg = "conflict constraint %s" % str(when_spec) | ||||
|             when_spec_id = self.condition(when_spec, name=pkg.name, msg=when_spec_msg) | ||||
| 
 | ||||
|             for constraint, conflict_msg in constraints: | ||||
|             for conflict_spec, conflict_msg in conflict_specs: | ||||
|                 conflict_spec = spack.spec.Spec(conflict_spec) | ||||
|                 if conflict_msg is None: | ||||
|                     if constraint == spack.spec.Spec(): | ||||
|                         conflict_msg = no_constraint_msg.format(pkg.name, trigger) | ||||
|                     conflict_msg = f"{pkg.name}: " | ||||
|                     if when_spec == spack.spec.Spec(): | ||||
|                         conflict_msg += f"conflicts with '{conflict_spec}'" | ||||
|                     else: | ||||
|                         conflict_msg = default_msg.format(pkg.name, trigger, constraint) | ||||
|                         conflict_msg += f"'{conflict_spec}' conflicts with '{when_spec}'" | ||||
| 
 | ||||
|                 spec_for_msg = ( | ||||
|                     spack.spec.Spec(pkg.name) if constraint == spack.spec.Spec() else constraint | ||||
|                 spec_for_msg = conflict_spec | ||||
|                 if conflict_spec == spack.spec.Spec(): | ||||
|                     spec_for_msg = spack.spec.Spec(pkg.name) | ||||
|                 conflict_spec_msg = f"conflict is triggered when {str(spec_for_msg)}" | ||||
|                 conflict_spec_id = self.condition( | ||||
|                     conflict_spec, name=conflict_spec.name or pkg.name, msg=conflict_spec_msg | ||||
|                 ) | ||||
|                 constraint_msg = f"conflict applies to spec {str(spec_for_msg)}" | ||||
|                 constraint_id = self.condition(constraint, name=pkg.name, msg=constraint_msg) | ||||
|                 self.gen.fact( | ||||
|                     fn.pkg_fact(pkg.name, fn.conflict(trigger_id, constraint_id, conflict_msg)) | ||||
|                     fn.pkg_fact( | ||||
|                         pkg.name, fn.conflict(conflict_spec_id, when_spec_id, conflict_msg) | ||||
|                     ) | ||||
|                 ) | ||||
|                 self.gen.newline() | ||||
| 
 | ||||
|   | ||||
| @@ -2823,10 +2823,11 @@ def _old_concretize(self, tests=False, deprecation_warning=True): | ||||
|                 # external specs are already built, don't worry about whether | ||||
|                 # it's possible to build that configuration with Spack | ||||
|                 continue | ||||
|             for conflict_spec, when_list in x.package_class.conflicts.items(): | ||||
|                 if x.satisfies(conflict_spec): | ||||
|                     for when_spec, msg in when_list: | ||||
|                         if x.satisfies(when_spec): | ||||
| 
 | ||||
|             for when_spec, conflict_list in x.package_class.conflicts.items(): | ||||
|                 if x.satisfies(when_spec): | ||||
|                     for conflict_spec, msg in conflict_list: | ||||
|                         if x.satisfies(conflict_spec): | ||||
|                             when = when_spec.copy() | ||||
|                             when.name = x.name | ||||
|                             matches.append((x, conflict_spec, when, msg)) | ||||
|   | ||||
| @@ -1905,7 +1905,7 @@ def test_installed_specs_disregard_conflicts(self, mutable_database, monkeypatch | ||||
|         """ | ||||
|         # Add a conflict to "mpich" that match an already installed "mpich~debug" | ||||
|         pkg_cls = spack.repo.PATH.get_pkg_class("mpich") | ||||
|         monkeypatch.setitem(pkg_cls.conflicts, "~debug", [(Spec(), None)]) | ||||
|         monkeypatch.setitem(pkg_cls.conflicts, Spec(), [("~debug", None)]) | ||||
| 
 | ||||
|         # If we concretize with --fresh the conflict is taken into account | ||||
|         with spack.config.override("concretizer:reuse", False): | ||||
|   | ||||
| @@ -46,7 +46,7 @@ def test_constraints_from_context(mock_packages): | ||||
|     assert "b" in pkg_cls.dependencies[spack.spec.Spec("@1.0")] | ||||
| 
 | ||||
|     assert pkg_cls.conflicts | ||||
|     assert (spack.spec.Spec("+foo@1.0"), None) in pkg_cls.conflicts["%gcc"] | ||||
|     assert (spack.spec.Spec("%gcc"), None) in pkg_cls.conflicts[spack.spec.Spec("+foo@1.0")] | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.regression("26656") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Todd Gamblin
					Todd Gamblin