Add the upstream status to the output of Porting/Maintainers for --opened.
[p5sagit/p5-mst-13.2.git] / Porting / Maintainers.pm
CommitLineData
0cf51544 1#
2# Maintainers.pm - show information about maintainers
3#
4
5package Maintainers;
6
7use strict;
8
9use 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.
12use 5.008;
0cf51544 13
14require "Maintainers.pl";
15use vars qw(%Modules %Maintainers);
16
d8528f07 17use 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 23require Exporter;
24
25use File::Find;
26use Getopt::Long;
27
28my %MANIFEST;
29if (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
40sub get_module_pat {
41 my $m = shift;
42 split ' ', $Modules{$m}{FILES};
43}
44
45sub 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
65sub 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
72sub 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
1be1464a 82--opened list all modules of modified files
0cf51544 83Matching is case-ignoring regexp, author matching is both by
84the short id and by the full name and email. A "module" may
85not be just a module, it may be a file or files or a subdirectory.
86The options may be abbreviated to their unique prefixes
87__EOF__
88 exit(0);
89}
90
91my $Maintainer;
92my $Module;
93my $Files;
678b26d7 94my $Check;
d933dc9e 95my $Opened;
0cf51544 96
97sub 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;
1be1464a 109
d933dc9e 110 if ($Opened) {
fdd40f96 111 chomp (@Files = `git ls-files -m --full-name`);
d933dc9e 112 die if $?;
d933dc9e 113 } else {
114 @Files = @ARGV;
115 }
0cf51544 116
117 usage() if @Files && ($Maintainer || $Module || $Files);
118
119 for my $mean ($Maintainer, $Module) {
120 warn "$0: Did you mean '$0 $mean'?\n"
121 if $mean && -e $mean && $mean ne '.' && !$Files;
122 }
123
124 warn "$0: Did you mean '$0 -mo $Maintainer'?\n"
125 if defined $Maintainer && exists $Modules{$Maintainer};
126
127 warn "$0: Did you mean '$0 -ma $Module'?\n"
128 if defined $Module && exists $Maintainers{$Module};
129
130 return ($Maintainer, $Module, $Files, @Files);
131}
132
133sub show_results {
134 my ($Maintainer, $Module, $Files, @Files) = @_;
135
136 if ($Maintainer) {
137 for my $m (sort keys %Maintainers) {
138 if ($m =~ /$Maintainer/io || $Maintainers{$m} =~ /$Maintainer/io) {
139 my @modules = get_maintainer_modules($m);
140 if ($Module) {
141 @modules = grep { /$Module/io } @modules;
142 }
143 if ($Files) {
144 my @files;
145 for my $module (@modules) {
146 push @files, get_module_files($module);
147 }
148 printf "%-15s @files\n", $m;
149 } else {
150 if ($Module) {
151 printf "%-15s @modules\n", $m;
152 } else {
153 printf "%-15s $Maintainers{$m}\n", $m;
154 }
155 }
156 }
157 }
158 } elsif ($Module) {
159 for my $m (sort { lc $a cmp lc $b } keys %Modules) {
160 if ($m =~ /$Module/io) {
161 if ($Files) {
162 my @files = get_module_files($m);
163 printf "%-15s @files\n", $m;
164 } else {
adc42316 165 printf "%-15s %-12s %s\n", $m, $Modules{$m}{MAINTAINER}, $Modules{$m}{UPSTREAM}||'unknown';
0cf51544 166 }
167 }
168 }
3428fdd5 169 } elsif ($Check) {
170 if( @Files ) {
171 missing_maintainers( qr{\.(?:[chty]|p[lm]|xs)\z}msx, @Files)
172 }
173 else {
174 duplicated_maintainers();
175 }
0cf51544 176 } elsif (@Files) {
177 my %ModuleByFile;
178
179 for (@Files) { s:^\./:: }
180
181 @ModuleByFile{@Files} = ();
182
183 # First try fast match.
184
185 my %ModuleByPat;
186 for my $module (keys %Modules) {
187 for my $pat (get_module_pat($module)) {
188 $ModuleByPat{$pat} = $module;
189 }
190 }
191 # Expand any globs.
192 my %ExpModuleByPat;
193 for my $pat (keys %ModuleByPat) {
194 if (-e $pat) {
195 $ExpModuleByPat{$pat} = $ModuleByPat{$pat};
196 } else {
197 for my $exp (glob($pat)) {
198 $ExpModuleByPat{$exp} = $ModuleByPat{$pat};
199 }
200 }
201 }
202 %ModuleByPat = %ExpModuleByPat;
203 for my $file (@Files) {
204 $ModuleByFile{$file} = $ModuleByPat{$file}
205 if exists $ModuleByPat{$file};
206 }
207
208 # If still unresolved files...
209 if (my @ToDo = grep { !defined $ModuleByFile{$_} } keys %ModuleByFile) {
210
211 # Cannot match what isn't there.
212 @ToDo = grep { -e $_ } @ToDo;
213
214 if (@ToDo) {
215 # Try prefix matching.
216
217 # Remove trailing slashes.
218 for (@ToDo) { s|/$|| }
219
220 my %ToDo;
221 @ToDo{@ToDo} = ();
222
223 for my $pat (keys %ModuleByPat) {
224 last unless keys %ToDo;
225 if (-d $pat) {
226 my @Done;
227 for my $file (keys %ToDo) {
228 if ($file =~ m|^$pat|i) {
229 $ModuleByFile{$file} = $ModuleByPat{$pat};
230 push @Done, $file;
231 }
232 }
233 delete @ToDo{@Done};
234 }
235 }
236 }
237 }
238
239 for my $file (@Files) {
240 if (defined $ModuleByFile{$file}) {
241 my $module = $ModuleByFile{$file};
242 my $maintainer = $Modules{$ModuleByFile{$file}}{MAINTAINER};
c5654d5b 243 my $upstream = $Modules{$module}{UPSTREAM}||'unknown';
244 printf "%-15s [%-7s] $module $maintainer $Maintainers{$maintainer}\n", $file, $upstream;
0cf51544 245 } else {
246 printf "%-15s ?\n", $file;
247 }
248 }
249 }
fdd40f96 250 elsif ($Opened) {
251 print "(No files are modified)\n";
252 }
0cf51544 253 else {
254 usage();
255 }
256}
257
3428fdd5 258my %files;
259
260sub 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
269sub 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 278sub warn_maintainer {
279 my $name = shift;
280 warn "File $name has no maintainer\n" if not $files{$name};
281}
282
3428fdd5 283sub 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 2941;
295