Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-ports-kde
Path: blob/main/Tools/scripts/bump_revision.pl
16461 views
1
#!/usr/bin/env -S perl -wt
2
3
#
4
# This script helps with bumping the PORTREVISION of all ports that depend on a
5
# set of ports, for instance, when in the latter set one of the ports bumped the
6
# .so library version.
7
#
8
# The shebang line above includes -T (taint) to be more distrustful
9
# about the environment, for security reasons, and is considered
10
# good Perl practice.
11
#
12
# You can use either the
13
# -l (shaLlow, avoid grandparent dependencies, slower) or
14
# -g option (include grandparent dependencies) option.
15
#
16
# MAINTAINER= [email protected]
17
#
18
19
use strict;
20
use Getopt::Std;
21
use Carp 'verbose';
22
use Cwd;
23
use File::Basename;
24
use Data::Dumper;
25
$Data::Dumper::Indent = 1; # simple indent
26
$Data::Dumper::Purity = 1; # Perl syntax
27
my $debug = 0;
28
29
use vars qw/$opt_n $opt_f $opt_i $opt_u $opt_l $opt_g $opt_p $opt_h/;
30
31
# launder environment
32
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
33
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
34
35
sub usage {
36
print <<EOF;
37
Usage: $0 [options] [<category>/]<portname>
38
39
Options:
40
-l - shaLlow, only bump ports with direct dependencies. (default).
41
-g - Grandchildren, also bump for indirect dependencies.
42
-n - Check only (dry-run), do not change Makefiles.
43
-f - No tmpdir, just use the directory where INDEX resides.
44
-i <filename> - Use this for INDEX name. Defaults to \${PORTSDIR}/INDEX-n,
45
where n is the major version of the OS, or \${PORTSDIR}/INDEX if missing.
46
-p <dirname> - Set portsdir, if different from /usr/ports.
47
48
Improvements, suggestions, questions -> mandree\@FreeBSD.org
49
EOF
50
exit 1;
51
}
52
53
# flush STDOUT for each and every write even if writing to a pipe.
54
$| = 1;
55
56
sub bumpMakefile {
57
my ($taintedorigin) = @_;
58
my $origin;
59
60
if ($taintedorigin =~ /^([-\@\w.\/]+)$/) {
61
$origin = $1;
62
} else {
63
Carp::carp "cannot untaint $taintedorigin - invalid characters";
64
return;
65
}
66
67
my $makefile = "$origin/Makefile";
68
my $fin;
69
unless(open($fin, $makefile)) {
70
print "-- Cannot open Makefile of $origin, ignored.\n";
71
return;
72
}
73
my @lines = <$fin>;
74
if ($!) { die "Error while reading $makefile: $!. Aborting"; }
75
close($fin) or die "Can't close $makefile b/c $!";
76
chomp(@lines);
77
78
my $revision = 1;
79
80
foreach my $line (@lines) {
81
last if ($line =~ /^MAINTAINER/);
82
$revision += $1 if ($line =~ /PORTREVISION\??=[ \t]*(\d+)$/);
83
}
84
85
my $printedrev = 0;
86
open(my $fout, '>', "$makefile.bumped");
87
foreach my $line (@lines) {
88
if (!$printedrev) {
89
if ($line =~ /^CATEGORIES??=/ || $line =~ /^PORTEPOCH??=/) {
90
print $fout "PORTREVISION= $revision\n";
91
$printedrev = 1;
92
# Fall through!
93
}
94
if ($line =~ /^PORTREVISION\?=/) {
95
print $fout "PORTREVISION?= $revision\n";
96
$printedrev = 1;
97
next;
98
}
99
if ($line =~ /^PORTREVISION=/) {
100
print $fout "PORTREVISION= $revision\n";
101
$printedrev = 1;
102
next;
103
}
104
}
105
print $fout "$line\n";
106
}
107
close($fout) or die "Can't close $makefile b/c $!";
108
rename "$makefile.bumped", $makefile or die "Can't rename $makefile.bumped to $makefile: $!";
109
}
110
111
my $osversion = `uname -r`;
112
chomp $osversion;
113
$osversion =~ s/\..*//;
114
115
my $shallow = 0;
116
my ($portsdir, $INDEX);
117
{
118
$opt_i = "";
119
$opt_u = "";
120
getopts("fghi:lnu:p:") or die "Aborting";
121
usage() if $opt_h;
122
if ($ENV{'DEBUG'}) { $debug = $ENV{'DEBUG'}; }
123
$shallow = $opt_l if $opt_l;
124
if ($opt_l and $opt_g) {
125
die "Options -g and -l given, which are mutually exclusive. Pick either.";
126
}
127
if (not $opt_l and not $opt_g) {
128
warn "Neither -g nor -l given. Defaulting to -l";
129
$opt_l = 1;
130
}
131
$portsdir = $opt_p ? $opt_p : '/usr/ports';
132
133
$INDEX = "$portsdir/INDEX-$osversion";
134
$INDEX = $opt_i if ($opt_i);
135
if (!-f $INDEX) { $INDEX = "$portsdir/INDEX"; }
136
137
die "$INDEX doesn't seem to exist. Please check the value supplied with -i,\n" .
138
"or use -i /path/to/INDEX, or check your -p PORTSDIR." unless(-f $INDEX);
139
}
140
usage() unless(@ARGV);
141
142
my $TMPDIR = File::Basename::dirname($INDEX);
143
144
#
145
# Sanity checking
146
#
147
if (-d "$TMPDIR/.git" and not $opt_f and not $opt_n) {
148
die "$TMPDIR/.git exists, cowardly refusing to proceed.\n";
149
}
150
151
152
# must launder $portsdir (from command line => tainted) first
153
if ($portsdir =~ /^([-\@\w.\/]+)$/) {
154
$portsdir = $1; }
155
else {
156
die "Portsdir \"$portsdir\" contains unsafe characters. Aborting";
157
}
158
159
chdir "$portsdir" or die "cannot cd to $portsdir: $!\nAborting";
160
161
#
162
# Read the index, save some interesting keys
163
#
164
my %index = ();
165
{
166
print "Reading $INDEX\n";
167
open(my $fin, '<', "$INDEX") or die "Cannot open $INDEX for reading.";
168
my @lines = <$fin>;
169
if ($!) { die "Error while reading $INDEX: $! Aborting"; }
170
chomp(@lines);
171
close($fin);
172
173
my @a;
174
my @b;
175
my $origin;
176
my $cat_port;
177
my $pkgname;
178
map {
179
@a = split(/\|/, $_); # columns per PORTINDEX(5) aka INDEX(5)
180
@b = split(/\//, $a[1]);
181
182
$cat_port = $b[-2]."/".$a[0];
183
$cat_port =~ s/-[^-]+$//;
184
$origin = $b[-2]."/".$b[-1];
185
186
unless ($b[-1]) { die "undefined portname"; }
187
unless ($origin) { die "undefined origin"; }
188
189
@{ $index{$a[0]} }{'portname', 'origin', 'comment', 'deps'}
190
= ($b[-1], $origin, $a[3], ());
191
192
if ($a[8]) { # run dependencies
193
@b = split(" ", $a[8]);
194
@{ $index{$a[0]}{deps} }{@b} = (1) x @b;
195
}
196
undef;
197
} @lines;
198
199
print "- Processed ", scalar keys(%index), " entries.\n";
200
if ($debug and $debug > 1) {
201
print STDERR Dumper(\%index);
202
}
203
}
204
205
my %DEPPORTS = ();
206
207
my %byorigin = map { ($index{$_}{'origin'} => $_) } keys %index;
208
my %byportname = map { ($index{$_}{'portname'} => $_) } keys %index;
209
210
foreach my $PORT (@ARGV) {
211
#
212
# See if the port really exists.
213
# If specified as category/portname, that should be enough.
214
# If specified as portname, check all categories for existence or duplicates.
215
#
216
my $r = $index{$PORT};
217
if (!defined $r) { $r = $byportname{$PORT}; }
218
if (!defined $r) { $r = $byorigin{$PORT}; }
219
if (defined $r) { print "Found $PORT as $r.\n"; $PORT = $r; }
220
else { die "Cannot find $PORT in $INDEX! Aborting"; }
221
222
#
223
# Figure out all the ports depending on this one.
224
#
225
{
226
print "Searching for ports depending on $PORT\n";
227
my $count = 0;
228
229
foreach my $p (keys(%index)) {
230
my $q = $index{$p}{'deps'}{$PORT};
231
if ($q) {
232
$DEPPORTS{$p} = 1;
233
++$count;
234
}
235
}
236
print "- Found $count ports depending on $PORT.\n";
237
}
238
}
239
240
#
241
# In shallow mode, strip all those who don't have a direct dependency
242
#
243
sub direct_dependency($@) {
244
if ($debug) { print STDERR Dumper \@_; }
245
my ($port, @requisites) = @_;
246
open F, '-|', '/usr/bin/make', '-C', $port, qw/-V _RUN_DEPENDS -V _LIB_DEPENDS/ or die "cannot launch make: $!";
247
my @lines = <F>;
248
chomp @lines;
249
my $deps = join(" ", @lines);
250
my %deps = map { $_ =~ s[/usr/ports/][]; $_ =~ s[$portsdir/][]; ($_ => 1) } split " ", $deps;
251
if ($!) { die "cannot read depends from make: $!"; }
252
close F or Carp::carp "cannot read depends from make: $!";
253
my $required = grep { $_ } map { defined $deps{$_} } @requisites;
254
return $required;
255
}
256
257
if ($shallow) {
258
my $n = keys %DEPPORTS;
259
my $idx = 1;
260
foreach my $p (keys %DEPPORTS) {
261
print "- Checking requisites of port $idx/$n... \r";
262
print "\n" if $debug;
263
++$idx;
264
my $pp = $index{$p}->{'origin'};
265
unless (direct_dependency($pp, map { $index{$_}{origin} } @ARGV)) {
266
delete $DEPPORTS{$p};
267
}
268
}
269
print "- Found ", scalar keys(%DEPPORTS), " ports depending directly on either of @ARGV.\n";
270
}
271
272
my $ports = join(" ", keys %DEPPORTS);
273
274
#
275
# Create a temp directory and cvs checkout the ports
276
# (don't do error checking, too complicated right now)
277
#
278
unless ($opt_f or $opt_n) {
279
$TMPDIR = ".bump_revision_pl_tmpdir.$$";
280
die "This code fragment has not been updated for Git yet.";
281
print "svn checkout into $TMPDIR...\n";
282
mkdir($TMPDIR, 0755);
283
chdir($TMPDIR);
284
system "svn checkout --depth=immediates svn+ssh://repo.freebsd.org/ports/head/ ports" and die "SVN checkout failed (wait value $?), aborting";
285
chdir('ports');
286
system "svn update --set-depth=infinity $ports" and die "SVN checkout failed (wait value $?), aborting";
287
}
288
289
#
290
# Bump portrevisions
291
#
292
{
293
print "Updating Makefiles\n";
294
foreach my $p (sort keys(%DEPPORTS)) {
295
my $origin = $index{$p}->{'origin'};
296
print "- Updating Makefile of $origin\n";
297
unless($opt_n) {
298
bumpMakefile "$origin";
299
}
300
}
301
}
302
303
#
304
# Commit the changes. Not automated.
305
#
306
unless ($opt_n) {
307
print <<EOF;
308
All PORTREVISIONs have been updated. You are nearly done, only one
309
thing remains: Committing to the ports tree. This program is not
310
going to do that for you, you have to do it manually.
311
312
\$ cd $TMPDIR
313
\$ git commit
314
315
Then, remove the temp directory ($TMPDIR).
316
EOF
317
}
318
319