Path: blob/main/Tools/scripts/bump_revision.pl
16461 views
#!/usr/bin/env -S perl -wt12#3# This script helps with bumping the PORTREVISION of all ports that depend on a4# set of ports, for instance, when in the latter set one of the ports bumped the5# .so library version.6#7# The shebang line above includes -T (taint) to be more distrustful8# about the environment, for security reasons, and is considered9# good Perl practice.10#11# You can use either the12# -l (shaLlow, avoid grandparent dependencies, slower) or13# -g option (include grandparent dependencies) option.14#15# MAINTAINER= [email protected]16#1718use strict;19use Getopt::Std;20use Carp 'verbose';21use Cwd;22use File::Basename;23use Data::Dumper;24$Data::Dumper::Indent = 1; # simple indent25$Data::Dumper::Purity = 1; # Perl syntax26my $debug = 0;2728use vars qw/$opt_n $opt_f $opt_i $opt_u $opt_l $opt_g $opt_p $opt_h/;2930# launder environment31delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};32$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';3334sub usage {35print <<EOF;36Usage: $0 [options] [<category>/]<portname>3738Options:39-l - shaLlow, only bump ports with direct dependencies. (default).40-g - Grandchildren, also bump for indirect dependencies.41-n - Check only (dry-run), do not change Makefiles.42-f - No tmpdir, just use the directory where INDEX resides.43-i <filename> - Use this for INDEX name. Defaults to \${PORTSDIR}/INDEX-n,44where n is the major version of the OS, or \${PORTSDIR}/INDEX if missing.45-p <dirname> - Set portsdir, if different from /usr/ports.4647Improvements, suggestions, questions -> mandree\@FreeBSD.org48EOF49exit 1;50}5152# flush STDOUT for each and every write even if writing to a pipe.53$| = 1;5455sub bumpMakefile {56my ($taintedorigin) = @_;57my $origin;5859if ($taintedorigin =~ /^([-\@\w.\/]+)$/) {60$origin = $1;61} else {62Carp::carp "cannot untaint $taintedorigin - invalid characters";63return;64}6566my $makefile = "$origin/Makefile";67my $fin;68unless(open($fin, $makefile)) {69print "-- Cannot open Makefile of $origin, ignored.\n";70return;71}72my @lines = <$fin>;73if ($!) { die "Error while reading $makefile: $!. Aborting"; }74close($fin) or die "Can't close $makefile b/c $!";75chomp(@lines);7677my $revision = 1;7879foreach my $line (@lines) {80last if ($line =~ /^MAINTAINER/);81$revision += $1 if ($line =~ /PORTREVISION\??=[ \t]*(\d+)$/);82}8384my $printedrev = 0;85open(my $fout, '>', "$makefile.bumped");86foreach my $line (@lines) {87if (!$printedrev) {88if ($line =~ /^CATEGORIES??=/ || $line =~ /^PORTEPOCH??=/) {89print $fout "PORTREVISION= $revision\n";90$printedrev = 1;91# Fall through!92}93if ($line =~ /^PORTREVISION\?=/) {94print $fout "PORTREVISION?= $revision\n";95$printedrev = 1;96next;97}98if ($line =~ /^PORTREVISION=/) {99print $fout "PORTREVISION= $revision\n";100$printedrev = 1;101next;102}103}104print $fout "$line\n";105}106close($fout) or die "Can't close $makefile b/c $!";107rename "$makefile.bumped", $makefile or die "Can't rename $makefile.bumped to $makefile: $!";108}109110my $osversion = `uname -r`;111chomp $osversion;112$osversion =~ s/\..*//;113114my $shallow = 0;115my ($portsdir, $INDEX);116{117$opt_i = "";118$opt_u = "";119getopts("fghi:lnu:p:") or die "Aborting";120usage() if $opt_h;121if ($ENV{'DEBUG'}) { $debug = $ENV{'DEBUG'}; }122$shallow = $opt_l if $opt_l;123if ($opt_l and $opt_g) {124die "Options -g and -l given, which are mutually exclusive. Pick either.";125}126if (not $opt_l and not $opt_g) {127warn "Neither -g nor -l given. Defaulting to -l";128$opt_l = 1;129}130$portsdir = $opt_p ? $opt_p : '/usr/ports';131132$INDEX = "$portsdir/INDEX-$osversion";133$INDEX = $opt_i if ($opt_i);134if (!-f $INDEX) { $INDEX = "$portsdir/INDEX"; }135136die "$INDEX doesn't seem to exist. Please check the value supplied with -i,\n" .137"or use -i /path/to/INDEX, or check your -p PORTSDIR." unless(-f $INDEX);138}139usage() unless(@ARGV);140141my $TMPDIR = File::Basename::dirname($INDEX);142143#144# Sanity checking145#146if (-d "$TMPDIR/.git" and not $opt_f and not $opt_n) {147die "$TMPDIR/.git exists, cowardly refusing to proceed.\n";148}149150151# must launder $portsdir (from command line => tainted) first152if ($portsdir =~ /^([-\@\w.\/]+)$/) {153$portsdir = $1; }154else {155die "Portsdir \"$portsdir\" contains unsafe characters. Aborting";156}157158chdir "$portsdir" or die "cannot cd to $portsdir: $!\nAborting";159160#161# Read the index, save some interesting keys162#163my %index = ();164{165print "Reading $INDEX\n";166open(my $fin, '<', "$INDEX") or die "Cannot open $INDEX for reading.";167my @lines = <$fin>;168if ($!) { die "Error while reading $INDEX: $! Aborting"; }169chomp(@lines);170close($fin);171172my @a;173my @b;174my $origin;175my $cat_port;176my $pkgname;177map {178@a = split(/\|/, $_); # columns per PORTINDEX(5) aka INDEX(5)179@b = split(/\//, $a[1]);180181$cat_port = $b[-2]."/".$a[0];182$cat_port =~ s/-[^-]+$//;183$origin = $b[-2]."/".$b[-1];184185unless ($b[-1]) { die "undefined portname"; }186unless ($origin) { die "undefined origin"; }187188@{ $index{$a[0]} }{'portname', 'origin', 'comment', 'deps'}189= ($b[-1], $origin, $a[3], ());190191if ($a[8]) { # run dependencies192@b = split(" ", $a[8]);193@{ $index{$a[0]}{deps} }{@b} = (1) x @b;194}195undef;196} @lines;197198print "- Processed ", scalar keys(%index), " entries.\n";199if ($debug and $debug > 1) {200print STDERR Dumper(\%index);201}202}203204my %DEPPORTS = ();205206my %byorigin = map { ($index{$_}{'origin'} => $_) } keys %index;207my %byportname = map { ($index{$_}{'portname'} => $_) } keys %index;208209foreach my $PORT (@ARGV) {210#211# See if the port really exists.212# If specified as category/portname, that should be enough.213# If specified as portname, check all categories for existence or duplicates.214#215my $r = $index{$PORT};216if (!defined $r) { $r = $byportname{$PORT}; }217if (!defined $r) { $r = $byorigin{$PORT}; }218if (defined $r) { print "Found $PORT as $r.\n"; $PORT = $r; }219else { die "Cannot find $PORT in $INDEX! Aborting"; }220221#222# Figure out all the ports depending on this one.223#224{225print "Searching for ports depending on $PORT\n";226my $count = 0;227228foreach my $p (keys(%index)) {229my $q = $index{$p}{'deps'}{$PORT};230if ($q) {231$DEPPORTS{$p} = 1;232++$count;233}234}235print "- Found $count ports depending on $PORT.\n";236}237}238239#240# In shallow mode, strip all those who don't have a direct dependency241#242sub direct_dependency($@) {243if ($debug) { print STDERR Dumper \@_; }244my ($port, @requisites) = @_;245open F, '-|', '/usr/bin/make', '-C', $port, qw/-V _RUN_DEPENDS -V _LIB_DEPENDS/ or die "cannot launch make: $!";246my @lines = <F>;247chomp @lines;248my $deps = join(" ", @lines);249my %deps = map { $_ =~ s[/usr/ports/][]; $_ =~ s[$portsdir/][]; ($_ => 1) } split " ", $deps;250if ($!) { die "cannot read depends from make: $!"; }251close F or Carp::carp "cannot read depends from make: $!";252my $required = grep { $_ } map { defined $deps{$_} } @requisites;253return $required;254}255256if ($shallow) {257my $n = keys %DEPPORTS;258my $idx = 1;259foreach my $p (keys %DEPPORTS) {260print "- Checking requisites of port $idx/$n... \r";261print "\n" if $debug;262++$idx;263my $pp = $index{$p}->{'origin'};264unless (direct_dependency($pp, map { $index{$_}{origin} } @ARGV)) {265delete $DEPPORTS{$p};266}267}268print "- Found ", scalar keys(%DEPPORTS), " ports depending directly on either of @ARGV.\n";269}270271my $ports = join(" ", keys %DEPPORTS);272273#274# Create a temp directory and cvs checkout the ports275# (don't do error checking, too complicated right now)276#277unless ($opt_f or $opt_n) {278$TMPDIR = ".bump_revision_pl_tmpdir.$$";279die "This code fragment has not been updated for Git yet.";280print "svn checkout into $TMPDIR...\n";281mkdir($TMPDIR, 0755);282chdir($TMPDIR);283system "svn checkout --depth=immediates svn+ssh://repo.freebsd.org/ports/head/ ports" and die "SVN checkout failed (wait value $?), aborting";284chdir('ports');285system "svn update --set-depth=infinity $ports" and die "SVN checkout failed (wait value $?), aborting";286}287288#289# Bump portrevisions290#291{292print "Updating Makefiles\n";293foreach my $p (sort keys(%DEPPORTS)) {294my $origin = $index{$p}->{'origin'};295print "- Updating Makefile of $origin\n";296unless($opt_n) {297bumpMakefile "$origin";298}299}300}301302#303# Commit the changes. Not automated.304#305unless ($opt_n) {306print <<EOF;307All PORTREVISIONs have been updated. You are nearly done, only one308thing remains: Committing to the ports tree. This program is not309going to do that for you, you have to do it manually.310311\$ cd $TMPDIR312\$ git commit313314Then, remove the temp directory ($TMPDIR).315EOF316}317318319