diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index dc667ea42338..4ac78fec1e8a 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1905,6 +1905,7 @@ def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir self.coredata = self.environment.get_coredata() self.backend = backend self.subproject = subproject + self.summary = {} if modules is None: self.modules = {} else: @@ -2012,6 +2013,7 @@ def build_func_dict(self): 'subdir': self.func_subdir, 'subdir_done': self.func_subdir_done, 'subproject': self.func_subproject, + 'summary': self.func_summary, 'shared_library': self.func_shared_lib, 'shared_module': self.func_shared_module, 'static_library': self.func_static_lib, @@ -2271,7 +2273,7 @@ def func_option(self, nodes, args, kwargs): @stringArgs def func_subproject(self, nodes, args, kwargs): if len(args) != 1: - raise InterpreterException('Subproject takes exactly one argument') + raise InterpreterException('Subproject takes exactly one argument') dirname = args[0] return self.do_subproject(dirname, kwargs) @@ -2364,6 +2366,7 @@ def do_subproject(self, dirname, kwargs): self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname) self.build_def_files += subi.build_def_files self.build.merge(subi.build) + self.summary.update(subi.summary) return self.subprojects[dirname] def get_option_internal(self, optname): @@ -2606,6 +2609,54 @@ def func_message(self, node, args, kwargs): argstr = self.get_message_string_arg(node) mlog.log(mlog.bold('Message:'), argstr) + @FeatureNew('summary', '0.50.0') + def func_summary(self, node, args, kwargs): + if self.subproject in self.summary: + raise InterpreterException('Summary can only be called once per project.') + + if len(args) > 1: + raise InterpreterException('Summary accepts zero or one argument.') + try: + # To convert None to a dict + arg = args[0] or {} + except IndexError: + arg = {} + + if not isinstance(arg, dict): + raise InterpreterException('Summary argument must be a dictionary.') + if not all(isinstance(a, dict) for a in kwargs.values()): + raise InterpreterException('Summary kwargs must be dictionaries.') + + self.summary[self.subproject] = (arg, kwargs) + + def _print_summary(self): + """Print the configuration summary. + + Called after all projects and subprojects are configured and used to + print all of the configuration at once. + """ + mlog.log('') + mlog.log(mlog.bold('Configuration Summary:')) + + def printer(arg, kwargs): + for k, v in arg.items(): + mlog.log(' ', k, '=', v) + for s, t in kwargs.items(): + mlog.log(' ', mlog.bold(s + ':')) + for k, v in t.items(): + mlog.log(' ', k, '=', v) + mlog.log('') + + for project, (arg, kwargs) in sorted(self.summary.items()): + if project == '': + continue + mlog.log('', mlog.bold('Subproject : ' + project)) + printer(arg, kwargs) + + if self.summary.get(''): + mlog.log('', mlog.bold('Main Project')) + printer(*self.summary['']) + @FeatureNew('warning', '0.44.0') @noKwargs def func_warning(self, node, args, kwargs): @@ -3877,6 +3928,8 @@ def run(self): FeatureDeprecated.report(self.subproject) if not self.is_subproject(): self.print_extra_warnings() + if self.subproject == '': + self._print_summary() def print_extra_warnings(self): for c in self.build.compilers.values():