#!/usr/bin/perl -w
#
# Do-pkg - convert a binary distribution into a Mac OS X PKG and put it
# inside a Disk Image (.dmg). Additionally, add a separate package,
# including the required Startup Item to automatically start MySQL on
# bootup.
#
# The script currently assumes the following environment (which should exist
# like that, if the Do-compile script was used to build the binary
# distribution)
#
# - there must be a binary distribution (*.tar.gz) in the directory
#   `hostname` of the current directory
# - the extracted and compiled source tree should be located in the
#   `hostname` directory, too
#
# Use the "--help" option for more info!
#
# written by Lenz Grimmer <lenz@mysql.com>
#

use Cwd;
use File::Basename;
use File::Copy;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
use Sys::Hostname;

$opt_dry_run= undef;
$opt_help= undef;
$opt_log= undef;
$opt_mail= "";
$opt_skip_dmg= undef;
$opt_skip_prefpane= undef;
$opt_skip_si= undef;
$opt_suffix= undef;
$opt_verbose= undef;
$opt_version= undef;

GetOptions(
	"dry-run",
	"help|h",
	"log|l:s",
	"mail|m=s",
	"skip-prefpane|p",
	"skip-dmg|skip-disk-image|s",
	"skip-si|skip-startup-item",
	"suffix=s",
	"verbose|v",
	"version=s",
) || &print_help;

# Include helper functions
$PWD= cwd();
$LOGGER= "$PWD/logger.pm";
if (-f "$LOGGER")
{
	do "$LOGGER";
}
else
{
	die "ERROR: $LOGGER cannot be found!\n";
}

$PM= "/Developer/Applications/PackageMaker.app/Contents/MacOS/PackageMaker";
# Try another location on 10.3.3
unless (-e "$PM")
{
	$PM= "/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker";
}

$TMP= $ENV{TMPDIR};
$TMP eq "" ?  $TMP= $TMP . "/PKGBUILD.$$": $TMP= "/tmp/PKGBUILD.$$";
$PKGROOT= "$TMP/PMROOT";
$PKGDEST= "$TMP/PKG";
$RESOURCE_DIR= "$TMP/Resources";
$SUFFIX= $opt_suffix;
$VERSION= $opt_version;
($MAJOR, $MINOR, $RELEASE)= split(/\./, $VERSION);
$NAME= "mysql$SUFFIX-$VERSION";
$HOST= hostname();
$ID= getpwuid($>);
$HOST=~ /^([^.-]*)/;
$HOST= $1;
$LOGFILE= "$PWD/Logs/$HOST-$MAJOR.$MINOR$SUFFIX.log";
$BUILDDIR= "$PWD/$HOST";
$PREFPANE= "$PWD/mysql-administrator/source/mac/PreferencePane/build/MySQL.prefPane";
$SRCBASEDIR= <$BUILDDIR/mysql*-$VERSION>;
$SUPFILEDIR= <$SRCBASEDIR/support-files/MacOSX>;
$TAR= <$BUILDDIR/$NAME-apple-darwin*-powerpc.tar.gz>;
$TAR =~ /.*\/$NAME(.*)\.tar\.gz$/;
$ARCH= $1;
$NAME= $NAME . $ARCH;
$INFO= <$SUPFILEDIR/Info.plist>;
$DESC= <$SUPFILEDIR/Description.plist>;
$SI_INFO= <$SUPFILEDIR/StartupItem.Info.plist>;
$SI_DESC= <$SUPFILEDIR/StartupItem.Description.plist>;
$SI_PARAMS= <$SUPFILEDIR/StartupParameters.plist>;
$SI_POST= <$SUPFILEDIR/StartupItem.postinstall>;
$SI_NAME= "MySQLStartupItem";
$SI_DIR_NAME= "MySQLCOM";
$SI_SCRIPT= <$SUPFILEDIR/MySQLCOM>;
@RESOURCES= qw/ ReadMe.txt postinstall preinstall /;
@LICENSES= ("$SRCBASEDIR/COPYING","$SRCBASEDIR/MySQLEULA.txt");

&print_help("") if ($opt_help || !$opt_suffix || !$opt_version);

#
# Override predefined Log file name
#
if (defined $opt_log)
{
	if ($opt_log ne "")
	{
		if ($opt_log =~ /^\/.*/)
		{
			$LOGFILE= $opt_log;
		}
		else
		{
			$LOGFILE= $PWD . "/" . $opt_log;
		}
	}
}

# Creating the UFS disk image requires root privileges
die("You must be root to run this script!") if ($ID ne "root" && !$opt_dry_run);

@files= ($TAR, $INFO, $DESC);
@files= (@files, $SI_INFO, $SI_DESC, $SI_POST, $SI_SCRIPT) unless $opt_skip_si;
foreach $file (@files)
{
	&abort("Unable to find $file!") unless (-f "$file");
}

# Remove old temporary build directories first
&logger("Cleaning up temporary build directories");
&run_command("rm -rf $TMP", "Could not clean up $TMP!");
&logger("Creating temp directories");
foreach $dir ($TMP, $PKGROOT, $PKGDEST, $RESOURCE_DIR)
{
	if (!-d $dir)
	{
		&logger("Creating directory $dir!");
		unless($opt_dry_run)
		{
			mkdir($dir) or &abort("Could not make directory $dir!");
		}
	}
}

foreach $resfile (@RESOURCES)
{
	&logger("Copying $SUPFILEDIR/$resfile to $RESOURCE_DIR");
	unless($opt_dry_run)
	{
		copy("$SUPFILEDIR/$resfile", "$RESOURCE_DIR") or
		&abort("Error while copying $SUPFILEDIR/$resfile to $RESOURCE_DIR");
	}
}

# Search for license file
foreach $license (@LICENSES)
{
	if (-f "$license")
	{
		&logger("Copy $license to $RESOURCE_DIR/License.txt");
		unless($opt_dry_run)
		{
			copy("$license", "$RESOURCE_DIR/License.txt") or
			&abort("Error while copying $license to $RESOURCE_DIR");
		}
	}
}

&abort("Could not find a license file!")
unless (-f "$RESOURCE_DIR/License.txt");

# Extract the binary tarball and create the "mysql" symlink
&logger("Extracting $TAR to $PKGROOT");
&run_command("gnutar zxf $TAR -C $PKGROOT", "Unable to extract $TAR!");
&run_command("cd $PKGROOT ; ln -s mysql* ./mysql", "Unable to create symlink!");
&run_command("chown -R root:wheel $PKGROOT/*", "Cannot chown $PKGROOT!");

# Now build the PGK using PackageMaker
# The "|| true" is a nasty hack to work around a problem with Package Maker
# returning a non-zero value, even though the package was created correctly
&logger("Running PackageMaker");
$command= "$PM -build -p $PKGDEST/$NAME.pkg -f $PKGROOT -r $RESOURCE_DIR -i $INFO -d $DESC || true";
&run_command($command, "Error while building package $NAME.pkg!");

#
# Build the Startup Item PKG
#
unless ($opt_skip_si)
{
	&logger("Cleaning up $PKGROOT");
	&run_command("rm -rf $PKGROOT/*", "Unable to clean up $PKGROOT!");
	&logger("Cleaning up $RESOURCE_DIR");
	&run_command("rm -rf $RESOURCE_DIR/*", "Unable to clean up $RESOURCE_DIR!");

	my $SI_DIR= $PKGROOT . "/" . $SI_DIR_NAME;
	&logger("Installing MySQL StartupItem files into $SI_DIR");
	unless($opt_dry_run)
	{
		mkdir("$SI_DIR")
			or &abort("Error creating $SI_DIR");
		copy("$SI_SCRIPT", "$SI_DIR/")
			or &abort("Error copying $SI_SCRIPT!");
		chmod(0755, "$SI_DIR/" . basename("$SI_SCRIPT"));
		copy("$SI_PARAMS", "$SI_DIR/")
			or &abort("Error copying $SI_PARAMS!");
		chmod(0644, "$SI_DIR/" . basename("$SI_PARAMS"));
		&run_command("chown -R root:wheel $PKGROOT/*", "Cannot chown $PKGROOT!");
		copy("$SI_POST", "$RESOURCE_DIR/postinstall")
			or &abort("Error copying $SI_POST!");
		chmod(0644, "$RESOURCE_DIR/postinstall");
	}

	&logger("Building $SI_NAME.pkg using PackageMaker");
	$command= "$PM -build -p $PKGDEST/$SI_NAME.pkg -f $PKGROOT -r $RESOURCE_DIR -i $SI_INFO -d $SI_DESC || true";
	&run_command($command, "Error while building package $SI_NAME.pkg!");
}

#
# Include the MySQL Preference Pane
#
unless ($opt_skip_prefpane)
{
	&abort("Could not find PrefPane helper application. Did you compile and install it?")
		unless (-f "$PREFPANE/Contents/Resources/mahelper");
	&logger("Including $PREFPANE in $PKGDEST");
	&run_command("mkdir $PKGDEST/MySQL.prefPane", "Could not create $PKGDEST/MySQL.prefPane!");
	&run_command("ditto $PREFPANE $PKGDEST/MySQL.prefPane", "Could not copy $PREFPANE into $PKGDEST!");
	&run_command("chown -R root:wheel $PKGDEST/MySQL.prefPane", "Cannot chown $PKGDEST/MySQL.prefPane!");
}

if ($opt_skip_dmg)
{
	&logger("SUCCESS: Package $PKGDEST/$NAME.pkg created");
	exit 0;
}

# Determine the size of the Disk image to be created and add a 5% safety
# margin for filesystem overhead
&logger("Determining required disk image size for $PKGDEST");
unless($opt_dry_run)
{
	chomp($_= `du -sk $PKGDEST`);
	@size= split();
	$size= int($size[0]+($size[0]*0.05));
	&logger("Disk image size: $size KB");
}

unless($opt_dry_run)
{
	&abort("Zero bytes? Something is wrong here!") if ($size == 0);
}

# Now create and mount the disk image
$TMPNAME= $NAME . ".tmp";
&logger("Creating temporary Disk image $TMPNAME.dmg");
$command= "hdiutil create $TMPNAME -size ${size}k -ov -fs UFS -volname $NAME";
&run_command($command, "Unable to create disk image $TMPNAME.dmg!");
&logger("Attaching Disk image $TMPNAME.dmg");
&run_command("hdid $TMPNAME.dmg", "Unable to attach $TMPNAME.dmg!");

# Install the PKG into the .dmg
chomp($mountpoint=`mount | grep "\/Volumes\/$NAME" | cut -f3 -d" "`) if (!$opt_dry_run);
&logger("Copying $PKGDEST/$NAME.pkg to Disk image /Volumes/$NAME");
&run_command("ditto $PKGDEST /Volumes/$NAME", "Could not copy $PKGDEST to /Volumes/$NAME!");
&run_command("ditto $SUPFILEDIR/ReadMe.txt /Volumes/$NAME", "Could not copy $SPFILEDIR/ReadMe.txt to /Volumes/$NAME!");
&run_command("chown root:wheel /Volumes/$NAME/ReadMe.txt", "Could not fix ownerships of /Volumes/$NAME/ReadMe.txt!");
chomp($mountpoint=`mount | grep "\/Volumes\/$NAME" | cut -f1 -d" "`) if (!$opt_dry_run);
&abort("/Volumes/$NAME not attached!") if (!$mountpoint && !$opt_dry_run);
&logger("Unmounting $mountpoint");
&run_command("hdiutil detach $mountpoint", "Unable to detach $mountpoint");
&run_command("rm -f $NAME.dmg", "Unable to remove $NAME.dmg!") if (-f "$NAME.dmg");
&logger("Compressing disk image");
$command= "hdiutil convert $TMPNAME.dmg -format UDZO -imagekey zlib-level=9 -o $NAME.dmg";
&run_command($command, "Unable to compress disk image!");

# Final cleanups
&logger("Removing $TMPNAME.dmg");
&run_command("rm -f $TMPNAME.dmg", "Unable to remove $TMPNAME.dmg!");
&logger("Removing $TMP");
&run_command("rm -rf $TMP", "Unable to remove $TMP!");

&logger("SUCCESS: $NAME.dmg created.") if (!$opt_dry_run);
exit 0;

sub print_help
{
	my $message= $_[0];
	if ($message ne "")
	{
		print "\n";
		print "ERROR: $message\n";
	}
	print <<EOF;

Usage: Do-pkg <options> --suffix=<suffix> --version=<version>

Creates a Mac OS X installation package (PKG) and stores it inside
a Disk Image (.dmg) file. You need to create a binary distribution
tarball with scripts/make_binary_distribution first!

NOTE: You need to run this script with root privileges (required
      to create the disk image)

Options:

    --dry-run                        Dry run without executing
-h, --help                           Print this help
-l, --log[=<filename>]               Write a log file [to <filename>]
                                     (default is "$LOGFILE")
-m, --mail=<address>                 Mail a failure report to the given
                                     address (and include a log file snippet,
                                     if logging is enabled)
                                     Note that the \@-Sign needs to be quoted!
                                     Example: --mail=user\\\@domain.com
-p, --skip-prefpane                  Skip including the PreferencePane
-s, --skip-disk-image, --skip-dmg    Just build the PKGs, don't put it into a
                                     disk image afterwards
    --skip-startup-item, --skip-si   Skip the creation of the StartupItem PKG
    --suffix=<suffix>                The package suffix
                                     (e.g. "-standard" or "-pro)
    --version=<version>              The MySQL version number
                                     (e.g. 4.0.11-gamma)
-v, --verbose                        Verbose execution

EOF
	exit 1;
}