#!/usr/bin/perl # # addport - perl script that adds new ports to the # FreeBSD Ports Collection. # Created by: Will Andrews # and Michael Haro # # $Id: addport,v 1.5 2000/04/22 22:19:43 mharo Exp $ # $FreeBSD$ # # MAINTAINER= mharo@FreeBSD.org # however feel free to submit patches to will@FreeBSD.org. =) # use Cwd "abs_path"; use Getopt::Std; use Sys::Hostname; use strict; my %opts; getopts('d:h:ins:tu:v', \%opts); my $distdir = $opts{'s'} if ($opts{'s'} ne ""); my $dir = $opts{'d'}; my $h = "freefall.FreeBSD.org"; $h = $opts{'h'} if ($opts{'h'} ne ""); my $n = ""; $n = "-n " if $opts{'n'}; my $u = $ENV{USER}; $u = $opts{'u'} if ($opts{'u'} ne ""); my $more_testing = $opts{'t'}; my $vanilla = $opts{'v'}; my $interactive = $opts{'i'}; my $tmpdir; my $repo; my $ssh; if( !defined $ENV{"CVS_RSH"} ) { $ENV{CVS_RSH} = "ssh"; } my $make = "make"; my $portlint = "portlint -N -a -c"; my $perl = "perl"; my $cp = "cp"; my $mv = "mv"; my $rm = "rm"; # now check to make sure this isn't running on freefall chomp(my $myhost = lc(hostname())); if ($myhost ne lc($h)) { $ssh = "$ENV{CVS_RSH} $u\@$h"; $repo = "$u\@$h:/home/ncvs"; } else { $ssh = ""; $repo = "/home/ncvs"; } my $cvs = "cvs $n-d $repo"; # stuff that always happens when we start BEGIN { $tmpdir=`mktemp -d -t ap`; chomp $tmpdir; if ($tmpdir eq "") { errx(1,"making random tmpdir didn't work, aborting."); } } # stuff that always happens when we exit END { # only remove $tmpdir if it points to something in /tmp # this is a silly little security thing if (defined($rm) && defined($tmpdir)) { system("$rm -rf $tmpdir") if ($tmpdir =~ m,/tmp/,); } } # setup the list of commands to run on the new port(s). my @commands; if (!$vanilla) { push(@commands, "$make clean check-categories"); push(@commands, "$portlint"); if (-d $distdir) { push(@commands, "$make DISTDIR='$distdir' FETCH_BEFORE_ARGS='-btA' checksum"); } elsif ($myhost eq "freefall.freebsd.org") { push(@commands, "$make DISTDIR='$tmpdir' FETCH_BEFORE_ARGS='-btA' checksum"); } else { push(@commands, "$make FETCH_BEFORE_ARGS='-btA' checksum"); } if ($more_testing) { push(@commands, "$make distclean"); push(@commands, "$make build"); } } if ($dir eq "") { warnx("Need to specify a directory with -d argument!"); usage(); exit 1; } my @dirs = split(/\,/, $dir); my $portname; my $module; foreach my $thisdir (@dirs) { # do some dir sanity checking first errx(1, "Please specify valid directories to import new ports from.") if $thisdir eq ""; errx(1, "$thisdir is either not a directory or does not exist.") if (! -d $thisdir); $thisdir = abs_path($thisdir); print "Working with port directory $thisdir.\n"; $portname = `basename $thisdir`; # avoid problems with dirs containing `/' in cvs chomp $portname; if ($opts{'i'}) { if (prompt("Port directory name will be $portname in CVS Repo. OK? ")) { do { $portname = query("Preferred name for port directory? "); } while (prompt("Is the new name $portname OK? ")); } } chdir $thisdir or err(1, "$thisdir"); # now run the tests on this baby. for (@commands) { system("$_") && errx(1, "'$_' had problems. aborting."); } # Get the category name and make it suitable for use with cvs my $category; $_ = `grep CATEGORIES Makefile`; m/\w+\W+([\w-]+)/; $category = $1; chomp $category; if ($opts{'i'}) { if (prompt("Port $portname will be put in category $category. OK? " )) { do { $category = query("Preferred category for $portname? "); } while (prompt("Is the new category $category OK? ")); } } chomp(my $cvs_category = $category); $cvs_category =~ s/-/_/g; $module = $portname; if ($opts{'i'}) { if (prompt("Port will be added as module $portname. OK? ")) { do { $module = query("Preferred module name for $module? "); } while (prompt("Is the new module name $module OK? ")); } } print "We're ready to commit.\n"; print "Source directory: $thisdir\n"; print "Target CVS Repo directory: ports/$category/$portname\n"; print "Modules entry: $module --> ports/$category/$portname\n"; prompt("Adding port $portname to $category OK? ") && errx(1, "user abort requested"); chdir $tmpdir or err(1, "$tmpdir"); # let's get our hands dirty. if (! -d $category) { system("$cvs co -l ports_$cvs_category") && errx(1, "can't get temporary category directory, aborting."); system("$mv ports_$cvs_category $category"); } chdir $category or err(1,"$category"); system("$cp -PRp $thisdir ."); system("$cvs add `find $portname -type d | grep -v CVS`") && errx(1, "cvs add for dirs failed, aborting."); system("$cvs add `find $portname -type f | grep -v CVS`") && errx(1, "cvs add for files failed, aborting."); # figure out where the port name belongs in category Makefile my @ports = &lsports; errx(1, "Error: $portname already exists in $category\'s Makefile") if (&contains($portname, @ports)); my $port = ""; foreach my $tmp (sort(@ports)) { if ($tmp gt $portname) { $port = $tmp; last; } } # now let's insert it my $cmd; if ($port eq "") { # there were no previous SUBDIR += lines, so we're going to # put ourselves after the last comment (we can't be after a # .include for example). my $lastcommentnum = &lastcomment; $cmd = "$lastcommentnum\n+\ni\n"; } else { # OK, append ourselves in the right place, so things *stay* sorted. $cmd = "/^ SUBDIR += $port/\ni\n"; } print "Inserting new port into $category/Makefile...\n"; open(ED, "|ed Makefile") || die "Cannot start ed to actually insert module\n"; print ED "$cmd SUBDIR += $portname\n.\nw\nq\n"; close(ED); # commit the actual port. chdir "$tmpdir/$category" or err(1, "$tmpdir/$category"); system("$cvs ci Makefile $portname") && errx(1, "cvs commit failed, aborting."); system("$ssh $perl /usr/local/bin/modulesupdate $module ports/$category/$portname") && errx(1, "adding port to modules failed, aborting."); } print <; return $reply; } sub usage { #addport,v \$Revision: 1.5 $ print <, SYNOPSIS $0 [-h host] [-u user] [-s distdir] [-intv] -d directory Where "directory" contains the comma-delimited list of root directories of new ports that you wish to add to the Ports Collection. The name of this directory *WILL* matter in regards to the repository! OPTIONS -h host Use a cvshost besides freefall.FreeBSD.org -i Interactive mode; allow more control over where things are placed. This is required in order to change things like module names etc. -n Do not actually commit anything. -s distdir Use a different directory besides the default, for downloading distfiles. This defaults to the temporary directory set up on freefall. -u user Use a different username (default: $u). -t Do more port testing -v Plain vanilla "add it" - no testing at all. This option overrides -t in all cases. EXAMPLES % addport -n -d greatgame,helpfuldev,shoot Will show what happens but not actually commit ports named "greatgame", "helpfuldev", and "shoot". % addport -t -v thisport Will not perform testing (does not matter in which order -t and -v are used) on port "thisport" but just add it. EOF } sub contains { # look if the first parameter is contained in the list following it my ($item, @list) = @_; foreach my $i (@list) { return 1 if $i eq $item; } return 0; } sub lsports { my @rv = (); open(F, "Makefile") || die "can't open Makefile: $!"; while() { chomp; chomp; next if $_ !~ m/SUBDIR/; s/^[ \t]+SUBDIR[ \t]+\+?=[\ \t]+//; push(@rv, $_); } close(F); return @rv; } # this finds the last comment in the Makefile sub lastcomment { my $num = 0; my $diff = 0; open(F, "Makefile"); while() { chomp; if ($_ =~ m/^#/) { $num += $diff; $num++; $diff = 0; } else { $diff += 1; } next; } return $num; }