Conversation
Still need to parameterize the unit tests.
Generalized the _create_test_repo test fixture to be parameterized.
|
This is similar to #35045 and currently discussed again. The idea was not to do verbatim mappings of |
I think I understand this use case but just want to clarify before I modify my code. In my current implementation this is achievable via two statements but I could modify it to just be a single function if that is the desired functionality. class Parent(Package):
depends_on("mpi")
class Child(Parent):
# Proposal with one function call
remove_depends_on("mpi", when="@4.0:")
# Is currently equivalent to two function calls
remove_depends_on("mpi")
depends_on("mpi", when="@:3.9") |
|
It's not very ergonomic when there are multiple |
|
I'll take a crack at implementing this to avoid the multiple |
Started modifying remove_conflict to use the complement methods.
Added support for partially removing conflicts based on ranges.
|
I added support for partially removing directives as suggested. The following now works class X(Package):
conflicts("mpi", when="@3:")
remove_conflict("mpi", when="@5:")
# This is equivalent to conflicts("mpi", when="@3:4")I need to clean things up a bit and apply the same support to the other directives. @haampie Does this satisfy the feature you were suggesting earlier? It would probably be good to have someone familiar with the versioning code check how I implemented the |
* develop: (752 commits) mesa: add v23.3.3 and use py-packaging while python>=3.12 (spack#49121) gcc: add v15.1.0 (spack#50212) draco: add v7.20.0 (spack#49996) sgpp: update dependencies and variants (spack#49384) input_analysis.py: fix conditional requirements (spack#50194) boost: add 1.88.0 (spack#50158) mapl: add v2.55.1 (spack#50201) mepo: add v2.3.2 (spack#50202) py-repligit: add v0.1.1 (spack#50204) [package updates] Bump version of cp2k and sirius (spack#50141) petsc4py: update ldshared.patch for v3.20.1, and skip for v3.23.1+ (spack#50170) namd: add v3.0.1 (spack#50192) geomodel: depend on c (spack#49781) CompilerAdaptor: add support for opt_flags/debug_flags (spack#50126) Add ls alias to spack {compiler, external} (spack#50189) covfie: depend on c (spack#50190) lua-sol2: add v3.5.0 (spack#49970) crtm-fix: fix directory logic (spack#50172) py-build: add v1.2.2 (spack#50148) py-pillow: fix build (spack#50177) ...
Started turning remove_directive into a class.
Started parametrizing tests for removal directives.
| def drop_version(ver: Union[str, int]): | ||
| """Try to remove a specific version from a package.""" |
There was a problem hiding this comment.
Can this be made into drop_versions(), and can it accept a VersionRange or a VersionList?
Ideally this would use satisfies to figure out what to drop so that I can drop ranges of versions, e.g., given:
class Gcc(Package):
version("14.2.0")
version("9.5.0")
version("8.5.0")
version("8.3.0")
version("5.4.0")
version("5.2.0")
version("4.9.3")
version("4.9.2")
version("4.9.1")You could write:
drop_versions("=4.9.2,5.4.0:8,14")
and get:
class MyrGcc(Package):
version("9.5.0")
version("5.2.0")
version("4.9.3")
version("4.9.1")Can that work?
There was a problem hiding this comment.
Yes, I'm modifying it right now to do that. I'm currently adding support for DropPatch and DropVersion but have had to generalize the code a bit because of their different function signatures. Not a big deal but I probably won't finish until next week. The good news is that the generalization should make it apply to all of the directives.
There was a problem hiding this comment.
I have drop_patch working now and will do drop_version(s) next week. I've had some time to think about your request for drop_versions. I'm still open to doing the drop_versions modification but have realized that some of the directives like drop_patch accept a lot of arguments so they probably won't be compatible with the version range idea. Even drop_version will have a similar issue when it supports the when argument. I've just been doing [drop_version(ver) for ver in ["1.3", "1.1"]] to achieve something similar. Anyways, I'll update everyone when this is ready for another review.
…n1/spack into override-package-directives
becker33
left a comment
There was a problem hiding this comment.
We do not have a general Spec.complement() method, and I think that without one it is not possible to implement the when argument in this PR correctly. I see 3 things we can do about this:
- Remove the
whenoption for now, and make it future work for when we have aSpec.complementcapability - Implement a general
Spec.complementcapability - Leave this as-is and document (very loudly) that the
whenclauses fordrop*directives only accept versions. - Replace the
whenoption with anexceptoption (eliminating the need for a complement operation).
I think (2) is infeasible -- the spec syntax does not lend itself well to a complement capability. It's not clear what the complement of +foo is for a spec that only defines that variant on some subset of versions (presumably it's the union of ~foo and the complement of the versions on which foo is defined?), and it's even less clear what the complement of cflags=-O3 is (I think it's any setting such that -O3 is not in the cflags?) and neither of those are things we can currently express in a Spec object.
I'm open to option (3), but I understand why some maintainers won't be (because it is a deviation from what when accepts in every other directive, it will be hard for users to keep the mental model.
@danielsjensen1 do you have strong feelings between options (1), (3), and (4)? Is the when option necessary for the use case that's motivating you to work on this?
| filtered = {} | ||
| for when, directives in directive_dict.items(): | ||
| for directive_entry in self.iterate_directives(directives): | ||
| if self.get_directive(directive_entry) == removal_directive: |
There was a problem hiding this comment.
I think this would be clearer if we combine the get_directive and make_directive methods into a single method entry_matches_removal. Then this line becomes
| if self.get_directive(directive_entry) == removal_directive: | |
| if self.entry_matches_removal(directive_entry): |
and the default implementation for that method could be return self.get_directive(directive_entry) == self.make_directive(pkg), but it gives us more room to rename those methods to something easier to follow as well.
| def add_to_filtered(self, filtered, when, directive_entry): | ||
| filtered.setdefault(when, []).append(directive_entry) |
There was a problem hiding this comment.
It would be great if we could refactor the add_to_filtered methods for each directive type to share code with how the entries are added to the dictionary in the directive itself. It seems fragile to have two different ways of registering a conflict / dependency / etc. in a dictionary implemented separately in two different code paths.
| def iterate_directives(self, directives): | ||
| return directives.items() | ||
|
|
||
| def get_directive(self, directive_entry): |
There was a problem hiding this comment.
Reading this, I'm curious about the following case:
class Foo(Package):
depends_on("mpi@1:")
class Bar(Foo):
drop_depends_on("mpi")
It appears that in this case bar would still depend on mpi@1, is that correct?
We should consider whether it's better to have spec satisfaction as the matching function for these cases. It seems more natural that way, although it does make it impossible to wipe out a general constraint without also wiping the more specific constraints.
There was a problem hiding this comment.
Yes, you are correct that this will still depend on mpi@1 with the current code. I'll work on changing it to do spec comparison.
lib/spack/spack/directives.py
Outdated
| * ``version`` | ||
| * ``requires`` | ||
| * ``redistribute`` | ||
| * ``drop_all_conflicts`` |
There was a problem hiding this comment.
Once we are happy with the rest of this PR, we should look at extending it to extends, provides, variant, and maybe even resource. We should also definitely add a drop_all_patches directive, but that's trivial to add.
|
Thanks @becker33 for looking at this and your feedback. My main motivation for this work was to override some |
# Conflicts: # lib/spack/spack/directives.py # lib/spack/spack/package.py # lib/spack/spack/test/directives.py
|
@danielsjensen1 we talked about this in the Spack team meeting today.
Where do you stand on coming back around to this? Can we make it a priority for Spack 1.2? |
|
Thanks @becker33 for helping make the decision. I'll start cleaning things up and make this a priority for Spack 1.2. |
|
@danielsjensen1 any updates on this? I'm hungry to use it... |
|
@danielsjensen1 are you going to have time to work on this? I would like to get it in for 1.2, as I think this is a really valuable feature, but I realize you have other stuff going on! Can we help? |
|
I'm working on this again now. The |
This merge request adds support for removing directives when desired. This is especially useful when inheriting a package because often the child doesn't have the same versions, dependencies, conflicts, etc. In the past I've used hacks to get around this as shown below. I tried to follow the style in the surrounding code but am open to suggestions if anyone thinks it should look or function differently.
Example of old hack:
The Xyce package has the following dependency.
I can change this dependency in a child class using the following two lines
In this case the Trilinos dependency is incorrect and I should (and will eventually) fix the parent package. The proposed code in this merge request would change the lines above to
If this is acceptable and on the right track then I would be open to adding support for deleting other directives; I've just found that the
versions,depends_on, andconflictsdirectives are the most important for me.