143890927Smillert#!perl 243890927Smillert# Reports, in a perl source tree, which dual-lived core modules have not the 343890927Smillert# same version than the corresponding module on CPAN. 443003dfeSmillert# with -t option, can compare multiple source trees in tabular form. 543890927Smillert 643890927Smillertuse 5.9.0; 743890927Smillertuse strict; 843890927Smillertuse Getopt::Std; 943890927Smillertuse ExtUtils::MM_Unix; 1043890927Smillertuse lib 'Porting'; 1143003dfeSmillertuse Maintainers qw(get_module_files reload_manifest %Modules); 1243003dfeSmillertuse Cwd; 1343003dfeSmillert 1443003dfeSmillertuse List::Util qw(max); 1543890927Smillert 1643890927Smillertour $packagefile = '02packages.details.txt'; 1743890927Smillert 1843890927Smillertsub usage () { 1943890927Smillert die <<USAGE; 2043003dfeSmillert$0 2143003dfeSmillert$0 -t home1[:label] home2[:label] ... 2243003dfeSmillert 2343003dfeSmillertReport which core modules are outdated. 2443890927SmillertTo be run at the root of a perl source tree. 2543003dfeSmillert 2643890927SmillertOptions : 2743890927Smillert-h : help 2843890927Smillert-v : verbose (print all versions of all files, not only those which differ) 2943890927Smillert-f : force download of $packagefile from CPAN 3043890927Smillert (it's expected to be found in the current directory) 3143003dfeSmillert-t : display in tabular form CPAN vs one or more perl source trees 3243890927SmillertUSAGE 3343890927Smillert} 3443890927Smillert 3543890927Smillertsub get_package_details () { 3643890927Smillert my $url = 'http://www.cpan.org/modules/02packages.details.txt.gz'; 3743890927Smillert unlink $packagefile; 3843890927Smillert system("wget $url && gunzip $packagefile.gz") == 0 3943890927Smillert or die "Failed to get package details\n"; 4043890927Smillert} 4143890927Smillert 4243003dfeSmillertgetopts('fhvt'); 4343890927Smillertour $opt_h and usage; 4443003dfeSmillertour $opt_t; 4543003dfeSmillert 4643003dfeSmillertmy @sources = @ARGV ? @ARGV : '.'; 47898184e3Ssthendie "Too many directories specified without -t option\n" 4843003dfeSmillert if @sources != 1 and ! $opt_t; 4943003dfeSmillert 5043003dfeSmillert@sources = map { 5143003dfeSmillert # handle /home/user/perl:bleed style labels 5243003dfeSmillert my ($dir,$label) = split /:/; 5343003dfeSmillert $label = $dir unless defined $label; 5443003dfeSmillert [ $dir, $label ]; 5543003dfeSmillert } @sources; 5643003dfeSmillert 57850e2753Smillertour $opt_f || !-f $packagefile and get_package_details; 5843890927Smillert 5943890927Smillert# Load the package details. All of them. 6043890927Smillertmy %cpanversions; 61*5759b3d2Safresh1open my $fh, '<', $packagefile or die $!; 6243890927Smillertwhile (<$fh>) { 6343890927Smillert my ($p, $v) = split ' '; 6443003dfeSmillert next if 1../^\s*$/; # skip header 6543890927Smillert $cpanversions{$p} = $v; 6643890927Smillert} 6743890927Smillertclose $fh; 6843890927Smillert 6943003dfeSmillertmy %results; 7043003dfeSmillert 7143003dfeSmillert# scan source tree(s) and CPAN module list, and put results in %results 7243003dfeSmillert 7343003dfeSmillertforeach my $source (@sources) { 7443003dfeSmillert my ($srcdir, $label) = @$source; 7543003dfeSmillert my $olddir = getcwd(); 7643003dfeSmillert chdir $srcdir or die "chdir $srcdir: $!\n"; 7743003dfeSmillert 7843003dfeSmillert # load the MANIFEST file in the new directory 7943003dfeSmillert reload_manifest; 8043003dfeSmillert 8143890927Smillert for my $dist (sort keys %Modules) { 8243890927Smillert next unless $Modules{$dist}{CPAN}; 8343890927Smillert for my $file (get_module_files($dist)) { 8443003dfeSmillert next if $file !~ /(\.pm|_pm.PL)\z/ 8543003dfeSmillert or $file =~ m{^t/} or $file =~ m{/t/}; 8643003dfeSmillert my $vcore = '!EXIST'; 8743003dfeSmillert $vcore = MM->parse_version($file) // 'undef' if -f $file; 8843003dfeSmillert 8943003dfeSmillert # get module name from filename to lookup CPAN version 9043890927Smillert my $module = $file; 9143003dfeSmillert $module =~ s/\_pm.PL\z//; 9243890927Smillert $module =~ s/\.pm\z//; 9343890927Smillert # some heuristics to figure out the module name from the file name 94b39c5158Smillert $module =~ s{^(lib|ext|dist|cpan)/}{} 95b39c5158Smillert and $1 =~ /(?:ext|dist|cpan)/ 9643003dfeSmillert and ( 9743003dfeSmillert # ext/Foo-Bar/Bar.pm 9843003dfeSmillert $module =~ s{^(\w+)-(\w+)/\2$}{$1/lib/$1/$2}, 9943003dfeSmillert # ext/Encode/Foo/Foo.pm 10043003dfeSmillert $module =~ s{^(Encode)/(\w+)/\2$}{$1/lib/$1/$2}, 10143003dfeSmillert $module =~ s{^[^/]+/}{}, 10243003dfeSmillert $module =~ s{^lib/}{}, 10343890927Smillert ); 10443890927Smillert $module =~ s{/}{::}g; 10543003dfeSmillert my $vcpan = $cpanversions{$module} // 'undef'; 10643003dfeSmillert $results{$dist}{$file}{$label} = $vcore; 10743003dfeSmillert $results{$dist}{$file}{CPAN} = $vcpan; 10843003dfeSmillert } 10943003dfeSmillert } 11043003dfeSmillert 11143003dfeSmillert chdir $olddir or die "chdir $olddir: $!\n"; 11243003dfeSmillert} 11343003dfeSmillert 11443003dfeSmillert# output %results in the requested format 11543003dfeSmillert 11643003dfeSmillertmy @labels = ((map $_->[1], @sources), 'CPAN' ); 11743003dfeSmillert 11843003dfeSmillertif ($opt_t) { 11943003dfeSmillert my %changed; 12043003dfeSmillert my @fields; 12143003dfeSmillert for my $dist (sort { lc $a cmp lc $b } keys %results) { 12243003dfeSmillert for my $file (sort keys %{$results{$dist}}) { 12343003dfeSmillert my @versions = @{$results{$dist}{$file}}{@labels}; 12443003dfeSmillert for (0..$#versions) { 12543003dfeSmillert $fields[$_] = max($fields[$_], 12643003dfeSmillert length $versions[$_], 12743003dfeSmillert length $labels[$_], 12843003dfeSmillert length '!EXIST' 12943003dfeSmillert ); 13043003dfeSmillert } 13143003dfeSmillert if (our $opt_v or grep $_ ne $versions[0], @versions) { 13243003dfeSmillert $changed{$dist} = 1; 13343003dfeSmillert } 13443003dfeSmillert } 13543003dfeSmillert } 13643003dfeSmillert printf "%*s ", $fields[$_], $labels[$_] for 0..$#labels; 13743003dfeSmillert print "\n"; 13843003dfeSmillert printf "%*s ", $fields[$_], '-' x length $labels[$_] for 0..$#labels; 13943003dfeSmillert print "\n"; 14043003dfeSmillert 14143003dfeSmillert my $field_total; 14243003dfeSmillert $field_total += $_ + 1 for @fields; 14343003dfeSmillert 14443003dfeSmillert for my $dist (sort { lc $a cmp lc $b } keys %results) { 14543003dfeSmillert next unless $changed{$dist}; 14643003dfeSmillert print " " x $field_total, " $dist\n"; 14743003dfeSmillert for my $file (sort keys %{$results{$dist}}) { 14843003dfeSmillert my @versions = @{$results{$dist}{$file}}{@labels}; 14943003dfeSmillert for (0..$#versions) { 15043003dfeSmillert printf "%*s ", $fields[$_], $versions[$_]//'!EXIST' 15143003dfeSmillert } 15243003dfeSmillert print " $file\n"; 15343003dfeSmillert } 15443003dfeSmillert } 15543003dfeSmillert} 15643003dfeSmillertelse { 15743003dfeSmillert for my $dist (sort { lc $a cmp lc $b } keys %results) { 158b39c5158Smillert my $distname_printed = 0; 15943003dfeSmillert for my $file (sort keys %{$results{$dist}}) { 16043003dfeSmillert my ($vcore, $vcpan) = @{$results{$dist}{$file}}{@labels}; 16143890927Smillert if (our $opt_v or $vcore ne $vcpan) { 162898184e3Ssthen print "\n$dist ($Modules{$dist}{MAINTAINER}):\n" unless ($distname_printed++); 163b39c5158Smillert print "\t$file: core=$vcore, cpan=$vcpan\n"; 16443890927Smillert } 16543890927Smillert } 16643890927Smillert } 16743003dfeSmillert} 168