From c175e97a8862be5c87196aa94cb911975c84cf84 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Feb 2020 10:19:50 -0500 Subject: summary: Add list_sep keyword argument This allows having lists on a single line instead of having each value aligned on a new line. --- docs/markdown/Reference-manual.md | 3 ++- docs/markdown/snippets/summary.md | 5 +++++ mesonbuild/interpreter.py | 21 +++++++++++++-------- run_unittests.py | 3 ++- test cases/unit/72 summary/meson.build | 1 + 5 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 docs/markdown/snippets/summary.md diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 475b711..ff82164 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1239,9 +1239,10 @@ pair doesn't appear twice. All sections will be collected and printed at the end of the configuration in the same order as they have been called. Keyword arguments: +- `section` title to group a set of key/value pairs. - `bool_yn` if set to true, all boolean values will be replaced by green YES or red NO. -- `section` title to group a set of key/value pairs. +- `list_sep` *Since 0.54.0* string used to separate list values (e.g. `', '`). Example: ```meson diff --git a/docs/markdown/snippets/summary.md b/docs/markdown/snippets/summary.md new file mode 100644 index 0000000..2ab7996 --- /dev/null +++ b/docs/markdown/snippets/summary.md @@ -0,0 +1,5 @@ +## Summary improvements + +A new `list_sep` keyword argument has been added to `summary()` function. +If defined and the value is a list, elements will be separated by the provided +string instead of being aligned on a new line. diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c29ed89..c9e559c 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1791,6 +1791,9 @@ class Summary: bool_yn = kwargs.get('bool_yn', False) if not isinstance(bool_yn, bool): raise InterpreterException('bool_yn keyword argument must be boolean') + list_sep = kwargs.get('list_sep') + if list_sep is not None and not isinstance(list_sep, str): + raise InterpreterException('list_sep keyword argument must be string') for k, v in values.items(): if k in self.sections[section]: raise InterpreterException('Summary section {!r} already have key {!r}'.format(section, k)) @@ -1803,9 +1806,7 @@ class Summary: formatted_values.append(mlog.green('YES') if i else mlog.red('NO')) else: formatted_values.append(i) - if not formatted_values: - formatted_values = [''] - self.sections[section][k] = formatted_values + self.sections[section][k] = (formatted_values, list_sep) self.max_key_len = max(self.max_key_len, len(k)) def dump(self): @@ -1815,11 +1816,14 @@ class Summary: if section: mlog.log(' ', mlog.bold(section)) for k, v in values.items(): + v, list_sep = v indent = self.max_key_len - len(k) + 3 - mlog.log(' ' * indent, k + ':', v[0]) - indent = self.max_key_len + 5 - for i in v[1:]: - mlog.log(' ' * indent, i) + end = ' ' if v else '' + mlog.log(' ' * indent, k + ':', end=end) + if list_sep is None: + indent = self.max_key_len + 6 + list_sep = '\n' + ' ' * indent + mlog.log(*v, sep=list_sep) mlog.log('') # newline @@ -2949,7 +2953,8 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.log(mlog.bold('Message:'), *args) @noArgsFlattening - @permittedKwargs({'section', 'bool_yn'}) + @FeatureNewKwargs('summary', '0.54.0', ['list_sep']) + @permittedKwargs({'section', 'bool_yn', 'list_sep'}) @FeatureNew('summary', '0.53.0') def func_summary(self, node, args, kwargs): if len(args) == 1: diff --git a/run_unittests.py b/run_unittests.py index aa27a1d..09bdcf1 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4347,10 +4347,11 @@ recommended as it is not supported on some platforms''') A list: string 1 True - empty list: + empty list: A number: 1 yes: YES no: NO + coma list: a, b, c Subprojects sub: YES diff --git a/test cases/unit/72 summary/meson.build b/test cases/unit/72 summary/meson.build index c155889..df4540d 100644 --- a/test cases/unit/72 summary/meson.build +++ b/test cases/unit/72 summary/meson.build @@ -12,3 +12,4 @@ summary({'Some boolean': false, summary('A number', 1, section: 'Configuration') summary('yes', true, bool_yn : true, section: 'Configuration') summary('no', false, bool_yn : true, section: 'Configuration') +summary('coma list', ['a', 'b', 'c'], list_sep: ', ', section: 'Configuration') -- cgit v1.1 From 8101b0ddab1dea8de3b2588a4b75da525865c404 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 25 Feb 2020 10:29:47 -0500 Subject: summary: Add more info in Subprojects section This adds a warnings counter for subprojects that passed. This is to encourage developpers to check warnings in the logs and hopefully fix them. Otherwise they could be hidden in hundreds lines of logs. This also print the error message for subprojects that did not pass. The error message is often enough to fix the issue (e.g. missing dependency) and it's easier than searching in the logs why a subproject failed. --- docs/markdown/snippets/summary.md | 3 +++ mesonbuild/interpreter.py | 40 +++++++++++++++++++++++++++------------ mesonbuild/mlog.py | 4 ++++ run_unittests.py | 2 +- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/docs/markdown/snippets/summary.md b/docs/markdown/snippets/summary.md index 2ab7996..67f29da 100644 --- a/docs/markdown/snippets/summary.md +++ b/docs/markdown/snippets/summary.md @@ -3,3 +3,6 @@ A new `list_sep` keyword argument has been added to `summary()` function. If defined and the value is a list, elements will be separated by the provided string instead of being aligned on a new line. + +The automatic `subprojects` section now also print the number of warnings encountered +during that subproject configuration, or the error message if the configuration failed. diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c9e559c..53225c4 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -963,10 +963,14 @@ class Test(InterpreterObject): class SubprojectHolder(InterpreterObject, ObjectHolder): - def __init__(self, subinterpreter, subproject_dir, name): + def __init__(self, subinterpreter, subproject_dir, name, warnings=0, disabled_feature=None, + exception=None): InterpreterObject.__init__(self) ObjectHolder.__init__(self, subinterpreter) self.name = name + self.warnings = warnings + self.disabled_feature = disabled_feature + self.exception = exception self.subproject_dir = subproject_dir self.methods.update({'get_variable': self.get_variable_method, 'found': self.found_method, @@ -2602,10 +2606,9 @@ external dependencies (including libraries) must go to "dependencies".''') dirname = args[0] return self.do_subproject(dirname, 'meson', kwargs) - def disabled_subproject(self, dirname, feature=None): - sub = SubprojectHolder(None, self.subproject_dir, dirname) - if feature: - sub.disabled_feature = feature + def disabled_subproject(self, dirname, disabled_feature=None, exception=None): + sub = SubprojectHolder(None, self.subproject_dir, dirname, + disabled_feature=disabled_feature, exception=exception) self.subprojects[dirname] = sub return sub @@ -2613,7 +2616,7 @@ external dependencies (including libraries) must go to "dependencies".''') disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: mlog.log('Subproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled') - return self.disabled_subproject(dirname, feature) + return self.disabled_subproject(dirname, disabled_feature=feature) default_options = mesonlib.stringlistify(kwargs.get('default_options', [])) default_options = coredata.create_options_dict(default_options) @@ -2654,7 +2657,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not required: mlog.log(e) mlog.log('Subproject ', mlog.bold(subprojdir), 'is buildable:', mlog.red('NO'), '(disabling)') - return self.disabled_subproject(dirname) + return self.disabled_subproject(dirname, exception=e) raise e subdir = os.path.join(self.subproject_dir, resolved) @@ -2682,7 +2685,7 @@ external dependencies (including libraries) must go to "dependencies".''') # fatal and VS CI treat any logs with "ERROR:" as fatal. mlog.exception(e, prefix=mlog.yellow('Exception:')) mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)') - return self.disabled_subproject(dirname) + return self.disabled_subproject(dirname, exception=e) raise e def _do_subproject_meson(self, dirname, subdir, default_options, kwargs, ast=None, build_def_files=None): @@ -2694,7 +2697,12 @@ external dependencies (including libraries) must go to "dependencies".''') subi.subproject_stack = self.subproject_stack + [dirname] current_active = self.active_projectname + current_warnings_counter = mlog.log_warnings_counter + mlog.log_warnings_counter = 0 subi.run() + subi_warnings = mlog.log_warnings_counter + mlog.log_warnings_counter = current_warnings_counter + mlog.log('Subproject', mlog.bold(dirname), 'finished.') mlog.log() @@ -2706,7 +2714,8 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted)) self.active_projectname = current_active self.subprojects.update(subi.subprojects) - self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname) + self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname, + warnings=subi_warnings) # Duplicates are possible when subproject uses files from project root if build_def_files: self.build_def_files = list(set(self.build_def_files + build_def_files)) @@ -2982,11 +2991,18 @@ external dependencies (including libraries) must go to "dependencies".''') all_subprojects = collections.OrderedDict() for name, subp in sorted(self.subprojects.items()): value = subp.found() - if not value and hasattr(subp, 'disabled_feature'): - value = 'Feature {!r} disabled'.format(subp.disabled_feature) + if subp.disabled_feature: + value = [value, 'Feature {!r} disabled'.format(subp.disabled_feature)] + elif subp.exception: + value = [value, str(subp.exception)] + elif subp.warnings > 0: + value = [value, '{} warnings'.format(subp.warnings)] all_subprojects[name] = value if all_subprojects: - self.summary_impl('Subprojects', all_subprojects, {'bool_yn': True}) + self.summary_impl('Subprojects', all_subprojects, + {'bool_yn': True, + 'list_sep': ' ', + }) # Print all summaries, main project last. mlog.log('') # newline main_summary = self.summary.pop('', None) diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index b28eca1..ecd93f3 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -59,6 +59,7 @@ log_disable_stdout = False # type: bool log_errors_only = False # type: bool _in_ci = 'CI' in os.environ # type: bool _logged_once = set() # type: T.Set[T.Tuple[str, ...]] +log_warnings_counter = 0 # type: int def disable() -> None: global log_disable_stdout @@ -254,6 +255,9 @@ def _log_error(severity: str, *rargs: T.Union[str, AnsiDecorator], else: log(*args, **kwargs) + global log_warnings_counter + log_warnings_counter += 1 + if log_fatal_warnings: raise MesonException("Fatal warnings enabled, aborting") diff --git a/run_unittests.py b/run_unittests.py index 09bdcf1..cbc2d1f 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4355,7 +4355,7 @@ recommended as it is not supported on some platforms''') Subprojects sub: YES - sub2: NO + sub2: NO Problem encountered: This subproject failed ''') expected_lines = expected.split('\n')[1:] out_start = out.find(expected_lines[0]) -- cgit v1.1