Skip to content

solver: set preferences from input specs using %%#51383

Merged
tgamblin merged 13 commits intospack:developfrom
alalazo:features/propagation-as-preference
Nov 3, 2025
Merged

solver: set preferences from input specs using %%#51383
tgamblin merged 13 commits intospack:developfrom
alalazo:features/propagation-as-preference

Conversation

@alalazo
Copy link
Copy Markdown
Member

@alalazo alalazo commented Oct 1, 2025

fixes #51244
depends on #51314

This PR allows using %% to set preferences on input specs, and is particularly useful when dealing with unify: false environments.

An example of an environment that can be concretized with this PR is:

spack:
  definitions:
  - compilers: ["%%llvm_gfortran", "%%c,cxx,fortran=gcc"]
  specs:
  - matrix:
    - [mpileaks, hdf5, trilinos]
    - [$compilers]
  toolchains:
    llvm_gfortran:
    - spec: "%c=llvm"
      when: "%c"
    - spec: "%cxx=llvm"
      when: "%cxx"
    - spec: "%fortran=gcc"
      when: "%fortran"
  packages:
    callpath:
      require:
      - "%c=gcc"
  concretizer:
    unify: false

which will produce the following result for mpileaks %%llvm_gfortran:
2025-10-01_11-53

%% is now a propagated preference. i.e., it forces everything in a node's sub-DAG to use the preference unless it is impossible.

To allow for exceptions using %, %%, and spec syntax while still allowing global preferences in packages.yaml, the penalty taken for not fulfilling a %% preference is higher than that of a preference in packages.yaml. In this way we can propagate compilers to the link,run closure of a node, and set global preferences for other nodes.

The performance penalty for using %% is not insignificant, but it is also not present when not using %%. This has essentially zero cost for solves that do not use the new functionality.

@alalazo alalazo force-pushed the features/propagation-as-preference branch from 46fa46c to 24d7a18 Compare October 2, 2025 13:47
@haampie
Copy link
Copy Markdown
Member

haampie commented Oct 3, 2025

Summary from DMs:

  • Package API version needs to be bumped
  • The following makes little sense: x %%y implies x %(y %y).
    • If %% applies only to the link(/run) sub-dag, this is not a problem in practice. The %%y is by convention a build dependency, so foo %%gcc@15 would not imply foo %(gcc@15 %gcc@15).
  • Users still need to write %%c,cxx,fortran=compiler. Don't heuristically expand %%compiler to %[when=%c]c=compiler ... for all languages, cause languages are (or should be) defined by the repository and may evolve.
  • Without virtuals, %%cmake@3 could mean %[when=%cmake]cmake@3, cause you already branch on whether virtuals are set.

Other thoughts:

An issue remains when clang is used as a compiler but also libLLVM is used as a link dep. With the above, %%c,cxx=clang would still imply llvm %c,cxx=clang, which is undesirable. Same with libgccjit.

  • Maybe a pragmatic workaround is to not enforce %%c,cxx=clang on dependencies that are providers of c or cxx, even if they are link/run deps.
  • Or, probably too involved, you could follow edges to non-c/cxx providers (instead of following link/run edges).

Certain things related to compiler dependencies may work now as a happy coincidence: compilers are either externals or reused. That might not always be the case.

@alalazo alalazo force-pushed the features/propagation-as-preference branch from 5078ca3 to 7759b90 Compare October 6, 2025 09:34
@tgamblin tgamblin added this to the v1.1.0 milestone Oct 6, 2025
@alalazo alalazo force-pushed the features/propagation-as-preference branch from 7759b90 to 4f599f9 Compare October 7, 2025 03:47
@alalazo alalazo force-pushed the features/propagation-as-preference branch from 876572a to 068fcd8 Compare October 21, 2025 09:23
@alalazo alalazo added the feature A feature is missing in Spack label Oct 21, 2025
@alalazo alalazo force-pushed the features/propagation-as-preference branch from 068fcd8 to a14ec33 Compare October 24, 2025 08:02
@alalazo alalazo marked this pull request as ready for review October 24, 2025 20:16
@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Oct 24, 2025

Still missing docs, but should be ready for review

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Oct 27, 2025

Documentation here is minimal, and a small refactor has been extracted in #51474. If people agree I would keep it like it is in this PR, and have another dedicated PR to add more examples in the environment section to explain how the feature can be used in common scenarios.

The use cases would be those in the tests added to cmd/env.py.

@tldahlgren tldahlgren added the v1.1.0 PRs to backport for v1.1.0 label Oct 27, 2025
@psakievich psakievich self-requested a review October 27, 2025 19:21
@psakievich
Copy link
Copy Markdown
Contributor

psakievich commented Oct 28, 2025

Testing this locally. I'm hitting a strange error:

$ spack --backtrace add trilinos%%mixed_llvm
Traceback (most recent call last):
  File "/scratch/psakiev/spack-dev/environments/spack/bin/spack", line 49, in <module>
    sys.exit(main())
             ^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/main.py", line 1095, in main
    return _main(argv)
           ^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/main.py", line 1047, in _main
    return finish_parse_and_run(parser, cmd_name, args, env_format_error)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/main.py", line 1078, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/main.py", line 591, in _invoke_command
    return_val = command(parser, args)
                 ^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/cmd/add.py", line 32, in add
    if not env.add(spec, args.list_name):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/environment/environment.py", line 1320, in add
    existing = str(spec) in list_to_change.yaml_list
               ^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/spec.py", line 4420, in __str__
    return self._str(color=False)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/spec.py", line 4416, in _str
    return self._long_spec(color=color)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/spec.py", line 4359, in _long_spec
    return f"{self.format(color=color)} {self._format_dependencies(color=color)}".strip()
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/psakiev/spack-dev/environments/spack/lib/spack/spack/spec.py", line 4315, in _format_dependencies
    for edge in sorted(direct, key=lambda x: x.spec.name):
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: '<' not supported between instances of 'str' and 'NoneType'

I'm working on trying to debug, but figured I'd post early incase it triggers some thoughts on a root cause before I find it.

Update 1:

I wonder if this is due to how spack add is functioning for a toolchain:

$ spack add trilinos%mixed_llvm
==> Adding trilinos %[when='%fortran'] fortran=gcc %[when='%c'] c=clang %[when='%cxx'] cxx=clang to environment /scratch/psakiev/spack-dev/environments/demo-envs/percent-percent

The expansion is probably not parsable since we are communicating config, not specs.

Update 2:

Even when I manually put the trilinos%%mixed_llvm in as the root spec I'm still seeing this error.

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Oct 29, 2025

@psakievich I couldn't reproduce. I just copied the example I have in the description, and tried to concretize:
Screenshot from 2025-10-29 20-22-21

@alalazo alalazo force-pushed the features/propagation-as-preference branch from a89bd41 to bbcc790 Compare October 30, 2025 09:56
This commit allows basic support for setting preferences from
input using the %% syntax in specs.

Signed-off-by: Massimiliano Culpo <[email protected]>
This commit extends the support to toolchains

Signed-off-by: Massimiliano Culpo <[email protected]>
This changeset computes the transitive "link" closure of the nodes
where %% is applied. Having the node in the closure of a certain
root is a condition to activate the requirement.

Signed-off-by: Massimiliano Culpo <[email protected]>
Signed-off-by: Massimiliano Culpo <[email protected]>
Signed-off-by: Massimiliano Culpo <[email protected]>
Signed-off-by: Massimiliano Culpo <[email protected]>
Now, %% affects link + run dependencies instead of just link.

The penalty taken by not fulfilling a %% preference is higher
than that of a preference in packages.yaml. In this way we
can propagate compilers to the link,run closure of a node,
and set global preferences for other nodes.

Signed-off-by: Massimiliano Culpo <[email protected]>
Signed-off-by: Massimiliano Culpo <[email protected]>
@alalazo alalazo force-pushed the features/propagation-as-preference branch from bbcc790 to f1d36af Compare October 30, 2025 20:08
@psakievich
Copy link
Copy Markdown
Contributor

psakievich commented Oct 30, 2025

@alalazo I am now able to reproduce, and good news, use with all of sierra. Concretization is notably slower:

  • %gcc: 0min 35sec (used a single root inside sierra bundle)
  • %%gcc: 1min 28sec (used a single root inside sierra bundle)
  • %llvm_fortran: 2min 43sec
  • %%llvm_fortran: 3min 42sec

I'll test again with the newer commits I see.

Also noted that I can't apply compiler constraints or propagations to bundle packages:

==> SOLVING SPEC:
  sierra %gcc
==> Error: 'sierra %gcc' cannot depend on gcc`

==> SOLVING SPEC:
  sierra %%gcc
==> Error: 'sierra %%gcc' cannot depend on gcc

It's nice that toolchains seem to work, but they are quite a bit slower.

Update

Timings for %% about the same with f1d36afec8efae4c60207ab0b315d3f5b9a7a037

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Oct 30, 2025

Using propagation is slow, due to the closures we have to compute to only propagate on the link/run subdag. I guess the good news is that when we don't use propagation, times are the same as before 😆 .

Also noted that I can't apply compiler constraints or propagations to bundle packages:

Well, that's how things work and wasn't changed in this PR. To propagate compiler constraints you have to use toolchains, or conditional dependencies directly.

Do you have a reproducer for the error in #51383 (comment) ?

@psakievich
Copy link
Copy Markdown
Contributor

Using propagation is slow, due to the closures we have to compute to only propagate on the link/run subdag. I guess the good news is that when we don't use propagation, times are the same as before 😆 .

Also noted that I can't apply compiler constraints or propagations to bundle packages:

Well, that's how things work and wasn't changed in this PR. To propagate compiler constraints you have to use toolchains, or conditional dependencies directly.

I figured as much, but it took me a couple of try's before I realized it.

Do you have a reproducer for the error in #51383 (comment) ?

I never did figure this one out. I recloned spack and started with your reproducer. I suspect I inadvertently switched to the wrong branch or something else user error related.

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Nov 3, 2025

First the good news. This doesn't affect the concretization time, on our usual benchmark, when the option is not used. Tested against:

radiuss.pr.csv
radiuss.develop.csv

comparison

Now the bad news. I started seeing fluctuations with very long solve times on some scrambled output of py-cinemasci. This happens both on develop and on this PR (and that's why I didn't include the spec above). I'll investigate that, but I bet it's some package change that went in in the last couple of weeks 🫠

@tgamblin tgamblin merged commit 8030f3e into spack:develop Nov 3, 2025
33 of 34 checks passed
@alalazo alalazo linked an issue Nov 4, 2025 that may be closed by this pull request
1 task
hippo91 pushed a commit to hippo91/spack that referenced this pull request Nov 4, 2025
This PR allows using `%%` to set propagated preferences on input
specs, and is particularly useful when dealing with `unify: false`
environments.

An example of an environment that can be concretized with this PR is:

```yaml
spack:
  definitions:
  - compilers: ["%%llvm_gfortran", "%%c,cxx,fortran=gcc"]
  specs:
  - matrix:
    - [mpileaks, hdf5, trilinos]
    - [$compilers]
  toolchains:
    llvm_gfortran:
    - spec: "%c=llvm"
      when: "%c"
    - spec: "%cxx=llvm"
      when: "%cxx"
    - spec: "%fortran=gcc"
      when: "%fortran"
  packages:
    callpath:
      require:
      - "%c=gcc"
  concretizer:
    unify: false
```

`%%` is now a *propagated* preference. i.e., it forces everything in a node's
sub-DAG to use the preference unless it is impossible.

To allow for exceptions using `%`, `%%`, and spec syntax while still allowing
global preferences in `packages.yaml`,  the penalty taken for not fulfilling a `%%`
preference is higher than that of a preference in `packages.yaml`. In this way we
can propagate compilers to the `link,run` closure of a node, and set global
preferences for other nodes.

The performance penalty for using `%%` is not insignificant, but it is also not
present when *not* using `%%`. This has essentially zero cost for solves that do
not use the new functionality.

---------

Signed-off-by: Massimiliano Culpo <[email protected]>
@alalazo alalazo deleted the features/propagation-as-preference branch March 2, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature A feature is missing in Spack v1.1.0 PRs to backport for v1.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compiler dependencies are not being passed to dependencies if package doesn't depend on the language Propagate compilers with a %% sigil

5 participants