# RPM Package Management System -*-perl-*-
# Copyright (C) 1995 Red Hat, Inc
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# -*-perl-*-

require "spec.pl";
require "pack.pl";

sub do_build {
    local ( $mode, *args ) = @_;
    local ( $lsecs, $failed, $specfile, $pname, $script );
    local ( %spec, %pspec, %spec_source, %spec_patch );
    local ( $user, $system, $cuser, $csystem, $lsecs, $secs );
    local ( $hrs, $mins );

    $lsecs = 0;
    $failed = 0;
    foreach $specfile (@args) {
	if (! -f $specfile) {
	    &error("Couldn't find specfile: $specfile");
	}

	# Reset rpm{"root"} each time
	if ($rpm{"rootspec"}) {
	    $rpm{"root"} = $rpm{"rootspec"};
	} else {
	    undef $rpm{"root"};
	}

	# Parse spec file and build
	&normal("Parsing spec file");
	$failed |= &parse_spec_file($specfile, *spec, *pspec, *spec_source, *spec_patch, 1);
	last if ($failed);
	$pname = "$spec{'name'}-$spec{'version'}-$spec{'release'}";
	if ($mode & ($DO_PREP | $DO_BUILD | $DO_INSTALL)) {
	    $script = &tempname();
	    &debug("Writing build script: $script");
	    &write_build_script($script, $mode, *spec, *pspec, *spec_source, *spec_patch);
	    if (! $rpm{"test"}) {
		&normal("Building $pname");
		&debug("Executing build script: $script");
		$failed |= system($script);
		last if ($failed);
	    }
	}

	# Can be used for testing rpm
	# &pack_info(*spec, *pspec);
    
	# File list check
	if ($mode & $DO_LIST_CHECK) {
	    &normal("Doing list check");
	    $failed |= &list_check(*spec, *pspec);
	    last if ($failed);
	}

	# Binary package
	if ($mode & $DO_BINARY) {
	    $script = &tempname();
	    if ($rpm{'root'}) {
		&normal("Binary packaging (root = $rpm{'root'})");
	    } else {
		&normal("Binary packaging");
	    }
	    &write_pack_bin_script($script, *spec, *pspec);
	    if (! $rpm{"test"}) {
		&debug("Executing binary pack script: $script");
		$failed |= system($script);
		last if ($failed);
	    }
	}
    
	# Source package
	if ($mode & $DO_SOURCE) {
	    $script = &tempname();
	    &normal("Source packaging");
	    &write_pack_src_script($script, $specfile, *spec, *spec_source, *spec_patch);
	    if (! $rpm{"test"}) {
		&debug("Executing source pack script: $script");
		$failed |= system($script);
		last if ($failed);
	    }
	}

	# Clean stage
	if ($pspec{"clean"} && ($mode & $DO_CLEAN)) {
	    $script = &tempname();
	    open(OF, ">$script");
	    print OF "#!/bin/sh -e\n";
	    print OF "# Clean script generated by rpm $rpm{'version'} on $date\n";
	    print OF "# package name: $spec{'name'}\n";
	    print OF "#      version: $spec{'version'}\n";
	    print OF "#      release: $spec{'release'}\n";
	    if (&isverbose) {
		print OF "\nset -x\n\n";
	    } else {
		printf OF "exec > /dev/null\n";
	    }
	    if (! &isquiet) {
		print OF "echo \"\tclean stage...\" >&2\n";
	    }
	    print OF "cd $rpm{'builddir'}\n";
	    print OF $pspec{"clean"};
	    print OF "exit 0\n";
	    close(OF);
	    chmod 0755, $script;

	    if (! $rpm{"test"}) {
		&debug("Executing clean script: $script");
		$failed |= system($script);
		last if ($failed);
	    }
	}

	($user,$system,$cuser,$csystem) = times;
	$secs = $user + $system + $cuser + $csystem - $lsecs;
	$lsecs = $secs;
	$hrs = int($secs / 3600);
	$mins = int($secs / 60) - ($hrs * 60);
	$secs %= 60;
	&normal(sprintf("Time to build $pname: %02d:%02d:%02d", $hrs, $mins, $secs));
    }

    ($user,$system,$cuser,$csystem) = times;
    $secs = $user + $system + $cuser + $csystem;
    $hrs = int($secs / 3600);
    $mins = int($secs / 60) - ($hrs * 60);
    $secs %= 60;
    &normal(sprintf("\nTotal build time: %02d:%02d:%02d", $hrs, $mins, $secs));
    if ($failed) {
	&error("Build failed!");
    }
}

# Expand %setup macro
#    -n name ........... Set the name of the build directory (BUILDDIR) to
#                        name.  This is used by --clean.  The default is
#                        $NAME-$VERSION.  Other possibilities include $NAME,
#                        ${NAME}${VERSION}, or whatever else the main tar
#                        file uses.
#    -c ................ Create and cd to the named directory before
#                        doing the untar.
#    -b # .............. Untar Source# _before_ cd'ing into the directory.
#                        This doesn't make sense with -c.
#    -a # .............. Untar Source# _after_ cd'ing into the directory.
#    -T ................ The default action is to untar "Source" (i.e.,
#                        Source0), either before the cd (without -c), or
#                        after the cd (with -c).  This flag inhibits this
#                        default action and will require a "-b 0" or "-a 0"
#                        to get the main source file untar'ed.  NOTE:
#                        unless -D is specified the build directory will
#                        still be REMOVED.
#    -D ................ Do _not_ delete the directory before unpacking.
#                        This is only useful if there are more than one
#                        %setup command and this is _not_ the first of those
#                        command.  Of course, multiple %setup commands should
#                        be very, very uncommon.

sub setup_macro {
    local ( *spec_source, $_ ) = @_;
    local ( $res ) = "";
    local ( $bd ) = $rpm{"builddir"};

    &debug("ENTERING: setup_macro()");

    $res .= "\n### Setup macro: $_\n";

#    chop;

    $lopt_a = $lopt_b = $lopt_c = "";
    $lopt_n = $spec_buildsubdir;
    $lopt_D = $lopt_T = "";
    @_ = split;
    shift;
    &rpm_getopts("n:cb:a:TD", @_);
    $spec_buildsubdir = $lopt_n;

    # CD to the build directory
    $res .= "cd $bd\n";

    # Delete any sources left over from a previous build
    if (! $lopt_D) {
	$res .= "rm -rf $spec_buildsubdir\n";
    }

    # If necessary, create and CD into the proper directory
    if ($lopt_c) {
	$res .= "mkdir -p $spec_buildsubdir\n";
	$res .= "cd $spec_buildsubdir\n";
    }

    # Do the default action
    if ((! $lopt_c) && (! $lopt_T)) {
	$res .= &do_untar($spec_source{"0"});
    }
    # Do any before action
    if ($lopt_b ne "") {
	$res .= &do_untar($spec_source{$lopt_b});
    }
    # cd into the build subdirectory
    if (!$lopt_c) {
	$res .= "cd $spec_buildsubdir\n";
    }
    if ($lopt_c && (! $lopt_T)) {
	$res .= &do_untar($spec_source{"0"});
    }
    # Do any after action
    if ($lopt_a ne "") {
	$res .= &do_untar($spec_source{$lopt_a});
    }

    # Clean up permissions and owners
    $res .= "cd $bd/$spec_buildsubdir\n";
    $res .= "chown -R root.root .\n";
    $res .= "chmod -R a+rX,g-w,o-w .\n";
    $res .= "### end %setup macro\n";

    &debug("EXITING: setup_macro()");
    return $res;
}

# Expand %patch macro
#    # ................. Apply Patch# as a patch.
#    -p # .............. Specifies the number of directories to strip for the
#                        patch(1) command.  This is usually 0 (the default)
#                        or 1, but could be larger for brain-dead diff files.
#    -P ................ The default action is to apply "Patch" (i.e.,
#                        Patch0).  This flag inhibits this default action and
#                        will require a "0" to get the main source file
#                        untar'ed.  This option would be useful for a second
#                        %patch macro that required a different "-p #" from
#                        the first macro.
#
# We also handle "%patchX" which is shorthand for "%patch X -P"

sub patch_macro {
    local ( *spec_patch, $_ ) = @_;
    local ( $patch_num, $strip, @rest, $p );
    local ( $res ) = "";

    &debug("ENTERING: patch_macro()");
    $res .= "\n### Patch macro: $_\n";

#    chop;

    s/%patch([0-9]+)/%patch $1 -P/;
    $res .= "### Expanded to: $_\n";

    $patch_num = $strip = 0;
    $lopt_p = $lopt_P = "";
    @_ = split;
    shift;
    @rest = &rpm_getopts("p:P", @_);
    $strip = $lopt_p if ($lopt_p ne "");

    if (! $lopt_P) {
	$res .= &do_patch($spec_patch{"0"}, $strip);
    }
    
    foreach $p (@rest) {
	$res .= &do_patch($spec_patch{$p}, $strip);
    }

    $res .= "### end %patch macro\n";

    &debug("EXITING: patch_macro()");
    return $res;
}

sub write_build_script {
    local ( $f, $mode, *spec, *pspec, *spec_source, *spec_patch ) = @_;
    local ( $date ) = `date`;

    &debug("ENTERING: write_build_script()");

    open (OF, ">$f");

    # The -e causes the shell to immediately exit on error
    print OF "#!/bin/sh -e\n";
    print OF "# Build script generated by rpm $rpm{'version'} on $date\n";
    print OF "# package name: $spec{'name'}\n";
    print OF "#      version: $spec{'version'}\n";
    print OF "#      release: $spec{'release'}\n";

    foreach $i (sort by_number keys %spec_source) {
	print OF "#     source $i: $spec_source{$i}\n";
    }

    foreach $i (sort by_number keys %spec_patch) {
	print OF "#      patch $i: $spec_patch{$i}\n";
    }

    print OF "\n# rpm options specified: $rpm{'options'}\n";

    if (&isverbose) {
	print OF "\nset -x\n\n";
    } else {
	printf OF "exec > /dev/null\n";
    }

    # set some environment variables
    print OF "RPM_SOURCE_DIR=$rpm{'sourcedir'}\n";
    print OF "RPM_BUILD_DIR=$rpm{'builddir'}\n";
    print OF "RPM_DOC_DIR=$rpm{'docdir'}\n";

    if ($mode & $DO_PREP) {
	print OF "################### %prep section\n\n";
	if (! &isquiet) {
	    print OF "echo \"\tprep...\" >&2\n";
	}
	print OF $pspec{"prep"};
    }
    if ($mode & $DO_BUILD) {
	print OF "################### %build section\n\n";
	if (! &isquiet) {
	    print OF "echo \"\tbuild...\" >&2\n";
	}
	print OF "cd $rpm{'builddir'}/$spec_buildsubdir\n";
	print OF $pspec{"build"};
    }
    if ($mode & $DO_INSTALL) {
	print OF "################### %install section\n\n";
	if (! &isquiet) {
	    print OF "echo \"\tinstall...\" >&2\n";
	}
	print OF "cd $rpm{'builddir'}/$spec_buildsubdir\n";
	print OF $pspec{"install"};
    }

    if ($rpm{"clean"}) {
        print OF "\n################# Clean up\n";
        if (! &isquiet) {
            print OF "echo \"\tclean...\" >&2\n";
        }
        print OF "cd $rpm{'builddir'}\n";
        print OF "rm -rf $spec_buildsubdir\n\n";
    }

    print OF "\n################# Normal exit\n";
    print OF "exit 0;\n";

    close OF;
    chmod 0755, $f;

    &debug("EXITING: patch_macro()");
}

sub do_untar {
    local ( $tarfile ) = @_;
    local ( $sfd ) = $rpm{"sourcedir"};
    local ( $taropts );

    &debug("ENTERING: do_untar($tarfile)");
    if (! -f "$sfd/$tarfile") {
	&error("missing source file: $tarfile");
    }

    if (&isverbose) {
	$taropts = "-xvvf";
    } else {
	$taropts = "-xf";
    }
    if (&is_compressed("$sfd/$tarfile")) {
	&debug("EXITING: do_untar()");
	return "gzip -dc $sfd/$tarfile | tar $taropts -
if [ \$? -ne 0 ]; then
  exit $?
fi\n";
    } else {
	&debug("EXITING: do_untar()");
	return "tar $taropts $sfd/$tarfile\n";
    }
}

sub do_patch {
    local ( $patchfile, $strip ) = @_;
    local ( $sfd ) = $rpm{"sourcedir"};

    &debug("ENTERING: do_patch()");

    if (! -f "$sfd/$patchfile") {
	&error("missing patch file: $patchfile");
    }

    if (&is_compressed("$sfd/$patchfile")) {
	&debug("EXITING: do_patch()");
	return "gzip -dc $sfd/$patchfile | patch -p$strip -s
if [ \$? -ne 0 ]; then
  exit $?
fi\n";
    } else {
	&debug("EXITING: do_patch()");
	return "patch -p$strip -s < $sfd/$patchfile\n";
    }
}

1;
