Conversation
|
@mamelara please confirm this fixes the problem in your use case? I think my reproducer captured what was going on, but it's always good to check. |
|
I'll be able to look through this before Wednesday (8/2) |
scheibelp
left a comment
There was a problem hiding this comment.
Overall I would have preferred an approach which explicitly checks if the spec package is already installed and just avoids concretizing in that case. Is there a reason that would be problematic? I have a few notes on issues that I think would come up (but apparently aren't tested).
| sorted_keys = [k for k in sorted(self.keys()) if self[k] != []] | ||
| cond_symbol = ' ' if len(sorted_keys) > 0 else '' | ||
| return cond_symbol + ' '.join( | ||
| str(key) + '=\"' + ' '.join( |
There was a problem hiding this comment.
Why are the escape quotes no longer needed?
There was a problem hiding this comment.
Because we no longer use escape quotes on the command line, and the string representation only had them to match the command line syntax. This was really a change that was missed in a previous PR of mine that I noticed while working on this one.
| ret = False | ||
| for flag in spack.spec.FlagMap.valid_compiler_flags(): | ||
| if flag not in spec.compiler_flags: | ||
| spec.compiler_flags[flag] = list() |
There was a problem hiding this comment.
Since all this logic was moved into the "if", I don't see how this can add flags for a given category if there are already flags defined in the spec. I suppose a test must be missing for this. This seems to be the primary change that avoids adding compiler flags to already-installed specs. I would prefer a more explicit approach, for example add a check at the beginning of this method like if spec.package.installed: return
There was a problem hiding this comment.
Overall my understanding is that the original complaint on the slack channel had to do with flags being added to already-installed packages; the initial check for spec._concrete resolves this and the additional changes in this function are not necessary to deal with that problem. That being said my understanding is that this logic is intended to have the following effects:
- Ensure that you do not merge flags from different compilers
- Ensure that when a user explicitly sets compiler flags on a package, that flags from other compilers will not add to it
IMO this could benefit from an added unit test.
| ret = True | ||
|
|
||
| # Include the compiler flag defaults from the config files | ||
| # This ensures that spack will detect conflicts that stem from a change |
There was a problem hiding this comment.
Since this logic hasn't changed, is it possible that compiler flags will be added from the default compiler spec to the already-installed dependency?
There was a problem hiding this comment.
Not from the default compiler. But you're right, it could add flags to the spec if the flags for the compiler used to build that package have changed.
There was a problem hiding this comment.
Bailing out at the beginning of the function if spec._concrete resolves my original concern here. But I wanted to make sure: it was your intent that the compiler definition be able to merge into compiler flags explicitly set by the user (although above in this PR you don't want to merge flags from other compiler specs)?
| flags = set() | ||
|
|
||
| if (nearest_flags - flags) or n.compiler_flags[flag] == []: | ||
| spec.compiler_flags[flag] = list(nearest_flags | flags) |
There was a problem hiding this comment.
IMO this should still be changed (flags is an empty set)
| nearest_flags = set(n.compiler_flags[flag]) | ||
| flags = set() | ||
|
|
||
| if (nearest_flags - flags) or n.compiler_flags[flag] == []: |
There was a problem hiding this comment.
I would prefer or flag in n.compiler_flags to or n.compiler_flags[flag] == []. For that matter, if I'm right that flag in n.compiler_flags implies that n.compiler_flags is a nonempty list, then I think you can strip the "if" altogether and do the next line unconditionally.
There was a problem hiding this comment.
Those statements are not equivalent. flag in n.compiler_flags returns True iff there is no entry for flag in n.compiler_flags, while n.compiler_flags[flag] == [] returns True iff there is an entry for flag in n.compiler_flags and that entry is [].
There was a problem hiding this comment.
If it is true that flag in n.compiler_flags what are the possible values of n.compiler_flags[flag]? I was assuming it would be a (possibly empty) container of some sort. Can it be None?
There was a problem hiding this comment.
It being empty means something different than it not being present. Not present means it hasn't been concretized, empty means it has been concretized to empty.
There was a problem hiding this comment.
- From the logic preceding this statement, we know that
flag in n.compiler_flags == True - My understanding is that if
flag in n.compiler_flags, then it is a list (possibly empty).
If these two statements are correct, then based on how we arrived at this point, if nearest_flags or n.compiler_flags[flag] == [] is always true, and the if isn't required.
On top of that, based on the fact that you moved all of this into the if statement, the general logic is "set the flags here if they weren't set before"; but if we are at this point then we know that that self.compiler_flags hasn't been set.
|
Also I was having trouble replicating results when pulling this branch - If I install libdwarf with But if I then try the suggestion I get an error: Could you let me know if I missed something there? Thanks! |
|
In your attempted reproducer you used a backslash |
|
@scheibelp I added the explicit concreteness checks we discussed. |
|
I'll be able to look at this tomorrow (9/19) |
scheibelp
left a comment
There was a problem hiding this comment.
I have a couple questions, a couple requests for simplifying the logic, and a request for a unit test.
Most of that goes away if concretize_compiler_flags is changed to only have the initial check for spec._concrete (vs. the additional changes in it). The additional changes are fine but I wanted to clarify the intent there since I don't think they are strictly required to handle the bug this was originally intended to address. Copied from the relevant comment:
My understanding is that this logic is intended to have the following effects:
- Ensure that you do not merge flags from different compilers
- Ensure that when a user explicitly sets compiler flags on a package, that flags from other compilers will not add to it
IMO this could benefit from an added unit test.
| compiler_match = lambda other: ( | ||
| spec.compiler == other.compiler and | ||
| spec.architecture == other.architecture) | ||
| def compiler_match(other, strict=True): |
There was a problem hiding this comment.
IMO this shouldn't take a strict argument (I talk more about that where this function is called with strict=False) - i.e. I think the definition should stay the same.
Additionally: Now that there is a spec._concrete check at the beginning, I'm not sure that these changes are required now.
If you disagree, then IMO this could be more-succinctly written (in a way that makes it clearer):
compiler_matches = (spec.compiler == other.compiler and spec.arch == other.arch)
other_compiler_is_concrete = other.compiler and other.compiler.concrete
return (strict and compiler_matches) or not other_compiler_is_concrete
| flags = set() | ||
|
|
||
| if (nearest_flags - flags) or n.compiler_flags[flag] == []: | ||
| spec.compiler_flags[flag] = list(nearest_flags | flags) |
There was a problem hiding this comment.
IMO this should still be changed (flags is an empty set)
| nearest_flags = set(n.compiler_flags[flag]) | ||
| flags = set() | ||
|
|
||
| if (nearest_flags - flags) or n.compiler_flags[flag] == []: |
There was a problem hiding this comment.
- From the logic preceding this statement, we know that
flag in n.compiler_flags == True - My understanding is that if
flag in n.compiler_flags, then it is a list (possibly empty).
If these two statements are correct, then based on how we arrived at this point, if nearest_flags or n.compiler_flags[flag] == [] is always true, and the if isn't required.
On top of that, based on the fact that you moved all of this into the if statement, the general logic is "set the flags here if they weren't set before"; but if we are at this point then we know that that self.compiler_flags hasn't been set.
| except StopIteration: | ||
| try: | ||
| n = next(p for p in spec.traverse(direction='parents') | ||
| if (compiler_match(p, False) and |
There was a problem hiding this comment.
Given the definition of compiler_match and the prior checks performed. This would be equivalent to
if any((not x.compiler or not x.compiler.concrete) for x in spec.traverse(direction='parents'))
And:
- would be clearer
- avoids nested try/catch (not an error, but also makes things simpler)
| ret = True | ||
|
|
||
| # Include the compiler flag defaults from the config files | ||
| # This ensures that spack will detect conflicts that stem from a change |
There was a problem hiding this comment.
Bailing out at the beginning of the function if spec._concrete resolves my original concern here. But I wanted to make sure: it was your intent that the compiler definition be able to merge into compiler flags explicitly set by the user (although above in this PR you don't want to merge flags from other compiler specs)?
| ret = False | ||
| for flag in spack.spec.FlagMap.valid_compiler_flags(): | ||
| if flag not in spec.compiler_flags: | ||
| spec.compiler_flags[flag] = list() |
There was a problem hiding this comment.
Overall my understanding is that the original complaint on the slack channel had to do with flags being added to already-installed packages; the initial check for spec._concrete resolves this and the additional changes in this function are not necessary to deal with that problem. That being said my understanding is that this logic is intended to have the following effects:
- Ensure that you do not merge flags from different compilers
- Ensure that when a user explicitly sets compiler flags on a package, that flags from other compilers will not add to it
IMO this could benefit from an added unit test.
|
Closing: This is superceded by #5580 |
This fixes an issue reported by @mamelara on the slack channel.
Previously, packages specified by hash would inherit additional compiler flags in the spec from their parents, and would therefore rebuild.
Example of previous behavior, assuming there is an installation of libelf with hash beginning
lgvbuilt withcflags="-g -O0":Fixed behavior: