-
Notifications
You must be signed in to change notification settings - Fork 6.9k
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
Fix python variant of merge_config #5417
Conversation
Should we fix the parsing of these "not set" lines for the future, in case someone mistakenly adds one? |
I am still not sure if this was actually valid but we can do that, it will require rewriting the python script though. |
I think the "is not set" syntax is valid kconfig fragment syntax, even though it looks like a comment. Are you sure about this? |
it is valid for kconfig in general, was not sure if it is supported as fragment. We have been using direct assignment instead with the exception of the few files changed in the PR. |
I believe we should support this "as fragment", because otherwise it wouldn't be valid cp build/zephyr/.config prj.conf which is the recommended way to save a GUI-edited .conf file isn't it? |
In the title, veriant should be variant. |
Is “is not set” used only for variables that default to “n”, and never for ones that have been explicitly ser to “n” but default to “y”? Maybe we could simply ignore lines that begin with “#”? |
I now have a version that mimics the original shell script and does exactly the same, will upload after more testing |
8d525ab
to
eba4958
Compare
updated, still WIP, just uploaded to get through sanitycheck |
ffc38af
to
0d02c09
Compare
ok, I think i have it now, please review |
scripts/kconfig/merge_config.py
Outdated
else: | ||
with open('%s/.config' % args.output_dir, 'w') as file: | ||
file.write(initfile_content) | ||
with open('%s/.config.test' % args.output_dir, 'w') as file: |
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.
Is this debug code that was included by mistake?
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.
yep
Changed the python variant to support fragments with comments indicating an option is not being set. Signed-off-by: Anas Nashif <[email protected]>
0d02c09
to
7cbac50
Compare
There is something off with the redundancy-detection logic. I have a bunch of fixes/refactorings on top of your work so I'll do a PR to your branch that fixes the fact that the redundancy detector uses substring to detect redundancies.
|
maybe
|
Rewrite of merge_config.py, several unrelated changes including; Do not attribute meaning to lines that look like this: Only to lines that look like this: Removed the unimplemented 'runmake' and 'alltarget' arguments. Invoke the python interpreter instead of the script directly to avoid reliance on bash's shebang feature. Use exact line match instead of substring to detect redundant config's. And more ... Signed-off-by: Sebastian Bøe <[email protected]>
rewrite merge_config
As a nitpick: it's often better to use a list comprehension instead of |
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 it's almost there, but still has some issues. Here is an update which hopefully fixes the issues I've pointed out in this review:
# '# CONFIG_FOO is not set' should be treated by merge_config as a | ||
# state like any other | ||
|
||
is_not_set_pattern = re.compile('^# (CONFIG_[a-zA-Z0-9_]*) is not set.*') |
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 like this change a lot.
# In other words [(config_name, config_line), ... ] | ||
# | ||
# Note that "# CONFIG_ARM is not set" is not the same as a | ||
# comment, it has meaning (for now). |
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 really think the confusing "is not set" syntax is the "correct" syntax within kconfig.
Some data on "# CONFIG_FOO is not set" versus "CONFIG_FOO=n":
-
465 defconfig files in Linux v4.14 use "# CONFIG_FOO is not set" syntax. defconfig files are not full .config files, but they do determine the default settings, so they are fragments that need to be merged properly.
-
In contrast, only 1 defconfig (for the unicore arch, so not exactly common) and only 38 files total in 4.14 use CONFIG_FOO=n, and almost all of those are in the rcutorture selftests. So that seems to be the "wrong" syntax for config fragments, since it isn't really used anywhere, even though people say CONFIG_FOO=n informally.
All of the following also seem to say that "is not set" is valid:
- https://github.com/torvalds/linux/blob/master/scripts/diffconfig#L39 -
- https://github.com/torvalds/linux/blob/master/scripts/config#L166 -
- https://stackoverflow.com/questions/41885015/what-exactly-does-linux-kernels-make-defconfig-do
So I think that the Zephyr habit of writing CONFIG_FOO=n directly in the .conf fragments should probably be deprecated.
Don't get me wrong; I think this is an extremely annoying and confusing "feature" of Kconfig. It's even worse that CONFIG_FOO=n usage happens sometimes, even in upstream Linux. But I think it's important to preserve behavior in the Python re-implementations as long as Zephyr still uses Kconfig.
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 disagree. IMHO "# is not set" should eventually be deprecated in favour of "=n" for obvious reasons.
Zephyr usability trumps Linux kernel Kconfig compatibility.
EDIT: #5443
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 disagree. IMHO "# is not set" should eventually be deprecated in favour of "=n" for obvious reasons.
I can see why you say so.
There are many more people who are familiar with the Linux style than the Zephyr style, though, and I think it's "obvious" that we want to obey principle of least surprise, especially since Zephyr is a Linux Foundation project.
@nashif, what's your decision?
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.
If I may weigh in, I think that both formats should be supported and should do exactly the same:
CONFIG_BT=n
# CONFIG_BT is not set
Should both turn Bluetooth OFF if present in the .conf or defconfig files
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.
Maybe I've missed something, but does conf
treat those 2 differently in the .config
file?
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.
To get this merged, I suggest we retain KBuild compatibilty for now, and then we can discuss the long-term Kconfig syntax at a later point.
print("-- Merging {}".format(fragment_path)) | ||
|
||
for config_name, line in fragment_conf: | ||
for (i, (prev_config_name, prev_line)) in enumerate(list(conf)): |
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.
In the worst case, if there are m
config fragments with n
CONFIG_ lines each, this runtime is O(m * n^2)
. It could be O(m * n)
by keeping a hash of the configs you've seen before and handling the "seen it before" test with a membership test. This can be done by using an OrderedDict from CONFIG_ to line instead of list of (CONFIG_, line) tuples.
# state like any other | ||
|
||
is_not_set_pattern = re.compile('^# (CONFIG_[a-zA-Z0-9_]*) is not set.*') | ||
pattern = re.compile('^(CONFIG_[a-zA-Z0-9_]*)[= ].*') |
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 has issues:
-
It matches strings like "CONFIG_FOO did not actually receive a value".
-
If the base file sets "# CONFIG_FOO is not set", and some other fragment sets "CONFIG_FOO=n", that gets treated like an override, even though it's redundant (and CONFIG_FOO=n should be changed to "is not set").
I think two changes are needed:
-
this function should probably patch things up for now by converting "=n" to "is not set", then the .conf fragments should be changed to "is not set" syntax. The "patch-up" can then be removed if necessary.
-
if the line doesn't match "CONFIG_FOO=n" exactly, the pattern should be
'^(CONFIG_[a-zA-Z0-9_]*)\s*=.*'
instead
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.
"It matches strings like "CONFIG_FOO did not actually receive a value"
That is a minor problem because that is invalid syntax, but it would be great for
usability if you were able to fix this!
If the base file sets "# CONFIG_FOO is not set", and some other fragment sets "CONFIG_FOO=n", that gets treated like an override, even though it's redundant (and CONFIG_FOO=n should be changed to "is not set").
I don't see how this affects the user, but if you can find a clean fix then why not :)
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.
That is a minor problem because that is invalid syntax, but it would be great for
usability if you were able to fix this!
Glad you agree it's OK to fix. It's fixed in my patch.
I don't see how this affects the user, but if you can find a clean fix then why not :)
This script prints warnings on overrides. Warnings affect users.
is_redefine = line != prev_line | ||
if not args.quiet: | ||
if is_redefine: | ||
print("-- Value of {} is redefined by fragment {}".format(config_name, fragment_path)) |
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 it'd be an improvement to print these to stderr
@carlescufi make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET
It's very possible that I've misunderstood some nuance of how it is supposed to work, so don't be afraid to correct me if I have. :) |
@ulfalizer The thing is that Zephyr uses it slightly differently than the kernel. Right now we invoke merge_config.sh like so:
Note the So if I understand you correctly your script does both the EDIT: What I mean is that if I add this in
then the resulting |
@carlescufi I actually did an earlier version that functions more like the Zephyr version before I looked closer at with open(sys.argv[2], "w") as merged_config:
node = kconf.top_node
while True:
if isinstance(node.item, Symbol) and node.item.user_value is not None:
if node.item.visibility:
merged_config.write(node.item.config_string)
else:
print("warning: an assignment to {0} appeared in one of "
"the config fragments but couldn't be respected in "
"the merged configuration, due to {0} not being "
"visible".format(node.item.name))
# Iterative tree walk using parent pointers
if node.list:
node = node.list
elif node.next:
node = node.next
else:
while node.parent:
node = node.parent
if node.next:
node = node.next
break
else:
break The iterative tree walk could be replaced with something simpler to read like in That spammy warning could be replaced with something more informative like in the first version too. |
@ulfalizer thanks very much for all the suggestions. |
That version still looks at the |
@ulfalizer yes, and that's why I think the better solution is to consolidate the Kconfig parsing and the fragment merging in your first version instead of keeping it separate. |
@carlescufi Yeah, I think that might lead to less head-scratching from tricky dependency stuff in the end too. |
@ulfalizer since both you and @nashif are here, I think there was a change that we required to |
@carlescufi A loop in the |
@ulfalizer well thanks very much for the offer! I will let @nashif confirm since he's the one that has been looking into this switch from C to Python in detail. |
already have a patch for this: commit 35c4ef1f65d37c4e0cd0c6fea9fa9138a9418eb0
Author: Anas Nashif <[email protected]>
Date: Tue Dec 12 10:46:23 2017 -0500
zephyr: support globbing
Signed-off-by: Anas Nashif <[email protected]>
diff --git a/kconfiglib.py b/kconfiglib.py
index 4d763a3..254c8b4 100644
--- a/kconfiglib.py
+++ b/kconfiglib.py
@@ -368,6 +368,7 @@ import os
import platform
import re
import sys
+import glob
# File layout:
#
@@ -1453,12 +1454,15 @@ class Kconfig(object):
prev_node.next = prev_node = node
elif t0 == _T_SOURCE:
- self._enter_file(self._expand_syms(self._next_token()))
- prev_node = self._parse_block(None, # end_token
- parent,
- visible_if_deps,
- prev_node)
- self._leave_file()
+ f = self._expand_syms(self._next_token())
+ g = glob.glob(f)
+ for s in g:
+ self._enter_file(s)
+ prev_node = self._parse_block(None, # end_token
+ parent,
+ visible_if_deps,
+ prev_node)
+ self._leave_file()
elif t0 == end_token:
# We have reached the end of the block. Terminate the final |
@nashif Small nitpick: The imports are sorted alphabetically. If you submit a PR, we could pick up there. |
Unless there's a huge number of |
will do after some more testing, thanks. |
@nashif + # Test 'source' globbing
+
+ verify_locations(c.syms["GLOBBED1"].nodes, "tests/Kglobbed1:1")
+ verify_locations(c.syms["GLOBBED2"].nodes, "tests/Kglobbed2:1")
+ verify_locations(c.syms["GLOBBED3"].nodes, "tests/sub/Kglobbed3:1")
+
print("Testing visibility") |
Functions similarly to scripts/kconfig/merge_config.sh from the kernel. Came up in zephyrproject-rtos/zephyr#5417.
@ulfalizer I've posted a PR with globbing support including EDIT: Scratch that, I am dumb, during my tests I had overwritten the defconfig :| I blame the flu |
@ulfalizer how hard would it be to write a kconfigdiff with your library? something that is able to load 2 |
Codecov Report
@@ Coverage Diff @@
## master #5417 +/- ##
=========================================
Coverage ? 53.16%
=========================================
Files ? 425
Lines ? 35175
Branches ? 0
=========================================
Hits ? 18702
Misses ? 16473
Partials ? 0 Continue to review full report at Codecov.
|
@ulfalizer So after playing around a bit more with your script with globbing added, I see a few differences between the In this case, and for my particular application, Another example is here: Since Do you know why that would be the case? |
@carlescufi Normally, Here's the code from the C implementation (in static struct property *sym_get_default_prop(struct symbol *sym)
{
struct property *prop;
for_all_defaults(sym, prop) {
prop->visible.tri = expr_calc_value(prop->visible.expr);
if (prop->visible.tri != no)
return prop;
}
return NULL;
} Here's the corresponding Kconfiglib code (which also includes some logic from for default, cond in self.defaults:
cond_val = expr_value(cond)
if cond_val:
val = min(expr_value(default), cond_val)
self._write_to_conf = True
break I suspect some things would fail in Kconfiglib's compatibility test suite too if the behavior didn't match here. Here's two test files you could use (with e.g. default
.config
|
@carlescufi The |
Yes, that is possible, although I am surprised about such a fundamental difference
So what you are saying is that in Linux's
I assume here Linux's |
@carlescufi Someone changing the behavior there seems a bit arbitrary, but it's the only explanation I can think of. How heavily modified (and old) is the Zephyr Here's the kind of stuff you see in the Linux Kconfig files:
|
Happy new year all :).
+1 to not reinventing the wheel. |
@mbolivar I have Zephyr building with Kconfiglib, but unfortunately not all the changes can go upstream to @ulfalizer's repo due to differences between Linux's Kconfig and ours. autoconf generation can though, and it's currently in PR form: ulfalizer/Kconfiglib#35 |
@carlescufi saw the discussion about I assumed the way forward was to fix the Zephyr Kconfigs so they are compatible with Linux, so we can use kconfiglib and the "standard" conf. Sorry if I misunderstood. Happy to pick up my changes to merge_config.py if we're going to keep our incompatibilities for the time being. |
@mbolivar happy new year!
There are 2 different items that make our Kconfig files incompatible with vanilla Linux Kconfig:
I plan to do this in 2 steps: First leave the Kconfig files unchanged but use a modified version of Kconfiglib to reproduce our current behavior. This is to ensure that Kconfiglib indeed respects everything else about Kconfig and to avoid changes in Kconfig files at the same time that we replace
There's no more need for a merge_config.py. As soon as I post my PR you'll see that there's a single |
I think we should close this PR now that #5569 is posted. |
Do not use comment indicating variable is not set, this confuses the merge
script.
Fixes #5374