This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add documentation about tags (Closes #24)
[metaconfig.git] / bin / patmake
CommitLineData
459d3fb5
MBT
1#!/usr/bin/perl
2 eval "exec perl -S $0 $*"
3 if $running_under_some_shell;
4
5# $Id: patmake.SH 20 2008-01-04 23:14:00Z rmanfredi $
6#
7# Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
8#
9# You may redistribute only under the terms of the Artistic Licence,
10# as specified in the README file that comes with the distribution.
11# You may reuse parts of this distribution only within the terms of
12# that same Artistic Licence; a copy of which may be found at the root
13# of the source tree for dist 4.0.
14#
15# Original Author: Larry Wall <lwall@netlabs.com>
16# Contribution by: Graham Stoney <greyham@research.canon.oz.au>
17#
18# $Log: patmake.SH,v $
19# Revision 3.0.1.5 1995/09/25 09:21:19 ram
20# patch59: now calls patsend with -i to add more instructions
21#
22# Revision 3.0.1.4 1994/10/29 16:43:09 ram
23# patch36: a lot of setup is now performed by patlog
24# patch36: added various escapes in strings for perl5 support
25#
26# Revision 3.0.1.3 1994/01/24 14:30:55 ram
27# patch16: now prefix error messages with program's name
28# patch16: added ~/.dist_profile awareness
29#
30# Revision 3.0.1.2 1993/08/24 12:18:59 ram
31# patch3: now asks for patch mailing/posting after all patches edited
32# patch3: patch release notification is done via new patnotify
33# patch3: random cleanup, removed old RCS logs
34#
35# Revision 3.0.1.1 1993/08/19 06:42:38 ram
36# patch1: leading config.sh searching was not aborting properly
37#
38# Revision 3.0 1993/08/18 12:10:45 ram
39# Baseline for dist 3.0 netwide release.
40#
41
42$defeditor='/usr/bin/vi';
43$version = '3.5';
44$patchlevel = '0';
45$mailer = '/usr/sbin/sendmail';
46
47$progname = &profile; # Read ~/.dist_profile
48require 'getopts.pl';
49&usage unless &Getopts("hV");
50
51if ($opt_V) {
52 print STDERR "$progname $version PL$patchlevel\n";
53 exit 0;
54} elsif ($opt_h) {
55 &usage;
56}
57
58&readpackage;
59&readusers;
60
61$FILEOVERHEAD = 40; # Name of files, Index, Prereq
62$MAXPATSIZE = 50000; # Maximum allowed size for a patch
63$PATOVERHEAD = 2500; # Litterature
64$FIRST_PAT = 3000; # Give space for first patch (descriptions)
65
66if (-f 'patchlevel.h') {
67 open(PL,"patchlevel.h") || die "$progname: can't open patchlevel.h: $!\n";
68 while (<PL>) {
69 if (/^#define\s+PATCHLEVEL\s+(\d+)/) {
70 $last = $1;
71 $patchline = $.; # Record PATCHLEVEL line
72 }
73 }
74 die "$progname: malformed patchlevel.h file.\n" if $last eq '';
75 $bnum = $last + 1;
76}
77else {
78 $patchline = 1;
79 $bnum = 1;
80 $last = '';
81}
82
83@ARGV = <[Mm]akefile*>;
84$mf = '';
85if ($#ARGV > 0) {
86 while (<>) {
87 $mf .= $_ if /^[a-z.]+\s*:/; # Rules in makefile
88 }
89}
90$after = '';
91$after .= "\t\tConfigure -ders\n" if -f 'Configure';
92$after .= "\t\tmake depend\n" if $mf =~ /^depend:/m;
93$after .= "\t\tmake\n" if $mf;
94$after .= "\t\tmake test\n" if $mf =~ /^test:/m;
95$after .= "\t\tmake install\n" if $mf =~ /^install:/m;
96$after .= "\t\tmake install.man\n" if $mf =~ /^install\.man:/m;
97
98chdir 'bugs' if -d 'bugs';
99die "$progname: patch #$bnum already exists.\n" if -f "patch$bnum";
100
101@patlist=<*.$bnum>;
102die "$progname: no diff files for patch #$bnum.\n" if
103 $patlist[0] =~ /^\*/ || $patlist[0] eq '';
104
105# Whether they asked for a changelog file or not, call patlog.
106# This will create at least the .pri and .subj and .clog files that we need.
107# If a changelog file is needed, it will update it and create the necessary
108# patch before we go on and put all those patches together.
109# Note that we make use of the '-r' option, in case they have already
110# run patlog themselves and made the necessary adjustments. Since -r supersedes
111# -u, it's safe to allow ~/.dist_profile processing anyway.
112
113chdir '..' if -d '../bugs';
114system 'perl', '-S', 'patlog', '-r'; # Must be called from top-level dir
115chdir 'bugs' if -d 'bugs';
116
117@patlist=<*.$bnum>; # Reget it, in case Changes.xx appeared due to patlog
118
119# Look for size of each diff file
120for (@patlist) {
121 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
122 $blksize,$blocks) = stat($_);
123 $size{$_} = $size;
124}
125
126# Sort the array, biggest sizes first
127sub revnum { $size{$a} < $size{$b} ? 1 : $size{$a} > $size{$b} ? -1 : 0; }
128@patlist = sort revnum @patlist;
129
130# Put files in a patch
131for (@patlist) {
132 $i=1;
133 # Find the patch in which the current file can go
134 # Divide size by 15 to count the 3 spaces added in front of each line
135 while (($newtot = int($tot[$i] + $size{$_} + $size{$_}/15 + $FILEOVERHEAD)) >
136 $MAXPATSIZE-$PATOVERHEAD-($i == 1 ? $FIRST_PAT : 0) && $tot[$i]) {
137 $i++;
138 }
139 # Adding $_ to patch $i giving $newtot bytes
140 $tot[$i] = $newtot; # Update size of kit $i
141 $list[$i] .= " $_"; # Add file to the kit $i
142}
143
144$numpat = $#list; # Number of patches to generate
145
146if ($numpat > 1) {
147 print "$progname: Warning: generating $numpat patches.\n";
148 sleep(1);
149}
150
151$hah = " (hah!)" if $bnum == 1;
152$patbase = $bnum; # First patch generated
153
154open(PRIORITY, ".pri$bnum");
155chop($priority = <PRIORITY>);
156close PRIORITY;
157$priority = 'LOW' unless $priority;
158
159for ($i = 1; $i <= $numpat; $i++) { # For all patches...
160 open(PATCH,">patch$bnum") || die "Can't create patch #$bnum";
161 chop($date=`date`);
162 print PATCH
163"System: $package version $baserev
164Patch #: $bnum
165";
166 print PATCH "Priority: $priority\n" unless $priority eq '';
167 # Print subjects only for first patch
168 if ($i == 1) {
169 open(SUBJECTS, ".subj$bnum");
170 print PATCH while <SUBJECTS>;
171 close SUBJECTS;
172 } else {
173 print PATCH "Subject: patch #$patbase, continued\n";
174 }
175 print PATCH
176"Date: $date
177From: $maintname <$maintloc>
178
179Description:
180";
181 # Print description and repeat-by only for first patch
182 if ($i == 1) {
183 open(LOGS, ".clog$bnum");
184 $_ = <LOGS>; $_ = <LOGS>; $_ = <LOGS>; # Skip first three lines
185 print PATCH while <LOGS>;
186 close LOGS;
187 print PATCH "Repeat-By: \n";
188 } else {
189 print PATCH "\tSee patch #$patbase.\n\n";
190 }
191 print PATCH
192"
193Fix: From rn, say \"| patch -p -N -d DIR\", where DIR is your $package source
194 directory. Outside of rn, say \"cd DIR; patch -p -N <thisarticle\".
195 If you don't have the patch program, apply the following by hand,
196 or get patch (version 2.0, latest patchlevel).
197
198 After patching:
199";
200 # Do $after only after last patch
201 if ($i == $numpat) {
202 print PATCH $after;
203 } else {
204 printf PATCH
205"\t\t*** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #%d FIRST ***\n",
206$patbase + $numpat - 1;
207 }
208 print PATCH "
209 If patch indicates that patchlevel is the wrong version, you may need
210 to apply one or more previous patches, or the patch may already
211 have been applied. See the patchlevel.h file to find out what has or
212 has not been applied. In any event, don't continue with the patch.
213
214 If you are missing previous patches$hah they can be obtained from me:
215
216 $maintname <$maintloc>
217
218";
219 if ($mailagent ne 'false') {
220 print PATCH
221" If you send a mail message of the following form it will greatly speed
222 processing:
223
224 Subject: Command
225 \@SH mailpatch PATH $package $baserev LIST
226 ^ note the c
227
228 where PATH is a return path FROM ME TO YOU either in Internet notation,
229 or in bang notation from some well-known host, and LIST is the number
230 of one or more patches you need, separated by spaces, commas, and/or
231 hyphens. Saying 35- says everything from 35 to the end.
232
233 To get some more detailed instructions, send me the following mail:
234
235 Subject: Command
236 \@SH mailhelp PATH
237
238";
239 }
240 if ($ftpsite) {
241 print PATCH
242" You can also get the patches via anonymous FTP from
243 $ftpsite.
244";
245 }
246 # Print patchlevel at the top of each patch
247 print PATCH "
248Index: patchlevel.h
249";
250 if ($last eq '') {
251 `echo "#define PATCHLEVEL 1" >patchlevel.h`;
252 `cp /dev/null patchlevel.h.null`;
253 print PATCH `diff -c patchlevel.h.null patchlevel.h`;
254 unlink 'patchlevel.h', 'patchlevel.h.null';
255 }
256 else {
257 print PATCH
258"Prereq: $last
259${patchline}c${patchline}
260< #define PATCHLEVEL $last
261---
262> #define PATCHLEVEL $bnum
263";
264 }
265 $last = $bnum; # Update last patch
266 push(@patset, $bnum); # Record set of generated patch(es)
267
268 @ARGV = split(' ', $list[$i]);
269 while (<>) { print PATCH; }
270 print PATCH "\n*** End of Patch $bnum ***\n";
271 close PATCH;
272
273 # Update patchlevel.h file
274 $editor = $ENV{'VISUAL'};
275 $editor = $ENV{'EDITOR'} unless $editor;
276 $editor = $defeditor unless $editor;
277 $editor = 'vi' unless $editor;
278 system $editor, "patch$bnum";
279 if (-s "patch$bnum") {
280 system 'chmod', '-w', "patch$bnum"; # Protect newly created patch
281 chdir '..';
282 `echo "#define PATCHLEVEL 0" >patchlevel.h` unless -f 'patchlevel.h';
283 open(PL,"patchlevel.h") ||
284 die "$progname: can't open patchlevel.h: $!\n";
285 open(PLN,">patchlevel.h+") ||
286 die "$progname: can't create new patchlevel.h: $!\n";
287 while (<PL>) {
288 if (/^#define\s+PATCHLEVEL\s+(\d+)/) {
289 $bnum = $1;
290 $bnum++; # Update patch level
291 print PLN "#define PATCHLEVEL $bnum\n";
292 } else {
293 print PLN; # Simply copy other lines
294 }
295 }
296 close PLN;
297 close PL;
298 `mv -f patchlevel.h+ patchlevel.h`;
299 die "$progname: malformed patchlevel.h file.\n" if $bnum eq '';
300 } else {
301 unlink "patch$bnum";
302 die "$progname: aborted.\n";
303 }
304
305 chdir 'bugs' || die "$progname: cannot cd to bugs: $!\n";
306
307 # Find priority for next patch in loop
308 $priority='';
309 open(PATCH, "patch$bnum") || die "Cannot re-open patch #$bnum !\n";
310 while (<PATCH>) {
311 /^Priority:\s*(\S+)\s*$/ && ($priority = $1);
312 }
313 close PATCH;
314
315 $bnum++; # For next patch in loop
316}
317
318utime time, time, 'patchlevel.h'; # Reset timestamp on patchlevel
319
320if (@patset == 1) {
321 $bnum = pop(@patset);
322 $patch = "patch #$bnum";
323} else {
324 $bmin = shift(@patset);
325 $bmax = pop(@patset);
326 $bnum = "$bmin-$bmax";
327 $patch = "patches #$bmin thru #$bmax";
328}
329
330# Post generated patches
331if ($newsgroups) {
332 print "\nDo you wish to post $patch to $newsgroups? [y] ";
333 $ans = <stdin>;
334 system 'patpost', $bnum unless $ans =~ /^n/i;
335}
336
337# Mail generated patches
338if ($recipients) {
339 print "\n";
340 if (0 == ($recipients =~ tr/ //)) {
341 print "Do you wish to send $patch to $recipients? [y] ";
342 } else {
343 print "The following people are on the recipient list:\n\n";
344 foreach $addr (split(' ', $recipients)) {
345 print "\t$addr\n";
346 }
347 print "\nDo you wish to send $patch to them? [y] ";
348 }
349 $ans = <stdin>;
350 system 'patsend', '-i', $bnum, $recipients unless $ans =~ /^n/i;
351}
352
353# Copy patches to FTP directory
354if ($ftpdir) {
355 print "\nDo you wish to copy $patch to $ftpdir? [y] ";
356 $ans = <stdin>;
357 system 'patftp', $bnum unless $ans =~ /^n/i;
358}
359
360# Notify people about it.
361if ($notify) {
362 print "\n";
363 if (0 == ($notify =~ tr/ //)) {
364 print "Do you wish to notify $notify? [y] ";
365 } else {
366 print "The following people are on the notify list:\n\n";
367 foreach $addr (split(' ', $notify)) {
368 print "\t$addr\n";
369 }
370 print "\nDo you wish to notify them? [y] ";
371 }
372 $ans = <STDIN>;
373 system 'patnotify', $notify unless $ans =~ /^n/i;
374}
375
376sub usage {
377 print STDERR <<EOM;
378Usage: $progname [-hV]
379 -h : print this message and exit
380 -V : print version number and exit
381EOM
382 exit 1;
383}
384
385sub readpackage {
386 if (! -f '.package') {
387 if (
388 -f '../.package' ||
389 -f '../../.package' ||
390 -f '../../../.package' ||
391 -f '../../../../.package'
392 ) {
393 die "Run in top level directory only.\n";
394 } else {
395 die "No .package file! Run packinit.\n";
396 }
397 }
398 open(PACKAGE,'.package');
399 while (<PACKAGE>) {
400 next if /^:/;
401 next if /^#/;
402 if (($var,$val) = /^\s*(\w+)=(.*)/) {
403 $val = "\"$val\"" unless $val =~ /^['"]/;
404 eval "\$$var = $val;";
405 }
406 }
407 close PACKAGE;
408}
409
410sub readusers {
411 return unless open(USERS, 'users');
412 local($_);
413 local($status, $name, $pl);
414 while (<USERS>) {
415 next if /^#/;
416 chop if /\n$/; # Emacs may leave final line without \n
417 ($status, $pl, $name) = split;
418 # Handle oldstyle two-field user file format (PL13 and before)
419 $name = $pl unless defined $name;
420 if ($status eq 'M') {
421 $recipients = $recipients ? "$recipients $name" : $name;
422 } elsif ($status eq 'N') {
423 $notify = $notify ? "$notify $name" : $name;
424 }
425 }
426 close USERS;
427}
428
429# Perform ~name expansion ala ksh...
430# (banish csh from your vocabulary ;-)
431sub tilda_expand {
432 local($path) = @_;
433 return $path unless $path =~ /^~/;
434 $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name
435 $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~
436 $path;
437}
438
439# Set up profile components into %Profile, add any profile-supplied options
440# into @ARGV and return the command invocation name.
441sub profile {
442 local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile');
443 local($me) = $0; # Command name
444 $me =~ s|.*/(.*)|$1|; # Keep only base name
445 return $me unless -s $profile;
446 local(*PROFILE); # Local file descriptor
447 local($options) = ''; # Options we get back from profile
448 unless (open(PROFILE, $profile)) {
449 warn "$me: cannot open $profile: $!\n";
450 return;
451 }
452 local($_);
453 local($component);
454 while (<PROFILE>) {
455 next if /^\s*#/; # Skip comments
456 next unless /^$me/o;
457 if (s/^$me://o) { # progname: options
458 chop;
459 $options .= $_; # Merge options if more than one line
460 }
461 elsif (s/^$me-([^:]+)://o) { # progname-component: value
462 $component = $1;
463 chop;
464 s/^\s+//; # Trim leading and trailing spaces
465 s/\s+$//;
466 $Profile{$component} = $_;
467 }
468 }
469 close PROFILE;
470 return unless $options;
471 require 'shellwords.pl';
472 local(@opts);
473 eval '@opts = &shellwords($options)'; # Protect against mismatched quotes
474 unshift(@ARGV, @opts);
475 return $me; # Return our invocation name
476}
477