| #!/usr/bin/perl |
| |
| use strict; |
| use Pod::Usage; |
| |
| |
| my %opts; |
| my $descr; |
| |
| LocalGetOptions(\%opts, |
| ['r|regexp-ignore-steps=s','Ignore these REGEXP steps'], |
| ['s|start-at=s', 'Skip all steps until ARG'], |
| ['i|interactive', 'Prompt whether to do each step'], |
| ['n|dry-run', 'Dry run only. Don\'t actually do anything.'], |
| ['h|help', 'Help']); |
| |
| pod2usage(1) if ($opts{'h'}); |
| my $version = shift; |
| pod2usage( "No version supplied" ) if (!$version); |
| my $extrajunk = shift; |
| pod2usage( "Unexpected argument ($extrajunk)" ) if ($extrajunk); |
| my $vtag = $version; |
| my $branch = ""; |
| # Ensure we have both numeric and tag-style version labels |
| # plus the branch that we're working from (if relevant) |
| if ( $version =~ /^Ext/) { |
| $version =~ s/^Ext-//g; |
| $version =~ s/-/./g; |
| } else { |
| $vtag =~ s/\./-/g; |
| $vtag = "Ext-" . $vtag; |
| } |
| if ( $version =~ /^([0-9]\.[0-9])\.[0-9]/) { |
| ($branch = $version) =~ s/([0-9]\.[0-9])\.[0-9].*/\1/; |
| $branch =~ s/\./-/; |
| $branch = "V${branch}-patches"; |
| } |
| |
| # Make sure we're at the root of the build tree |
| # (if we're in the dist directory, then cd ..) |
| chdir("..") if (-f 'makerelease'); |
| die "Not at root of build tree" if (! -f 'configure.in'); |
| |
| # |
| # TESTING: |
| # |
| # Step 1) Ensure the build tree is up to date |
| System("update","svn -u status"); |
| System("update","svn -q update"); |
| |
| # Step 2) Change the libtool version information in Makefile.top |
| manualstep('version:manualedit', |
| "You (may) need to edit Makefile.top to update the library version numbering |
| (usually just for the first pre-release of a given version). |
| See the comments in RELEASE-INSTRUCTIONS |
| about LIBCURRENT, LIBAGE and LIBREVISION. |
| |
| I'll commit the file for you after you're done |
| "); |
| System("version:commit", |
| "svn commit -m \"version update\" Makefile.top"); |
| |
| # Step 3) configure and build and test it |
| my $transportargs = ""; |
| if ( $^O eq "linux" ) { |
| $transportargs = "--with-transports=IPX"; |
| } |
| my $configureargs = "--cache=config.cache --with-defaults ". |
| "--with-mib-modules=' examples examples/example ". |
| " testhandler smux Rmon' ". |
| " $transportargs --enable-ipv6 ". |
| "--enable-embedded-perl --enable-shared"; |
| System("build:distclean","make distclean") if (-f 'Makefile'); |
| System("build:configure","./configure $configureargs"); |
| System("build:make","make"); |
| System("build:test","make test TESTOPTS=-n"); |
| |
| # |
| # DOCUMENTATION |
| # |
| # Step 4) Update doxygen version number, and generate man pages |
| System("docs:doxygenconf", |
| "perl local/Version-Munge.pl -v $version -M -P -C -t doxygen"); |
| System("docs:make","make docs"); |
| System("docs:mancp","make mancp"); |
| System("docs:update","svn -u status man"); |
| manualstep('docs:manualaddnewman', |
| "Update man/Makefile.in with details of any new man pages, |
| and run 'svn add' on them. |
| I'll commit these changes for you after you're done |
| "); |
| System("docs:commit","svn commit -m \"documentation update\" man"); |
| |
| # Step 5) Check code for illegal constructs |
| System("code:checkcomments","make checks"); |
| |
| # Step 6) Update Makefile dependencies |
| System("code:makedepend","make distdepend"); |
| System("code:commitdepend","svn commit -m \"make depend\" `find . -name Makefile.depend`"); |
| |
| # Step 7) Update ChangeLog |
| # XXX: May need to extend the folding line-length, |
| # to avoid svn2cl mangling CHANGES/NEWS entries |
| System("changelog:svn2cl", |
| "svn2cl -f ChangeLog.add --break-before-msg --stop-on-copy"); |
| System("changelog:changelogfix", |
| "perl dist/changelogfix $branch < ChangeLog.add > ChangeLog.reallyadd"); |
| manualstep("changelog:manualedit", |
| "You need to manually insert the *relevent* portions of |
| 'ChangeLog.reallyadd' into the ChangeLog file. |
| I'll commit these changes for you afterwards"); |
| System("changelog:commit","svn commit -m \"version update\" ChangeLog"); |
| |
| # Step 8) Update README/NEWS/CHANGES |
| System("docs:newnews", |
| "perl dist/extractnews -s ----- -e ----- ChangeLog"); |
| System("docs:newnews", |
| "perl dist/extractnews -c CHANGES.new2 -n NEWS.new2 ChangeLog.reallyadd"); |
| manualstep("docs:README", |
| "You need to manually insert the relevent portions of |
| 'CHANGES.new' and 'NEWS.new' into the CHANGES and NEWS file. |
| (There are alternative versions in 'CHANGES.new2' and 'NEWS.new2') |
| You may wish to update the README file as well. |
| I'll commit these changes for you afterwards"); |
| System("docs:commit", |
| "svn commit -m \"version update\" README NEWS CHANGES"); |
| |
| # |
| # RELEASE |
| # |
| # Step 9) Resync code (omitted) |
| # Step 10) Update version |
| System("release:versionstamp", |
| "perl local/Version-Munge.pl -v $version -M -P -C"); |
| |
| # Step 11) Create the release tag checkpoint |
| my $root="https://net-snmp.svn.sourceforge.net/svnroot/net-snmp"; |
| if ( $branch ) { |
| System("release:tag", |
| "svn copy -m \"$version release\" $root/branches/$branch $root/tags/$vtag"); |
| } else { |
| System("release:tag", |
| "svn copy -m \"$version release\" $root/trunk $root/tags/$vtag"); |
| } |
| |
| # Step 12) Construct the tarball |
| my $tar = "star artype=ustar"; # XXX - check for star/gtar/etc |
| System("release:makedist", |
| "svn export $root/tags/${vtag}/net-snmp net-snmp-$version"); |
| System("release:removefiles", |
| "net-snmp-$version/remove-files net-snmp-$version"); |
| System("release:makedist", |
| "$tar -c -z -f net-snmp-${version}.tar.gz net-snmp-$version"); |
| System("release:makezipclean", |
| "rm -f net-snmp-${version}.zip"); |
| System("release:makezip", |
| "zip -r net-snmp-${version}.zip net-snmp-$version"); |
| |
| |
| # Step 13) Sign (or checksum) the package |
| my $sig1; |
| my $sig2; |
| my $pkg1 = "net-snmp-${version}.tar.gz"; |
| my $pkg2 = "net-snmp-${version}.zip"; |
| |
| System("release:searching-gpg-keys", |
| "gpg --list-secret-keys net-snmp-admin"); |
| if($? != 0) { |
| $sig1 = "${pkg1}.md5"; |
| $sig2 = "${pkg2}.md5"; |
| System("release:md5","md5sum $pkg1 > $sig1"); |
| System("release:md5","md5sum $pkg2 > $sig2"); |
| } else { |
| # currently only rstory and hardaker have the gpg keys till Wes |
| # sees someone else in person ;-) |
| $sig1 = "${pkg1}.asc"; |
| System("release:gpg","gpg -u net-snmp-admin -a --detach-sign $pkg1"); |
| $sig2 = "${pkg2}.asc"; |
| System("release:gpg","gpg -u net-snmp-admin -a --detach-sign $pkg2"); |
| } |
| |
| # |
| # RELEASE TESTING: |
| # |
| # Step 14) Unpack, build and test the release tarball |
| System("posttest:untar", "rm -rf net-snmp-${version}"); |
| System("posttest:untar", "tar xzf net-snmp-${version}.tar.gz"); |
| chdir("net-snmp-${version}"); |
| System("posttest:configure","./configure $configureargs"); |
| System("posttest:make","make"); |
| System("posttest:test","make test"); |
| chdir(".."); |
| |
| # Step 15) Double-check there are no outstanding changes |
| System("release:update","svn -u status"); |
| |
| # |
| # Steps 16 and following are NOT handled automatically. |
| # See the RELEASE-INSTRUCTIONS for full details. |
| # |
| print STDERR "**************************************** FINISHED ********************\n"; |
| print STDERR "\nDouble check the SVN status output above for any outstanding changes\n\n"; |
| print STDERR "Run the following command to upload the relase to SF:\n"; |
| print STDERR " rsync -v $pkg1 $pkg2 $sig1 $sig2 frs.sourceforge.net:uploads/\n"; |
| print STDERR "See RELEASE-INSTRUCTIONS for any additional steps\n"; |
| |
| |
| ###################################################################### |
| sub System { |
| my $name = shift; |
| my $cmd = $descr = join(" ", @_); |
| my $rc; |
| while (dostep($name)) { |
| print STDERR " running: ",$cmd,"\n"; |
| last if ($opts{'n'}); |
| system(@_); |
| $rc = checkresult(); |
| last if ($rc == 0); |
| } |
| } |
| |
| sub checkresult { |
| if ($?) { |
| print STDERR "The above STEP failed. Continue anyway (y/n/r)? "; |
| my $ans = <STDIN>; |
| return 1 if ($ans =~ /^r/); |
| if ($ans =~ /^n/) { |
| print STDERR " EXITING\n"; |
| exit; |
| } |
| } |
| return 0; |
| } |
| |
| sub dostep { |
| my $name = shift; |
| print STDERR "\n********** STEP: $name ******************************\n"; |
| if ($descr) { |
| print STDERR " [$descr]\n"; |
| $descr = undef; |
| } |
| print "\n"; |
| if ($opts{'s'} && $name ne $opts{'s'}) { |
| print STDERR " [skipping]\n"; |
| return 0; |
| } |
| $opts{'s'} = ''; |
| if ($opts{r} && $name =~ /$opts{r}/) { |
| print STDERR " [skipping]\n"; |
| return 0; |
| } elsif ($opts{'i'}) { |
| print STDERR " Do this step (y/n/q)? "; |
| my $ans = <STDIN>; |
| if ($ans =~ /^n/) { |
| print STDERR " [skipping]\n"; |
| return 0; |
| } |
| if ($ans =~ /^q/) { |
| print STDERR " QUITTING\n"; |
| exit; |
| } |
| } |
| return 1; |
| } |
| |
| sub manualstep { |
| my $tag = shift; |
| |
| if (dostep($tag)) { |
| print STDERR "\n\n",join(" ",@_); |
| |
| print STDERR "\n\n Hit return when done: "; |
| |
| return 1 if ($opts{'n'}); |
| |
| my $bogus = <STDIN>; |
| return 1; |
| } |
| return 0; |
| } |
| |
| ####################################################################### |
| # getopt long gui portability code |
| # |
| sub LocalGetOptions { |
| if (eval {require Getopt::GUI::Long;}) { |
| import Getopt::GUI::Long; |
| Getopt::GUI::Long::Configure(qw(display_help no_ignore_case)); |
| return GetOptions(@_); |
| } else { |
| require Getopt::Long; |
| Getopt::Long::Configure(qw(auto_help no_ignore_case)); |
| import Getopt::Long; |
| } |
| GetOptions(LocalOptionsMap(@_)); |
| } |
| |
| sub LocalOptionsMap { |
| my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH') |
| ? (1, 1, $_[0]) : (0, 2)); |
| for (my $i = $st; $i <= $#_; $i += $cb) { |
| if ($_[$i]) { |
| next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/); |
| push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]); |
| push @opts, $_[$i+1] if ($cb == 2); |
| } |
| } |
| return @opts; |
| } |
| |
| __END__ |
| |
| =head1 NAME |
| |
| makerelease - software package release script |
| |
| =head1 SYNOPSIS |
| |
| makerelease [options] version |
| |
| =head1 ARGUMENTS |
| |
| For full releases, the version should be specified in the form I<5.x.y> |
| |
| For pre-releases, the version should be specified in the form I<5.x.y.preN> |
| |
| For release candidates, the version should be specified in the form I<5.x.y.rcN> |
| |
| Alternatively, it is also possible to specify versions in the form Ext-5-x-y |
| |
| =head1 OPTIONS |
| |
| =over 8 |
| |
| =item B<-r {step}| --regexp-ignore-steps={step}> |
| |
| Ignore steps matching the regular expression {step} |
| |
| =item B<-s {step}| --start-at={step}> |
| |
| Skip all steps preceding the specified (exact) {step} |
| |
| =item B<-i | --interactive> |
| |
| Prompt whether or not to execute each step |
| |
| =item B<-n | --dry-run> |
| |
| Print the commands that would be executed, but do not execute them. |
| |
| =item B<-h | --help> |
| |
| Display this help message. |
| |
| =back |