Add add-package.pl to the core (was Re: Why no (XML|DBI|WWW|Template) modules in...
Jos I. Boumans [Mon, 2 Jul 2007 15:20:37 +0000 (17:20 +0200)]
From: "Jos I. Boumans" <kane@dwim.org>
Message-Id: <A819F8C7-19C9-4ECE-8CF5-80FAAF54F890@dwim.org>

p4raw-id: //depot/perl@31518

MANIFEST
Porting/add-package.pl [new file with mode: 0644]

index c623746..09c40dd 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -3193,6 +3193,7 @@ pod/splitman                      Splits perlfunc into multiple man pages
 pod/splitpod                   Splits perlfunc into multiple pod pages
 Policy_sh.SH           Hold site-wide preferences between Configure runs.
 Porting/apply          Apply patches sent by mail
+Porting/add-package.pl Add/Update CPAN modules that are part of Core
 Porting/check83.pl     Check whether we are 8.3-friendly
 Porting/checkAUTHORS.pl        Check that the AUTHORS file is complete
 Porting/checkcase.pl   Check whether we are case-insensitive-fs-friendly
diff --git a/Porting/add-package.pl b/Porting/add-package.pl
new file mode 100644 (file)
index 0000000..db4c531
--- /dev/null
@@ -0,0 +1,441 @@
+#!/opt/bin/perl
+use strict;
+use warnings;
+
+use Cwd;
+use Getopt::Std;
+use File::Basename;
+use FindBin;
+
+my $Opts = {};
+getopts( 'r:p:e:vud', $Opts );
+
+my $Cwd         = cwd();
+my $Verbose     = 1;
+my $ExcludeRe   = $Opts->{e} ? qr/$Opts->{e}/ : undef;
+my $Debug       = $Opts->{v} || 0;
+my $RunDiff     = $Opts->{d} || 0;
+my $PkgDir      = $Opts->{p} || cwd();
+my $MasterRepo  = $Opts->{r} or die "Need repository!\n". usage();
+
+### strip trailing slashes;
+$MasterRepo =~ s|/$||;
+
+my $CPV         = $Debug ? '-v' : '';
+my $TestBin     = 'ptardiff';
+my $PkgDirRe    = quotemeta( $PkgDir .'/' );
+my $Repo        = $MasterRepo . '-' . basename( $PkgDir ) . '.' . $$;
+
+### chdir there
+chdir $PkgDir or die "Could not chdir to $PkgDir: $!";
+
+### set up the repo dir from the master repo
+{   print "Setting up working repo under '$Repo'..." if $Verbose;
+    unless( -d $Repo ) {
+        system( "mkdir -p $Repo" )
+            and die "Could not create working repo '$Repo': $?";
+    }
+
+    system( "cp -Rf $MasterRepo/* $Repo" )
+        and die "Copying master repo to $Repo failed: $?";
+
+    print "done\n" if $Verbose;
+}
+
+### copy over all files under lib/
+{   print "Copying libdir..." if $Verbose;
+    die "No lib/ directory found\n" unless -d 'lib';
+    system( "cp -fR $CPV lib $Repo" ) and die "Copy of lib/ failed: $?";
+    print "done\n" if $Verbose;
+}
+
+### find the directory to put the t/ and bin/ files under
+my $RelTopDir;      # topdir from the repo root
+my $TopDir;         # full path to the top dir
+my $ModName;        # name of the module
+my @ModFiles;       # the .PMs in this package
+{   print "Creating top level dir..." if $Verbose;
+
+    ### make sure we get the shortest file, so we dont accidentally get
+    ### a subdir
+    @ModFiles   =  sort { length($a) <=> length($b) }
+                   map  { chomp; $_ }
+                   grep { $ExcludeRe ? $_ !~ $ExcludeRe : 1 }
+                   grep /\.p(?:m|od)$/,
+                    `find $PkgDir/lib -type f`
+                        or die "No TopDir detected\n";
+
+    $RelTopDir  = $ModFiles[0];
+    $RelTopDir  =~ s/^$PkgDirRe//;
+    $RelTopDir  =~ s/\.p(m|od)$//;
+    $TopDir     = "$Repo/$RelTopDir";
+
+    ### create the dir if it's not there yet
+    unless( -d $TopDir ) {
+        system( "mkdir $TopDir" ) and die "Creating dir $TopDir failed: $?";
+    }
+
+    ### the module name, like Foo::Bar
+    ### slice syntax not elegant, but we need to remove the
+    ### leading 'lib/' entry
+    ### stupid temp vars! stupid perl! it doesn't do @{..}[0..-1] :(
+    {   my @list = @{[split '/', $RelTopDir]};
+        $ModName = join '::', @list[1 .. $#list];
+    }
+
+    ### the .pm files in this package
+    @ModFiles = map { s|^$PkgDirRe||; $_ } @ModFiles
+        or die "Could not detect modfiles\n";
+
+    print "done\n" if $Verbose;
+}
+
+my $TopDirRe = quotemeta( $TopDir . '/' );
+
+### copy over t/ and bin/ directories to the $TopDir
+my @TestFiles;
+{   print "Copying t/* files to $TopDir..." if $Verbose;
+
+   -d 't'
+       ? system( "cp -fR $CPV t $TopDir" ) && die "Copy of t/ failed: $?"
+       : warn "No t/ directory found\n";
+
+    @TestFiles =    map { chomp; s|^$TopDirRe||; $_ }
+                    ### should we get rid of this file?
+                    grep { $ExcludeRe && $_ =~ $ExcludeRe
+                        ? do {  warn "Removing $_\n";
+                                system("rm $_") and die "rm '$_' failed: $?";
+                                undef
+                            }
+                        : 1
+                     } `find $TopDir/t -type f`
+        or die "Could not detect testfiles\n";
+
+    print "done\n" if $Verbose;
+}
+
+my @BinFiles;
+BIN: {
+    unless (-d 'bin') {
+        print "No bin/ directory found\n" if $Verbose;
+        last BIN;
+    }
+    print "Copying bin/* files to $TopDir..." if $Verbose;
+
+    system("cp -fR $CPV bin/* $TopDir/bin/") && die "Copy of bin/ failed: $?";
+
+    @BinFiles = map { chomp; s|^$TopDirRe||; $_ }
+                ### should we get rid of this file?
+                grep { $ExcludeRe && $_ =~ $ExcludeRe
+                    ? do {  warn "Removing $_\n";
+                            system("rm $_") and die "rm '$_' failed: $?";
+                            undef
+                        }
+                    : 1
+                 } `find $TopDir/bin -type f`
+        or die "Could not detect binfiles\n";
+
+    print "done\n" if $Verbose;
+}
+
+### add files where they are required
+my @NewFiles;
+{   for my $bin ( map { basename( $_ ) } @BinFiles ) {
+        print "Registering $bin with system files...\n";
+
+        ### fix installperl, so these files get installed by other utils
+        ### ./installperl:    return if $name =~
+        ### /^(?:cpan|instmodsh|prove|corelist|ptar|ptardiff|config_data)\z/;
+        {   my $file = 'installperl';
+
+            ### not there already?
+            unless( `grep $TestBin $Repo/$file| grep $bin` ) {
+                print "   Adding $bin to $file..." if $Verbose;
+
+                ### double \\| required --> once for in this script, once
+                ### for the cli
+                system("$^X -pi -e 's/($TestBin\\|)/$bin|\$1/' $Repo/$file")
+                    and die "Could not add $bin to $file: $?";
+                print "done\n" if $Verbose;
+            } else {
+                print "    $bin already mentioned in $file\n" if $Verbose;
+            }
+        }
+
+        ### fix utils.lst, so the new tools are mentioned
+        {   my $file = 'utils.lst';
+
+            ### not there already?
+            unless( `grep $bin $Repo/$file` ) {
+                print "    Adding $bin to $file..." if $Verbose;
+
+                ### double \\| required --> once for in this script, once
+                ### for the cli
+                system("$^X -pi -e 's!($TestBin)!\$1\nutils/$bin!' $Repo/$file")
+                    and die "Could not add $bin to $file: $?";
+                print "done\n" if $Verbose;
+            } else {
+                print "    $bin already mentioned in $file\n" if $Verbose;
+            }
+        }
+
+        ### make a $bin.PL file and fix it up
+        {   my $src  = "utils/${TestBin}.PL";
+            my $file = "utils/${bin}.PL";
+
+            ### not there already?
+            unless( -e "$Repo/$file" ) {
+                print "    Creating $file..." if $Verbose;
+
+                ### important part of the template looks like this
+                ### (we'll need to change it):
+                # my $script = File::Spec->catfile(
+                #    File::Spec->catdir(
+                #        File::Spec->updir, qw[lib Archive Tar bin]
+                #    ), "module-load.pl");
+
+                ### copy another template file
+                system( "cp -f $Repo/$src $Repo/$file" )
+                    and die "Could not create $file from $src: $?";
+
+                ### change the 'updir' path
+                ### make sure to escape the \[ character classes
+                my $updir = join ' ', (split('/', $RelTopDir), 'bin');
+                system( "$^X -pi -e'".
+                        's/^(.*?File::Spec->updir, qw\[).+?(\].*)$/'.
+                        "\$1 $updir \$2/' $Repo/$file"
+                ) and die "Could not fix updir for $bin in $file: $?";
+
+
+                ### change the name of the file from $TestBin to $bin
+                system( "$^X -pi -e's/$TestBin/$bin/' $Repo/$file" )
+                    and die "Could not update $file with '$bin' as name: $?";
+
+                print "done\n" if $Verbose;
+
+            } else {
+                print "    $file already exists\n" if $Verbose;
+            }
+
+            ### we've may just have created a new file, it will have to
+            ### go into the manifest
+            push @NewFiles, $file;
+        }
+
+        ### add an entry to utils/Makefile for $bin
+        {   my $file = "utils/Makefile";
+
+            ### not there already?
+            unless( `grep $bin $Repo/$file` ) {
+                print "    Adding $bin entries to $file..." if $Verbose;
+
+                ### $bin appears on 4 lines in this file, so replace all 4
+                ### first, pl =
+                system( "$^X -pi -e'/^pl\\s+=/ && s/(${TestBin}.PL)/".
+                        "\$1 ${bin}.PL/' $Repo/$file"
+                ) and die "Could not add $bin to the pl = entry: $?";
+
+                ### next, plextract =
+                system( "$^X -pi -e'/^plextract\\s+=/ " .
+                        "&& s/(${TestBin})/\$1 $bin/' $Repo/$file"
+                ) and die "Could not add $bin to the plextract = entry: $?";
+
+                ### third, plextractexe =
+                system( "$^X -pi -e'/^plextractexe\\s+=/ " .
+                        "&& s!(\./${TestBin})!\$1 ./$bin!' $Repo/$file"
+                ) and die "Could not add $bin to the plextractexe = entry: $?";
+
+                ### last, the make directive $bin:
+                system( "$^X -pi -e'/^(${TestBin}:.+)/; \$x=\$1 or next;" .
+                        "\$x =~ s/$TestBin/$bin/g;" . '$_.=$/.$x.$/;' .
+                        "' $Repo/$file"
+                ) and die "Could not add $bin as a make directive: $?";
+
+                print "done\n" if $Verbose;
+            } else {
+                print "    $bin already added to $file\n" if $Verbose;
+            }
+        }
+
+        ### add entries to win32/Makefile and win32/makefile.mk
+        ### they contain the following lines:
+        # ./win32/makefile.mk:            ..\utils\ptardiff       \
+        # ./win32/makefile.mk:        xsubpp instmodsh prove ptar ptardiff
+        for my $file ( qw[win32/Makefile win32/makefile.mk] ) {
+            unless ( `grep $bin $Repo/$file` ) {
+                print "    Adding $bin entries to $file..." if $Verbose;
+
+               system( "$^X -pi -e'/^(.+?utils.${TestBin}.+)/;".
+                        '$x=$1 or next;' .
+                        "\$x =~ s/$TestBin/$bin/g;" . '$_.=$x.$/;' .
+                        "' $Repo/$file"
+                ) and die "Could not add $bin to UTILS section in $file: $?\n";
+
+                system( "$^X -pi -e's/( $TestBin)/\$1 $bin/' $Repo/$file" )
+                    and die "Could not add $bin to $file: $?\n";
+
+                print "done\n" if $Verbose;
+            } else {
+                print "    $bin already added to $file\n" if $Verbose;
+            }
+        }
+
+        ### we need some entries in a vms specific file as well..
+        ### except, i dont understand how it works or what it does, and it
+        ### looks all a bit odd... so lets just print a warning...
+        ### the entries look something like this:
+        # ./vms/descrip_mms.template:utils4 = [.utils]enc2xs.com
+        #   [.utils]piconv.com [.utils]cpan.com [.utils]prove.com
+        #   [.utils]ptar.com [.utils]ptardiff.com [.utils]shasum.com
+        # ./vms/descrip_mms.template:[.utils]ptardiff.com : [.utils]ptardiff.PL
+        #   $(ARCHDIR)Config.pm
+        {   my $file = 'vms/descrip_mms.template';
+
+            unless( `grep $bin $Repo/$file` ) {
+                print $/.$/;
+                print "    WARNING! You should add entries like the following\n"
+                    . "    to $file (Using $TestBin as an example)\n"
+                    . "    Unfortunately I dont understand what these entries\n"
+                    . "    do, so I wont change them automatically:\n\n";
+
+                print `grep -nC1 $TestBin $Repo/$file`;
+                print $/.$/;
+
+            } else {
+                print "    $bin already added to $file\n" if $Verbose;
+            }
+        }
+    }
+}
+
+### binary files must be encoded!
+### XXX use the new 'uupacktool.pl'
+{   my $pack = "$Repo/uupacktool.pl";
+
+    ### pack.pl encodes binary files for us
+    -e $pack or die "Need $pack to encode binary files!";
+
+    ### chdir, so uupacktool writes relative files properly
+    ### into it's header...
+    my $curdir = cwd();
+    chdir($Repo) or die "Could not chdir to '$Repo': $!";
+
+    for my $aref ( \@ModFiles, \@TestFiles, \@BinFiles ) {
+        for my $file ( @$aref ) {
+            my $full = -e $file                 ? $file              :
+                       -e "$RelTopDir/$file"    ? "$RelTopDir/$file" :
+                       die "Can not find $file in $Repo or $TopDir\n";
+
+            if( -f $full && -s _ && -B _ ) {
+                print "Binary file $file needs encoding\n" if $Verbose;
+
+                my $out = $full . '.packed';
+
+                ### does the file exist already?
+                ### and doesn't have +w
+                if( -e $out && not -w _ ) {
+                    system("chmod +w $out")
+                        and die "Could not set chmod +w to '$out': $!";
+                }
+
+                ### -D to remove the original
+                system("$^X $pack -D -p $full $out")
+                    and die "Could not encode $full to $out";
+
+
+                $file .= '.packed';
+            }
+        }
+    }
+
+    chdir($curdir) or die "Could not chdir back to '$curdir': $!";
+}
+
+### update the manifest
+{   my $file        = $Repo . '/MANIFEST';
+    my @manifest;
+    {   open my $fh, "<$file" or die "Could not open $file: $!";
+        @manifest    = <$fh>;
+        close $fh;
+    }
+
+    ### fill it with files from our package
+    my %pkg_files;
+    for ( @ModFiles ) {
+        $pkg_files{$_}              = "$_\t$ModName\n";
+    }
+
+    for ( @TestFiles ) {
+        $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\t$ModName tests\n"
+    }
+
+    for ( @BinFiles ) {
+        $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\tthe ".
+                                            basename($_) ." utility\n";
+    }
+
+    for ( @NewFiles ) {
+        $pkg_files{$_}              = "$_\tthe ".
+                                        do { m/(.+?)\.PL$/; basename($1) } .
+                                        " utility\n"
+    }
+
+    ### remove all the files that are already in the manifest;
+    delete $pkg_files{ [split]->[0] } for @manifest;
+
+    print "Adding the following entries to the MANIFEST:\n" if $Verbose;
+    print "\t$_" for sort values %pkg_files;
+    print $/.$/;
+
+    push @manifest, values %pkg_files;
+
+    {   chmod 0755, $file;
+        open my $fh, ">$file" or die "Could not open $file for writing: $!";
+        #print $fh sort { lc $a cmp lc $b } @manifest;
+        ### XXX stolen from pod/buildtoc:sub do_manifest
+        print $fh
+            map  { $_->[0] }
+            sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] }
+            map  { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] }
+            @manifest;
+
+        close $fh;
+    }
+}
+
+### would you like us to show you a diff?
+if( $RunDiff ) {
+    my $diff = $Repo; $diff =~ s/$$/patch/;
+
+    print "Generating diff..." if $Verbose;
+
+    ### weird RV ;(
+    my $master = basename( $MasterRepo );
+    my $repo   = basename( $Repo );
+    my $chdir  = dirname( $MasterRepo );
+
+    system( "cd $chdir; diff -ruN $master $repo > $diff" );
+        #and die "Could not write diff to '$diff': $?";
+    die "Could not write diff to '$diff'" unless -e $diff && -s _;
+
+    print "done\n" if $Verbose;
+    print "\nDiff can be applied with patch -p1 in $MasterRepo\n\n";
+    print "  Diff written to: $diff\n\n" if $Verbose;
+}
+
+sub usage {
+    my $me = basename($0);
+    return qq[
+
+Usage: $me -r PERL_REPO_DIR [-p PACKAGE_DIR] [-v] [-d] [-e REGEX]
+
+Options:
+  -r    Path to perl-core repository
+  -v    Run verbosely
+  -e    Perl regex matching files that shouldn't be included
+  -d    Create a diff as patch file
+  -p    Path to the package to add. Defaults to cwd()
+
+    \n];
+
+}