]>
git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/GetMaintainer.py
2 # Retrieves the people to request review from on submission of a commit.
4 # Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
9 from __future__
import print_function
10 from collections
import defaultdict
11 from collections
import OrderedDict
18 'exclude': re
.compile(r
'^X:\s*(?P<exclude>.*?)\r*$'),
19 'file': re
.compile(r
'^F:\s*(?P<file>.*?)\r*$'),
20 'list': re
.compile(r
'^L:\s*(?P<list>.*?)\r*$'),
21 'maintainer': re
.compile(r
'^M:\s*(?P<maintainer>.*<.*?>)\r*$'),
22 'reviewer': re
.compile(r
'^R:\s*(?P<reviewer>.*?)\r*$'),
23 'status': re
.compile(r
'^S:\s*(?P<status>.*?)\r*$'),
24 'tree': re
.compile(r
'^T:\s*(?P<tree>.*?)\r*$'),
25 'webpage': re
.compile(r
'^W:\s*(?P<webpage>.*?)\r*$')
28 def printsection(section
):
29 """Prints out the dictionary describing a Maintainers.txt section."""
31 for key
in section
.keys():
32 print("Key: %s" % key
)
33 for item
in section
[key
]:
36 def pattern_to_regex(pattern
):
37 """Takes a string containing regular UNIX path wildcards
38 and returns a string suitable for matching with regex."""
40 pattern
= pattern
.replace('.', r
'\.')
41 pattern
= pattern
.replace('?', r
'.')
42 pattern
= pattern
.replace('*', r
'.*')
44 if pattern
.endswith('/'):
46 elif pattern
.endswith('.*'):
47 pattern
= pattern
[:-2]
48 pattern
+= r
'(?!.*?/.*?)'
52 def path_in_section(path
, section
):
53 """Returns True of False indicating whether the path is covered by
54 the current section."""
55 if not 'file' in section
:
58 for pattern
in section
['file']:
59 regex
= pattern_to_regex(pattern
)
61 match
= re
.match(regex
, path
)
63 # Check if there is an exclude pattern that applies
64 for pattern
in section
['exclude']:
65 regex
= pattern_to_regex(pattern
)
67 match
= re
.match(regex
, path
)
75 def get_section_maintainers(path
, section
):
76 """Returns a list with email addresses to any M: and R: entries
77 matching the provided path in the provided section."""
81 if path_in_section(path
, section
):
82 for address
in section
['maintainer'], section
['reviewer']:
83 # Convert to list if necessary
84 if isinstance(address
, list):
85 maintainers
+= address
88 for address
in section
['list']:
89 # Convert to list if necessary
90 if isinstance(address
, list):
95 return maintainers
, lists
97 def get_maintainers(path
, sections
, level
=0):
98 """For 'path', iterates over all sections, returning maintainers
102 for section
in sections
:
103 tmp_maint
, tmp_lists
= get_section_maintainers(path
, section
)
105 maintainers
+= tmp_maint
110 # If no match found, look for match for (nonexistent) file
111 # REPO.working_dir/<default>
112 print('"%s": no maintainers found, looking for default' % path
)
114 maintainers
= get_maintainers('<default>', sections
, level
=level
+ 1)
116 print("No <default> maintainers set for project.")
120 return maintainers
+ lists
122 def parse_maintainers_line(line
):
123 """Parse one line of Maintainers.txt, returning any match group and its key."""
124 for key
, expression
in EXPRESSIONS
.items():
125 match
= expression
.match(line
)
127 return key
, match
.group(key
)
130 def parse_maintainers_file(filename
):
131 """Parse the Maintainers.txt from top-level of repo and
132 return a list containing dictionaries of all sections."""
133 with
open(filename
, 'r') as text
:
134 line
= text
.readline()
136 section
= defaultdict(list)
138 key
, value
= parse_maintainers_line(line
)
140 section
[key
].append(value
)
142 line
= text
.readline()
143 # If end of section (end of file, or non-tag line encountered)...
144 if not key
or not value
or not line
:
145 # ...if non-empty, append section to list.
147 sectionlist
.append(section
.copy())
152 def get_modified_files(repo
, args
):
153 """Returns a list of the files modified by the commit specified in 'args'."""
154 commit
= repo
.commit(args
.commit
)
155 return commit
.stats
.files
157 if __name__
== '__main__':
158 PARSER
= argparse
.ArgumentParser(
159 description
='Retrieves information on who to cc for review on a given commit')
160 PARSER
.add_argument('commit',
162 help='git revision to examine (default: HEAD)',
165 PARSER
.add_argument('-l', '--lookup',
166 help='Find section matches for path LOOKUP',
168 ARGS
= PARSER
.parse_args()
170 REPO
= SetupGit
.locate_repo()
172 CONFIG_FILE
= os
.path
.join(REPO
.working_dir
, 'Maintainers.txt')
174 SECTIONS
= parse_maintainers_file(CONFIG_FILE
)
177 FILES
= [ARGS
.lookup
]
179 FILES
= get_modified_files(REPO
, ARGS
)
185 addresslist
= get_maintainers(file, SECTIONS
)
187 ADDRESSES
+= addresslist
189 for address
in list(OrderedDict
.fromkeys(ADDRESSES
)):
190 print(' %s' % address
)