Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow packages to set default variants in dependencies #16909

Open
mwkrentel opened this issue Jun 2, 2020 · 10 comments
Open

Allow packages to set default variants in dependencies #16909

mwkrentel opened this issue Jun 2, 2020 · 10 comments
Labels

Comments

@mwkrentel
Copy link
Member

This is a request for a new feature and discussion.

I'd like to allow packages to set the preferences (defaults) for
variants in their dependency packages and do so from their
package.py file. Of course, this can be done now in packages.yaml
or the command line, but allowing this in package.py has some
advantages.

For example, my project (hpctoolkit) depends on libunwind, and
libunwind has a variant xz to support xz/lzma compressed symbol
tables, with default False. Of course, hpctoolkit will run either
way, but normally we prefer with xz on. Right now, what I have is:

  depends_on('libunwind@1.4: +xz')

But this is a hard constraint. You can't turn off xz without
editing package.py. I'd rather turn this into a soft preference
with something like:

  depends_on('libunwind@1.4:', prefer='+xz')

That way, the default for libunwind remains False, but if you're
building libunwind as part of hpctoolkit, then the default is True.
Of course, I can't go around and reset the defaults for all of our
dependency packages just to suit my needs.

So why not use packages.yaml? We could, and in fact, we do
distribute a packages.yaml file, mostly as a reference point. But
the file is long and it's tedious to review it for every new machine.

Right now, I'm confronted with a dilemma: I can leave off +xz and
make everyone use our packages.yaml file, or else they won't build
hpctoolkit the way we want. Or, I can require +xz which is
heavy-handed and inflexible. With this feature, I can make the
one-button spack install hpctoolkit almost always produce the right
spec, but still allow flexibility when needed.

Theoretically, I could add an xz variant to hpctoolkit and set the
default there, but that's really awkward and adds a duplicate variant,
just because the default doesn't suit my needs.

Note: the precedence of prefer='+xz' should be next to last. It
should override the default in the dependency package, but you should
be able to override it from packages.yaml or the command line.

@becker33 Is this possible? A good idea?

@alalazo
Copy link
Member

alalazo commented Jun 3, 2020

Related: #391 #2594

@mwkrentel
Copy link
Member Author

The flip side of the above is another example of where I would use
this, to turn off some feature that we don't use.

For example, hpctoolkit (and dyninst) require elfutils, but we don't
use the NLS features (native lang support). So, rather than build
gettext and all its prereqs, if you're building only hpctoolkit, then
it makes sense to build elfutils~nls.

Again, we could require ~nls, but that's heavy-handed and would
interfere with someone else's use case if they needed +nls.

So, the better option is to let hpctoolkit say, "we prefer ~nls, but
if you want to override with packages.yaml or the command line, you're
free to do so."

Again, IMO, it's much better to put this once in the hpctoolkit package.py
file than make every user put it in their own packages.yaml.

nag @tgamblin

@alalazo
Copy link
Member

alalazo commented Mar 20, 2023

@mwkrentel What would be the plan when different edges have conflicting preferences? The minimal example is a diamond-like structure:

  graph TD;
      id1[root]-->id2[a\nprefers c+foo];
      id1-->id3[b\nprefers c-foo];
      id2-->id4[c ?foo];
      id3-->id4;

@mwkrentel
Copy link
Member Author

@alalazo In that case, since ROOT knows that it depends on both A and B,
I would say it's up to ROOT to set a preference. In general, higher in
the dag should take precedence.

If root doesn't set any preference, then flip a coin. That is, either
choice is correct.

Remember, these are only preferences, roughly equivalent to defaults.
You can always override if you care.

But in this case, root should decide.

@alalazo
Copy link
Member

alalazo commented Mar 20, 2023

root doesn't have any preference in the example, and there can be cases - like the above - where conflicting preferences are expressed at the same depth in the DAG.

If root doesn't set any preference, then flip a coin. That is, either choice is correct.

We are not talking about correct choices, but "optimal" choices. I think there should be only one optimal choice in general, or otherwise spack spec hdf5 might activate/deactivate options based on the result of flipping a coin 😉

One could object that we already do something similar in packages.yaml (deal with possibly incoherent preferences coming from different nodes), but my point of view is:

  1. packages.yaml is under user control, anything painful that a user does there is self-inflicted somehow
  2. package.py is under maintainers control, so anything painful that comes from the package.py file is inflicted by us on users

This is not to push back on the feature, but to say that we need at least to answer questions like this before we think of letting packages control other packages defaults.

@mwkrentel
Copy link
Member Author

First, let me review one of my main use cases. Hpctoolkit uses
elfutils, but we don't use NLS. So, to avoid building gettext,
texinfo, etc, I put elfutils ~nls in packages.yaml.

Fine, but that means that EVERY user of hpctoolkit either has to write
their own packages.yaml file or else build a bunch of unnecessary
packages. Wouldn't it be better if I could write that preference in
the hpctoolkit package.py file once so that spack install hpctoolkit
would do what we recommend rather than every user copying it?

All of this is equivalent to putting a packages.yaml file in the
hpctoolkit directory and saying that if you install hpctoolkit, then
you pull in this file.

You say that I'm imposing my choice (~nls) on everyone else.
But I could just as well say that elfutils is imposing its choice (+nls)
on everyone else.

But I'm not imposing it on everyone, only the users of hpctoolkit.
We're saying, "here is what we as the hpctoolkit developers recommend.
You're free to change it, but this is what we recommend."

It's impossible to settle on what is the best default. All you can
try to do is annoy the fewest number of people and leave some knob for
reversing the decision.

In this case, "building hpctoolkit + dyninst + elfutils" is more
information than just "building elfutils". So, I'm trying to use that
information.


As for the diagram with no preference at root, how about these
semantics. For any dag, construct a (deterministic) total order for
the dag, walk the linear order bottom up and for any package that says
"prefers", set the default to that, last one wins.

Or, you could be more sophisticated and say: (1) if root sets a preference,
the it wins because it dominates both A and B, but (2) if root doesn't set
a preference and you have conflicting settings without something dominating
them both, then revert to the package FOO default.


I understand that all this is complicated. All I'm really trying to
say is, "here is what we as developers recommend" and write that once
rather than have every user copy it. That's what I really want.

@alalazo
Copy link
Member

alalazo commented Mar 21, 2023

I think this can be a nice topic for the Wed. meeting, if you like to talk about that. The point I want to make, is that right now each package decide its own defaults. So, elfutils decides for elfutils.

If we move to a model where a dependent can change the defaults of a dependency in package.py, I think we'll have conflicts that can't be resolved which would lead to more confusion.

I wonder if this use case, which I completely understand, could be solved by other means.

@mwkrentel
Copy link
Member Author

Another possibility, somewhat more radical, is to follow the boost
library model. Boost has variants for several libraries and the
default for all of them is False. That makes a minimal build and then
packages and users are free to add whatever they need.

Many variants add some feature to a package and dependencies to
support the feature. Not all variants are like this, but many are.
We could set the default for these variants all to False and then rely
on packages that depend on them to turn on the ones they need.

But you don't build something just because some package thinks it's a good
idea and everyone should have this.

That's a huge change, but it's a cleaner model for what features to build.
The problem I'm running into here is that it's awkward for a package to say
that it doesn't need something and would prefer not to build it.

@mwkrentel
Copy link
Member Author

We discussed this at today's Wed meeting. To summarize what I'd like:

We as hpctoolkit developers recommend a certain config (turn this on,
turn that off) that are counter to other packages default variants.
Of course, this can all be expressed in a packages.yaml file.
Experienced users know how to use packages.yaml, adjust for their
own needs, etc.

What I want is a way to enforce our recommendations so that the
beginning user can also get our config without having to write and
install packages.yaml. They should be able to clone spack and run
the one-button (*) spack install hpctoolkit and still get as close
to our config as feasible.

  • = There's an old Dilbert cartoon where a computer salesman says,
    "this machine is so easy to use that it has only one button and we
    push it at the factory for you."

@alalazo
Copy link
Member

alalazo commented Mar 23, 2023

We discussed this at today's Wed meeting.

For reference, the minutes of the meeting: https://github.com/spack/spack/wiki/Telcon%3A-2023-03-22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants