diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 4f8014014..e5b37961e 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -43,6 +43,13 @@ class Conf: self.build_dir = os.path.dirname(self.build_dir) self.build = None self.max_choices_line_length = 60 + self.name_col = [] + self.value_col = [] + self.choices_col = [] + self.descr_col = [] + self.has_choices = False + self.all_subprojects = set() + self.yielding_options = set() if os.path.isdir(os.path.join(self.build_dir, 'meson-private')): self.build = build.load(self.build_dir) @@ -79,71 +86,89 @@ class Conf: # are erased when Meson is executed the next time, i.e. when # Ninja is run. - def print_aligned(self, arr): - if not arr: - return + def print_aligned(self): + col_widths = (max([len(i) for i in self.name_col], default=0), + max([len(i) for i in self.value_col], default=0), + max([len(i) for i in self.choices_col], default=0)) - titles = {'name': 'Option', 'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'} - if self.default_values_only: - titles['value'] = 'Default Value' - - name_col = [titles['name'], '-' * len(titles['name'])] - value_col = [titles['value'], '-' * len(titles['value'])] - choices_col = [titles['choices'], '-' * len(titles['choices'])] - descr_col = [titles['descr'], '-' * len(titles['descr'])] - - choices_found = False - for opt in arr: - name_col.append(opt['name']) - descr_col.append(opt['descr']) - if isinstance(opt['value'], list): - value_col.append('[{0}]'.format(', '.join(make_lower_case(opt['value'])))) - else: - value_col.append(make_lower_case(opt['value'])) - if opt['choices']: - choices_found = True - if isinstance(opt['choices'], list): - choices_list = make_lower_case(opt['choices']) - current = '[' - while choices_list: - i = choices_list.pop(0) - if len(current) + len(i) >= self.max_choices_line_length: - choices_col.append(current + ',') - name_col.append('') - value_col.append('') - descr_col.append('') - current = ' ' - if len(current) > 1: - current += ', ' - current += i - choices_col.append(current + ']') - else: - choices_col.append(make_lower_case(opt['choices'])) + for line in zip(self.name_col, self.value_col, self.choices_col, self.descr_col): + if self.has_choices: + print('{0:{width[0]}} {1:{width[1]}} {2:{width[2]}} {3}'.format(*line, width=col_widths)) else: - choices_col.append('') + print('{0:{width[0]}} {1:{width[1]}} {3}'.format(*line, width=col_widths)) - col_widths = (max([len(i) for i in name_col], default=0), - max([len(i) for i in value_col], default=0), - max([len(i) for i in choices_col], default=0), - max([len(i) for i in descr_col], default=0)) + def split_options_per_subproject(self, options): + result = {} + for k, o in options.items(): + subproject = '' + if ':' in k: + subproject, optname = k.split(':') + if o.yielding and optname in options: + self.yielding_options.add(k) + self.all_subprojects.add(subproject) + result.setdefault(subproject, {})[k] = o + return result - for line in zip(name_col, value_col, choices_col, descr_col): - if choices_found: - print(' {0:{width[0]}} {1:{width[1]}} {2:{width[2]}} {3:{width[3]}}'.format(*line, width=col_widths)) + def _add_line(self, name, value, choices, descr): + self.name_col.append(' ' * self.print_margin + name) + self.value_col.append(value) + self.choices_col.append(choices) + self.descr_col.append(descr) + + def add_option(self, name, descr, value, choices): + if isinstance(value, list): + value = '[{0}]'.format(', '.join(make_lower_case(value))) + else: + value = make_lower_case(value) + + if choices: + self.has_choices = True + if isinstance(choices, list): + choices_list = make_lower_case(choices) + current = '[' + while choices_list: + i = choices_list.pop(0) + if len(current) + len(i) >= self.max_choices_line_length: + self._add_line(name, value, current + ',', descr) + name = '' + value = '' + descr = '' + current = ' ' + if len(current) > 1: + current += ', ' + current += i + choices = current + ']' else: - print(' {0:{width[0]}} {1:{width[1]}} {3:{width[3]}}'.format(*line, width=col_widths)) + choices = make_lower_case(choices) + else: + choices = '' + + self._add_line(name, value, choices, descr) + + def add_title(self, title): + titles = {'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'} + if self.default_values_only: + titles['value'] = 'Default Value' + self._add_line('', '', '', '') + self._add_line(title, titles['value'], titles['choices'], titles['descr']) + self._add_line('-' * len(title), '-' * len(titles['value']), '-' * len(titles['choices']), '-' * len(titles['descr'])) + + def add_section(self, section): + self.print_margin = 0 + self._add_line('', '', '', '') + self._add_line(section + ':', '', '', '') + self.print_margin = 2 def print_options(self, title, options): - print('\n{}:'.format(title)) if not options: - print(' No {}\n'.format(title.lower())) - arr = [] + return + if title: + self.add_title(title) for k, o in sorted(options.items()): - d = o.description - v = o.printable_value() - c = o.choices - arr.append({'name': k, 'descr': d, 'value': v, 'choices': c}) - self.print_aligned(arr) + printable_value = o.printable_value() + if k in self.yielding_options: + printable_value = '' + self.add_option(k, o.description, printable_value, o.choices) def print_conf(self): def print_default_values_warning(): @@ -180,26 +205,44 @@ class Conf: test_options = {k: o for k, o in self.coredata.builtins.items() if k in test_option_names} core_options = {k: o for k, o in self.coredata.builtins.items() if k in core_option_names} - self.print_options('Core options', core_options) - if self.default_values_only or self.build.environment.is_cross_build(): - self.print_options('Core options (for host machine)', self.coredata.builtins_per_machine.host) - self.print_options( - 'Core options (for build machine)', - {'build.' + k: o for k, o in self.coredata.builtins_per_machine.build.items()}) - else: - self.print_options('Core options', self.coredata.builtins_per_machine.host) + def insert_build_prefix(k): + idx = k.find(':') + if idx < 0: + return 'build.' + k + return k[:idx + 1] + 'build.' + k[idx + 1:] + + core_options = self.split_options_per_subproject(core_options) + host_compiler_options = self.split_options_per_subproject(self.coredata.compiler_options.host) + build_compiler_options = self.split_options_per_subproject({insert_build_prefix(k): o for k, o in self.coredata.compiler_options.build.items()}) + project_options = self.split_options_per_subproject(self.coredata.user_options) + show_build_options = self.default_values_only or self.build.environment.is_cross_build() + + self.add_section('Main project options') + self.print_options('Core options', core_options['']) + self.print_options('', self.coredata.builtins_per_machine.host) + if show_build_options: + self.print_options('', {insert_build_prefix(k): o for k, o in self.coredata.builtins_per_machine.build.items()}) self.print_options('Backend options', self.coredata.backend_options) self.print_options('Base options', self.coredata.base_options) - if self.default_values_only or self.build.environment.is_cross_build(): - self.print_options('Compiler options (for host machine)', self.coredata.compiler_options.host) - self.print_options( - 'Compiler options (for build machine)', - {'build.' + k: o for k, o in self.coredata.compiler_options.build.items()}) - else: - self.print_options('Compiler options', self.coredata.compiler_options.host) + self.print_options('Compiler options', host_compiler_options['']) + if show_build_options: + self.print_options('', build_compiler_options['']) self.print_options('Directories', dir_options) - self.print_options('Project options', self.coredata.user_options) self.print_options('Testing options', test_options) + self.print_options('Project options', project_options['']) + for subproject in sorted(self.all_subprojects): + if subproject == '': + continue + self.add_section('Subproject ' + subproject) + if subproject in core_options: + self.print_options('Core options', core_options[subproject]) + if subproject in host_compiler_options: + self.print_options('Compiler options', host_compiler_options[subproject]) + if subproject in build_compiler_options and show_build_options: + self.print_options('', build_compiler_options[subproject]) + if subproject in project_options: + self.print_options('Project options', project_options[subproject]) + self.print_aligned() # Print the warning twice so that the user shouldn't be able to miss it if self.default_values_only: