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