This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
The test for #76474 should open file descriptor 0, not 1.
[perl5.git] / Porting / bump-perl-version
CommitLineData
ae1b7029
DM
1#!/usr/bin/perl
2#
3# bump-perl-version, DAPM 14 Jul 2009
4#
5# A utility to find, and optionally bump, references to the perl version
6# number in various files within the perl source
7#
8# It's designed to work in two phases. First, when run with -s (scan),
9# it searches all the files in MANIFEST looking for strings that appear to
10# match the current perl version (or which it knows are *supposed* to
11# contain the current version), and produces a list of them to stdout,
12# along with a suggested edit. For example:
13#
14# $ Porting/bump-perl-version -s 5.10.0 5.10.1 > /tmp/scan
15# $ cat /tmp/scan
16# Porting/config.sh
17#
18# 52: -archlib='/opt/perl/lib/5.10.0/i686-linux-64int'
19# +archlib='/opt/perl/lib/5.10.1/i686-linux-64int'
20# ....
21#
22# At this point there will be false positives. Edit the file to remove
23# those changes you don't want made. Then in the second phase, feed that
24# list in, and it will change those lines in the files:
25#
26# $ Porting/bump-perl-version -u < /tmp/scan
27#
28# (so line 52 of Porting/config.sh is now updated)
29
30# This utility 'knows' about certain files and formats, and so can spot
31# 'hidden' version numbers, like PERL_SUBVERSION=9.
32#
33# A third variant makes use of this knowledge to check that all the things
34# it knows about are at the current version:
35#
36# $ Porting/bump-perl-version -c 5.10.0
37#
38# XXX this script hasn't been tested against a major version bump yet,
39# eg 5.11.0 to 5.12.0; there may be things it missed - DAPM 14 Jul 09
40#
41# Note there are various files and directories that it skips; these are
42# ones that are unlikely to contain anything needing bumping, but which
43# will generate lots fo false positives (eg pod/*). These are listed on
44# STDERR as they are skipped.
45
46use strict;
47use warnings;
204fc54e 48use autodie;
ae1b7029
DM
49use Getopt::Std;
50use ExtUtils::Manifest;
51
52
53sub usage { die <<EOF }
54
55@_
56
57usage: $0 -c <C.C.C>
58 -s <C.C.C> <N.N.N>
59 -u
dc40e497 60 -i <C.C.C> <N.N.N>
ae1b7029
DM
61
62 -c check files and warn if any known string values (eg
63 PERL_SUBVERSION) don't match the specified version
64
65 -s scan files and produce list of possible change lines to stdout
66
67 -u read in the scan file from stdin, and change all the lines specified
68
dc40e497
LB
69 -i scan files and make changes inplace
70
ae1b7029
DM
71 C.C.C the current perl version, eg 5.10.0
72 N.N.N the new perl version, eg 5.10.1
73EOF
74
75my %opts;
dc40e497 76getopts('csui', \%opts) or usage;
ae1b7029 77if ($opts{u}) {
47e01c32 78 @ARGV == 0 or usage('no version version numbers should be specified');
ae1b7029
DM
79 # fake to stop warnings when calculating $oldx etc
80 @ARGV = qw(99.99.99 99.99.99);
81}
82elsif ($opts{c}) {
83 @ARGV == 1 or usage('required one version number');
84 push @ARGV, $ARGV[0];
85}
86else {
87 @ARGV == 2 or usage('require two version numbers');
88}
dc40e497 89usage('only one of -c, -s, -u and -i') if keys %opts > 1;
ae1b7029
DM
90
91my ($oldx, $oldy, $oldz) = $ARGV[0] =~ /^(\d+)\.(\d+)\.(\d+)$/
92 or usage("bad version: $ARGV[0]");
93my ($newx, $newy, $newz) = $ARGV[1] =~ /^(\d+)\.(\d+)\.(\d+)$/
94 or usage("bad version: $ARGV[1]");
95
96my $old_decimal = sprintf "%d.%03d%03d", $oldx, $oldy, $oldz; # 5.011001
97
98# each entry is
99# 0 a regexp that matches strings that might contain versions;
100# 1 a sub that returns two strings based on $1 etc values:
101# * string containing captured values (for -c)
102# * a string containing the replacement value
103# 2 what we expect the sub to return as its first arg; undef implies
104# don't match
105# 3 a regex restricting which files this applies to (undef is all files)
106#
107# Note that @maps entries are checks in order, and only the first to match
108# is used.
109
110my @maps = (
111 [
112 qr{^((?:api_)?version(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
113 sub { $2, "$1$newy$3" },
114 $oldy,
115 qr/config/,
116 ],
117 [
118 qr{^(subversion(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
119 sub { $2, "$1$newz$3" },
120 $oldz,
121 qr/config/,
122 ],
123 [
124 qr{^(api_subversion(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
544af516
FR
125 sub { $2, ($newy % 2) ? "$1$newz$3" : "${1}0$3" },
126 ($oldy % 2) ? $oldz : 0,
ae1b7029
DM
127 qr/config/,
128 ],
129 [
130 qr{^(api_versionstring(?:=|\s+)'?) ([\d\.]+) ('?) (?!\.)}x,
544af516
FR
131 sub { $2, ($newy % 2) ? "$1$newx.$newy.$newz$3": "$1$newx.$newy.0$3" },
132 ($oldy % 2) ? "$oldx.$oldy.$oldz" : "$oldx.$oldy.0",
ae1b7029
DM
133 qr/config/,
134 ],
135 [
136 qr{(version\s+'?) (\d+) ('?\s+subversion\s+'?) (\d+) ('?) (?!\.)}x,
137 sub { "$2-$4", "$1$newy$3$newz$5" },
138 "$oldy-$oldz",
139 qr/config/,
140 ],
141 [
142 qr{\b (PERL_(?:API_)?VERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
143 sub { $2, "$1$newy$3"},
144 $oldy,
145 ],
146 [
147 qr{\b (PERL_SUBVERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
148 sub { $2, "$1$newz$3"},
544af516 149 ($oldy % 2) ? $oldz : 0,
ae1b7029
DM
150 ],
151 [
152 qr{\b (PERL_API_SUBVERSION(?:=|\s+)'?) (\d+) ('?) (?!\.)}x,
544af516
FR
153 sub { $2, ($newy % 2) ? "$1$newz$3" : "${1}0$3" },
154 $oldz,
ae1b7029
DM
155 ],
156 # these two formats are in README.vms
157 [
158 qr{\b perl-(\d+\^\.\d+\^\.\d+) \b}x,
159 sub { $1, "perl-$newx^.$newy^.$newz"},
160 undef,
161 ],
162 [
163 qr{\b ($oldx _ $oldy _$oldz) \b}x,
164 sub { $1, ($newx . '_' . $newy . '_' . $newz)},
165 undef,
166 ],
167 # 5.8.9
168 [
8b8cdb3a 169 qr{ $oldx\.$oldy\.$oldz \b}x,
ae1b7029
DM
170 sub {"", "$newx.$newy.$newz"},
171 undef,
172 ],
173
174 # 5.008009
175 [
8b8cdb3a 176 qr{ $old_decimal \b}x,
ae1b7029
DM
177 sub {"", sprintf "%d.%03d%03d", $newx, $newy, $newz },
178 undef,
179 ],
180
d4f05c0b 181 # perl511, perl511.dll, perl511.lib, perl511s.lib, libperl511.a
7dfca73b 182 [
d4f05c0b
VP
183 qr{\b ((?:lib)?) perl (\d\d\d) (s?) \b }x,
184 sub {$2, "$1perl$newx$newy$3" },
7dfca73b 185 "$oldx$oldy",
d4f05c0b 186 qr/makedef|win32|hints/, # makedef.pl, README.win32, win32/*, hints/*
7dfca73b
JD
187 ],
188
2e55877c
DL
189 # microperl locations should be bumped for major versions
190 [
191 qr{(/)(\d\.\d{2})(["'/])},
192 sub { $2, "$1$newx.$newy$3" },
193 "$oldx.$oldy",
194 qr/uconfig/,
195 ],
196
ae1b7029
DM
197);
198
199
200# files and dirs that we likely don't want to change version numbers on.
201
202my %SKIP_FILES = map { ($_ => 1) } qw(
203 Changes
204 MANIFEST
87f4ab41 205 Porting/epigraphs.pod
ae1b7029 206 Porting/how_to_write_a_perldelta.pod
5bd03515 207 Porting/release_managers_guide.pod
87f4ab41 208 Porting/release_schedule.pod
8b8cdb3a 209 Porting/bump-perl-version
ae1b7029 210 pod.lst
5bd03515 211 pp_ctl.c
ae1b7029
DM
212);
213my @SKIP_DIRS = qw(
214 ext
215 lib
216 pod
d634733c 217 cpan
ae1b7029
DM
218 t
219);
220
221my @mani_files = sort keys %{ExtUtils::Manifest::maniread('MANIFEST')};
222my %mani_files = map { ($_ => 1) } @mani_files;
223die "No entries found in MANIFEST; aborting\n" unless @mani_files;
224
dc40e497 225if ($opts{c} or $opts{s} or $opts{i}) {
ae1b7029
DM
226 do_scan();
227}
228elsif ($opts{u}) {
229 do_update();
230}
231else {
47e01c32 232 usage('one of -c, -s or -u must be specified');
ae1b7029
DM
233}
234exit 0;
235
236
237
238
239sub do_scan {
240 for my $file (@mani_files) {
241 next if grep $file =~ m{$_/}, @SKIP_DIRS;
242 if ($SKIP_FILES{$file}) {
243 warn "(skipping $file)\n";
244 next;
245 }
204fc54e 246 open my $fh, '<', $file;
ae1b7029 247 my $header = 0;
dc40e497
LB
248 my @stat = stat $file;
249 my $mode = $stat[2];
250 my $file_changed = 0;
251 my $new_contents = '';
ae1b7029
DM
252
253 while (<$fh>) {
dc40e497 254 my $line_changed;
ae1b7029
DM
255 for my $map (@maps) {
256 my ($pat, $sub, $expected, $file_pat) = @$map;
257
258 next if defined $file_pat and $file !~ $file_pat;
259 next unless $_ =~ $pat;
260 my ($got, $replacement) = $sub->();
261
262 if ($opts{c}) {
263 # only report unexpected
264 next unless defined $expected and $got ne $expected;
265 }
266 my $newstr = $_;
267 $newstr =~ s/$pat/$replacement/
268 or die "Internal error: substitution failed: [$pat]\n";
dc40e497 269 $new_contents .= $newstr if $opts{i};
ae1b7029 270 if ($_ ne $newstr) {
dc40e497
LB
271 $file_changed = 1;
272 $line_changed = 1;
273 if ($opts{s}) {
274 print "\n$file\n" unless $header;
275 $header=1;
276 printf "\n%5d: -%s +%s", $., $_, $newstr;
277 }
ae1b7029
DM
278 }
279 last;
280 }
dc40e497
LB
281 $new_contents .= $_ if $opts{i} && !$line_changed ;
282 }
283 if ($opts{i} && $file_changed) {
284 warn "Updating $file inplace\n";
285 open my $fh, '>', $file;
286 binmode $fh;
287 print $fh $new_contents;
288 close $fh;
289 chmod $mode & 0777, $file;
ae1b7029
DM
290 }
291 }
292 warn "(skipped $_/*)\n" for @SKIP_DIRS;
293}
294
295sub do_update {
296
297 my %changes;
298 my $file;
299 my $line;
300
301 # read in config
302
303 while (<STDIN>) {
304 next unless /\S/;
305 if (/^(\S+)$/) {
306 $file = $1;
307 die "No such file in MANIFEST: '$file'\n" unless $mani_files{$file};
308 die "file already seen; '$file'\n" if exists $changes{$file};
309 undef $line;
310 }
311 elsif (/^\s+(\d+): -(.*)/) {
312 my $old;
313 ($line, $old) = ($1,$2);
47e01c32 314 die "$.: old line without preceding filename\n"
ae1b7029
DM
315 unless defined $file;
316 die "Dup line number: $line\n" if exists $changes{$file}{$line};
317 $changes{$file}{$line}[0] = $old;
318 }
319 elsif (/^\s+\+(.*)/) {
320 my $new = $1;
321 die "$.: replacement line seen without old line\n" unless $line;
322 $changes{$file}{$line}[1] = $new;
323 undef $line;
324 }
325 else {
326 die "Unexpected line at ;line $.: $_\n";
327 }
328 }
329
330 # suck in file contents to memory, then update that in-memory copy
331
332 my %contents;
333 for my $file (sort keys %changes) {
204fc54e 334 open my $fh, '<', $file;
33c1015f 335 binmode $fh;
ae1b7029
DM
336 $contents{$file} = [ <$fh> ];
337 chomp @{$contents{$file}};
204fc54e 338 close $fh;
ae1b7029
DM
339
340 my $entries = $changes{$file};
341 for my $line (keys %$entries) {
342 die "$file: no such line: $line\n"
343 unless defined $contents{$file}[$line-1];
344 if ($contents{$file}[$line-1] ne $entries->{$line}[0]) {
345 die "$file: line mismatch at line $line:\n"
346 . "File: [$contents{$file}[$line-1]]\n"
347 . "Config: [$entries->{$line}[0]]\n"
348 }
349 $contents{$file}[$line-1] = $entries->{$line}[1];
350 }
351 }
352
353 # check the temp files don't already exist
354
355 for my $file (sort keys %contents) {
356 my $nfile = "$file-new";
357 die "$nfile already exists in MANIFEST; aborting\n"
358 if $mani_files{$nfile};
359 }
360
361 # write out the new files
362
363 for my $file (sort keys %contents) {
364 my $nfile = "$file-new";
204fc54e 365 open my $fh, '>', $nfile;
33c1015f 366 binmode $fh;
ae1b7029 367 print $fh $_, "\n" for @{$contents{$file}};
204fc54e 368 close $fh;
ae1b7029 369
204fc54e 370 my @stat = stat $file;
ae1b7029
DM
371 my $mode = $stat[2];
372 die "stat $file fgailed to give a mode!\n" unless defined $mode;
204fc54e 373 chmod $mode & 0777, $nfile;
ae1b7029
DM
374 }
375
376 # and rename them
377
378 for my $file (sort keys %contents) {
379 my $nfile = "$file-new";
380 warn "updating $file ...\n";
204fc54e 381 rename $nfile, $file;
ae1b7029
DM
382 }
383}
384