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