Repository: spack/spack

Issue:
Spec constraints under the "all" attribute in package requirements should not be validated
### Steps to reproduce

Using the following `packages.yaml`:
```yaml
packages:
  all:
    require:
    -  any_of: ["~shared", "@:"]
```
we cannot concretize:
```console
$ spack spec hdf5~mpi
==> Error: variant "shared" not found in package "alsa-lib" [required from package "alsa-lib"]
```

### Error message

```console
$ spack -d spec hdf5~mpi
==> [2023-01-19-16:52:46.806926] Imported spec from built-in commands
==> [2023-01-19-16:52:46.807517] Imported spec from built-in commands
==> [2023-01-19-16:52:46.808670] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/config.yaml
==> [2023-01-19-16:52:46.825069] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/bootstrap.yaml
==> [2023-01-19-16:52:46.853263] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/repos.yaml
==> [2023-01-19-16:52:46.856945] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/config.yaml
==> [2023-01-19-16:52:46.874094] DATABASE LOCK TIMEOUT: 3s
==> [2023-01-19-16:52:46.874207] PACKAGE LOCK TIMEOUT: No timeout
==> [2023-01-19-16:52:46.874739] DATABASE LOCK TIMEOUT: 3s
==> [2023-01-19-16:52:46.874774] PACKAGE LOCK TIMEOUT: No timeout
==> [2023-01-19-16:52:46.874822] [BOOTSTRAP CONFIG SCOPE] name=_builtin
==> [2023-01-19-16:52:46.875196] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/bootstrap.yaml
==> [2023-01-19-16:52:46.880118] [BOOTSTRAP CONFIG SCOPE] name=defaults, path=/home/culpo/PycharmProjects/spack/etc/spack/defaults
==> [2023-01-19-16:52:46.880165] [BOOTSTRAP CONFIG SCOPE] name=defaults/linux, path=/home/culpo/PycharmProjects/spack/etc/spack/defaults/linux
==> [2023-01-19-16:52:46.880190] [BOOTSTRAP CONFIG SCOPE] name=bootstrap, path=/home/culpo/.spack/bootstrap/config
==> [2023-01-19-16:52:46.880208] [BOOTSTRAP CONFIG SCOPE] name=bootstrap/linux, path=/home/culpo/.spack/bootstrap/config/linux
==> [2023-01-19-16:52:46.880439] Reading config from file /home/culpo/.spack/bootstrap/config/linux/compilers.yaml
==> [2023-01-19-16:52:46.898910] [BOOTSTRAP ROOT SPEC] patchelf@0.13.1: %gcc target=x86_64
==> [2023-01-19-16:52:46.899167] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/bootstrap.yaml
==> [2023-01-19-16:52:46.906232] [BOOTSTRAP EXECUTABLES patchelf] Try installed specs with query 'patchelf@0.13.1: %gcc target=x86_64'
==> [2023-01-19-16:52:46.968655] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/config.yaml
==> [2023-01-19-16:52:47.021877] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/modules.yaml
==> [2023-01-19-16:52:47.031136] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/linux/modules.yaml
==> [2023-01-19-16:52:47.092388] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/packages.yaml
==> [2023-01-19-16:52:47.136099] [BOOTSTRAP ROOT SPEC] gnupg@2.3: %gcc target=x86_64
==> [2023-01-19-16:52:47.140151] [BOOTSTRAP ROOT SPEC] clingo-bootstrap@spack+python %gcc target=x86_64
==> [2023-01-19-16:52:47.140204] [BOOTSTRAP MODULE clingo] Try importing from Python
==> [2023-01-19-16:52:47.204083] [BOOTSTRAP MODULE clingo] The installed spec "clingo-bootstrap@spack+python %gcc target=x86_64 ^python@3.8/ba5ijauisd3uuixtmactc36vps7yfsrl" provides the "clingo" Python module
==> [2023-01-19-16:52:47.204458] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/concretizer.yaml
==> [2023-01-19-16:52:47.208096] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/repos.yaml
==> [2023-01-19-16:52:47.250519] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/packages.yaml
==> [2023-01-19-16:52:47.273280] Reading config from file /home/culpo/.spack/linux/packages.yaml
==> [2023-01-19-16:52:47.275760] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/config.yaml
==> [2023-01-19-16:52:47.290661] Reading config from file /home/culpo/PycharmProjects/spack/etc/spack/defaults/mirrors.yaml
==> [2023-01-19-16:52:47.291741] Checking existence of https://mirror.spack.io/build_cache/index.json
==> [2023-01-19-16:52:48.041838] Failure reading URL: HTTP Error 404: Not Found
==> [2023-01-19-16:52:49.136668] Reading config from file /home/culpo/.spack/linux/compilers.yaml
Traceback (most recent call last):
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1500, in _spec_clauses
    variant_def, _ = pkg_cls.variants[vname]
KeyError: 'shared'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1401, in spec_clauses
    clauses = self._spec_clauses(*args, **kwargs)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1503, in _spec_clauses
    raise RuntimeError(msg.format(vname, spec.name))
RuntimeError: variant "shared" not found in package "alsa-lib"

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/culpo/PycharmProjects/spack/bin/spack", line 54, in <module>
    sys.exit(main())
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack_installable/main.py", line 37, in main
    sys.exit(spack.main.main(argv))
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/main.py", line 1004, in main
    return _main(argv)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/main.py", line 959, in _main
    return finish_parse_and_run(parser, cmd_name, env_format_error)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/main.py", line 987, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/main.py", line 636, in _invoke_command
    return_val = command(parser, args)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/spec.py", line 103, in spec
    concretized_specs = spack.cmd.parse_specs(args.specs, concretize=True)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/__init__.py", line 224, in parse_specs
    spec.concretize(tests=tests)  # implies normalize
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/spec.py", line 2950, in concretize
    self._new_concretize(tests)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/spec.py", line 2925, in _new_concretize
    result = solver.solve([self], tests=tests)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 2529, in solve
    result, _, _ = self.driver.solve(setup, specs, reuse=reusable_specs, output=output)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 687, in solve
    setup.setup(self, specs, reuse=reuse)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 2101, in setup
    self.pkg_rules(pkg, tests=self.tests)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1143, in pkg_rules
    self.package_requirement_rules(pkg)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1019, in package_requirement_rules
    self.emit_facts_from_requirement_rules(rules, virtual=False)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1276, in emit_facts_from_requirement_rules
    member_id = self.condition(
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1168, in condition
    requirements = self.spec_clauses(named_cond, body=True, required_from=name)
  File "/home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py", line 1406, in spec_clauses
    raise RuntimeError(msg)
RuntimeError: variant "shared" not found in package "alsa-lib" [required from package "alsa-lib"]
```

### Information on your system

* **Spack:** 0.20.0.dev0 (0cdb7ccdd39debd93b3111513a7dc7d8d03adb7a)
* **Python:** 3.8.10
* **Platform:** linux-ubuntu20.04-icelake
* **Concretizer:** clingo


### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
@adamjstewart @eugeneswalker 

Resolving patch:
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -1012,11 +1012,13 @@ def package_compiler_defaults(self, pkg):
     def package_requirement_rules(self, pkg):
         pkg_name = pkg.name
         config = spack.config.get("packages")
-        requirements = config.get(pkg_name, {}).get("require", []) or config.get("all", {}).get(
-            "require", []
-        )
+        requirements, raise_on_failure = config.get(pkg_name, {}).get("require", []), True
+        if not requirements:
+            requirements, raise_on_failure = config.get("all", {}).get("require", []), False
         rules = self._rules_from_requirements(pkg_name, requirements)
-        self.emit_facts_from_requirement_rules(rules, virtual=False)
+        self.emit_facts_from_requirement_rules(
+            rules, virtual=False, raise_on_failure=raise_on_failure
+        )
 
     def _rules_from_requirements(self, pkg_name, requirements):
         """Manipulate requirements from packages.yaml, and return a list of tuples
@@ -1161,11 +1163,13 @@ def condition(self, required_spec, imposed_spec=None, name=None, msg=None, node=
         named_cond.name = named_cond.name or name
         assert named_cond.name, "must provide name for anonymous condtions!"
 
+        # Check if we can emit the requirements before updating the condition ID counter.
+        # In this way, if a condition can't be emitted but the exception is handled in the caller,
+        # we won't emit partial facts.
+        requirements = self.spec_clauses(named_cond, body=True, required_from=name)
+
         condition_id = next(self._condition_id_counter)
         self.gen.fact(fn.condition(condition_id, msg))
-
-        # requirements trigger the condition
-        requirements = self.spec_clauses(named_cond, body=True, required_from=name)
         for pred in requirements:
             self.gen.fact(fn.condition_requirement(condition_id, *pred.args))
 
@@ -1261,23 +1265,39 @@ def provider_requirements(self):
             rules = self._rules_from_requirements(virtual_str, requirements)
             self.emit_facts_from_requirement_rules(rules, virtual=True)
 
-    def emit_facts_from_requirement_rules(self, rules, virtual=False):
-        """Generate facts to enforce requirements from packages.yaml."""
+    def emit_facts_from_requirement_rules(self, rules, *, virtual=False, raise_on_failure=True):
+        """Generate facts to enforce requirements from packages.yaml.
+
+        Args:
+            rules: rules for which we want facts to be emitted
+            virtual: if True the requirements are on a virtual spec
+            raise_on_failure: if True raise an exception when a requirement condition is invalid
+                for the current spec. If False, just skip that condition
+        """
         for requirement_grp_id, (pkg_name, policy, requirement_grp) in enumerate(rules):
             self.gen.fact(fn.requirement_group(pkg_name, requirement_grp_id))
             self.gen.fact(fn.requirement_policy(pkg_name, requirement_grp_id, policy))
-            for requirement_weight, spec_str in enumerate(requirement_grp):
+            requirement_weight = 0
+            for spec_str in requirement_grp:
                 spec = spack.spec.Spec(spec_str)
                 if not spec.name:
                     spec.name = pkg_name
                 when_spec = spec
                 if virtual:
                     when_spec = spack.spec.Spec(pkg_name)
-                member_id = self.condition(
-                    required_spec=when_spec, imposed_spec=spec, name=pkg_name, node=virtual
-                )
+
+                try:
+                    member_id = self.condition(
+                        required_spec=when_spec, imposed_spec=spec, name=pkg_name, node=virtual
+                    )
+                except Exception as e:
+                    if raise_on_failure:
+                        raise RuntimeError("cannot emit requirements for the solver") from e
+                    continue
+
                 self.gen.fact(fn.requirement_group_member(member_id, pkg_name, requirement_grp_id))
                 self.gen.fact(fn.requirement_has_weight(member_id, requirement_weight))
+                requirement_weight += 1
 
     def external_packages(self):
         """Facts on external packages, as read from packages.yaml"""


Test patch:
diff --git a/lib/spack/spack/test/concretize_requirements.py b/lib/spack/spack/test/concretize_requirements.py
--- a/lib/spack/spack/test/concretize_requirements.py
+++ b/lib/spack/spack/test/concretize_requirements.py
@@ -413,3 +413,18 @@ def test_incompatible_virtual_requirements_raise(concretize_scope, mock_packages
     spec = Spec("callpath ^zmpi")
     with pytest.raises(UnsatisfiableSpecError):
         spec.concretize()
+
+
+def test_non_existing_variants_under_all(concretize_scope, mock_packages):
+    if spack.config.get("config:concretizer") == "original":
+        pytest.skip("Original concretizer does not support configuration" " requirements")
+    conf_str = """\
+    packages:
+      all:
+        require:
+        - any_of: ["~foo", "@:"]
+    """
+    update_packages_config(conf_str)
+
+    spec = Spec("callpath ^zmpi").concretized()
+    assert "~foo" not in spec