#!/usr/bin/perl -w
#
#
#	makerpm.pl - A Perl script for building binary distributions
#		     of Perl packages
#
#	This script is Copyright (C) 1999	Jochen Wiedmann
#						Am Eisteich 9
#						72555 Metzingen
#					        Germany
#
#						E-Mail: joe@ispsoft.de
#
#	You may distribute under the terms of either the GNU General
#	Public License or the Artistic License, as specified in the
#	Perl README.
#

use strict;

use Cwd ();
use File::Find ();
use File::Path ();
use File::Spec ();
use Getopt::Long ();

use vars qw($VERSION);
$VERSION = "makerpm 0.1003 22-July-1999, (C) 1999 Jochen Wiedmann";

=pod

=head1 NAME

  makerpm - Build binary distributions of Perl packages


=head1 SYNOPSIS

Create a SPECS file:

  makerpm --specs --source=<package>-<version>.tar.gz

Apply the SPECS file (which in turn uses makerpm.pl):

  rpm -ba <package>-<version>.spec


=head1 DESCRIPTION

The I<makerpm> script is designed for creating binary distributions of
Perl modules, for example RPM packages (Linux) or PPM files (Windows,
running ActivePerl).


=head2 Creating RPM packages

To create a new binary and source RPM, you typically store the tar.gz
file in F</usr/src/redhat/SOURCES> (F</usr/src/packages/SOURCES> in
case of SuSE and F</usr/src/OpenLinux/SOURCES> in case of Caldera) and
do a

  makerpm --specs --source=<package>-<version>.tar.gz

This will create a SPECS file in F</usr/src/redhat/SPECS>
(F</usr/src/packages/SOURCES> in case of SuSE and
F</usr/src/OpenLinux/SOURCES> in case of Caldera) which you
can use with

  rpm -ba /usr/src/redhat/SPECS/<package>-<version>.spec

If the default behaviour is fine for you, that will do. Otherwise see
the list of options below.


=head2 Creating PPM packages

Not yet implemented


=head2 Command Line Options

Possible command line options are:

=over 8

=item --build

Compile the sources, typically by running

	perl Makefile.PL
	make

=item --build-root=<dir>

Installation of the Perl package occurs into a separate directory, the
build root directory. For example, a package DBI 1.07 could be installed
into F</var/tmp/DBI-1.07>. Binaries are installed into F<$build_root/usr/bin>
rather than F</usr/bin>, man pages in F<$build_root/usr/man> and so on.

The idea is making the build process really reproducible and building the
package without destructing an existing installation.

You don't need to supply a build root directory, a default name is
choosen.

=item --copyright=<msg>

Set the packages copyright message. The default is

  GNU General Public or Artistic License, as specified in the Perl README

=item --debug

Turns on debugging mode. Debugging mode prevents most things from really
being done and implies verbose mode.

=item --help

Print the usage message and exit.

=item --install

Install the sources, typically by running

	make install

Installation doesn't occur into the final destination. Instead a
so-called buildroot directory (for example F</var/tmp/build-root>)
is created and installation is adapted relative to that directory.
See the I<--build-root> option for details.

=item --make=<path>

Set path of the I<make> binary; defaults to the location read from Perl's
Config module. L<Config(3)>.

=item --makeopts=<opts>

Set options for running "make" and "make install"; defaults to none.

=item --makemakeropts=<opts>

If you need certain options for running "perl Makefile.PL", this is
your friend. By default no options are set.

=item --mode=<mode>

Set build mode, for example RPM or PPM. By default the build mode
is read from the script name: If you invoke it as I<makerpm>, then
RPM mode is choosen. When running as I<makeppm>, then PPM mode is
enabled.

=item --package-name=<name>

=item --package-version=<version>

Set the package name and version. These options are required for --build and
--install.

=item --prep

Extract the sources and prepare the source directory.

=item --rpm-top-dir=<dir>

=item --rpm-build-dir=<dir>

=item --rpm-source-dir=<dir>

=item --rpm-specs-dir=<dir>

Sets certain directory names related to RPM mode, defaults to
F</usr/src/redhat> (or F</usr/src/packages> on SuSE Linux or
F</usr/src/OpenLinux> on Caldera) F<$topdir/BUILD>, F<$topdir/SOURCES>
and F<$topdir/SPECS>.

=item --rpm-group=<group>

Sets the RPM group; defaults to Development/Languages/Perl.

=item --setup-dir=<dir>

Name of setup directory; defaults to <package>-<version>. The setup
directory is the name of the directory that is created by extracting
the sources. Example: DBI-1.07.

=item --source=<file>

Source file name; used to determine defaults for --package-name and
--package-version. This option is required for --specs and --prep.

=item --summary=<msg>

Summary line; defaults to "The Perl package <name>".

=item --verbose

Turn on verbose mode. Lots of debugging messages are emitted.

=item --version

Print version string and exit.

=back

=cut


package Distribution;


$Distribution::TMP_DIR = '/tmp';
foreach my $dir (qw(/var/tmp /tmp C:/Windows/temp D:/Windows/temp)) {
    if (-d $dir) {
	$Distribution::TMP_DIR = $dir;
	last;
    }
}

$Distribution::COPYRIGHT = "Artistic or GNU General Public License,"
    . " as specified by the Perl README";


sub new {
    my $proto = shift;
    my $self = { @_ };
    bless($self, ref($proto) || $proto);

    if ($self->{'source'}  &&
	$self->{'source'} =~ /(.*(?:\/|\\))?(.*)-(.+)
                              (\.(tar\.gz|tgz|zip))$/x) {
	$self->{'package-name'} ||= $2;
	$self->{'package-version'} ||= $3;
    }

    $self->{'name'} = $self->{'package-name'}
	or die "Missing package name";
    $self->{'version'} = $self->{'package-version'}
	or die "Missing package version";

    $self->{'source_dirs'} ||= [ File::Spec->curdir() ];
    $self->{'default_setup_dir'} = "$self->{'name'}-$self->{'version'}";
    $self->{'setup-dir'} ||= $self->{'default_setup_dir'};
    $self->{'build_dir'} = File::Spec->curdir();
    $self->{'make'} ||= $Config::Config{'make'};
    $self->{'build-root'} ||= File::Spec->catdir($Distribution::TMP_DIR,
						 $self->{'setup-dir'});
    $self->{'copyright'} ||= $Distribution::COPYRIGHT;
    $self->{'summary'} ||= "The Perl package $self->{'name'}";

    $self->{'start_perl'} = $self->{'perl-path'}
	|| substr($Config::Config{'startperl'}, 2);
    $self->{'start_perl'} = undef if $self->{'start_perl'} eq 'undef';

    $self;
}


sub Extract {
    my $self = shift;  my $dir = shift || File::Spec->curdir();
    print "Changing directory to $dir\n" if $self->{'verbose'};
    chdir $dir || die "Failed to chdir to $dir: $!";

    # Look for the source file
    my $source = $self->{'source'} || die "Missing source definition";
    if (! -f $source) {
	foreach my $dir (@{$self->{'source_dirs'}}) {
	    print "Looking for $source in $dir\n" if $self->{'debug'};
	    my $s = File::Spec->catfile($dir, $source);
	    if (-f $s) {
		print "Found $source in $dir\n" if $self->{'debug'};
		$source = $s;
		last;
	    }
	}
    }

    $dir = $self->{'setup-dir'};
    if (-d $dir) {
	print "Removing directory $dir" if $self->{'verbose'};
	File::Path::rmtree($dir, 0, 0) unless $self->{'debug'};
    }

    print "Extracting $source\n" if $self->{'verbose'};
    eval { require Archive::Tar; require Compress::Zlib; };
    if ($@) {
	# Archive::Tar is not available; fallback to tar and gzip
	my $command = "gzip -cd $source | tar xf -";
	my $output = `$command 2>&1`;
	die "Archive::Tar and Compress::Zlib are not available\n"
	    . " and using tar and gzip failed.\n"
	    . " Command was: $command\n"
	    . " Output was: $output\n"
		if $output;
    } else {
	die "Failed to extract archive $source: " . Archive::Tar->error()
	    unless defined(Archive::Tar->extract_archive($source));
    }
}

sub Modes {
    my $self = shift; my $dir = shift || File::Spec->curdir();
    print "Changing directory to $dir\n" if $self->{'verbose'};
    chdir $dir || die "Failed to chdir to $dir: $!";
    my $handler = sub {
	my($dev, $ino, $mode, $nlink, $uid, $gid) = stat;
	my $new_mode = 0444;
	$new_mode |= 0200 if $mode & 0200;
	$new_mode |= 0111 if $mode & 0100;
	chmod $new_mode, $_
	    or die "Failed to change mode of $File::Find::name: $!";
	chown 0, 0, $_
	    or die "Failed to change ownership of $File::Find::name: $!";
    };

    $dir = File::Spec->curdir();
    print "Changing modes in $dir\n" if $self->{'verbose'};
    File::Find::find($handler, $dir);
}

sub Prep {
    my $self = shift;
    my $old_dir = Cwd::cwd();
    eval {
	my $dir = $self->{'build_dir'};
	print "Changing directory to $dir\n" if $self->{'verbose'};
	chdir $dir || die "Failed to chdir to $dir: $!";
	if (-d $self->{'setup-dir'}) {
	    print "Removing directory: $self->{'setup-dir'}\n"
		if $self->{'verbose'};
	    File::Path::rmtree($self->{'setup-dir'}, 0, 0);
	}
	$self->Extract();
	$self->Modes($self->{'setup-dir'});
    };
    my $status = $@;
    print "Changing directory to $old_dir\n" if $self->{'verbose'};
    chdir $old_dir;
    die $@ if $status;
}

sub PerlMakefilePL {
    my $self = shift; my $dir = shift || File::Spec->curdir();
    print "Changing directory to $dir\n" if $self->{'verbose'};
    chdir $dir || die "Failed to chdir to $dir: $!";
    my $command = "$^X Makefile.PL " . ($self->{'makemakeropts'} || '');
    print "Creating Makefile: $command\n" if $self->{'verbose'};
    exit 1 if system $command;
}

sub Make {
    my $self = shift;
    if (my $dir = shift) {
	print "Changing directory to $dir\n" if $self->{'verbose'};
	chdir $dir || die "Failed to chdir to $dir: $!";
    }
    my $command = "$self->{'make'} " . ($self->{'makeopts'} || '');
    print "Running Make: $command\n";
    exit 1 if system $command;

    if ($self->{'runtests'}) {
	$command .= " test";
	print "Running Make Test: $command\n";
	exit 1 if system $command;
    }
}

sub ReadLocations {
    my %vars;
    my $fh = Symbol::gensym();
    open($fh, "<Makefile") || die "Failed to open Makefile: $!";
    while (my $line = <$fh>) {
	# Skip comments and/or empty lines
	next if $line =~ /^\s*\#/ or $line =~ /^\s*$/;
	if ($line =~ /^\s*(\w+)\s*\=\s*(.*)\s*$/) {
	    # Variable definition
	    my $var = $1;
	    my $val = $2;
	    $val =~ s/\$(\w)/defined($vars{$1})?$vars{$1}:''/gse;
	    $val =~ s/\$\((\w+)\)/defined($vars{$1})?$vars{$1}:''/gse;
	    $val =~ s/\$\{(\w+)\}/defined($vars{$1})?$vars{$1}:''/gse;
            $vars{$var} = $val;
	}
    }
    \%vars;
}

sub AdjustPaths {
    my $self = shift; my $build_root = shift;
    my $adjustPathsSub = sub {
	my $f = $_;
	return unless -f $f && ! -z _;
	my $fh = Symbol::gensym();
	open($fh, "+<$f") or die "Failed to open $File::Find::name: $!";
	local $/ = undef;
	my $contents;
	die "Failed to read $File::Find::name: $!"
	    unless defined($contents = <$fh>);
	my $modified;
	if ($self->{'start_perl'}) {
	    $contents =~ s/^\#\!(\S+)/\#\!$self->{'start_perl'}/s;
	    $modified = 1;
	}
	if ($contents =~ s/\Q$build_root\E//gs) {
	    $modified = 1;
	}
	if ($modified) {
	    seek($fh, 0, 0) or die "Failed to seek in $File::Find::name: $!";
	    (print $fh $contents)
		or die "Failed to write $File::Find::name: $!";
	    truncate $fh, length($contents)
		or die "Failed to truncate $File::Find::name: $!";
	}
	close($fh) or die "Failed to close $File::Find::name: $!";
    };
    File::Find::find($adjustPathsSub, $self->{'build-root'});
}


sub MakeInstall {
    my $self = shift;
    if (my $dir = shift) {
	print "Changing directory to $dir\n" if $self->{'verbose'};
	chdir $dir || die "Failed to chdir to $dir: $!";
    }

    my $locations = ReadLocations();

    my $command = "$self->{'make'} " . ($self->{'makeopts'} || '')
	. " install";
    foreach my $key (qw(INSTALLPRIVLIB INSTALLARCHLIB INSTALLSITELIB
                        INSTALLSITEARCH INSTALLBIN INSTALLSCRIPT
			INSTALLMAN1DIR INSTALLMAN3DIR)) {
	my $d = File::Spec->canonpath(File::Spec->catdir($self->{'build-root'},
							 $locations->{$key}));
	$command .= " $key=$d";
    }
    print "Running Make Install: $command\n" if $self->{'verbose'};
    exit 1 if !$self->{'debug'} and system $command;

    print "Adjusting Paths in $self->{'build-root'}\n";
    $self->AdjustPaths($self->{'build-root'});

    my($files, $dirs) = $self->Files($self->{'build-root'});
    my $fileList = '';
    foreach my $dir (sort keys %$dirs) {
	next if $dirs->{$dir};
	$fileList .= "%dir $dir\n";
    }
    foreach my $file (sort keys %$files) {
	$fileList .= "$file\n";
    }

    my($filelist_path, $specs_path) = $self->FileListPath();
    if ($filelist_path) {
	my $fh = Symbol::gensym();
	(open($fh, ">$filelist_path")  and  (print $fh $fileList)
	 and  close($fh))
	    or  die "Failed to create list of files in $filelist_path: $!";
    }
    $specs_path;
}


sub Build {
    my $self = shift;
    my $old_dir = Cwd::cwd();
    eval {
	my $dir = $self->{'build_dir'};
	print "Changing directory to $dir\n" if $self->{'verbose'};
	chdir $dir || die "Failed to chdir to $dir: $!";
	$self->PerlMakefilePL($self->{'setup-dir'});
	$self->Make();
    };
    my $status = $@;
    chdir $old_dir;
    die $@ if $status;
}

sub CleanBuildRoot {
    my $self = shift; my $dir = shift || die "Missing directory name";
    print "Cleaning build root $dir\n" if $self->{'verbose'};
    File::Path::rmtree($dir, 0, 0) unless $self->{'debug'};
}

sub Install {
    my $self = shift;
    my $old_dir = Cwd::cwd();
    my $filelist;
    eval {
	my $dir = $self->{'build_dir'};
	print "Changing directory to $dir\n" if $self->{'verbose'};
	chdir $dir || die "Failed to chdir to $dir: $!";
	$self->CleanBuildRoot($self->{'build-root'});
	$filelist = $self->MakeInstall($self->{'setup-dir'});
    };
    my $status = $@;
    chdir $old_dir;
    die $@ if $status;
    $filelist;
}


package Distribution::RPM;

@Distribution::RPM::ISA = qw(Distribution);

{
    my($source_dir, $build_dir, $specs_dir);
    sub Init {
	my $proto = shift; my $fatal = shift;
	my $topdir;
	if (!$source_dir) {
	    my $rpm_output = `rpm --showrc`;
	    my $rpm_version = `rpm --version`;
	    if ($rpm_version =~ /rpm\s+version\s+2\.+/i) {
		foreach my $ref (['topdir', \$topdir],
				 ['specdir', \$specs_dir],
				 ['sourcedir', \$source_dir],
				 ['builddir', \$build_dir]) {
		    my $var = $ref->[0];
		    if ($rpm_output =~ /^$var\s+\S+\s+(.*)/m) {
			${$ref->[1]} = $1;
		    }
		}
	    } elsif ($rpm_version =~ /rpm\s+version\s+3\.+/i) {
		my $varfunc;
		$varfunc = sub {
		    my $var = shift;
		    my $val;
		    if ($rpm_output =~ /^\S+\s+$var\s+(.*)/m) {
			$val = $1;
			while ($val =~ /\%\{(\S+)\}/) {
			    my $vr = $1;
			    my $vl = &$varfunc($vr);
			    if (defined($vl)) {
				$val =~ s/^\%\{\Q$vr\E\}/$vl/gs;
			    } else {
				return undef;
			    }
			}
			return $val;
		    }
		    return undef;
		};
		foreach my $ref (['_topdir', \$topdir],
				 ['_specdir', \$specs_dir],
				 ['_sourcedir', \$source_dir],
				 ['_builddir', \$build_dir]) {
		    ${$ref->[1]} = &$varfunc($ref->[0]);
		}
	    }
	    die "Cannot handle your RPM version: " . ($rpm_version || "")
		if (!$source_dir or !$specs_dir or !$build_dir) and $fatal;
	}
	if (!$topdir) {
	    foreach my $dir ("redhat", "packages", "OpenLinux") {
		if (-d "/usr/src/$dir") {
		    $topdir = "/usr/src/$dir";
		    last;
		}
	    }
	    die "Unable to determine RPM topdir";
	}
	$source_dir ||= "$topdir/SOURCES";
	$specs_dir ||= "$topdir/SPECS";
	$build_dir ||= "$topdir/BUILD";
	return ($source_dir, $build_dir, $specs_dir);
    }
}

sub new {
    my $proto = shift;
    my $self = $proto->SUPER::new(@_);
    ($self->{'rpm-source-dir'}, $self->{'rpm-build-dir'},
     $self->{'rpm-specs-dir'}) = $proto->Init(1);
    $self->{'rpm-group'} ||= 'Development/Languages/Perl';
    push(@{$self->{'source_dirs'}}, $self->{'rpm-source-dir'});
    $self->{'build_dir'} = $self->{'rpm-build-dir'};
    $self;
}


sub Files {
    my $self = shift;  my $buildRoot = shift;
    my(%files, %dirs);
    my $findSub = sub {
	if (-d $_) {
	    $dirs{$File::Find::name} ||= 0;
	    $dirs{$File::Find::dir} = 1;
	} elsif (-f _) {
	    $files{$File::Find::name} = 1;
	    $dirs{$File::Find::dir} = 1;
	} else {
	    die "Unknown file type: $File::Find::name";
	}
    };
    File::Find::find($findSub, $buildRoot);

    # Remove the trailing buildRoot
    my(%f, %d);
    while (my($key, $val) = each %files) {
	$key =~ s/^\Q$buildRoot\E//;
	$f{$key} = $val
    }
    while (my($key, $val) = each %dirs) {
	$key =~ s/^\Q$buildRoot\E//;
	$d{$key} = $val
    }
    (\%f, \%d, \%files, \%dirs);
}

sub FileListPath {
    my $self = shift;
    my $fl = $self->{'setup-dir'} . ".rpmfilelist";
    ($fl, File::Spec->catdir($self->{'setup-dir'}, $fl));
}

sub Specs {
    my $self = shift;
    my $old_dir = Cwd::cwd();
    eval {
	$self->Prep();
	$self->Build();
	my $filelist = $self->Install();

	my($files, $dirs) = $self->Files($self->{'build-root'});

	my $specs = <<"EOF";
%define packagename $self->{'name'}
%define packageversion $self->{'version'}
%define release 1
EOF
	my $mo = $self->{'makeopts'} || '';
	$mo =~ s/\n\t/ /sg;
        $specs .= sprintf("%%define makeopts \"%s\"\n",
			  ($mo ? sprintf("--makeopts=%s",
					 quotemeta($mo)) : ""));
	my $mmo = $self->{'makemakeropts'} || '';
	$mmo =~ s/\n\t/ /sg;
	$specs .= sprintf("%%define makemakeropts \"%s\"\n",
			  ($mmo ? sprintf("--makemakeropts=%s",
					  quotemeta($mmo)) : ""));
	my $setup_dir = $self->{'setup-dir'} eq $self->{'default_setup_dir'} ?
	    "" : " --setup-dir=$self->{'setup-dir'}";

	my $makerpm_path = File::Spec->catdir('$RPM_SOURCE_DIR', 'makerpm.pl');
	$makerpm_path = File::Spec->canonpath($makerpm_path) . $setup_dir .
	    " --source=$self->{'source'}";
	$specs .= <<"EOF";

Name:      perl-%{packagename}
Version:   %{packageversion}
Release:   %{release}
Group:     $self->{'rpm-group'}
Source:    $self->{'source'}
Source1:   makerpm.pl
Copyright: $self->{'copyright'}
BuildRoot: $self->{'build-root'}
Provides:  perl-%{packagename}
Summary:   $self->{'summary'}
EOF

	if (my $req = $self->{'require'}) {
	    $specs .= "Requires: " . join(" ", @$req) . "\n";
	}

	my $runtests = $self->{'runtests'} ? " --runtests" : "";


	$specs .= <<"EOF";

%description
$self->{'summary'}

%prep
$makerpm_path --prep

%build
$makerpm_path --build$runtests %{makeopts} %{makemakeropts}

%install
rm -rf \$RPM_BUILD_ROOT
$makerpm_path --install %{makeopts}

%clean
rm -rf \$RPM_BUILD_ROOT

%files -f $filelist
EOF

	my $specs_name = "$self->{'name'}-$self->{'version'}.spec";
	my $specs_file = File::Spec->catfile($self->{'rpm-specs-dir'},
					     $specs_name);
	$specs_file = File::Spec->canonpath($specs_file);
	print "Creating SPECS file $specs_file\n";
	print $specs if $self->{'verbose'};
	unless ($self->{'debug'}) {
	    my $fh = Symbol::gensym();
	    open(FILE, ">$specs_file") or die "Failed to open $specs_file: $!";
	    (print FILE $specs) or die "Failed to write to $specs_file: $!";
	    close(FILE) or die "Failed to close $specs_file: $!";
	}
    };
    my $status = $@;
    chdir $old_dir;
    die $@ if $status;
}


package main;

sub Mode {
    return "RPM" if $0 =~ /rpm$/i;
    undef;
}

sub Usage {
    my $mode = Mode() || "undef";
    my $build_root = File::Spec->catdir($Distribution::TMP_DIR,
					"<name>-<version>");
    my $start_perl = substr($Config::Config{'startperl'}, 2);

    my ($rpm_source_dir, $rpm_build_dir, $rpm_specs_dir) =
	Distribution::RPM->Init(1);

    print <<EOF;
Usage: $0 <action> [options]

Possible actions are:

  --prep	Prepare the source directory
  --build	Compile the sources
  --install	Install the compiled sources into the buildroot directory
  --specs	Create a SPECS file by performing the above steps in order
                to determine the list of installed files.

Possible options are:

  --build-root=<dir>		Set build-root directory for installation;
				defaults to $build_root.
  --copyright=<msg>		Set copyright message, defaults to
				"GNU General Public License or Artistic
				License, as specified in the Perl README".
  --debug       		Turn on debugging mode
  --help        		Print this message
  --make=<path>   		Set "make" path; defaults to $Config::Config{'make'}
  --makemakeropts=<opts>	Set options for running "perl Makefile.PL";
                                defaults to none.
  --makeopts=<opts>		Set options for running "make" and "make
                                install"; defaults to none.
  --mode=<mode>			Set build mode, defaults to $mode.
  --package-name=<name>		Set package name.
  --package-version=<name>	Set package version.
  --perl-path=<path>		Perl path to verify in generated scripts;
				defaults to $start_perl
  --require=<package>		Set prerequisite packages. May be used
				multiple times.
  --runtests			By default no "make test" is done. You
				can override this with --runtests.
  --setup-dir=<dir>		Name of setup directory; defaults to
				<name>-<version>
  --source=<file>		Source file name; used to determine defaults
                                for <name> and <version>.
  --summary=<msg>		One line desription of the package; defaults
				to "The Perl package <name>".
  --verbose			Turn on verbose mode.
  --version			Print version string and exit.

Options for RPM mode are:

  --rpm-build-dir=<dir>         RPM build directory; defaults to
      				$rpm_build_dir.
  --rpm-group=<group>           RPM group, default Development/Languages/Perl.
  --rpm-source-dir=<dir>        RPM source directory; defaults to
                                $rpm_source_dir.
  --rpm-specs-dir=<dir>         RPM specs directory; defaults to
                                $rpm_specs_dir.

$VERSION
EOF
    exit 1;
}

{
    my %o;
    Getopt::Long::GetOptions(\%o, 'build', 'build-root=s', 'copyright=s',
			     'debug', 'help', 'install', 'make=s',
			     'makemakeropts=s', 'makeopts=s', 'mode=s',
			     'package-name=s', 'package-version=s',
			     'prep', 'require=s@', 'rpm-base-dir=s',
			     'rpm-build-dir=s', 'rpm-source-dir=s',
			     'rpm-specs-dir=s', 'rpm-group=s',
			     'runtests', 'setup-dir=s', 'source=s', 'specs',
			     'summary=s',
			     'verbose', 'version=s');
    Usage() if $o{'help'};
    if ($o{'version'}) { print "$VERSION\n"; exit 1}
    $o{'verbose'} = 1 if $o{'debug'};

    my $class = 'Distribution::RPM';
    if ($o{'mode'}) {
	if ($o{'mode'} ne 'rpm') {
	    die "Unknown mode: $o{'mode'}";
        }
    }
    my $self = $class->new(%o);

    if ($o{'prep'}) {
	$self->Prep();
    } elsif ($o{'build'}) {
	$self->Build();
    } elsif ($o{'install'}) {
	$self->Install();
    } elsif ($o{'specs'}) {
	$self->Specs();
    } else {
	die "Missing action";
    }
}


__END__

=pod

=head1 INSTALLATION

Before using this script, you need to install the required packages:

  C<File::Spec>

If you are using Perl 5.00502 or later, then this package is already
part of your Perl installation. It is recommended to use the

  C<Archive::Tar>
  C<Compress::Zlib>

packages, if possible.

All of these packages are available on any CPAN mirror, for example

  ftp://ftp.funet.fi/pub/languages/perl/CPAN/modules/by-module

To install a package, fetch the corresponding distribution file, for
example

  Archive/Archive-Tar-0.21.tar.gz

extract it with

  gzip -cd Archive-Tar-0.21.tar.gz

and install it with

  cd Archive-Tar-0.21
  perl Makefile.PL
  make
  make test
  make install

Alternatively you might try automatic installation via the CPAN module:

  cpan		(until Perl 5.00503 you need: perl -MCPAN -e shell)
  install Archive::Tar
  install Compress::Zlib
  install File::Spec  (only with Perl 5.004 or lower)


=head1 AUTHOR AND COPYRIGHT

This script is Copyright (C) 1999

	Jochen Wiedmann
	Am Eisteich 9
	72555 Metzingen
        Germany

	E-Mail: joe@ispsoft.de

You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the Perl README.


=head1 CPAN

This file is available as a CPAN script. The following subsections are
for CPAN's automatic link generation and not for humans. You can safely
ignore them.


=head2 SCRIPT CATEGORIES

UNIX/System_administration


=head2 README

This script can be used to build RPM or PPM packages automatically.


=head2 PREREQUISITES

This script requires the C<File::Spec> package.


=head1 TODO

=over 8

=item -

Handling of prerequisites by reading PREREQ_PM from the Makefile

=item -

Support for installation and deinstallation scripts

=item -

Make package relocatable

=item -

Support for %description.

=back


=head1 CHANGES

1999-12-10  Peter J. Braam <braam@redhat.com>
       * Fixed the $base_dir: correct naming is topdir and compute it
         from the rpm --showrc like the rest

1999-09-13  Jochen Wiedmann <joe@ispsoft.de>

      * Modes: Fixed the use of ||= instead of |=; thanks to Tim Potter,
        Tim Potter <Tim.Potter@anu.edu.au>
      * Now using %files -f <listfile>

1999-07-22  Jochen Wiedmann <joe@ispsoft.de>

      * Now falling back to use of "tar" and "gzip", if Archive::Tar and
        Compress::Zlib are not available.
      * Added --runtests, suggested by Seth Chaiklin <seth@pc126.psy.aau.dk>.

1999-07-09  Jochen Wiedmann <joe@ispsoft.de>

      * Now using 'rpm --showrc' to determine RPM's base dirs.

1999-07-01  Jochen Wiedmann  <joe@ispsoft.de>

      * /usr/src/redhat was used rather than $Distribution::RPM::BASE_DIR.
      * The AdjustPaths function is now handling files zero size files
        properly.
      * An INSTALLATION section was added to the docs that describes
        the installation of prerequisites.
      * A warning for <HANDLE> being possibly "0" is now suppressed with
        Perl 5.004.

1999-05-24  Jochen Wiedmann <joe@ispsoft.de>

	* Added --perl-path and support for fixing startperl in scripts.
	  Some authors don't know how to fix it. :-(

=head1 SEE ALSO

L<ExtUtils::MakeMaker(3)>, L<rpm(1)>, L<ppm(1)>


=cut