Skip to content

Unify directive dict structure#40326

Merged
tgamblin merged 5 commits intodevelopfrom
unify-directive-dict-structure
Jan 9, 2024
Merged

Unify directive dict structure#40326
tgamblin merged 5 commits intodevelopfrom
unify-directive-dict-structure

Conversation

@tgamblin
Copy link
Copy Markdown
Member

@tgamblin tgamblin commented Oct 5, 2023

Metadata created by directives on Spack packages isn't structured consistently, and it makes implementing similar functionality for different directives difficult and repetitive, e.g. with the drop directive in #35045. It can also cause us to lose information about some parts of packages, e.g. how we lose the defaults of conditional variants in #38302.

Fundamentally, everything in a Spack package is conditional, which distinguishes Spack from other package managers. The right way to key directive metadata is by condition, which prevents issues like the ones described above. Also, storing by condition seems to make package loading faster, because there tend to be fewer top-level condition keys (from things like with when(<condition>): in packages than there are duplicated conditions in sub-dictionaries when you put conditions under some other top-level key (like the dependency name).

This PR converts Spack's metadata format as follows:

Directive Old New
conflicts
conflict_spec:
[(when_spec, msg), ...]
when_spec:
[ (conflict, msg), ... ]
requires
(requirement_spec, ...):
[(when_spec, policy, msg)]
when_spec:
[
((requirement_spec, ...), policy, msg),
...
]
depends_on
name:
{ when_spec: Dependency }
when_spec:
{ name: Dependency }
provides
provided_spec:
{ when_spec, ... }
when_spec:
{ provided_spec, ... }
extends
name:
(extendee_spec, unused_kwargs)
when_spec:
[extendee_spec, ...]
resource
(no change)
when_spec:
[Resource, ...]
when_spec:
[Resource, ...]
patch
(no change)
when_spec:
[Patch, ...]
when_spec:
[Patch, ...]

Notably missing from the list above are variants and versions. Variants require quite a bit more work, and I am leaving them for a follow-on PR, as they require a lot more refactoring of packages and I think that will merit its own discussion.

Versions should likely be done as well, but we don't yet support when on them. Likely that should be done too, as we have users who would like to put conditions (particularly platform/OS conditions) on them. That is also a significant refactor for a future PR.

This PR does a few refactorings along with the changes above:

  1. extendee_args: this method is unused, and we no longer pass kwargs to extends() directive.
  2. Code and concretizer refactors for all the code in Spack that uses the directives above.
  3. Add typing information to PackageBase for all the dictionaries above, ensuring that we parse all data that needs to be a Spec early (thus failing early), and that we get consistent metadata dictionaries on our package classes.
  4. Make _make_when_spec() internal to directives.py and don't use it in the solver. We don't need to make specs in the solver if we guarantee (via mypy that they're constructed when packages are parsed.

@spackbot-app spackbot-app bot added build-systems commands core PR affects Spack core functionality directives stand-alone-tests Stand-alone (or smoke) tests for installed packages tests General test capability(ies) labels Oct 5, 2023
@adamjstewart

This comment was marked as outdated.

@tgamblin

This comment was marked as outdated.

@bonachea

This comment was marked as outdated.

@adamjstewart

This comment was marked as outdated.

@tgamblin

This comment was marked as outdated.

@tgamblin tgamblin added this to the v0.21.0 milestone Oct 17, 2023
@tgamblin tgamblin force-pushed the unify-directive-dict-structure branch from b4fe996 to e4aaf9b Compare October 30, 2023 16:58
@tgamblin tgamblin modified the milestones: v0.21.0, v0.21.1 Nov 6, 2023
@tgamblin tgamblin force-pushed the unify-directive-dict-structure branch from e4aaf9b to f0299bd Compare November 10, 2023 11:43
tgamblin added a commit that referenced this pull request Dec 2, 2023
This was missed while backporting the new `spack info` command from #40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
alalazo pushed a commit that referenced this pull request Dec 5, 2023
This was missed while backporting the new `spack info` command from #40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
@tgamblin tgamblin force-pushed the unify-directive-dict-structure branch from f0299bd to d5f2c03 Compare January 2, 2024 02:43
@tgamblin tgamblin marked this pull request as ready for review January 2, 2024 02:53
@tgamblin tgamblin force-pushed the unify-directive-dict-structure branch 2 times, most recently from 769a5b4 to b32448c Compare January 2, 2024 05:18
alalazo pushed a commit that referenced this pull request Jan 2, 2024
This was missed while backporting the new `spack info` command from #40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
@alalazo alalazo self-assigned this Jan 3, 2024
tgamblin added a commit that referenced this pull request May 28, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

There are a couple other changes here that might be of interest:

1. The `generator` variant in `CMakePackage` is now only defined when `build_system=cmake`.
2. `spack info` has been updated to support the new metadata layout.
tgamblin added a commit that referenced this pull request May 28, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request May 28, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request May 29, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request May 29, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request May 29, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request May 30, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jun 5, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jun 9, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jul 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jul 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jul 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.
tgamblin added a commit that referenced this pull request Jul 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
kwryankrattiger pushed a commit to kwryankrattiger/spack that referenced this pull request Jul 9, 2024
…41975)

Needed for spack#40326, which can changes the iteration order over package dependencies during concretization.

While clingo doesn't have this problem, the original concretizer (which we still use for bootstrapping) can be sensitive to iteration order when evaluating dependency constraints in `when` conditions. This can cause it to ignore conditional dependencies unless the dependencies in the condition are listed first in the package.

The issue was in the way the original concretizer would disconnect specs *every* time `normalize()` ran. When specs were disconnected, `^dependency` constraints wouldn't see the dependency in the dependency condition loop.

We now only only disconnect *all* dependencies at the start of `concretize()` and `normalize()`, and we disconnect any leftover dependents from replaced externals at the *end* of `normalize()`.  This trims stale connections while keeping the ones that are needed to trigger dependency conditions.

- [x] refactor `flat_dependencies()` to not disconnect the spec by default.
- [x] `flat_dependencies()` is never called with `copy=True` -- remove the `copy` kwarg.
- [x] disconnect only once at the beginning of `normalize()` or `concretize()`.
- [x] add a test that perturbs dependency iteration order to ensure this doesn't regress.
- [x] disconnect unused dependents at end of `normalize()`
tgamblin added a commit that referenced this pull request Jul 18, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 2, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 3, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 7, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 11, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 13, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 16, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 17, 2024
Continuing the work started in #40326, his changes the structure of Variant metadata on
Packages from a single variant definition per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly) all modeled in the
   same way; and
2. fixes an issue where we would lose information about multiple variant
   definitions in a package (see #38302). We can now have, e.g., different defaults for
   the same variant in different versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously, the solver's job
   was to select value(s) for a single variant definition per name per package. Now, the
   solver needs to:

   a. Determine which variant definition should be used for a given node, which can depend
      on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected variant
      definition.

   The solver will always prefer the *last* matching variant definition based on declaration
   order in packages. This has the effect that variant definitions from derived classes are
   preferred over definitions from superclasses, and the last definition within the same
   class sticks. I think is intuitive b/c it matches python semantics. I hope this is fleshed
   out clearly in the solver logic.

2. The solver does a lot more validation on variant values at setup time now. In particular,
   it checks whether a variant value on a spec is valid given the other constraints on that
   spec.

3. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid given
   the conditions on variant definitions in packages. I haven't fixed those here but will
   open a separate PR to iterate on them.  I plan to make strict checking the defaults once
   all existing package issues are resolved. It's not clear to me that strict checking should
   be the default for the prevalidation done at solve time.

Signed-off-by: Todd Gamblin <[email protected]>
tgamblin added a commit that referenced this pull request Sep 17, 2024
Continuing the work started in #40326, his changes the structure
of Variant metadata on Packages from a single variant definition
per name with a list of `when` specs:

```
name: (Variant, [when_spec, ...])
```

to a Variant definition per `when_spec` per name:

```
when_spec: { name: Variant }
```

With this change, everything on a package *except* versions is
 keyed by `when` spec. This:

1. makes things consistent, in that conditional things are (nearly)
   all modeled in the same way; and

2. fixes an issue where we would lose information about multiple
   variant definitions in a package (see #38302). We can now have,
   e.g., different defaults for the same variant in different
   versions of a package.

Some notes:

1. This required some pretty deep changes to the solver. Previously,
   the solver's job was to select value(s) for a single variant definition
   per name per package. Now, the solver needs to:

   a. Determine which variant definition should be used for a given node,
      which can depend on the node's version, compiler, target, other variants, etc.
   b. Select valid value(s) for variants for each node based on the selected
      variant definition.

   When multiple variant definitions are enabled via their `when=` clause, we will
   always prefer the *last* matching definition, by declaration order in packages. This
   is implemented by adding a `precedence` to each variant at definition time, and we
   ensure they are added to the solver in order of precedence.

   This has the effect that variant definitions from derived classes are preferred over
   definitions from superclasses, and the last definition within the same class sticks.
   This matches python semantics. Some examples:

    ```python
    class ROCmPackage(PackageBase):
        variant("amdgpu_target", ..., when="+rocm")

    class Hipblas(ROCmPackage):
        variant("amdgpu_target", ...)
    ```

   The global variant in `hipblas` will always supersede the `when="+rocm"` variant in
   `ROCmPackage`. If `hipblas`'s variant was also conditional on `+rocm` (as it probably
   should be), we would again filter out the definition from `ROCmPackage` because it
   could never be activated. If you instead have:

    ```python
    class ROCmPackage(PackageBase):
        variant("amdgpu_target", ..., when="+rocm")

    class Hipblas(ROCmPackage):
        variant("amdgpu_target", ..., when="+rocm+foo")
    ```

   The variant on `hipblas` will win for `+rocm+foo` but the one on `ROCmPackage` will
   win with `rocm~foo`.

   So, *if* we can statically determine if a variant is overridden, we filter it out.
   This isn't strictly necessary, as the solver can handle many definitions fine, but
   this reduces the complexity of the problem instance presented to `clingo`, and
   simplifies output in `spack info` for derived packages. e.g., `spack info hipblas`
   now shows only one definition of `amdgpu_target` where before it showed two, one of
   which would never be used.

2. Nearly all access to the `variants` dictionary on packages has been refactored to
   use the following class methods on `PackageBase`:
    * `variant_names(cls) -> List[str]`: get all variant names for a package
    * `has_variant(cls, name) -> bool`: whether a package has a variant with a given name
    * `variant_definitions(cls, name: str) -> List[Tuple[Spec, Variant]]`: all definitions
      of variant `name` that are possible, along with their `when` specs.
    * `variant_items() -> `: iterate over `pkg.variants.items()`, with impossible variants
      filtered out.

   Consolidating to these methods seems to simplify the code a lot.

3. The solver does a lot more validation on variant values at setup time now. In
   particular, it checks whether a variant value on a spec is valid given the other
   constraints on that spec. This allowed us to remove the crufty logic in
   `update_variant_validate`, which was needed because we previously didn't *know* after
   a solve which variant definition had been used. Now, variant values from solves are
   constructed strictly based on which variant definition was selected -- no more
   heuristics.

4. The same prevalidation can now be done in package audits, and you can run:

   ```
   spack audit packages --strict-variants
   ```

   This turns up around 18 different places where a variant specification isn't valid
   given the conditions on variant definitions in packages. I haven't fixed those here
   but will open a separate PR to iterate on them. I plan to make strict checking the
   defaults once all existing package issues are resolved. It's not clear to me that
   strict checking should be the default for the prevalidation done at solve time.

There are a few other changes here that might be of interest:

  1. The `generator` variant in `CMakePackage` is now only defined when `build_system=cmake`.
  2. `spack info` has been updated to support the new metadata layout.
  3.  split out variant propagation into its own `.lp` file in the `solver` code.
  4. Add better typing and clean up code for variant types in `variant.py`.
  5. Add tests for new variant behavior.
vjranagit pushed a commit to vjranagit/spack that referenced this pull request Jan 18, 2026
This was missed while backporting the new `spack info` command from spack#40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
fwerner pushed a commit to fwerner/spack that referenced this pull request Mar 10, 2026
This was missed while backporting the new `spack info` command from spack#40326.

Variants should be sorted by name when invoking `spack info --variants-by-name`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build-systems commands core PR affects Spack core functionality directives stand-alone-tests Stand-alone (or smoke) tests for installed packages tests General test capability(ies)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants