Update Module::Build to 0.35
[p5sagit/p5-mst-13.2.git] / Porting / add-package.pl
CommitLineData
28f2d4d1 1#!/opt/bin/perl
2use strict;
3use warnings;
4
5use Cwd;
6use Getopt::Std;
7use File::Basename;
8use FindBin;
9
10my $Opts = {};
d55aad05 11getopts( 'r:p:e:c:vudn', $Opts );
28f2d4d1 12
d55aad05 13my $Cwd = cwd();
28f2d4d1 14my $Verbose = 1;
74182dbd 15my $ExcludeRe = $Opts->{e} ? qr/$Opts->{e}/i : undef;
28f2d4d1 16my $Debug = $Opts->{v} || 0;
17my $RunDiff = $Opts->{d} || 0;
18my $PkgDir = $Opts->{p} || cwd();
e23621c7 19my $Repo = $Opts->{r} or die "Need repository!\n". usage();
d55aad05 20my $Changes = $Opts->{c} || 'Changes ChangeLog';
e23621c7 21my $NoBranch = $Opts->{n} || 0;
28f2d4d1 22
23### strip trailing slashes;
e23621c7 24$Repo =~ s|/$||;
28f2d4d1 25
26my $CPV = $Debug ? '-v' : '';
27my $TestBin = 'ptardiff';
28my $PkgDirRe = quotemeta( $PkgDir .'/' );
e23621c7 29my $BranchName = basename( $PkgDir ) . '.' . $$;
30my $OrigRepo = $Repo;
28f2d4d1 31
e23621c7 32### establish working directory, either branch or full copy
33if ( $NoBranch ) {
34 ### create a copy of the repo directory
35 my $RepoCopy = "$Repo-$BranchName";
36 print "Copying repository to $RepoCopy ..." if $Verbose;
d55aad05 37
ab06b353 38 ### --archive == -dPpR, but --archive is not portable, and neither
39 ### is -d, so settling for -PpR
40 system( "cp -PpR -f $Repo $RepoCopy" )
e23621c7 41 and die "Copying master repo to $RepoCopy failed: $?";
28f2d4d1 42
e23621c7 43 ### Going forward, use the copy in place of the original repo
44 $Repo = $RepoCopy;
28f2d4d1 45
e23621c7 46 print "done\n" if $Verbose;
47}
48else {
49 ### create a git branch for the new package
50 print "Setting up a branch from blead called '$BranchName'..." if $Verbose;
51 chdir $Repo or die "Could not chdir to $Repo: $!";
52 unless ( -d '.git' ) {
53 die "\n$Repo is not a git repository\n";
54 }
55 my $status = `git status`;
56 unless ( $status =~ /nothing to commit/ims ) {
57 die "\nWorking directory not clean. Stopping.\n";
58 }
1e3c652e 59 system( "git checkout -b $BranchName blead" )
e23621c7 60 and die "Could not create branch '$BranchName': $?";
28f2d4d1 61
62 print "done\n" if $Verbose;
63}
64
e23621c7 65### chdir there
66chdir $PkgDir or die "Could not chdir to $PkgDir: $!";
67
28f2d4d1 68### copy over all files under lib/
e23621c7 69my @LibFiles;
28f2d4d1 70{ print "Copying libdir..." if $Verbose;
e23621c7 71 die "Can't (yet) copy from a repository (found .git or .svn)"
72 if -d '.git' || -d '.svn';
28f2d4d1 73 die "No lib/ directory found\n" unless -d 'lib';
74 system( "cp -fR $CPV lib $Repo" ) and die "Copy of lib/ failed: $?";
d55aad05 75
e23621c7 76 @LibFiles = map { chomp; $_ }
77 ### should we get rid of this file?
78 grep { $ExcludeRe && $_ =~ $ExcludeRe
17633433 79 ? do { warn "Removing $Repo/$_\n";
80 system("rm $Repo/$_") and die "rm '$Repo/$_' failed: $?";
e23621c7 81 undef
82 }
83 : 1
58118e0c 84 } `find lib -type f`
e23621c7 85 or die "Could not detect library files\n";
d55aad05 86
28f2d4d1 87 print "done\n" if $Verbose;
88}
89
90### find the directory to put the t/ and bin/ files under
91my $RelTopDir; # topdir from the repo root
92my $TopDir; # full path to the top dir
93my $ModName; # name of the module
94my @ModFiles; # the .PMs in this package
95{ print "Creating top level dir..." if $Verbose;
96
97 ### make sure we get the shortest file, so we dont accidentally get
98 ### a subdir
99 @ModFiles = sort { length($a) <=> length($b) }
100 map { chomp; $_ }
101 grep { $ExcludeRe ? $_ !~ $ExcludeRe : 1 }
102 grep /\.p(?:m|od)$/,
103 `find $PkgDir/lib -type f`
104 or die "No TopDir detected\n";
105
106 $RelTopDir = $ModFiles[0];
107 $RelTopDir =~ s/^$PkgDirRe//;
108 $RelTopDir =~ s/\.p(m|od)$//;
109 $TopDir = "$Repo/$RelTopDir";
110
111 ### create the dir if it's not there yet
112 unless( -d $TopDir ) {
113 system( "mkdir $TopDir" ) and die "Creating dir $TopDir failed: $?";
114 }
115
116 ### the module name, like Foo::Bar
117 ### slice syntax not elegant, but we need to remove the
118 ### leading 'lib/' entry
119 ### stupid temp vars! stupid perl! it doesn't do @{..}[0..-1] :(
120 { my @list = @{[split '/', $RelTopDir]};
121 $ModName = join '::', @list[1 .. $#list];
122 }
123
124 ### the .pm files in this package
125 @ModFiles = map { s|^$PkgDirRe||; $_ } @ModFiles
126 or die "Could not detect modfiles\n";
127
128 print "done\n" if $Verbose;
129}
130
131my $TopDirRe = quotemeta( $TopDir . '/' );
132
133### copy over t/ and bin/ directories to the $TopDir
134my @TestFiles;
135{ print "Copying t/* files to $TopDir..." if $Verbose;
136
137 -d 't'
138 ? system( "cp -fR $CPV t $TopDir" ) && die "Copy of t/ failed: $?"
139 : warn "No t/ directory found\n";
140
15e5e866 141 @TestFiles = map { chomp; s|^$TopDirRe||; s|//|/|g; $_ }
28f2d4d1 142 ### should we get rid of this file?
143 grep { $ExcludeRe && $_ =~ $ExcludeRe
144 ? do { warn "Removing $_\n";
58118e0c 145 system("rm $TopDir/$_") and die "rm '$_' failed: $?";
28f2d4d1 146 undef
147 }
148 : 1
58118e0c 149 } `find t -type f`
28f2d4d1 150 or die "Could not detect testfiles\n";
151
152 print "done\n" if $Verbose;
153}
154
e23621c7 155my $BinDir;
28f2d4d1 156my @BinFiles;
d55aad05 157my $TopBinDir;
28f2d4d1 158BIN: {
e23621c7 159 $BinDir = -d 'bin' ? 'bin' :
160 -d 'scripts' ? 'scripts' : undef ;
161 unless ($BinDir) {
162 print "No bin/ or scripts/ directory found\n" if $Verbose;
28f2d4d1 163 last BIN;
164 }
e23621c7 165 my $TopBinDir = "$TopDir/$BinDir/";
166 print "Copying $BinDir/* files to $TopBinDir..." if $Verbose;
167
168 my $CopyCmd = "cp -fR $CPV $BinDir $TopDir";
169 print "Running '$CopyCmd'..." if $Verbose;
28f2d4d1 170
e23621c7 171 system($CopyCmd) && die "Copy of $BinDir failed: $?";
28f2d4d1 172
15e5e866 173 @BinFiles = map { chomp; s|^$TopDirRe||; s|//|/|g; $_ }
28f2d4d1 174 ### should we get rid of this file?
175 grep { $ExcludeRe && $_ =~ $ExcludeRe
176 ? do { warn "Removing $_\n";
58118e0c 177 system("rm $TopDir/$_") and die "rm '$_' failed: $?";
28f2d4d1 178 undef
179 }
180 : 1
58118e0c 181 } `find $BinDir -type f`
28f2d4d1 182 or die "Could not detect binfiles\n";
183
184 print "done\n" if $Verbose;
185}
186
d55aad05 187### copy over change log
188my @Changes;
189foreach my $cl (split m/\s+/ => $Changes) {
190 -f $cl or next;
191 push @Changes, $cl;
192 print "Copying $cl files to $TopDir..." if $Verbose;
193
194 system( "cp -f $CPV $cl $TopDir" )
195 and die "Copy of $cl failed: $?";
196}
197
198
28f2d4d1 199### add files where they are required
200my @NewFiles;
e23621c7 201my @ChangedFiles;
28f2d4d1 202{ for my $bin ( map { basename( $_ ) } @BinFiles ) {
203 print "Registering $bin with system files...\n";
204
205 ### fix installperl, so these files get installed by other utils
206 ### ./installperl: return if $name =~
207 ### /^(?:cpan|instmodsh|prove|corelist|ptar|ptardiff|config_data)\z/;
208 { my $file = 'installperl';
209
210 ### not there already?
211 unless( `grep $TestBin $Repo/$file| grep $bin` ) {
212 print " Adding $bin to $file..." if $Verbose;
213
214 ### double \\| required --> once for in this script, once
215 ### for the cli
216 system("$^X -pi -e 's/($TestBin\\|)/$bin|\$1/' $Repo/$file")
217 and die "Could not add $bin to $file: $?";
218 print "done\n" if $Verbose;
e23621c7 219 push @ChangedFiles, $file;
28f2d4d1 220 } else {
221 print " $bin already mentioned in $file\n" if $Verbose;
222 }
223 }
224
225 ### fix utils.lst, so the new tools are mentioned
226 { my $file = 'utils.lst';
227
228 ### not there already?
229 unless( `grep $bin $Repo/$file` ) {
230 print " Adding $bin to $file..." if $Verbose;
231
232 ### double \\| required --> once for in this script, once
233 ### for the cli
234 system("$^X -pi -e 's!($TestBin)!\$1\nutils/$bin!' $Repo/$file")
235 and die "Could not add $bin to $file: $?";
236 print "done\n" if $Verbose;
e23621c7 237 push @ChangedFiles, $file;
28f2d4d1 238 } else {
239 print " $bin already mentioned in $file\n" if $Verbose;
240 }
241 }
242
243 ### make a $bin.PL file and fix it up
244 { my $src = "utils/${TestBin}.PL";
245 my $file = "utils/${bin}.PL";
246
247 ### not there already?
248 unless( -e "$Repo/$file" ) {
249 print " Creating $file..." if $Verbose;
250
251 ### important part of the template looks like this
252 ### (we'll need to change it):
253 # my $script = File::Spec->catfile(
254 # File::Spec->catdir(
255 # File::Spec->updir, qw[lib Archive Tar bin]
256 # ), "module-load.pl");
257
258 ### copy another template file
259 system( "cp -f $Repo/$src $Repo/$file" )
260 and die "Could not create $file from $src: $?";
261
262 ### change the 'updir' path
263 ### make sure to escape the \[ character classes
e23621c7 264 my $updir = join ' ', (split('/', $RelTopDir), $BinDir);
28f2d4d1 265 system( "$^X -pi -e'".
266 's/^(.*?File::Spec->updir, qw\[).+?(\].*)$/'.
267 "\$1 $updir \$2/' $Repo/$file"
268 ) and die "Could not fix updir for $bin in $file: $?";
269
270
271 ### change the name of the file from $TestBin to $bin
272 system( "$^X -pi -e's/$TestBin/$bin/' $Repo/$file" )
273 and die "Could not update $file with '$bin' as name: $?";
274
275 print "done\n" if $Verbose;
276
277 } else {
278 print " $file already exists\n" if $Verbose;
279 }
280
281 ### we've may just have created a new file, it will have to
282 ### go into the manifest
283 push @NewFiles, $file;
284 }
285
e23621c7 286 ### add an entry to utils/Makefile.SH for $bin
287 { my $file = "utils/Makefile.SH";
28f2d4d1 288
289 ### not there already?
290 unless( `grep $bin $Repo/$file` ) {
291 print " Adding $bin entries to $file..." if $Verbose;
292
293 ### $bin appears on 4 lines in this file, so replace all 4
294 ### first, pl =
295 system( "$^X -pi -e'/^pl\\s+=/ && s/(${TestBin}.PL)/".
296 "\$1 ${bin}.PL/' $Repo/$file"
297 ) and die "Could not add $bin to the pl = entry: $?";
298
299 ### next, plextract =
300 system( "$^X -pi -e'/^plextract\\s+=/ " .
301 "&& s/(${TestBin})/\$1 $bin/' $Repo/$file"
302 ) and die "Could not add $bin to the plextract = entry: $?";
303
304 ### third, plextractexe =
305 system( "$^X -pi -e'/^plextractexe\\s+=/ " .
306 "&& s!(\./${TestBin})!\$1 ./$bin!' $Repo/$file"
307 ) and die "Could not add $bin to the plextractexe = entry: $?";
308
309 ### last, the make directive $bin:
310 system( "$^X -pi -e'/^(${TestBin}:.+)/; \$x=\$1 or next;" .
311 "\$x =~ s/$TestBin/$bin/g;" . '$_.=$/.$x.$/;' .
312 "' $Repo/$file"
313 ) and die "Could not add $bin as a make directive: $?";
314
e23621c7 315 push @ChangedFiles, $file;
28f2d4d1 316 print "done\n" if $Verbose;
317 } else {
318 print " $bin already added to $file\n" if $Verbose;
319 }
320 }
321
322 ### add entries to win32/Makefile and win32/makefile.mk
323 ### they contain the following lines:
324 # ./win32/makefile.mk: ..\utils\ptardiff \
325 # ./win32/makefile.mk: xsubpp instmodsh prove ptar ptardiff
326 for my $file ( qw[win32/Makefile win32/makefile.mk] ) {
327 unless ( `grep $bin $Repo/$file` ) {
328 print " Adding $bin entries to $file..." if $Verbose;
329
330 system( "$^X -pi -e'/^(.+?utils.${TestBin}.+)/;".
331 '$x=$1 or next;' .
332 "\$x =~ s/$TestBin/$bin/g;" . '$_.=$x.$/;' .
333 "' $Repo/$file"
334 ) and die "Could not add $bin to UTILS section in $file: $?\n";
335
336 system( "$^X -pi -e's/( $TestBin)/\$1 $bin/' $Repo/$file" )
337 and die "Could not add $bin to $file: $?\n";
338
e23621c7 339 push @ChangedFiles, $file;
28f2d4d1 340 print "done\n" if $Verbose;
341 } else {
342 print " $bin already added to $file\n" if $Verbose;
343 }
344 }
345
346 ### we need some entries in a vms specific file as well..
347 ### except, i dont understand how it works or what it does, and it
348 ### looks all a bit odd... so lets just print a warning...
349 ### the entries look something like this:
350 # ./vms/descrip_mms.template:utils4 = [.utils]enc2xs.com
351 # [.utils]piconv.com [.utils]cpan.com [.utils]prove.com
352 # [.utils]ptar.com [.utils]ptardiff.com [.utils]shasum.com
353 # ./vms/descrip_mms.template:[.utils]ptardiff.com : [.utils]ptardiff.PL
354 # $(ARCHDIR)Config.pm
355 { my $file = 'vms/descrip_mms.template';
356
357 unless( `grep $bin $Repo/$file` ) {
358 print $/.$/;
359 print " WARNING! You should add entries like the following\n"
360 . " to $file (Using $TestBin as an example)\n"
361 . " Unfortunately I dont understand what these entries\n"
362 . " do, so I wont change them automatically:\n\n";
363
364 print `grep -nC1 $TestBin $Repo/$file`;
365 print $/.$/;
366
367 } else {
368 print " $bin already added to $file\n" if $Verbose;
369 }
370 }
371 }
372}
373
374### binary files must be encoded!
375### XXX use the new 'uupacktool.pl'
376{ my $pack = "$Repo/uupacktool.pl";
377
378 ### pack.pl encodes binary files for us
379 -e $pack or die "Need $pack to encode binary files!";
380
381 ### chdir, so uupacktool writes relative files properly
382 ### into it's header...
383 my $curdir = cwd();
384 chdir($Repo) or die "Could not chdir to '$Repo': $!";
385
386 for my $aref ( \@ModFiles, \@TestFiles, \@BinFiles ) {
387 for my $file ( @$aref ) {
388 my $full = -e $file ? $file :
389 -e "$RelTopDir/$file" ? "$RelTopDir/$file" :
390 die "Can not find $file in $Repo or $TopDir\n";
391
392 if( -f $full && -s _ && -B _ ) {
393 print "Binary file $file needs encoding\n" if $Verbose;
394
395 my $out = $full . '.packed';
396
397 ### does the file exist already?
398 ### and doesn't have +w
399 if( -e $out && not -w _ ) {
400 system("chmod +w $out")
401 and die "Could not set chmod +w to '$out': $!";
402 }
403
404 ### -D to remove the original
405 system("$^X $pack -D -p $full $out")
406 and die "Could not encode $full to $out";
407
408
409 $file .= '.packed';
410 }
411 }
412 }
413
414 chdir($curdir) or die "Could not chdir back to '$curdir': $!";
415}
416
417### update the manifest
418{ my $file = $Repo . '/MANIFEST';
419 my @manifest;
420 { open my $fh, "<$file" or die "Could not open $file: $!";
421 @manifest = <$fh>;
422 close $fh;
423 }
424
425 ### fill it with files from our package
426 my %pkg_files;
427 for ( @ModFiles ) {
428 $pkg_files{$_} = "$_\t$ModName\n";
429 }
430
431 for ( @TestFiles ) {
432 $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\t$ModName tests\n"
433 }
434
435 for ( @BinFiles ) {
436 $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\tthe ".
437 basename($_) ." utility\n";
438 }
439
d55aad05 440 for ( @Changes ) {
441 $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\t$ModName change log\n";
442 }
443
28f2d4d1 444 for ( @NewFiles ) {
445 $pkg_files{$_} = "$_\tthe ".
446 do { m/(.+?)\.PL$/; basename($1) } .
447 " utility\n"
448 }
449
450 ### remove all the files that are already in the manifest;
451 delete $pkg_files{ [split]->[0] } for @manifest;
452
453 print "Adding the following entries to the MANIFEST:\n" if $Verbose;
454 print "\t$_" for sort values %pkg_files;
455 print $/.$/;
456
457 push @manifest, values %pkg_files;
458
74182dbd 459 { chmod 0644, $file;
28f2d4d1 460 open my $fh, ">$file" or die "Could not open $file for writing: $!";
461 #print $fh sort { lc $a cmp lc $b } @manifest;
462 ### XXX stolen from pod/buildtoc:sub do_manifest
463 print $fh
464 map { $_->[0] }
465 sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] }
466 map { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] }
467 @manifest;
468
469 close $fh;
470 }
e23621c7 471 push @ChangedFiles, 'MANIFEST';
28f2d4d1 472}
473
e23621c7 474
28f2d4d1 475### would you like us to show you a diff?
476if( $RunDiff ) {
e23621c7 477 if ( $NoBranch ) {
28f2d4d1 478
e23621c7 479 my $diff = $Repo; $diff =~ s/$$/patch/;
28f2d4d1 480
e23621c7 481 ### weird RV ;(
482 my $master = basename( $OrigRepo );
483 my $repo = basename( $Repo );
484 my $chdir = dirname( $OrigRepo );
1df59df4 485
e23621c7 486 ### the .patch file is added by an rsync from the APC
487 ### but isn't actually in the p4 repo, so exclude it
488 my $cmd = "cd $chdir; diff -ruN --exclude=.patch $master $repo > $diff";
1df59df4 489
e23621c7 490 print "Running: '$cmd'\n";
1df59df4 491
e23621c7 492 print "Generating diff..." if $Verbose;
28f2d4d1 493
e23621c7 494 system( $cmd );
495 #and die "Could not write diff to '$diff': $?";
496 die "Could not write diff to '$diff'" unless -e $diff && -s _;
497
498 print "done\n" if $Verbose;
499 print "\nDiff can be applied with patch -p1 in $OrigRepo\n\n";
500 print " Diff written to: $diff\n\n" if $Verbose;
501 }
502 else {
503 my $diff = "$Repo/$BranchName"; $diff =~ s/$$/patch/;
504 my $cmd = "cd $Repo; git diff > $diff";
505
506 print "Running: '$cmd'\n";
507
508 print "Generating diff..." if $Verbose;
509
510 system( $cmd );
511 #and die "Could not write diff to '$diff': $?";
512 die "Could not write diff to '$diff'" unless -e $diff && -s _;
513
514 print "done\n" if $Verbose;
515 print " Diff written to: $diff\n\n" if $Verbose;
516 }
28f2d4d1 517}
518
e23621c7 519
520# add files to git index
521unless ( $NoBranch ) {
522 chdir $Repo;
d55aad05 523 system( "git add $CPV $_" )
524 for ( @LibFiles, @NewFiles, @ChangedFiles,
525 map { "$RelTopDir/$_" } @TestFiles, @BinFiles, @Changes );
e23621c7 526}
527
528# return to original directory
529chdir $Cwd;
530
28f2d4d1 531sub usage {
532 my $me = basename($0);
533 return qq[
534
535Usage: $me -r PERL_REPO_DIR [-p PACKAGE_DIR] [-v] [-d] [-e REGEX]
536
537Options:
e23621c7 538 -r Path to perl-core git repository
28f2d4d1 539 -v Run verbosely
d55aad05 540 -c File containing changelog (default 'Changes' or 'ChangeLog')
28f2d4d1 541 -e Perl regex matching files that shouldn't be included
542 -d Create a diff as patch file
543 -p Path to the package to add. Defaults to cwd()
e23621c7 544 -n No branching; repository is not a git repo
28f2d4d1 545
546 \n];
547
548}