-
-
Notifications
You must be signed in to change notification settings - Fork 300
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
-include: Show warning on counter-intuitive overwriting of properties #6493
base: master
Are you sure you want to change the base?
-include: Show warning on counter-intuitive overwriting of properties #6493
Conversation
@kriegfrj Like this? This is a pretty naive implementation. Not sure what side-effects there are. https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib.tests/testresources/ws/p1/bnd.bnd used by
which now fails because
is not true anymore, because of the new "Duplicate property key" warning. Thoughts? |
@kriegfrj do you have any opinion on the above? |
You are too quick for me! Sorry for the delayed response. The screenshot you showed (with the text changed as you noted) looks like a perfect implementation from a visual perspective. Regarding possible side-effects: I feel that the process of working through and fixing the test failures will help to determine the answer to that question. If there are significant behaviours that rely on the presence of duplicate properties, they should already be covered by test cases. One side effect that the implementation will need to handle: I recall (though admittedly my recollection may be a bit rusty) that there are some property types that are designed to be "mergeable" - ie, instead of silently ignoring subsequent property settings, it merges the setting with the inherited value. The implementation would need to be able to preserve this behaviour and only apply to non-mergeable properties. I think the test cases will already cover this. There might be some test cases which have duplicate properties in them for no good reason. The one that you flagged above seems to be one of those. In that case, I think we can simply alter the bnd.bnd file in the test to remove the duplicate headers (as the headers seem to be dummy placeholders that aren't used in the test anyway). Aside from any issues that the test cases highlight, I can't foresee any significant negative unintended side-effects of this change, because in the current implementation of bnd these headers would be silently ignored anyway and so don't have a material effect on the build results. The only negative effect (that I can see) is that someone's workspace that was previously showing as clean may start showing up with warnings. I would argue that this is actually a good thing for them in the long term as their "no warning" build result is actually a false negative. Morever, there probably shouldn't be too many instances of this happening, and the fix (in most cases) will be fairly easy (remove the useless property setting). If they have too many warnings to fix in one go, people can sweep the warnings under the carpet by adding a global Unless @pkriens has a concern, I am happy for you to proceed (with the caveats above). |
I just remembered: mergeable properties still don't allow duplicate keys - they are implemented by adding suffixes to the base property name (eg, |
thanks a lot @kriegfrj
I think so too but just wanted to have some confirmation from the long time bnd people :) So I conclude that currently it looks like there is no reason to have real duplicated keys in .bnd files , since they are skipped anyway (last one wins). So the warning is not bad. It rather gives the user a hint where to cleanup to avoid unintended side-effects. I will go forward and cleanup the .bnd files in the testcases. I did not want to do this in the first place, to first get feedback from you, if this is the right thing to do. I interpret the 👍 from @pkriens as a a "go" too 😄 I will post an update here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that the test and the change are in separate commits. @bjhargrave used to like changes in the code to be committed in the same commit as the corresponding test changes - it makes cherry-picking easier. I think this is a good in general but I'm not sure what the current committers think. I would rebase - put commit 2 first, and then merge 1 & 3 into a single commit (if that's not too difficult).
Other than that (and the below suggestions), I think we're good to go!
@@ -176,6 +180,10 @@ void parse() { | |||
|
|||
} | |||
|
|||
private boolean isDuplicateKey(String key) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method should simply be called containsKey()
, because this is what it is actually testing. Whether you are looking for a duplicate depends on the calling context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I can change that. IMO isDuplicateKey()
conveys the intention of duplicate checking better, because duplicate checking is what I want to do here ... but not worth to argue :)
assertFalse(project.check("Duplicate property keys")); | ||
assertEquals(1, project.getWarnings() | ||
.size()); | ||
assertEquals(0, project.getErrors() | ||
.size()); | ||
assertEquals("Duplicate property key: `Header-1`: <<Header-1: 2>>", project.getWarnings() | ||
.get(0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using AssertJ soft assertions here. I know that many of the other tests do not use them but that's mostly because those tests are ancient - a lot of the newer ones are starting to use them.
assertFalse(project.check("Duplicate property keys")); | |
assertEquals(1, project.getWarnings() | |
.size()); | |
assertEquals(0, project.getErrors() | |
.size()); | |
assertEquals("Duplicate property key: `Header-1`: <<Header-1: 2>>", project.getWarnings() | |
.get(0)); | |
assertThat(project.check("Duplicate property keys")).as("check").isFalse(); | |
assertThat(project.getWarnings()).as("warnings").containsExactly("Duplicate property key: `Header-1`: <<Header-1: 2>>") | |
assertThat(project.getErrors()).as("errors").isEmpty(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok will do.
Can I also double-check this comment? I think in my experience it was the first one that "won" for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with all of @kriegfrj comments.
@@ -149,6 +149,10 @@ void parse() { | |||
error("Invalid property key: `%s`", key); | |||
} | |||
|
|||
if (isDuplicateKey(key)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This presents a huge risk of breaking builds which now work. I don't think you should error. Perhaps a warning IF in pedantic mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bjhargrave As far as I see it the PropertiesParser.error() method actually makes only a warning:
loc = reporter.warning("%s: <<%s>>", Strings.format(msg, args), context); |
So does this warning break builds?
If not, should I better rename the method to "warning()"? It is private anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on @chrisrueger 's test case, I think that this does simply create a warning rather than an error in the final build result. So it shouldn't break a build.
Header-1: 1 | ||
Header-1: 2 | ||
Header-1: 3 | ||
Header-1: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an example of a bnd file which worked fine but you had to change to pass the tests. I think error on duplicate is a mistake. Warning when pedantic is probably the best you can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bjhargrave I was unsure wether to adjust the bnd.bnd or to adjust the test.
#6493 (comment)
I could also adjust the test and account for the warning.
Maybe should decide based on the outcome of #6493 (comment) wether this breaks the build or not.
Yes I will squash after review. |
Hmm as far as I can see it in e.g.
it just calls |
bnd files are java properties files. It is not illegal to have duplicate keys in a java properties file. So I think that we are in pedantic mode, then we can warn on duplicate keys. But otherwise we should not error or warn on duplicate keys. |
Ok. But doesn't a
Ok then I think we should find another approach which is more centered around the UI and not as deep as So let me come up with a different way. I think about
|
One thing I still don't understand: Why can a warning break builds? (I would understand error does, but warning?) @bjhargrave |
I like builds where there is no output which means success. (maven is not good at this, but gradle is better). When new warnings show up, my clean output is lost (unless I -fixupmessages to get back to clean). :-) |
I like that too :)
But this means we can never add any new warnings anymore. Sure I understand it's debatable what qualifies to be worth to be warned of. Anyway, thanks for the clarification. |
@bjhargrave wrote:
I think that you're asking the wrong question. It is true that that every valid Bnd file is a valid Java properties file - it does not automatically follow that every valid Java properties file should be a valid Bnd file. I believe that a duplicate key in a Bnd file is a problem - unless you can show me a valid use case, as far as I can see at best the duplicate will do nothing, or at worst it can cause the build to produce incorrect results. Not only can it produce incorrect results, but it can do so in a way that is non-intuitive and difficult to fix - as I noted in the initial issue, I ended up wasting a couple of hours on what turned out to be a simple fix, And I like to think of myself as a seasoned Bnd developer... I pity someone with less experience accidentally stumbling upon the same issue. Based on this alone, I argue that duplicate keys should be considered "dirt" in your workspace. The question, in my mind, is not whether it should be considered dirt, but whether we want to be able to see the dirt or to hide it.
I like this too, but the question is how does one best obtain the clean output - by finding and removing the dirt, or by hiding/ignoring it? And as @chrisrueger pointed out, if we were to follow this argument, we'd never add any new warnings because we might break existing builds. I don't see new warnings as a big issue because if you really want to go the "hide it" route, there is always the I think the fundamental question is: should a duplicate key be considered "dirt"? I think the answer is a clear yes (per above) and for that reason it should be a warning. I guess that we could argue about whether it meets the threshold for "pedantic", but unless you can identify a valid use case where a Bnd file has duplicate properties, I think it is best to introduce the warning. |
@kriegfrj wrote:
Well, that conclusion didn't last long... I've had a rethink.
Hmm, perhaps the issue here is not that we have duplicate keys, but a question of whether it is last-one-wins vs first-one-wins. I can now see a valid use case for duplicate keys if the syntax is last-one-wins (you override an inherited value). So maybe my problem was not simply that there was a duplicate, but that the last one wasn't winning. I'll investigate more - perhaps hold off on further implementation until I've had a chance to do this. |
@kriegfrj On that note: Do you know the new "Effective" tab in the bnd-editor (recently introduced... you need current SNAPSHOT build)? I recommend you get that because it might already be of some help to spot the actual (effective) value of a property, and also takes inheritance into account: Example:
![]() ![]() ![]() Here you see that my (duplicated) Now assume I define Header1 only in ![]() Now you see in the Effective-Tab of |
Yes, i saw the effective tab pop up when i upgraded the other day - that would indeed have been a useful debugging aid!
Yes, this is the problem I was having - my duplicate was from explicit include not implicit as with build.bnd, but likely the same root problem. Because it was in a different file it was not obvious. I think this behaviour is counter-intuitive. I think it would be more useful behavior if the property setting in the including file should override the property setting in the included file, not the other way around. I think one of the reasons i got stuck on it was because i was assuming "last wins" semantics. But this would be a breaking change and would need to be handled carefully. I think duplicate properties in the same file should still be a warning though, but it's actually a slightly different problem. |
You can handle this by prefixing the include file name with # Don't overwrite any existing properties (my.prop, will not be overwritten by my.prop in no.overwrite)
my.prop = don't overwrite
-include ~no.overwrite |
@kriegfrj Ok, after reading this sentence and the response @bjhargrave and re-reading [your initial ticket] (#6491 (comment)), I recognize I was completely on the wrong track. So I basically solved a different problem than you had in mind. My question is: |
That's useful to know, thanks! In my case though, the order of the two statements was reversed - the include was at the top of the file and the local definition came after. In your example it would at least be intuitive for the include to override the local definition because the include comes after the local definition. If there is good reason for this counter-intuitive behaviour that's fine - however, I think that makes an informative warning all the more necessary. Like: Property definition ineffective due to duplicate in included file x.bnd; consider using ~x.bnd. |
The order does not matter.
|
@bjhargrave wrote:
Thank you - that certainly explains Bnd's current behaviour and aligns with what I was seeing. However, I think it's still a worthwhile discussion to have as to whether this behaviour is counter-intuitive, and whether (and how) it might be improved. I would argue that the intuitive understanding of: -include: blah.bnd
my-prop: setting ... is that my-prop should override any setting of @chrisrueger wrote:
Me too! I did not realise that my problem was more complicated than merely having duplicate keys.
I think we have identified separate (but related) problems:
I think ideally we should fix the counter-intuitive behaviour but that would definitely be a breaking change and would need to be handled carefully. In the meantime, implementing warnings for duplicate properties would probably alleviate the second problem even if it doesn't fix it. Back to your current implementation: the above discussion affirmed that the properties are "last-wins", but your implementation will not flag the first property - only the second and subsequent. In other words, the first "loser" would not be flagged, and the "winner" would be flagged. Thinking of my use case, it meant that the property that was being ignored would not have been flagged with a warning - the warning would have appeared on the included file, which would be easier to miss. Ideally, when the property is stored its location would also be stored (not sure if this happens already?), and then when the duplicate is found it should enter the warning at the old location, not at the current location. You could then have the warning say |
I just had a quick look at the source and I believe that the above would be possible. For the processing of includes, we might need some code here somewhere which merges the properties from the included files, where the warning can be issued at the appropriate location: bnd/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java Lines 821 to 835 in 9ed91bd
|
I think so too, that we are dealing with separate problems and need to be careful about word choice, to not mix up certain aspects.
I would slightly rephrase to be more precise and not fall into the same trap I fell initially: 1. Warning about duplicate properties.
We should:
2. Counter-intuitive inheritance for duplicated properties ( This is a different beast. But I see it was your initial problem in #6491 |
@kriegfrj little update: I think I made some progress on the "unintuitive -include case" where your local property was overwritten by the include.
bnd/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java Lines 821 to 835 in 9ed91bd
Yes, thanks for that pointer. This is the place where I am fiddling at currently. My Example:
The warning will appear at 'the first loser". |
This is great progress, well done! That's exactly what I think we need. I have a minor suggestion about the wording: Instead of |
b0e9b19
to
30e8292
Compare
Thanks @kriegfrj for the feedback. Regarding wording: I am open for suggestions. But I like to have the message some kind of categorizing prefix.
Note: To reduce the cases where the warning is logged to a minimum, I currently only log if:
Conclusion: This PR now contains handling for the two cases of "duplicates" a) same-file-duplicate: the key appears multiple times within the same .bnd file At the end we should decide, if we really want to solve a) and b) both or just b). We could splitt the PR in multiple PRs if we want, because it was mainly my / our initial misunderstanding of the problems. Note: I will do cleanup, commit squashing at the end. Let's focus on getting the functionality right for now. |
Signed-off-by: Christoph Rueger <[email protected]>
testWarningOnCounterIntuitiveInclude Signed-off-by: Christoph Rueger <[email protected]>
Signed-off-by: Christoph Rueger <[email protected]>
adb8323
to
9c1519f
Compare
@kriegfrj @bjhargrave I have split this PR into two now. I updated the description of this PR to reflect what is left in here. So this PR remains only for the case of "Counter-intuitive -include first-wins behavior" The new PR for the "Duplicate properties in the same .bnd file" is here: |
Closes #6491
As suggested in the ticket
In this PR
This PR has some history / discussion and this description is updated to reflect the state of this PR at 2025/03/09.
(You can skip most of the initial discussions until #6493 (comment) )
This PR addresses a "usability" problem with -include which is mainly about this behavior:
The problem is that this behavior could be unexpected, because
-include: a.bnd
is included before theImport-Package: a.b.
So one might think that the last one wins. But this is not the case. Default behavior of
-include
is first-wins.This PR adds a warning so that developers notice this much easier.