Commit | Line | Data |
0cf51544 |
1 | # |
2 | # Maintainers.pm - show information about maintainers |
3 | # |
4 | |
5 | package Maintainers; |
6 | |
7 | use strict; |
8 | |
9 | use lib "Porting"; |
357244ac |
10 | # Please don't use post 5.008 features as this module is used by |
11 | # Porting/makemeta, and that in turn has to be run by the perl just built. |
12 | use 5.008; |
0cf51544 |
13 | |
14 | require "Maintainers.pl"; |
15 | use vars qw(%Modules %Maintainers); |
16 | |
d8528f07 |
17 | use vars qw(@ISA @EXPORT_OK $VERSION); |
0cf51544 |
18 | @ISA = qw(Exporter); |
19 | @EXPORT_OK = qw(%Modules %Maintainers |
20 | get_module_files get_module_pat |
21 | show_results process_options); |
d8528f07 |
22 | $VERSION = 0.02; |
0cf51544 |
23 | require Exporter; |
24 | |
25 | use File::Find; |
26 | use Getopt::Long; |
27 | |
28 | my %MANIFEST; |
29 | if (open(MANIFEST, "MANIFEST")) { |
30 | while (<MANIFEST>) { |
31 | if (/^(\S+)\t+(.+)$/) { |
32 | $MANIFEST{$1}++; |
33 | } |
34 | } |
35 | close MANIFEST; |
36 | } else { |
37 | die "$0: Failed to open MANIFEST for reading: $!\n"; |
38 | } |
39 | |
40 | sub get_module_pat { |
41 | my $m = shift; |
42 | split ' ', $Modules{$m}{FILES}; |
43 | } |
44 | |
45 | sub get_module_files { |
46 | my $m = shift; |
47 | sort { lc $a cmp lc $b } |
48 | map { |
49 | -f $_ ? # Files as-is. |
50 | $_ : |
51 | -d _ ? # Recurse into directories. |
52 | do { |
53 | my @files; |
54 | find( |
55 | sub { |
56 | push @files, $File::Find::name |
57 | if -f $_ && exists $MANIFEST{$File::Find::name}; |
58 | }, $_); |
59 | @files; |
60 | } |
61 | : glob($_) # The rest are globbable patterns. |
62 | } get_module_pat($m); |
63 | } |
64 | |
65 | sub get_maintainer_modules { |
66 | my $m = shift; |
67 | sort { lc $a cmp lc $b } |
68 | grep { $Modules{$_}{MAINTAINER} eq $m } |
69 | keys %Modules; |
70 | } |
71 | |
72 | sub usage { |
73 | print <<__EOF__; |
3428fdd5 |
74 | $0: Usage: $0 [[--maintainer M --module M --files]|[--check] file ...] |
0cf51544 |
75 | --maintainer M list all maintainers matching M |
76 | --module M list all modules matching M |
77 | --files list all files |
678b26d7 |
78 | --check check consistency of Maintainers.pl |
3428fdd5 |
79 | with a file checks if it has a maintainer |
80 | with a dir checks all files have a maintainer |
81 | otherwise checks for multiple maintainers |
d933dc9e |
82 | --opened list all modules of files opened by perforce |
0cf51544 |
83 | Matching is case-ignoring regexp, author matching is both by |
84 | the short id and by the full name and email. A "module" may |
85 | not be just a module, it may be a file or files or a subdirectory. |
86 | The options may be abbreviated to their unique prefixes |
87 | __EOF__ |
88 | exit(0); |
89 | } |
90 | |
91 | my $Maintainer; |
92 | my $Module; |
93 | my $Files; |
678b26d7 |
94 | my $Check; |
d933dc9e |
95 | my $Opened; |
0cf51544 |
96 | |
97 | sub process_options { |
98 | usage() |
99 | unless |
100 | GetOptions( |
101 | 'maintainer=s' => \$Maintainer, |
102 | 'module=s' => \$Module, |
103 | 'files' => \$Files, |
678b26d7 |
104 | 'check' => \$Check, |
d933dc9e |
105 | 'opened' => \$Opened, |
0cf51544 |
106 | ); |
107 | |
d933dc9e |
108 | my @Files; |
109 | |
110 | if ($Opened) { |
33768f13 |
111 | @Files = `p4 opened`; |
d933dc9e |
112 | die if $?; |
33768f13 |
113 | foreach (@Files) { |
114 | s!#.*!!s; |
115 | s!^//depot/(?:perl|.*?/perl)/!!; |
116 | } |
d933dc9e |
117 | } else { |
118 | @Files = @ARGV; |
119 | } |
0cf51544 |
120 | |
121 | usage() if @Files && ($Maintainer || $Module || $Files); |
122 | |
123 | for my $mean ($Maintainer, $Module) { |
124 | warn "$0: Did you mean '$0 $mean'?\n" |
125 | if $mean && -e $mean && $mean ne '.' && !$Files; |
126 | } |
127 | |
128 | warn "$0: Did you mean '$0 -mo $Maintainer'?\n" |
129 | if defined $Maintainer && exists $Modules{$Maintainer}; |
130 | |
131 | warn "$0: Did you mean '$0 -ma $Module'?\n" |
132 | if defined $Module && exists $Maintainers{$Module}; |
133 | |
134 | return ($Maintainer, $Module, $Files, @Files); |
135 | } |
136 | |
137 | sub show_results { |
138 | my ($Maintainer, $Module, $Files, @Files) = @_; |
139 | |
140 | if ($Maintainer) { |
141 | for my $m (sort keys %Maintainers) { |
142 | if ($m =~ /$Maintainer/io || $Maintainers{$m} =~ /$Maintainer/io) { |
143 | my @modules = get_maintainer_modules($m); |
144 | if ($Module) { |
145 | @modules = grep { /$Module/io } @modules; |
146 | } |
147 | if ($Files) { |
148 | my @files; |
149 | for my $module (@modules) { |
150 | push @files, get_module_files($module); |
151 | } |
152 | printf "%-15s @files\n", $m; |
153 | } else { |
154 | if ($Module) { |
155 | printf "%-15s @modules\n", $m; |
156 | } else { |
157 | printf "%-15s $Maintainers{$m}\n", $m; |
158 | } |
159 | } |
160 | } |
161 | } |
162 | } elsif ($Module) { |
163 | for my $m (sort { lc $a cmp lc $b } keys %Modules) { |
164 | if ($m =~ /$Module/io) { |
165 | if ($Files) { |
166 | my @files = get_module_files($m); |
167 | printf "%-15s @files\n", $m; |
168 | } else { |
169 | printf "%-15s $Modules{$m}{MAINTAINER}\n", $m; |
170 | } |
171 | } |
172 | } |
3428fdd5 |
173 | } elsif ($Check) { |
174 | if( @Files ) { |
175 | missing_maintainers( qr{\.(?:[chty]|p[lm]|xs)\z}msx, @Files) |
176 | } |
177 | else { |
178 | duplicated_maintainers(); |
179 | } |
0cf51544 |
180 | } elsif (@Files) { |
181 | my %ModuleByFile; |
182 | |
183 | for (@Files) { s:^\./:: } |
184 | |
185 | @ModuleByFile{@Files} = (); |
186 | |
187 | # First try fast match. |
188 | |
189 | my %ModuleByPat; |
190 | for my $module (keys %Modules) { |
191 | for my $pat (get_module_pat($module)) { |
192 | $ModuleByPat{$pat} = $module; |
193 | } |
194 | } |
195 | # Expand any globs. |
196 | my %ExpModuleByPat; |
197 | for my $pat (keys %ModuleByPat) { |
198 | if (-e $pat) { |
199 | $ExpModuleByPat{$pat} = $ModuleByPat{$pat}; |
200 | } else { |
201 | for my $exp (glob($pat)) { |
202 | $ExpModuleByPat{$exp} = $ModuleByPat{$pat}; |
203 | } |
204 | } |
205 | } |
206 | %ModuleByPat = %ExpModuleByPat; |
207 | for my $file (@Files) { |
208 | $ModuleByFile{$file} = $ModuleByPat{$file} |
209 | if exists $ModuleByPat{$file}; |
210 | } |
211 | |
212 | # If still unresolved files... |
213 | if (my @ToDo = grep { !defined $ModuleByFile{$_} } keys %ModuleByFile) { |
214 | |
215 | # Cannot match what isn't there. |
216 | @ToDo = grep { -e $_ } @ToDo; |
217 | |
218 | if (@ToDo) { |
219 | # Try prefix matching. |
220 | |
221 | # Remove trailing slashes. |
222 | for (@ToDo) { s|/$|| } |
223 | |
224 | my %ToDo; |
225 | @ToDo{@ToDo} = (); |
226 | |
227 | for my $pat (keys %ModuleByPat) { |
228 | last unless keys %ToDo; |
229 | if (-d $pat) { |
230 | my @Done; |
231 | for my $file (keys %ToDo) { |
232 | if ($file =~ m|^$pat|i) { |
233 | $ModuleByFile{$file} = $ModuleByPat{$pat}; |
234 | push @Done, $file; |
235 | } |
236 | } |
237 | delete @ToDo{@Done}; |
238 | } |
239 | } |
240 | } |
241 | } |
242 | |
243 | for my $file (@Files) { |
244 | if (defined $ModuleByFile{$file}) { |
245 | my $module = $ModuleByFile{$file}; |
246 | my $maintainer = $Modules{$ModuleByFile{$file}}{MAINTAINER}; |
247 | printf "%-15s $module $maintainer $Maintainers{$maintainer}\n", $file; |
248 | } else { |
249 | printf "%-15s ?\n", $file; |
250 | } |
251 | } |
252 | } |
253 | else { |
254 | usage(); |
255 | } |
256 | } |
257 | |
3428fdd5 |
258 | my %files; |
259 | |
260 | sub maintainers_files { |
261 | %files = (); |
678b26d7 |
262 | for my $k (keys %Modules) { |
263 | for my $f (get_module_files($k)) { |
264 | ++$files{$f}; |
265 | } |
266 | } |
3428fdd5 |
267 | } |
268 | |
269 | sub duplicated_maintainers { |
270 | maintainers_files(); |
678b26d7 |
271 | for my $f (keys %files) { |
272 | if ($files{$f} > 1) { |
273 | warn "File $f appears $files{$f} times in Maintainers.pl\n"; |
274 | } |
275 | } |
276 | } |
277 | |
357244ac |
278 | sub warn_maintainer { |
279 | my $name = shift; |
280 | warn "File $name has no maintainer\n" if not $files{$name}; |
281 | } |
282 | |
3428fdd5 |
283 | sub missing_maintainers { |
284 | my($check, @path) = @_; |
285 | maintainers_files(); |
286 | my @dir; |
357244ac |
287 | for my $d (@path) { |
288 | if( -d $d ) { push @dir, $d } else { warn_maintainer($d) } |
289 | } |
3428fdd5 |
290 | find sub { warn_maintainer($File::Find::name) if /$check/; }, @dir |
291 | if @dir; |
292 | } |
293 | |
0cf51544 |
294 | 1; |
295 | |