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