Hi gcc-patches mailing list, Richard Earnshaw via Sourceware Forge <[email protected]> has requested that the following forgejo pull request be published on the mailing list.
Created on: 2026-05-29 15:13:48+00:00 Latest update: 2026-07-06 14:34:15+00:00 Changes: 8 changed files, 6948 additions, 248 deletions Head revision: rearnsha/gcc-TEST ref MAINT-yaml commit 442ae21dadc7c1db5e2a7f62b845bb0dd6868cff Base revision: gcc/gcc-TEST ref trunk commit 3b443e764144526748d2b51d017d745f1956377e r17-2158-g3b443e76414452 Merge base: 3b443e764144526748d2b51d017d745f1956377e Full diff url: https://forge.sourceware.org/gcc/gcc-TEST/pulls/163.diff Discussion: https://forge.sourceware.org/gcc/gcc-TEST/pulls/163 Requested Reviewers: rdfm For the forge to be able to notify maintainers efficiently when a merge request is submitted, I need to be able to automate assigning reviewers as well as just labels. The information needed for this is largely in the MAINTAINERS file, but not in a format that is readily machine readable: it can be scraped, but it's a bit of an ad-hoc process and not something I'd want to maintain in the long term. To mitigate that, and to make it possible to augment this information with additional details, I've been experimenting with converting the raw data into YAML and then having a script build the official MAINTAINERS file form that. The results can be seen in the following patch. The results so far are /almost/ identical with only a limited set of differences, some of which can probably be resolved later on. - the free-form entries saying All XXX maintainers have been expanded into explicit lists; I really don't think it helps much to have yet another level of indirection to deal with here and in particular it complicates regenerating the MAINTAINERS file as the template rules would need to be handled specifically. - I've found some small sort-order differences; these could be easily rectified, but I think the sort order I'm using here (case-independent sort) is preferable to a case-dependent one: Lac should be sorted before LaD. It would be better, I think to tweak check-MAINTAINERS.py to prefer this new order, but that file becomes redundant if this code goes in. Finally, the name for Naveen Gowda has changed in the DCO list. The email address used appears elsewhere in the MAINTAINERS file with a slightly different name. This could be fixed with a small extension to the YAML data (the DCO entry could take an optional name), if it really matters. One additional field that I've added is a marker for what I consider to be inactive accounts. The forge is likely to be more aggressive in emailing developers and I don't want it to be forever mailing people who have long ceased to contribute to the project. For now I've scraped the GCC commit logs and posts to [email protected] and [email protected]; anybody who has not contributed to one of those sources in the last two years has been marked as inactive. The generator script has an option (-a) to build a version of the MAINTAINERS file with inactive users removed: we have a lot of retired devs! Changes since V1: Added a schema and additional checks to validate the yaml data Added a simple script to create a Write-After entry Split the edits to the base conversion of the original MAINTAINERS data into separate commits. This is just to make things easier in the case I need to re-run the conversion and will be collapsed before the final commit Changes since V2: Improve python formatting, and general cleanups Incorporate recent changes from the existing MAINTAINERS data Changes since V3: Fix the testsuite check on the MAINTAINERS file. The test now regenerates a copy of the file from the MAINTAINERS.yml data and compares it against the committed version. This checks both the contents of the YAML data and the generated result. Retire check-MAINTAINERS.py. Unless there are some new objections, I think this is now ready to be merged. --- Thanks for taking the time to contribute to GCC! Please be advised that https://forge.sourceware.org/ is currently a trial that is being used by the GCC community to experiment with a new workflow based on pull requests. Pull requests sent here may be forgotten or ignored. Patches that you want to propose for inclusion in GCC should use the existing email-based workflow, see https://gcc.gnu.org/contribute.html Changed files: - A: MAINTAINERS.yml - A: contrib/add-write-after.py - A: contrib/gen-MAINTAINERS.py - A: contrib/maintainer_utils.py - D: contrib/check-MAINTAINERS.py - M: .editorconfig - M: MAINTAINERS - M: gcc/testsuite/gcc.src/maintainers.exp Richard Earnshaw (8): editorconfig: Add rules for yaml/yml files. MAINTAINERS: scripts to generate the file from yaml data MAINTAINERS.yml: Correct Martin Liška's name MAINTAINERS.yml: Restore James Norris' email address MAINTAINERS.yml: Handle libgrust explicitly MAINTAINERS.yml: Add libcpp to all C and C++ maintainer's roles MAINTAINERS: generate from MAINTAINERS.yml MAINTAINERS: Add a script to create a new entry in the mainainers data .editorconfig | 6 + MAINTAINERS | 27 +- MAINTAINERS.yml | 6148 +++++++++++++++++++++++++ contrib/add-write-after.py | 150 + contrib/check-MAINTAINERS.py | 229 - contrib/gen-MAINTAINERS.py | 319 ++ contrib/maintainer_utils.py | 288 ++ gcc/testsuite/gcc.src/maintainers.exp | 29 +- 8 files changed, 6948 insertions(+), 248 deletions(-) create mode 100644 MAINTAINERS.yml create mode 100755 contrib/add-write-after.py delete mode 100755 contrib/check-MAINTAINERS.py create mode 100755 contrib/gen-MAINTAINERS.py create mode 100755 contrib/maintainer_utils.py Range-diff against v3: 1: 2e1dcddc702b = 1: 868e8e65a6eb editorconfig: Add rules for yaml/yml files. 2: 48e835e9eef2 = 2: 99fc690e953f MAINTAINERS: scripts to generate the file from yaml data 3: bdc86f883e9b = 3: 7e0ca591ca98 MAINTAINERS.yml: Correct Martin Liška's name 4: 4933bea58a24 = 4: bc91b839b8ef MAINTAINERS.yml: Restore James Norris' email address 5: 390d1ffc1fe1 = 5: 4dcc0af6a162 MAINTAINERS.yml: Handle libgrust explicitly 6: 8a579eb09b9d = 6: 9a23170c90ee MAINTAINERS.yml: Add libcpp to all C and C++ maintainer's roles 7: 3bbbfbfacca6 ! 7: c44c1ed7746a MAINTAINERS: generate from MAINTAINERS.yml @@ Metadata ## Commit message ## MAINTAINERS: generate from MAINTAINERS.yml - Now that the MAINTAINERS.yml data is as we desire it, use it to - generate a new version of the MAINTAINERS file. + Now that the MAINTAINERS.yml data is as we desire it, use it to generate + a new version of the MAINTAINERS file. Update the testsuite to check + that the MAINTAINERS file is correctly generated from the YAML. + + Remove contrib/check-MAINTAINERS.py: it's nolonger needed now that + we generate the file automatically. ChangeLog: * MAINTAINERS: Regenerate from MAINTAINERS.yml + contrib/ChangeLog: + + * check-MAINTAINERS.py: Delete. + + gcc/testsuite/ChangeLog: + + * gcc.src/maintainers.exp: + ## MAINTAINERS ## @@ Note @@ MAINTAINERS: Robin Dapp <[email protected] Aldy Hernandez <[email protected]> Michal Jires <[email protected]> Matthias Kretz <[email protected]> + + ## contrib/check-MAINTAINERS.py (deleted) ## +@@ +-#!/usr/bin/env python3 +- +-# Copyright (C) 2022-2026 Free Software Foundation, Inc. +-# +-# This file is part of GCC. +-# +-# GCC is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 3, or (at your option) +-# any later version. +-# +-# GCC is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with GCC; see the file COPYING. If not, write to +-# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +-# Boston, MA 02110-1301, USA. +- +-# Check that names in the file are sorted alphabetically by surname, name +-# bugzilla handle and email (in this order). +- +-import locale +-import sys +-import re +-from itertools import groupby +-from operator import itemgetter +-from difflib import ndiff +-import unidecode +- +-locale.setlocale(locale.LC_ALL, 'en_US.utf8') +- +-exit_code = 0 +- +-if len(sys.argv) != 2: +- print('Usage: ./check-MAINTAINERS.py path-to/MAINTAINERS') +- sys.exit(1) +- +- +-def get_name_for_sort(name): +- parts = name.split() +- surname = parts[-1] + ", " + " ".join(parts[:-1]) +- +- # Special-case some names +- if (name == 'Stefan Schulze Frielinghaus' +- or name == 'Kris Van Hees'): +- surname = " ".join(parts[1:]) + ", " + parts[0] +- if surname.startswith("d'"): +- surname = surname[2:] +- +- # Remove accents +- return unidecode.unidecode(surname) +- +- +-def check_group(name, lines, columns, matcher, sort_by): +- global exit_code +- named_lines = [] +- rex = re.compile (matcher) +- for line in lines: +- if line.startswith(' '): +- print(f'Line should not start with space: "{line}"') +- exit_code = 2 +- continue +- +- if line.endswith(' '): +- print(f'Line should not end with space: "{line}"') +- exit_code = 3 +- continue +- +- # Special-case some names +- if line == 'James Norris': +- named_lines.append((get_name_for_sort(line), "", "", line)) +- continue +- +- fields = rex.match(line) +- if not fields: +- print(f'Could not parse line: "{line}"') +- exit_code = 3 +- continue +- +- matched_keys = [ +- name for name, idx in sorted (rex.groupindex.items(), +- key=lambda x: x[1]) +- if fields.group(idx) is not None] +- +- pos = 0 +- for i, k in enumerate (matched_keys): +- if i >= len (columns): +- break +- if pos < columns[i]: +- pos = columns[i] +- start = fields.start(k) +- if k == 'email': +- start -= 1 # Account for the leading '<' +- if start == pos: +- True +- elif (start > pos +- and pos > 2 +- and k == 'email' +- and line[start-1] == " " +- and line[start-2] != " "): +- True +- elif (start > pos +- and k != 'email' +- and pos > 3 +- and line[start-2:start] == " " +- and line[start-3] != " "): +- True +- else: +- exit_code = 3 +- print (line) +- print (f"{k} starts in the wrong column: expected: {pos}, actual {start}") +- pos += 1 + len (fields[k].rstrip()) +- +- fields = fields.groupdict() +- if 'Team' in fields and fields['Team']: +- fields['User'] = fields['Team'] +- fields['email'] = "" +- pieces = [] +- for i, f in enumerate (sort_by): +- if f not in fields: +- print('Internal error: re mismatch') +- sys.exit(10) +- if f == 'User': +- pieces.append(get_name_for_sort(fields[f].rstrip())) +- elif f == 'component': +- pieces.append(fields[f].rstrip().lower()) +- else: +- pieces.append(fields[f].rstrip()) +- pieces.append (line) +- named_lines.append(pieces) +- +- order = [] +- for i, _ in enumerate (sort_by): +- order.append(i) +- order = tuple(order) +- lines = [line + "\n" for line in lines] +- sorted_lines = [line[-1] + "\n" +- for line in sorted(named_lines, +- key = itemgetter(*order))] +- if lines != sorted_lines: +- exit_code = 1 +- diff = [line for line in ndiff(lines, sorted_lines) +- if not line.startswith('? ')] +- print(f'Wrong order for {name}:\n') +- print(''.join(diff)) +- else: +- print(f'{name} are fine!') +- +- +-text = open(sys.argv[1]).read() +-if '\t' in text: +- print('The file should not contain tabs') +- exit_code = 9 +- +-sections = [ +- # heading, paragraph index, column numbers, regex, sort order +- ('Global Reviewers', +- 1, +- [0, 48], +- r'^(?P<User>.{47}) <(?P<email>.*)>$', +- ['User', 'email']), +- ('CPU Port Maintainers (CPU alphabetical order)', +- 1, +- [0, 24, 48], +- r'^(?P<component>.{23}) (?P<User>.*) <(?P<email>[^<>]*)>$', +- ['component', 'User', 'email']), +- ('OS Port Maintainers (OS alphabetical order)', +- 1, +- [0, 24, 48], +- r'^(?P<component>.{23}) (?P<User>.*) <(?P<email>[^<>]*)>$', +- ['component', 'User', 'email']), +- ('Language Front Ends Maintainer', +- 1, +- [0, 24, 48], +- r'^(?P<component>.{23}) (?P<User>.*) <(?P<email>[^<>]*)>$', +- ['component', 'User', 'email']), +- ('Various Maintainers', +- 1, +- [0, 24, 48], +- r'''(?x) +- ^(?P<component>(?:(?!\s\s).)*)\s\s+ +- (?:(?P<Team>All.*maintainers) +- |(?P<User>[^\s].*)\s+<(?P<email>[^<>]*)>)$''', +- ['component', 'User', 'email']), +- ('Reviewers', +- 1, +- [0, 24, 48], +- r'^(?P<component>(?:(?! ).)*) +(?P<User>[^\s].*) <(?P<email>[^<>]*)>$', +- ['component', 'User', 'email']), +- ('Write After Approval', +- 2, +- [0, 32, 48], +- r'^(?P<User>.{31}) (?P<account>.{15}) <(?P<email>[^<>]*)>$', +- ['User', 'email', 'account']), +- ('Bug database only accounts', +- 1, +- [0, 48], +- r'^(?P<User>.{47}) <(?P<email>[^<>]*)>$', +- ['User', 'email']), +- ('Contributing under the DCO', +- 2, +- [0, 48], +- r'^(?P<User>.{47}) <(?P<email>[^<>]*)>$', +- ['User', 'email']) +-] +- +-i = 0 +-count = 0 +-for is_empty, lines in groupby(text.splitlines(), lambda x: not x): +- if is_empty: +- continue +- lines = list(lines) +- if count > 0: +- count -= 1 +- if count == 0: +- check_group(sections[i][0], lines, sections[i][2], sections[i][3], +- sections[i][4]) +- i += 1 +- elif len(lines) == 1 and i < len(sections) and sections[i][0] in lines[0]: +- count = sections[i][1] +- +-if i < len(sections): +- print(f'Missing "{sections[i][0]}" section') +- exit_code = 10 +- +-sys.exit(exit_code) + + ## gcc/testsuite/gcc.src/maintainers.exp ## +@@ + load_lib "target-supports.exp" + + proc gcc_src_run_maintainers_verify_sh {} { +- set script check-MAINTAINERS.py ++ set script gen-MAINTAINERS.py + + global srcdir + set rootdir $srcdir/../.. + set contrib $rootdir/contrib + set maintainers $rootdir/MAINTAINERS ++ set maintainers_yml $rootdir/MAINTAINERS.yml + + if { ![check_effective_target_recent_python3] } { + unsupported "$script recent python3 is missing" + return + } + +- if { ![check_effective_target_python3_module "unidecode"] } { +- unsupported "$script 'unidecode' python3 module is missing" +- return ++ foreach pylib {"unidecode" "jsonschema" "yaml"} { ++ if { ![check_effective_target_python3_module $pylib] } { ++ unsupported "$script '$pylib' python3 module is missing" ++ return ++ } + } + +- +- set result [remote_exec host $contrib/$script $maintainers] ++ # This will generate a new copy of the MAINTAINERS data from the ++ # YAML data after performing some sanity checks on the input. There ++ # are two failure modes to consider - the YAML is invalid and the ++ # generated result does not match the expected MAINTAINERS file. ++ set result [remote_exec host $contrib/$script "-o MAINTAINERS.tst $maintainers_yml"] + set status [lindex $result 0] + if { $status == 0 } then { +- pass "$script" ++ set result [remote_exec host "diff" "-u $maintainers MAINTAINERS.tst"] ++ set status [lindex $result 0] ++ if { $status == 0 } then { ++ pass "$script" ++ } else { ++ send_log "$result\n" ++ fail "$script: $maintainers not updated correctly" ++ } + } else { + send_log "$result\n" +- fail "$script" ++ fail "$script: generate failed" + } + } + 8: 8455cd53d3b1 = 8: 442ae21dadc7 MAINTAINERS: Add a script to create a new entry in the mainainers data -- 2.54.0
