#!/usr/bin/perl -w

# make-tarball.pl Copyright 1999 Mike Hardy, licensed under GPL
# please see the file COPYING for details on licensing

# This is a short script to make an "official" cdr tarball

# This script relies on a few things.

# 1) The file containing the version string is:
#         <cdr>/cdr.pl
# 2) The tag to use in the source is:
#         <module>_<major>_<minor>_<patch>[_<text>]
# 3) The directory the source should be packaged from is:
#         <module>-<major>.<minor>.<patch>[-<text>]
# 4) The version to put into the version file in CVS when done is:
#         <major>.<minor>.<patch+1>-cvs unless there was [-<text>],
#         then just <major>.<minor>.<patch>-cvs
# 5) It expects the version file's version line to look like this:
#         $VERSION = "<version>";
# 6) It expects that you have CVS all set up in the shell you're using.
#         This includes all of the password stuff...

######################################################################


# Get all the arguments from the command-line
&GetArguments();

# Make sure they are sane
&CheckArguments();

# Set all of the version strings we're going to need for tags, source, etc
&SetVersionStrings();

# Check out the source we're going to release
&CheckOutTag($Options{"branch"}, $DirectoryName);

# Update the version file with the release version
&UpdateVersionFile($DirectoryName, $SourceVersionString);

# Tag the source in the release directory with the correct versioned tag
&TagSource($DirectoryName, $TagVersionString);

# Clean up all the CVS directories so the package is clean
&CleanCVSDirectories($DirectoryName);

# If we have a lower patch-level on this tree, make a diff
if($MakeDiff) {

    # Check out the next-lowest-patch-level
    &CheckOutTag($OldTagVersionString, $OldDirectoryName);

    # Clean all the CVS directories out of it
    &CleanCVSDirectories($OldDirectoryName);

    # Make a diff of the two cleaned releasable directories now
    &MakeDiff($OldDirectoryName, $DirectoryName);

    # Clean the directory out
    &DeleteDirectory($OldDirectoryName);

}

# Make the tarball now
&MakeTarball($DirectoryName);

# Clean all the old directories out
&DeleteDirectory($DirectoryName);

# Check the new source out again so we can change the string post-tarball
&CheckOutTag($Options{"branch"}, $DirectoryName);
&UpdateVersionFile($DirectoryName, $NewSourceVersionString);

# Clean this directory up now
&DeleteDirectory($DirectoryName);

# Should be all done
exit;


################################################################
#
#  There's no algorithmic logic below here, just implementation
#
################################################################

# Delete the directory given as an argument
sub DeleteDirectory() {
    my $directory = shift;
    print "Deleting directory $directory...\n";
    system("rm -fr $directory");
}


# tar and gzip the directory given as an argument
sub MakeTarball() {
    my $release_directory = shift;
    
    print "Making tarball...\n";
    $tarball_name = $release_directory . ".tar.gz";
    system("rm -f $tarball_name") if(-e $tarball_name);
    system("tar -zcf $tarball_name $release_directory");
}


#Label all of the source here with the new label given as an argument
sub TagSource() {
    my $directory = shift;
    my $tag = shift;
    print "Tagging source in $directory with tag $tag...\n";
    system("cd $directory;cvs tag -F $tag > /dev/null 2>&1");
}


# Make a diff of the two directories given as arguments
sub MakeDiff() {
    my $old_source = shift;
    my $new_source = shift;
    my $patch_name = "patch-" . $Options{"module"} . "-" . $OldSourceVersionString . "-" . $SourceVersionString;
    print "Making diff between $old_source and $new_source...\n";
    system("diff -uNr $old_source $new_source > $patch_name");
    system("gzip -9 $patch_name");
}


# Change the version file for the module in the directory specified to the version specified
sub UpdateVersionFile() {
    my $directory = shift;
    my $version_string = shift;
    my $module = $Options{"module"};
    print "Updating version file for $module...\n";

    # construct the filenames
    my $filename_only = "cdr.pl";
    my $filename = "$directory/" . $filename_only;
    my $newfilename = $filename . ".new";

    open OLDFILE, "<$filename";
    open NEWFILE, ">$newfilename";
    while(<OLDFILE>) {
	if(/\$VERSION \=/) {
	    print NEWFILE "\$VERSION = \"$version_string\";\n";
	} else {
	    print NEWFILE;
	}
    }
    close OLDFILE;
    close NEWFILE;

    system("mv -f $newfilename $filename");
    system("cd $directory/; cvs commit -f -m \"Tarball script: building new release - $version_string\" $filename_only > /dev/null 2>&1");
}
    
# work through the source directory given, cleaning things up, removing CVS directories
sub CleanCVSDirectories() {
    my $directory = shift;
    print "Cleaning source tree...\n";
    foreach $DIRECTORY (`find $directory | grep "CVS" | grep -v "CVS/"`) {
	system("rm -rf $DIRECTORY");
    }
    foreach $FILE (`find . -name ".cvsignore"`) {
	system("rm -f $FILE");
    }
}


# Check out the tag we've been given to work with and move it to the directory name given
sub CheckOutTag() {
    my $mod_version = shift;
    my $directory = shift;
    my $module = $Options{"module"};

    # Use CVS to check the source out
    print "Checking out tag $mod_version for $module...\n";
    
    if($mod_version eq "HEAD") {
	system("cvs co $module-src > /dev/null 2>&1");
    } else {
	system("cvs co -r$mod_version $module-src > /dev/null 2>&1");
    }
    
    # Move the source into the directory specified
    print "Moving $module to $directory\n";
    system("rm -rf $directory") if (-e $directory);
    system("mv $module $directory");
}



# Set the version strings to use given the arguments
sub SetVersionStrings() {
    
    ($major, $minor, $patch_plus) = split /\./, $Options{"version"};
    if($patch_plus =~ /(\d+)\-(.*)/) {
	$patch = $1;
	$plus = $2;
    } else {
	$patch = $patch_plus;
    }

    # set the string to insert into the source version file
    $SourceVersionString = "$major.$minor.$patch_plus";

    # set the string to be used for the directory to package from
    $DirectoryName = $Options{"module"} . "-$SourceVersionString";

    # set the string to use as the tag name in CVS
    $TagVersionString = uc($Options{"module"}) . "_" . 
			$major . "_" . $minor . "_" .
$patch;
    if(defined($plus)) { $TagVersionString .= "_" . $plus; }

    # set the name of the string to get the old CVS version out (if possible)
    $oldpatch = $patch - 1;
    if($oldpatch >= 0) {
	$OldTagVersionString = uc($Options{"module"}) . "_" . 
				$major . "_" . $minor . "_" . $oldpatch;
	$OldSourceVersionString = "$major.$minor.$oldpatch";
	$OldDirectoryName = $Options{"module"} . "-$major.$minor.$oldpatch";
	$MakeDiff = 1;
    } else {
	$MakeDiff = 0;
    }

    # set the name of the string to put into the source version file when done
    if(!defined($plus)) { $patch += 1; }
    $NewSourceVersionString = "$major.$minor.$patch-cvs";
}



# Get all of the command-line arguments from the user
sub GetArguments() {
    
    # Parse the command-line arguments
    foreach $Argument (@ARGV) {
	
	# Check to see if they tell us the version of the tarball to make
	if($Argument =~ /--version=(.*)/) {
	    $Options{"version"}= $1;
	    
	    # Check to see if they tell us which branch to work with
	} elsif($Argument =~ /--branch=(.*)/) {
	    $Options{"branch"}= $1
		
		# We have no idea what this is
	    } else {
		&print_usage("You have used unknown arguments.");
		exit;
	    }
    }

    $Options{"module"} = "cdr";
}

# Check the command-line arguments and set some internal defaults
sub CheckArguments() {

    # Let's make sure that there is a valid version string in here...
    if($Options{"version"} !~ /\d+\.\d+\.\d+.*/) {
	&print_usage("Incorrect version string.");
	exit;
    }
    
    
    # If there is no branch defined, we're using the tip revisions.
    # These releases are always developmental, and should use the HEAD "branch" name.
    if(!defined($Options{"branch"})) {
	$Options{"branch"} = "HEAD";
    }
}


# Show people how to use the damned thing
sub print_usage() {
    my $message = shift;
    
    print "\n***  ERROR: $message  ***\n";
    
   print <<"USAGE"

make-tarball.pl: official release generator.

   This script takes as arguments the version of the release, and the branch:
       
      ./make-tarball.pl --version=xx.yy.zz[-<string>] [--branch=<branchname>]

   If you omit the branch, it will implicitly work with the tip, using the DEVEL label.
	  
   Some examples would be:

	  To make a new tarball of an old development release:
      ./make-tarball.pl --version=1.1.3

	  To make a new stable release:
      ./make-tarball.pl --branch=STABLE --version=2.0.10

	  To make a new development release:
      ./make-tarball.pl --version=0.9.20
          
USAGE
}
