variant: added support for multi-valued variants#2386
variant: added support for multi-valued variants#2386tgamblin merged 35 commits intospack:developfrom
Conversation
|
|
||
| @key_ordering | ||
| class HashableMap(dict): | ||
| class HashableMap(collections.MutableMapping): |
There was a problem hiding this comment.
@tgamblin @scheibelp @becker33 The change here should be a more idiomatic way to define a custom mutable mapping. Not deriving from dict prevents inconsistencies among different methods, like __getitem__ and get, if one forgets to override both. On the other side delegating to dict instead of deriving from it comes with a performance cost that is non negligible for certain operations:
# current develop
$ time spack spec -Il trilinos
Input spec
...
real 0m3.177s
user 0m3.088s
sys 0m0.072s
# This branch
$ time spack spec -Il trilinos
Input spec
...
real 0m3.812s
user 0m3.704s
sys 0m0.088sLet me know if you want me to revert this.
There was a problem hiding this comment.
I think this is acceptable. The real cost is in the concretization algorithm.
|
Awesome feature! Needs documentation in the PR... |
|
@citibeth Added a box to check in the description above. |
|
@tgamblin @becker33 @davydden @adamjstewart and anybody else, I think this PR is ready for review. I would proceed like that: if this is considered to be a viable addition to the core, I'll address the reviews and after that is done I'll write Sphinx docs before merging. Sounds fine? |
| out.write(suffix) | ||
|
|
||
| print out.getvalue() | ||
| print(out.getvalue()) |
There was a problem hiding this comment.
There are no substantive changes in this file. Although I agree with them, I believe PRs are better if they present the minimum changes required for the feature at hand. Can you move these changes to a different PR?
|
|
||
| def __delitem__(self, key): | ||
| del self.dict[key] | ||
|
|
There was a problem hiding this comment.
How are these changes related to multi-valued variants? Can this be moved to a separate PR?
There was a problem hiding this comment.
I think this is the only point I didn't reply below: it is related to multi-value variant as VariantMap derives from HashableMap. So the answer is : no, it cannot be moved to a separate PR.
| for name, variant in spec.package_class.variants.items(): | ||
| pkg_cls = spec.package_class | ||
| for name, variant in pkg_cls.variants.items(): | ||
| if name not in spec.variants: |
| spec.variants[name] = VariantSpec( | ||
| variant.name, variant.default | ||
| ) | ||
| return changed |
| pkg.variants[name] = Variant( | ||
| name, default, description, values, exclusive, validator | ||
| ) | ||
|
|
| raise UnknownVariantError(spec.name, vname) | ||
| # FIXME: Move the logic below into the variant.py module | ||
| # Ensure correctness of variants (if the spec is not virtual) | ||
| if not spec.virtual: |
There was a problem hiding this comment.
Should this be FIXed before the PR is merged?
lib/spack/spack/spec.py
Outdated
| spec._add_variant(self.variant(), True) | ||
| name = self.variant() | ||
| spec.variants[name] = VariantSpec(name, True) | ||
| check_valid_token = False |
lib/spack/spack/spec.py
Outdated
| spec._add_variant(self.variant(), False) | ||
| name = self.variant() | ||
| spec.variants[name] = VariantSpec(name, False) | ||
| check_valid_token = False |
| depends_on('curl', when='enable=curl') | ||
| depends_on('fftw', when='enable=fftw') | ||
| depends_on('magics', when='enable=magics') | ||
|
|
There was a problem hiding this comment.
So... what have we gained here? How is enable=hdf5,netcdf,etc. any better than +hdf5+netcf+etc, or even semantically different? I believe there are cases where multi-valued variants are useful, but I don't think this is one of them. I am dubious about how useful exclusive=False really is.
There was a problem hiding this comment.
This change change also changes how the CDO package works --- which, if it is a good idea, should NOT be combined with a PR adding core Spack functionality. Previously, some of the options were enabled, some were not enabled by default. Now it looks like they are all disabled by default.
OK... I see how code that generates the --with and --without stuff for Autotools is now in autotools.py so we don't have to put it in our packages. We should debate whether this is the direction we really want to go.
| config_args.extend( | ||
| self.with_or_without('enable', lambda x: spec[x].prefix) | ||
| ) | ||
| return config_args |
There was a problem hiding this comment.
This is cryptic. We should not expect package authors to use lambda. Understanding this code also requires one know what with_or_without() does --- a specialized function only useful for Autotools. In the end, I think the gain has been minor, if not net negative.
|
I would like to learn more. But from what I've seen, I would suggest not merging this PR at this time. The core question here is, what functionality is added by the proposed feature that could not be done without it? What I've seen reviewing the code is the ability to replace specs like What about More complicated cases --- for example, one of 'a' and 'b' can be set; and 'c' can be set only 'a' is set --- will need an explicit checker subroutine anyway. Once you introduce two ways to do something, different users will choose one way or the other in an inconsistent fashion. That will make it harder for Spack users to write every I see that the Autotools package is able to now automatically generate Where I had HOPED this PR was going --- but maybe I was wrong --- is allowing variants with values other than bool (and clearly other than enum). For example, Finally...
|
There's nothing disruptive here. In fact one of the design principle I followed was to keep everything backward compatible. Every package works as it worked before. The only differences are that now you can group variants semantically ( For me a prove this is a worth change are the 85 lines deleted in
Except that you completely miss the point of reponsibility in the right place: that feature makes sense only if you have to do with an autotools package, and thus the code is in the
I wrote an extensive description for this PR, that you probably didn't care reading. I never stated anywhere I was going to provide custom types in variants.
and
Not True. I introduced build_systems in #1182 and that was not the purpose. The purpose was to aggregate in a base class all the methods that were specific to a given build system and give package authors hooks to run things before and after certain phases. In general, I wanted to extract general code and make it available to packagers. This is also the purpose of this PR.
This is a specious argument. Apart from a couple that could probably be reverted now (but why as they are not controversial?) all the others are to please |
|
@alalazo I am truly sorry I didn't see the description at the top of the PR. I'm not sure why... I suppose I just didn't look for it in my haste. I did look for changes to After having read those docs, I still have questions.
To be specific, I'm looking at
Maybe we need to consider changing terminology. To me, a "variant" is something that comes after a
I still think we need to consider things carefully before folding more stuff into the framework. Because it makes it harder for a naive user to look at a recipe and have any clue what it does. This PR uses the word
Clearly, anything to please
|
Just to be clear, we've had non-boolean variants for a long time. Check out the NetCDF package for an example. I believe the syntax for calling variants like this is something like P.S. This PR looks awesome. I agree that we shouldn't convert all "provide support for XYZ" into a single I noticed you added the |
As far as I can see only the ones I modified. But as I changed things that are used to evaluate the hash I added the tag to remain on the safe side.
Yes, the changes on |
The command line would be: spack install openmpi fabrics=\'mxm,pmi\'This syntax is there since a long time. As far as I can tell it was introduced in #360 by @becker33. Another orthogonal PR that would be worth trying with respect to user facing syntax is a modification of the parser to avoid the
For this particular case: I also don't agree with the general argument. |
To me, this is a non-starter: users won't know whether to type We need to acknowledge that this PR is a change. It doesn't just add a feature, but also changes the way It may be true that syntax of In any case, all my objections can be addressed if the PR is extended so the UI does not change. As long as the user can say |
citibeth
left a comment
There was a problem hiding this comment.
Please keep the UI the same, avoid adding unnecessary complication to the UI. openmpi+mxm+pmi should work, whether mxm and pmi are separate variants, or they are part of the variant group (multi-valued variant) called fabrics.
I won't do this change here: the syntax changes because instead of having 2 boolean variants you have a multi-valued one in |
davydden
left a comment
There was a problem hiding this comment.
Please keep the UI the same, avoid adding unnecessary complication to the UI. openmpi+mxm+pmi should work, whether mxm and pmi are separate variants, or they are part of the variant group (multi-valued variant) called fabrics
i personally don't mind the change in UI. Spack is in alpha, new features sometimes break backward compatibility. I think the feature implemented by @alalazo is an important contribution to Spack and its community. As long as spack info is cleaver enough to show that a particular variant is a multi-valued one, I don't see any problems, 👍
p.s. As always it's up to @tgamblin and @becker33 to decide on merging.
Just to be clear: there's no "change in the UI". There are two packages that have been reworked to have different variants. If this is considered to be a "change in the UI", then so is #2323 and any other PR that changes the variants of at least a package in some way. |
yes, i think should not qualify as "change in the UI" as it is indeed just rework of variants in a few packages. I used this term just because I quoted the text which used it. |
|
I believe there is some misunderstanding why I don't think this PR is right for Spack (in its current form). It's not that it changes things; but that it introduces more than one way to do the same thing, and will introduce random inconsistencies into our package repos. Suppose I'm a packager, and I have a bunch of on-off switches for my package. I could choose to make each of them separate variant, or to group some of them into multi-valued variants. For example, I could decide to make Then some packager might (in an extreme case) decide to just put all variants into a multi-valued variant called A multi-valued variant with It is this kind of confusion that I'm trying to avoid. Similar to the idea that package names should be all lower-case: it just make things easier for the user, they never have to ask "is this package upper or lower case?" (BTW, we still need to convert a few packages to lower case. Obviously, we would have been better off if Spack had enforced this from the beginning and barfed on non-lowercase package names). This PR adds some nice features under the hood. For example:
I have suggested the concept of "variant groups" in order to keep the UI simple and predictable, while maintaining the under-the-hood improvements in this PR. My suggestion requires no fundamental changes to this PR, only:
This change would allow packagers to choose either separate or multi-valued variants based on their coding needs, while having no effect on the user. |
I kind of agree with this. There will be no way to enforce this, and it will end up differing inconsistently. Although to be fair, the only way to know what variants are available in the first place is to view the package.py or to run I do agree that some kind of multi-valued variant/variant group is necessary though. Ensuring that mutually exclusive variants aren't combined is a pain in the ass right now. And things like fabrics and schedulers are logically related. It would be good to show the relation when running @alalazo How hard would it be to create variant groups such that the "in the package.py" stuff looks like what you have now, but you can call it from the command-line like you used to (or call it both ways,
Is there any way we can get import argparse
parser = argparse.ArgumentParser()
parser.add_argument('cflags')
args = parser.parse_args()
print(args.cflags.split(' '))and then: does the reason have something to do with the concretizer? Couldn't we just add quotes to the string internally? Or are we just blindly passing everything after |
1ec7244 to
691121f
Compare
Saying exclusive=False violates our prohibition on negative logic being the common case. The default was exclusive=True, and the packager had to say exclusive=False to get multi-valued variants. Now the default is multi=False and the packager needs t ask for multi=True to get multi-valued variants.
|
@tgamblin I changed the doctrings only in |
|
Removing |
|
@adamjstewart: can you take this PR and try running Also, can you try running: where @alalazo has shown that last one to break, but I think it's an existing bug that is made more prominent by this change, not a bug with this PR. Essentially, Anyway, expect that bug, but I'm interested in whether you can also find new ones. |
|
Our cluster is undergoing maintenance at the moment, so when it's back up I'll test this. |
|
Alright, we're back up.
From a long-standing Spack installation, on
I'm personally okay with this. I don't normally try to force installations to use a specific hash. For any PR (not just this one), if a variant is added or removed, Spack no longer recognizes the previously installed versions. This is annoying, but I agree that you really can't tell how it was built. Although personally I think if a user isn't explicitly asking for it to be built a certain way, we should still use it. But yeah, nothing new with this PR. |
|
@adamjstewart: Thanks! I think we can merge this, and address the spec issue in a separate PR. |
|
Ok, y'all. Multi-valued variants are (finally) merged. @alalazo: can you update the docs? |
Modifications: - added support for multi-valued variants - refactored code related to variants into variant.py - added new generic features to AutotoolsPackage that leverage multi-valued variants - modified openmpi to use new features - added unit tests for the new semantics
Modifications: - added support for multi-valued variants - refactored code related to variants into variant.py - added new generic features to AutotoolsPackage that leverage multi-valued variants - modified openmpi to use new features - added unit tests for the new semantics
Modifications: - added support for multi-valued variants - refactored code related to variants into variant.py - added new generic features to AutotoolsPackage that leverage multi-valued variants - modified openmpi to use new features - added unit tests for the new semantics
Resolves #1341
TLDR
This PR adds support for multi-valued variants. An example from the
openmpimodule is:Here
valuesis the list of allowed values andexclusive=Falsemeans that more than one value can be selected simultaneously. One can thus say from the command line:spack install openmpi schedulers=lsf,loadlevelerThe appropriate configure options will be activated by the line:
in the
Openmpi.configure_argsmethod. The methodAutotools.with_or_withoutis automatically transforming all the values activated in the variant into--with-{value}, and all the missing ones into--without-{value}.The PR also refactors variant dependent code moving it in the
spack.variantmodule and adds unit tests for the classes invariant.py.Variant directive
This PR changes the variant directive in the following, backward-compatible way:
The semantics for the new arguments is:
values: either a tuple of allowed values, or a callable used as a single value validator (seenetcdffor an example)exclusive: if True only one value per spec is allowed for this variantvalidator: optional callable used to enforce group logic during the semantic validationThe last argument (
validator) won't be needed most of the time, but in complex cases likemvapich2it provides a convenient hook to enforce additional validation logic (in that particular case one can select eitherslurmalone as the scheduler or any other combination of all the non-slurm schedulers).Miscellaneous notes
_cmp_keyI put thehash-changetag to stay on the safe sideHashableMap, see belowVariantclass (attached to packages) will act as a validator for theVariantSpecclass (attached to specs). I considered a couple of different designs for this (likeVariantbeing a factory forVariantSpecinstances) and the one implemented here seems the cleanest to me.Modifications
openmpi,netcdf,mvapich2andcdoto use new features