Skip to content
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

Add a summary() function for configuration summarization #4649

Merged
merged 4 commits into from
Dec 17, 2019

Conversation

dcbaker
Copy link
Member

@dcbaker dcbaker commented Dec 17, 2018

This has been requested multiple times in the mesa/xorg community, and
apparently in the GStreamer community as well.

This allows a configuration summary to be printed at the end of the configuring phase.

For example:

sec1 = {'driver' : 'foobar', 'OS' : 'Linux', 'API' : 1.7}
...
sec2 = {'driver' : 'dive comp', 'OS' : 'Minix', 'API' : 1.1.2}
...
sec3 = {'with' : {'mesa' : true, 'gbm' : false}}

summary(
  {'Backend' : 'OpenGL'},
  'Server' : sec1,
  'Client' : sec2,
  'Misc' : sec3,
)

Which would print something like:

Configuration Summary:
  Backend = OpenGL
  Server:
    driver = foobar
    OS = Linux
    API = 1.7
  Client:
    driver = dive comp
    OS = Minix
    API = 1.1.2
  Misc:
    with = {'mesa : true, 'gbm' : false}

Fixes #757

@dcbaker dcbaker changed the title WIP: Add a summary() function for configuration summarization RFC: Add a summary() function for configuration summarization Dec 17, 2018
@dcbaker
Copy link
Member Author

dcbaker commented Dec 17, 2018

I'm considering this RFC at the moment because I'd like some feedback on the API portion, namely, does it make more sense to use the special case dict structure, or to allow summary() to take arbitrary kwargs for sections and always print dictionaries? I'm kinda leaning toward the latter now that I look at it more.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 17, 2018

I switched to the keyword argument variant, which always treats dicts as dicts with no special meaning based on their level. I think I like this better as it's more consistent.

@tp-m
Copy link
Member

tp-m commented Dec 17, 2018

I think from an API point of view I prefer the "take arbitrary kwargs for sections and always print dictionaries" variant to the dict-inside-dicts variant. Just looks nicer and cleaner and also discourages nesting of sections.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 17, 2018

We're in agreement then, and should ask @jpakkane how he feels about the api?

@jpakkane
Copy link
Member

Interestingly I just manage to beat you by one day: #4643

It's a lot simpler, though...

@dcbaker
Copy link
Member Author

dcbaker commented Dec 18, 2018

Which one is simpler?

@jpakkane
Copy link
Member

The one thing the other MR does that this one does not is to store the summary per subproject and print all of them in one go at the very end (with the master project last and subprojects in alphabetical order).

@dcbaker
Copy link
Member Author

dcbaker commented Dec 20, 2018

That's how this one is supposed to work (minus the alphabetical order), but I change that.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 20, 2018

Apparently there are bugs :)

@dcbaker dcbaker force-pushed the summary-function branch 2 times, most recently from 061a997 to f9b3cd6 Compare December 20, 2018 22:56
@dcbaker
Copy link
Member Author

dcbaker commented Dec 20, 2018

Updated to sort subprojects alphabetically, print the main project last, and not print subproject summaries during subproject initialization.

@xclaesse
Copy link
Member

xclaesse commented Dec 20, 2018

The thing I don't like in this PR is the free-form keyword arguments. That means we cannot extend this function with other options. For example we'll never be able to add summary(..., colored : true) because that would print "colored=true" instead of being a setting of the summary function. I would rather have nested dict (with max depth to 2) to be future-proof.

From a readability POV, it makes no difference, it looks even more consistent to me. Allow multiple positional arguments that must be all dictionaries:

summary(
  {'Backend' : 'OpenGL'},
  {'Server' : sec1},
  {'Client' : sec2},
  {'Misc' : sec3},
)

Also that allows the incrementally construct the summary:

summary = [{'Backend' : 'OpenGL'}]
if something
  summary += {'foo' : 'bar'}
endif

summary(summary)

So it would be the same to have 1 list positional argument, or N dict positional arguments. Many functions works that way already.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 20, 2018

I tried that originally, and I don't like it because it makes dicts mean 2 things, for two levels of nesting they mean "indent this" but after that they don't:

foo = {'one' : {'two' : {'three' : {'four': 'foo'}}}}
summary(foo)

would print as:

one:
  two:
    three: {'four': 'foo'}

but

foo = {'one' : {'two' : {'three' : 'foo'}}}

prints as:

one:
  two:
    three: 'foo'

Which I feel violates least-surprise.

I agree that maintaining keywords for API use would be better, so how about:

summary({'Backend' : 'OpenGL'}, sections : {'Server' : sec1, 'Client' : sec2, 'Misc': sec3})

Which remove the free-form keywords, but keeps the behavior of nested dictionaries consistent.

@xclaesse
Copy link
Member

xclaesse commented Dec 21, 2018

summary({'Backend' : 'OpenGL'}, sections : {'Server' : sec1, 'Client' : sec2, 'Misc': sec3})

That's nested dictionaries too, I think the advantage of my proposal is that sections are in a list, so user controls the ordering.

foo = {'one' : {'two' : {'three' : {'four': 'foo'}}}}
foo = {'one' : {'two' : {'three' : 'foo'}}}

Of course put like that it's confusing, but with intermediary variables and a bit of indentation, it's not that bad. Taking your initial example:

sec1 = {'driver' : 'foobar', 'OS' : 'Linux', 'API' : 1.7}
sec2 = {'driver' : 'dive comp', 'OS' : 'Minix', 'API' : 1.1.2}
sec3 = {'with' : {'mesa' : true, 'gbm' : false}}
summary = [
  {'Backend' : 'OpenGL'},
  {'Server' : sec1},
  {'Client' : sec2},
  {'Misc' : sec3},
]

@xclaesse
Copy link
Member

xclaesse commented Dec 21, 2018

One thing I would like to do with keyword arguments, is enabling some automatic sections (doesn't need to be done in initial PR). One example that comes to mind is listing all subprojects with a YES/NO value to tell if they worked.

summary(subprojects: true)

Would print:

Subprojects:
  gst-plugins-good: Yes
  gst-plugins-bad: Failed
  gst-plugins-ugly: Disabled

Another extention can that be done later, is passing dependency objects

libfoo = dependency('foo', required : get_option('foo_feature'))
summary({'Dependencies' : [libfoo, libbar]})

Would print:

Dependencies:
  foo: Yes
  bar: Not found
  baz: Not found (feature 'baz' disabled)

All of this doesn't need to be implemented immediately, but I would like to make sure we keep in mind that this will likely be extended in the future, so we have to design it flexible.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 21, 2018

Do meson's dicts have an ordering guarantee? Is that behavior just leaking from python 3.7, or do we mean to have it?

@jpakkane
Copy link
Member

There is no ordering guarantee.

@dcbaker
Copy link
Member Author

dcbaker commented Dec 27, 2018

I guess that leaves us with either @xclaesse's list of lists (which I'm not a huge fan of, but I could live with I think), making dictionaries ordered (Because python guarantees it now, people will start relying on it so it might be worth us doing), or going to a more complicated object, maybe a module? Does anyone have preferences?

@tp-m
Copy link
Member

tp-m commented Dec 28, 2018

(Just thinking out loud here...)

Maybe we should take a step back and revisit some of the original ideas floating about in e.g. #757. I think we're perhaps trying to do too much at once here, trying to squeeze everything into a single summary() function.

How about something where a single summary() would handle the simple case of one single section (or no sections at all rather), but it would also return an object where one could then add sections with a name and a dict as one of the arguments? That's somewhere in between the original proposals where every single line item was a method, which perhaps was a bit tedious/verbose, and the everything-in-a-dict approach?

@tp-m
Copy link
Member

tp-m commented Dec 30, 2018

Or even simpler: let there be only a single summary() function with one call to summary() for each section and two positional parameters (title and a dict)?

@xclaesse
Copy link
Member

xclaesse commented Jan 4, 2019

Or even simpler: let there be only a single summary() function with one call to summary() for each section and two positional parameters (title and a dict)?

We already have to support multiple summary() calls (one per subproject) so that makes sense. I would rather have the title in an optional kwarg instead of positional arg because some key/value are not under a section.

I personally still prefer my proposal from #4649 (comment), but if people prefer @tp-m's proposal, I've got no strong opinion against it.

@dcbaker
Copy link
Member Author

dcbaker commented Jan 4, 2019

@tp-m, could you give me a psudeo-code example of what you're thinking?

@xclaesse
Copy link
Member

xclaesse commented Jan 4, 2019

@tp-m, could you give me a psudeo-code example of what you're thinking?

I guess something like that:

sec1 = {'driver' : 'foobar', 'OS' : 'Linux', 'API' : 1.7}
sec2 = {'driver' : 'dive comp', 'OS' : 'Minix', 'API' : 1.1.2}
sec3 = {'with' : {'mesa' : true, 'gbm' : false}}
summary({'Backend' : 'OpenGL'})
summary(sec1, title : 'Server')
summary(sec2, title : 'Client')
summary(sec3, title : 'Misc')

@tp-m
Copy link
Member

tp-m commented Jan 31, 2019

@tp-m, could you give me a psudeo-code example of what you're thinking?

Something like this I think:

Single section:

summary('gdk-pixbuf', { 'loaders' : ['jpeg', 'png', 'tiff'], 'MMX' : true, 'Altivec' : false })

Multiple sections:

summary('Server', {'driver' : 'foobar', 'OS' : 'Linux', 'API' : 1.7})
summary('Client',  {'driver' : 'dive comp', 'OS' : 'Minix', 'API' : 1.1.2})
summary('Misc', {'with' : {'mesa' : true, 'gbm' : false}})

(not sure what the behaviour of the nesting should be here in the Misc section, but that's details ;))

@dcbaker
Copy link
Member Author

dcbaker commented Jan 31, 2019

Okay, I'll update this today and see how the multiple calls to summary works out.

@mensinda
Copy link
Member

Since this is a new function, please also add this

'summary': self.func_do_nothing,

to the self.funcs.update call in the AstInterpreter (somwhere around here) before this gets merged.

@keszybz
Copy link
Contributor

keszybz commented Dec 11, 2019

meson.build:3161:0: ERROR: Options for section 'status' already set.

I think it'd be more user-friendly to merge instead, if the same section name is specified twice, and only warn or error out if the same dict key appears twice in the same section. For example when subdirectories are used, it is much more convenient to add items to the status in a few different places then to manually collect everything in a dictionary and only call summary() at the end. And the same goes for conditional output, e.g. architecture dependent.

I tried using this with systemd, and the output looks like this:

Main Project:
  status
    systemd = '244'
    split /usr = False
    split bin-sbin = True
    prefix directory = '/usr'
    rootprefix directory = '/usr'
    sysconf directory = '/etc'
    include directory = '/usr/include'
    lib directory = '/usr/lib64'
    rootlib directory = '/usr/lib64'
    SysV init scripts = '/etc/init.d'
    SysV rc?.d directories = '/etc/rc.d'
    PAM modules directory = '/usr/lib64/security'
    PAM configuration directory = '/etc/pam.d'
    RPM macros directory = '/usr/lib/rpm/macros.d'
    modprobe.d directory = '/usr/lib/modprobe.d'
    D-Bus policy directory = '/usr/share/dbus-1/system.d'
    D-Bus session directory = '/usr/share/dbus-1/services'
    D-Bus system directory = '/usr/share/dbus-1/system-services'
    bash completions directory = '/usr/share/bash-completion/completions'
    zsh completions directory = '/usr/share/zsh/site-functions'
    extra start script = '/etc/rc.local'
    debug shell = '/bin/sh @ /dev/tty9'
    TTY GID = 5
    users GID = '-'
    maximum system UID = 999
    maximum system GID = 999
    minimum dynamic UID = 61184
    maximum dynamic UID = 65519
    minimum container UID base = 524288
    maximum container UID base = 1878982656
    /dev/kvm access mode = '0666'
    render group access mode = '0666'
    certificate root directory = '/etc/ssl'
    support URL = 'https://lists.freedesktop.org/mailman/listinfo/systemd-devel'
    nobody user name = 'nobody'
    nobody group name = 'nobody'
    fallback hostname = 'localhost'
    symbolic gateway hostnames = '_gateway'
    default DNSSEC mode = 'allow-downgrade'
    default DNS-over-TLS mode = 'no'
    default cgroup hierarchy = 'unified'
    default net.naming-scheme setting = 'latest'
    default KillUserProcesses setting = True
    default locale = 'C.UTF-8'
    default user $PATH = '(same as system services)'
    systemd service watchdog = '3min'
    default DNS servers = '1.1.1.1\n                                            8.8.8.8\n                                            1.0.0.1\n                                            8.8.4.4\n                                            2606:4700:4700::1111\n                                            2001:4860:4860::8888\n                                            2606:4700:4700::1001\n                                            2001:4860:4860::8844'
    default NTP servers = 'time1.google.com\n                                            time2.google.com\n                                            time3.google.com\n                                            time4.google.com'
    time epoch = '1576062304 (2019-12-11T11:05:04+00:00)'
    static libsystemd = 'false'
    static libudev = 'false'
  EFI
    efi arch = 'x86_64'
    EFI machine type = 'ccache cc'
    EFI lib directory = '/usr/lib64'
    EFI lds directory = '/usr/lib64/gnuefi'
    EFI include directory = '/usr/include/efi'
  enablement status
    enabled features = 'libcryptsetup, PAM, AUDIT, IMA, SELinux, SECCOMP, SMACK, zlib, xz, lz4, bzip2, ACL, gcrypt, qrencode, microhttpd, gnutls, openssl, libcurl, idn, libidn2, libiptc, elfutils, binfmt, vconsole, quotacheck, tmpfiles, environment.d, sysusers, firstboot, randomseed, backlight, rfkill, logind, machined, portabled, importd, hostnamed, timedated, timesyncd, localed, networkd, resolve, DNS-over-TLS(gnutls), coredump, pstore, polkit, efi, gnu-efi, kmod, xkbcommon, pcre2, blkid, dbus, glib, nss-myhostname, nss-mymachines, nss-resolve, nss-systemd, hwdb, tpm, SysV compat, utmp, ldconfig, hibernate, adm group, wheel group, gshadow, valgrind, link-udev-shared, link-systemctl-shared'
    disabled features = 'AppArmor, libidn, DNS-over-TLS(openssl), legacy pkla, man pages, html pages, man page indices, debug hashmap, debug mmap cache, debug siphash, trace logging'

It is not bad. It is definitely much less work than the current hand-crafted summary we have. Nevertheless, it is not as readable as the hand-crafted yet, and I wouldn't want to switch from the handcrafted status to this until some things are improved.

Main Project

I'd prefer this to be something like "<project-name> <project-version> summary" instead. For projects that don't use subprojects this line is noise. Or maybe make this configurable though a keyword argument.

For the dictionary, I think just using the default dict formatter is not going to be good enough.

systemd = '244'
split /usr = False
split bin-sbin = True
prefix directory = '/usr'

It would be better to print the strings without quotes. Many things are internally strings but are never displayed quoted, like version numbers. Having the quotes on some settings or not others is visually displeasing. The same goes for stuff like "(not set)" or similar — they must be displayed without quotes.

default DNS servers = '1.1.1.1\n 8.8.8.8\n 1.0.0.1\n 8.8.4.4\n 2606:4700:4700::1111\n 2001:4860:4860::8888\n 2606:4700:4700::1001\n

For strings with quotes, it would be great if they could be split at newlines and aligned to the "=" sign. I.e display the first version as:

default DNS servers = 1.1.1.1
                                    8.8.8.8
                                    ...
                                    2606:4700:4700::1001

I also tried a version with a list:

default DNS servers = ['1.1.1.1', '8.8.8.8', '1.0.0.1', '8.8.4.4', '2606:4700:4700::1111', '2001:4860:4860::8888', '2606:4700:4700::1001', '2001:4860:4860::8844']

This looks better out of the box, but is not very readable. Breaking the list up and aligning to "=" would be great.

Maybe use color for the section headers?

keszybz added a commit to keszybz/systemd that referenced this pull request Dec 11, 2019
@keszybz
Copy link
Contributor

keszybz commented Dec 11, 2019

I pushed my test as https://github.com/keszybz/systemd/pull/new/meson-summary in case anyone wants to play with it.

@xclaesse
Copy link
Member

xclaesse commented Dec 11, 2019

meson.build:3161:0: ERROR: Options for section 'status' already set.

I think it'd be more user-friendly to merge instead, if the same section name is specified twice, and only warn or error out if the same dict key appears twice in the same section.

Agreed, I'll change that.

Main Project

I'd prefer this to be something like " summary" instead. For projects that don't use subprojects this line is noise. Or maybe make this configurable though a keyword argument.

Agreed. If we go with <project-name> <project-version> instead of Main Project the it's not noise any more even when there are no subprojects, since that tells the version which is useful information. I think I should also add the version for subprojects while at it.

For the dictionary, I think just using the default dict formatter is not going to be good enough.

systemd = '244'
split /usr = False
split bin-sbin = True
prefix directory = '/usr'

It would be better to print the strings without quotes. Many things are internally strings but are never displayed quoted, like version numbers. Having the quotes on some settings or not others is visually displeasing. The same goes for stuff like "(not set)" or similar — they must be displayed without quotes.

Agreed. When the value is a simple string the quoting is just noise. But when the value is array/dict then it could be needed maybe.

For strings with quotes, it would be great if they could be split at newlines and aligned to the "=" sign.

I don't think we should try to parse strings, the point here is project would have to use an array of string instead, as you did below.

default DNS servers = ['1.1.1.1', '8.8.8.8', '1.0.0.1', '8.8.4.4', '2606:4700:4700::1111', '2001:4860:4860::8888', '2606:4700:4700::1001', '2001:4860:4860::8844']

This looks better out of the box, but is not very readable. Breaking the list up and aligning to "=" would be great.

Agreed, I like how systemd breaks that list into lines, that could be the default behaviour when value is a list.

Maybe use color for the section headers?

Agreed, but I'm not sure which color :) Speaking about colors, I also wanted a mode where booleans would turn into YES/NO in green/red, but that can be future improvement.

@xclaesse xclaesse force-pushed the summary-function branch 2 times, most recently from b86c1d2 to 51dcea2 Compare December 11, 2019 21:19
@xclaesse
Copy link
Member

@keszybz I updated the PR with those suggestions. I dropped 2 use-cases to simplify the code:

  • We cannot have dictionary in dictionary any more. I don't think there is a real usage for that and it can always be added later.
  • The 2nd argument must be dict and not string any more. That means we cannot have a section with a single value on the same line, it must be a dict of key/value under the section.

I'm considering adding summary(dict) syntax (dict without section) for the case you want to skip the section name, as systemd does. And maybe summary(section, key, value) in the case you want to add key/value one by one. And while at it, summary(key, value) in the case you want to add key/value one by one without a section. But those syntax sugar can be added later.

@xclaesse xclaesse force-pushed the summary-function branch 3 times, most recently from 7165326 to 4529d8b Compare December 12, 2019 01:10
@xclaesse
Copy link
Member

I'm considering adding summary(dict) syntax (dict without section) for the case you want to skip the section name, as systemd does. And maybe summary(section, key, value) in the case you want to add key/value one by one. And while at it, summary(key, value) in the case you want to add key/value one by one without a section. But those syntax sugar can be added later.

Added all those variants now, in a separate commit in case we prefer merge the initial commit first.

@xclaesse
Copy link
Member

I also wanted a mode where booleans would turn into YES/NO in green/red, but that can be future improvement.

Done, with bool_yn keyword argument.

@keszybz
Copy link
Contributor

keszybz commented Dec 12, 2019

Very pretty ;)

mesonbuild/interpreter.py Show resolved Hide resolved
@xclaesse xclaesse force-pushed the summary-function branch 2 times, most recently from ada826b to b265ddb Compare December 12, 2019 19:54
@xclaesse
Copy link
Member

@dcbaker @jpakkane I think this is ready to merge now. I'm pretty happy with the improvements that I did in this last iteration.

@xclaesse xclaesse added this to the 0.53.0 milestone Dec 13, 2019
@xclaesse xclaesse changed the title RFC: Add a summary() function for configuration summarization Add a summary() function for configuration summarization Dec 13, 2019
@dcbaker
Copy link
Member Author

dcbaker commented Dec 17, 2019

@jpakkane, this looks good to me. Since it has frontend language changes I figured you'd like tot ake a peak before this lands.

@jpakkane jpakkane merged commit 3122bac into mesonbuild:master Dec 17, 2019
mlog.log(' ', mlog.bold(section))
for k, v in values.items():
indent = self.max_key_len - len(k) + 3
mlog.log(' ' * indent, k + ':', v[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crash on empty list: #6645

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enhancement: configuration summary printing
8 participants