]>
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."""
80 nowarn_status
= ['Supported', 'Maintained']
82 if path_in_section(path
, section
):
83 for status
in section
['status']:
84 if status
not in nowarn_status
:
85 print('WARNING: Maintained status for "%s" is \'%s\'!' % (path
, status
))
86 for address
in section
['maintainer'], section
['reviewer']:
87 # Convert to list if necessary
88 if isinstance(address
, list):
89 maintainers
+= address
92 for address
in section
['list']:
93 # Convert to list if necessary
94 if isinstance(address
, list):
99 return maintainers
, lists
101 def get_maintainers(path
, sections
, level
=0):
102 """For 'path', iterates over all sections, returning maintainers
103 for matching ones."""
106 for section
in sections
:
107 tmp_maint
, tmp_lists
= get_section_maintainers(path
, section
)
109 maintainers
+= tmp_maint
114 # If no match found, look for match for (nonexistent) file
115 # REPO.working_dir/<default>
116 print('"%s": no maintainers found, looking for default' % path
)
118 maintainers
= get_maintainers('<default>', sections
, level
=level
+ 1)
120 print("No <default> maintainers set for project.")
124 return maintainers
+ lists
126 def parse_maintainers_line(line
):
127 """Parse one line of Maintainers.txt, returning any match group and its key."""
128 for key
, expression
in EXPRESSIONS
.items():
129 match
= expression
.match(line
)
131 return key
, match
.group(key
)
134 def parse_maintainers_file(filename
):
135 """Parse the Maintainers.txt from top-level of repo and
136 return a list containing dictionaries of all sections."""
137 with
open(filename
, 'r') as text
:
138 line
= text
.readline()
140 section
= defaultdict(list)
142 key
, value
= parse_maintainers_line(line
)
144 section
[key
].append(value
)
146 line
= text
.readline()
147 # If end of section (end of file, or non-tag line encountered)...
148 if not key
or not value
or not line
:
149 # ...if non-empty, append section to list.
151 sectionlist
.append(section
.copy())
156 def get_modified_files(repo
, args
):
157 """Returns a list of the files modified by the commit specified in 'args'."""
158 commit
= repo
.commit(args
.commit
)
159 return commit
.stats
.files
161 if __name__
== '__main__':
162 PARSER
= argparse
.ArgumentParser(
163 description
='Retrieves information on who to cc for review on a given commit')
164 PARSER
.add_argument('commit',
166 help='git revision to examine (default: HEAD)',
169 PARSER
.add_argument('-l', '--lookup',
170 help='Find section matches for path LOOKUP',
172 ARGS
= PARSER
.parse_args()
174 REPO
= SetupGit
.locate_repo()
176 CONFIG_FILE
= os
.path
.join(REPO
.working_dir
, 'Maintainers.txt')
178 SECTIONS
= parse_maintainers_file(CONFIG_FILE
)
181 FILES
= [ARGS
.lookup
.replace('\\','/')]
183 FILES
= get_modified_files(REPO
, ARGS
)
189 addresslist
= get_maintainers(file, SECTIONS
)
191 ADDRESSES
+= addresslist
193 for address
in list(OrderedDict
.fromkeys(ADDRESSES
)):
194 if '<' in address
and '>' in address
:
195 address
= address
.split('>', 1)[0] + '>'
196 print(' %s' % address
)