Path: blob/main/Tools/scripts/bump_revision.pl
16124 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 Data::Dumper;23use File::Basename;2425use vars qw/$opt_n $opt_f $opt_i $opt_u $opt_l $opt_g $opt_p/;2627# launder environment28delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};29$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';3031sub usage {32print <<EOF;33Usage: $0 [options] [<category>/]<portname>3435Options:36-l - shaLlow, only bump ports with direct dependencies.37-g - Grandchildren, also bump for indirect dependencies (default).38-n - Check only (dry-run), do not change Makefiles.39-f - No tmpdir, just use the directory where INDEX resides.40-i <filename> - Use this for INDEX name. Defaults to \${PORTSDIR}/INDEX-n,41where n is the major version of the OS, or \${PORTSDIR}/INDEX if missing.42-p <dirname> - Set portsdir, if different from /usr/ports.4344Improvements, suggestions, questions -> mandree\@FreeBSD.org45EOF46exit 1;47}4849# flush STDOUT for each and every write even if writing to a pipe.50$| = 1;5152sub bumpMakefile {5354my ($p) = @_;5556my $makefile = "$p/Makefile";57my $fin;58unless(open($fin, $makefile)) {59print "-- Cannot open Makefile of $p, ignored.\n";60next;61}62my @lines = <$fin>;63if ($!) { die "Error while reading $makefile: $!. Aborting"; }64close($fin) or die "Can't close $makefile b/c $!";65chomp(@lines);6667my $revision = 1;6869foreach my $line (@lines) {70last if ($line =~ /^MAINTAINER/);71$revision += $1 if ($line =~ /PORTREVISION\??=[ \t]*(\d+)$/);72}7374my $printedrev = 0;75open(my $fout, '>', "$makefile.bumped");76foreach my $line (@lines) {77if (!$printedrev) {78if ($line =~ /^CATEGORIES??=/ || $line =~ /^PORTEPOCH??=/) {79print $fout "PORTREVISION= $revision\n";80$printedrev = 1;81# Fall through!82}83if ($line =~ /^PORTREVISION\?=/) {84print $fout "PORTREVISION?= $revision\n";85$printedrev = 1;86next;87}88if ($line =~ /^PORTREVISION=/) {89print $fout "PORTREVISION= $revision\n";90$printedrev = 1;91next;92}93}94print $fout "$line\n";95}96close($fout) or die "Can't close $makefile b/c $!";97rename "$makefile.bumped", $makefile or die "Can't rename $makefile.bumped to $makefile: $!";98}99100my $osversion = `uname -r`;101chomp $osversion;102$osversion =~ s/\..*//;103104my $shallow = 0;105my ($portsdir, $INDEX);106{107$opt_i = "";108$opt_u = "";109getopts("fgi:lnu:p:") or die "Aborting";110$shallow = $opt_l if $opt_l;111if ($opt_l and $opt_g) {112die "Options -g and -l given, which are mutually exclusive. Pick either.";113}114if (not $opt_l and not $opt_g) {115warn "Neither -g nor -l given. Defaulting to -g";116$opt_g = 1;117}118$portsdir = $opt_p ? $opt_p : '/usr/ports';119120$INDEX = "$portsdir/INDEX-$osversion";121$INDEX = $opt_i if ($opt_i);122if (!-f $INDEX) { $INDEX = "$portsdir/INDEX"; }123124die "$INDEX doesn't seem to exist. Please check the value supplied with -i,\n" .125"or use -i /path/to/INDEX, or check your -p PORTSDIR." unless(-f $INDEX);126}127usage() unless(@ARGV);128129my $TMPDIR = File::Basename::dirname($INDEX);130131#132# Sanity checking133#134if (-d "$TMPDIR/.svn" and not $opt_f and not $opt_n) {135die "$TMPDIR/.svn exists, cowardly refusing to proceed.\n";136}137138139# must launder $portsdir (from command line => tainted) first140if ($portsdir =~ /^([-\@\w.\/]+)$/) {141$portsdir = $1; }142else {143die "Portsdir \"$portsdir\" contains unsafe characters. Aborting";144}145146chdir "$portsdir" or die "cannot cd to $portsdir: $!\nAborting";147148#149# Read the index, save some interesting keys150#151my %index = ();152{153print "Reading $INDEX\n";154open(my $fin, '<', "$INDEX") or die "Cannot open $INDEX for reading.";155my @lines = <$fin>;156if ($!) { die "Error while reading $INDEX: $! Aborting"; }157chomp(@lines);158close($fin);159160my @a;161my @b;162my $port;163map {164@a = split(/\|/, $_);165@b = split(/\//, $a[1]);166167$port = $b[-2]."/".$b[-1];168169@{ $index{$port} }{'portname', 'portnameversion', 'origin', 'comment', 'deps'}170= ($b[-1], $a[0], $port, $a[3], ());171172if ($a[8]) {173@b = split(" ", $a[8]);174@{ $index{$port}{deps} }{@b} = (1) x @b;175}176177} @lines;178print "- Processed ", scalar keys(%index), " entries.\n";179}180181my %DEPPORTS = ();182183foreach my $PORT (@ARGV) {184#185# See if the port really exists.186# If specified as category/portname, that should be enough.187# If specified as portname, check all categories for existence or duplicates.188#189unless (defined $index{$PORT}) {190my @found = grep /\/$PORT$/, keys(%index);191my $count = @found;192193if ($count == 0) {194die "Cannot find ${PORT} in ${INDEX}.";195} elsif ($count == 1) {196$PORT = $found[0];197} else {198my $n = join(" ", @found);199die "Found ${PORT} more than once in ${INDEX}: $n. Try category/$PORT.\nAborting";200}201}202203my $PORTNAMEVERSION = $index{$PORT}{portnameversion};204print "Found $PORT as $PORTNAMEVERSION\n";205206#207# Figure out all the ports depending on this one.208#209{210print "Searching for ports depending on $PORT\n";211my $count = 0;212213foreach my $p (keys(%index)) {214if (defined $index{$p}{'deps'}{$PORTNAMEVERSION}) {215$DEPPORTS{$p} = 1;216++$count;217}218}219print "- Found $count ports depending on $PORT.\n";220}221}222223#224# In shallow mode, strip all those who don't have a direct dependency225#226sub direct_dependency($@) {227my ($port, @requisites) = @_;228open F, '-|', '/usr/bin/make', '-C', $port, qw/-V _RUN_DEPENDS -V _LIB_DEPENDS/ or die "cannot launch make: $!";229my @lines = <F>;230chomp @lines;231my $deps = join(" ", @lines);232my %deps = map { $_ =~ s[/usr/ports/][]; $_ =~ s[$portsdir/][]; ($_ => 1) } split " ", $deps;233if ($!) { die "cannot read depends from make: $!"; }234close F or die "cannot read depends from make: $!";235my $required = grep { $_ } map { defined $deps{$_} } @requisites;236return $required;237}238239if ($shallow) {240my $n = keys %DEPPORTS;241my $idx = 1;242foreach my $p (keys %DEPPORTS) {243print "- Checking requisites of port $idx/$n...\r";244++$idx;245unless (direct_dependency($p, @ARGV)) {246delete $DEPPORTS{$p};247}248}249print "- Found ", scalar keys(%DEPPORTS), " ports depending directly on either of @ARGV.\n";250}251252my $ports = join(" ", keys %DEPPORTS);253254#255# Create a temp directory and cvs checkout the ports256# (don't do error checking, too complicated right now)257#258unless ($opt_f or $opt_n) {259$TMPDIR = ".bump_revision_pl_tmpdir.$$";260print "svn checkout into $TMPDIR...\n";261mkdir($TMPDIR, 0755);262chdir($TMPDIR);263system "svn checkout --depth=immediates svn+ssh://repo.freebsd.org/ports/head/ ports" and die "SVN checkout failed (wait value $?), aborting";264chdir('ports');265system "svn update --set-depth=infinity $ports" and die "SVN checkout failed (wait value $?), aborting";266}267268#269# Bump portrevisions270#271{272print "Updating Makefiles\n";273foreach my $p (sort keys(%DEPPORTS)) {274print "- Updating Makefile of $p\n";275next if $opt_n;276bumpMakefile "$p";277}278}279280#281# Commit the changes. Not automated.282#283unless ($opt_n) {284print <<EOF;285All PORTREVISIONs have been updated. You are nearly done, only one286thing remains: Committing to the ports tree. This program is not287going to do that for you, you have to do it manually.288289\$ cd $TMPDIR290\$ svn commit291292Then, remove the temp directory ($TMPDIR).293EOF294}295296297