#!/usr/bin/perl -w
use strict;

#
# Use libraries
use Getopt::Long;
use File::Basename;
use File::Copy;
use Archive::Tar;

use RPM::Specfile;

use vars qw/$VERSION/;
$VERSION = $RPM::Specfile::VERSION;

#
# Setup some defaults
my %defaults;
$defaults{'perlver'} = '1:5.6.1';

if ($] >= 5.008) {
  $defaults{'perlver'} = '2:5.8.0';
}

$defaults{'outdir'}  = './';
$defaults{'tmpdir'}  = '/tmp';
$defaults{'email'}   = (getpwuid($<))[0] . '@redhat.com';
$defaults{'release'} = '8';
$defaults{'installdirs'} = "";

#
# Parse command line options
my %options;
GetOptions(\%options, "outdir=s", "tmpdir=s", "email=s", "name=s", "create",
    "test", "epoch=n", "version=s", "release=s", "perlver=s", "patch=s",
    "noarch", "arch=s", "noperlreqs", "usage", "buildall", "installdirs=s",
    "descfile=s", "help") or die_usage();

my $fullname = shift;

if(defined($options{'usage'})) {
    usage();
    exit(0);
}

#
# Make sure filename was provided
die_usage() unless $fullname;

#
# If we were given a description file, make sure it exists
if ($options{'descfile'}) {
  if (! -e $options{'descfile'}) {
    print STDERR "Description file given does not exist!\n";
    print STDERR "File:  ${options{'descfile'}}\n";
    exit(1);
  }
  if (! -r $options{'descfile'}) {
    print STDERR "Description file given is not readable!\n";
    print STDERR "File:  ${options{'descfile'}}\n";
    exit(1);
  }
}

#
# Overide defaults if necessary, otherwise keep them.
my $tarball = basename($fullname);
my $create = $options{create} || '';
my $email = $options{email} || $defaults{'email'};
my $outdir = $options{outdir} || $defaults{'outdir'};
my $tmpdir = $options{tmpdir} || $defaults{'tmpdir'};
my $noarch = $options{noarch} || '';
my $plat_perl_reqs = $options{'noperlreqs'} ? 0 : 1;
my $release = $options{'release'} || $defaults{'release'};
my $build_switch = 's';

#
# Set build arch - this is needed to find out where
# the binary rpm was placed, and copy it back to the
# current working directory.
my $build_arch;
if($options{'arch'}) {
  $build_arch = $options{'arch'};
}
elsif($options{'noarch'}) {
  $build_arch = 'noarch';
}
else {
  $build_arch = get_default_build_arch();
  if($build_arch eq '') {
    print STDERR "Could not get default build arch!\n";
    exit(1);
  }
}

$build_switch = 'a' if(defined($options{'buildall'}));

$tarball =~ /^(.+)\-([^-]+)\.tar\.gz$/;
my $name = $options{name} || $1;
my $ver = $options{version} || $2;

die "Module name/version not parsable from $tarball" unless $name and $ver;

$name =~ s/::/-/g;

copy($fullname, $tmpdir)
  or die "copy $fullname: $!";

if (not exists $options{noarch}) {
  my @files = Archive::Tar->list_archive("$tmpdir/$tarball");
  if (@files) {
    $noarch = 1;
    $noarch = 0 if grep { /\.(xs|c|cc|C)$/ } @files;
  }
}

my $patchfile = '';
if ($options{patch}) {
  copy($options{patch}, $tmpdir)
    or die "copy $options{patch}: $!";
  $patchfile = $options{patch};
}

my $spec = new RPM::Specfile;

# perlver.  turning perl versions into happy rpm epochs.  also used to
# determine INSTALLDIRS strategy

my $perlver = $defaults{'perlver'};
if ($options{perlver}) {
  if ($options{perlver} eq '5.6.1') {
    $perlver = "1:5.6.1";
  }
  elsif ($options{perlver} eq '5.8.0') {
    $perlver = "2:5.8.0";
  }
}

# some basic spec fields
$spec->name("perl-$name");
$spec->version($ver);
$spec->release($release);
$spec->epoch($options{epoch});
$spec->summary("$name Perl module");
$spec->group("Development/Libraries");
$spec->license("distributable");
$spec->buildrequires("perl >= $perlver");
$spec->packager($email);
$spec->add_changelog_entry($email, 'Specfile autogenerated');

# Setup spec description.  Defaults to summary, unless
# description file provided:
if($options{'descfile'}) {
   $spec->description(read_desc_file($options{'descfile'}));
}
else {
  $spec->description($spec->summary);
}

#
# Setup build architecture.
$spec->buildarch($options{'arch'}) if(defined($options{'arch'}));
$spec->buildarch('noarch') if $noarch;

#
# Use perl requirements by default (onward and upward...).
if ($plat_perl_reqs) {
  $spec->push_require(q|%(perl -MConfig -le 'if (defined $Config{useithreads}) { print "perl(:WITH_ITHREADS)" } else { print "perl(:WITHOUT_ITHREADS)" }')|);
  $spec->push_require(q|%(perl -MConfig -le 'if (defined $Config{usethreads}) { print "perl(:WITH_THREADS)" } else { print "perl(:WITHOUT_THREADS)" }')|);
  $spec->push_require(q|%(perl -MConfig -le 'if (defined $Config{uselargefiles}) { print "perl(:WITH_LARGEFILES)" } else { print "perl(:WITHOUT_LARGEFILES)" }')|);
}

$spec->push_source($tarball);
$spec->push_patch(basename($patchfile))
  if $patchfile;

# make a URL that can actually possibly take you to the right place
my $url_name = $name;
$url_name =~ s/-/::/g;
$url_name =~ s/([^a-zA-Z0-9])/sprintf "%%%x", ord $1/ge;
$spec->url("http://search.cpan.org/search?mode=module&query=$url_name");

# now we get into the multiline tags.  stolen mostly verbatim from
# cpanflute1

my $patch = '';
if ($patchfile) {
  $patch = "%patch0 -p1\n";
}

$spec->prep("%setup -q -n $name-%{version} $create\n$patch");
$spec->file_param("-f $name-$ver-filelist");
$spec->push_file("%defattr(-,root,root)");

my $test_clause = '';
$test_clause = "make test" if $options{test};

my $installdirs = "";
if ($options{'installdirs'}) {
  # perl 5.8 explicitly supports the INSTALLDIRS option.  in previous
  # perls, it was a vendor added option.  Red Hat and Debian provide
  # this for their perl 5.6.1, but the syntax for 5.8.0 is different,
  # at least in the Red Hat case

  $installdirs = "INSTALLDIRS=$options{'installdirs'}";
}

my $makefile_pl = qq{CFLAGS="\$RPM_OPT_FLAGS" perl Makefile.PL};
my $make_install = qq{make PREFIX=\$RPM_BUILD_ROOT/usr $installdirs install};

if ($perlver =~ /5.8.0/) {
  $makefile_pl = qq{CFLAGS="\$RPM_OPT_FLAGS" perl Makefile.PL PREFIX=\$RPM_BUILD_ROOT/usr $installdirs};
  $make_install = qq{make install};
}

$spec->build(<<EOB);
$makefile_pl
make
$test_clause
EOB

$spec->clean('rm -rf $RPM_BUILD_ROOT');
my $inst = q{
rm -rf $RPM_BUILD_ROOT
eval `perl '-V:installarchlib'`
mkdir -p $RPM_BUILD_ROOT/$installarchlib
$make_install

[ -x /usr/lib/rpm/brp-compress ] && /usr/lib/rpm/brp-compress

find $RPM_BUILD_ROOT/usr -type f -print | \
	sed "s@^$RPM_BUILD_ROOT@@g" | \
	grep -v perllocal.pod | \
	grep -v "\.packlist" > $name-$ver-filelist
if [ "$(cat $name-$ver-filelist)X" = "X" ] ; then
    echo "ERROR: EMPTY FILE LIST"
    exit 1
fi
};

$inst =~ s/\$name/$name/g;
$inst =~ s/\$ver/$ver/g;
$inst =~ s/\$make_install/$make_install/g;

$spec->install($inst);

# write the spec file.  create some macros.
$spec->write_specfile("$tmpdir/perl-$name.spec");

open FH, ">$tmpdir/macros"
  or die "Can't create $tmpdir/macros: $!";

print FH qq{
%_topdir         $tmpdir
%_builddir       %{_topdir}
%_rpmdir         $outdir
%_sourcedir      %{_topdir}
%_specdir        %{_topdir}
%_srcrpmdir      $outdir
%_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
};

close FH;

open FH, ">$tmpdir/rpmrc"
  or die "Can't create $tmpdir/rpmrc: $!";

print FH qq{
include: /usr/lib/rpm/rpmrc
macrofiles: /usr/lib/rpm/macros:$tmpdir/macros
};
close FH;

# Build the build command
my @cmd;
push (@cmd, 'rpmbuild');
push (@cmd, '--rcfile', "$tmpdir/rpmrc");
push (@cmd, "-b${build_switch}");
push (@cmd, '--rmsource');
push (@cmd, '--rmspec');
push (@cmd, '--clean',  "$tmpdir/perl-$name.spec");

# perform the build, die on error
my $retval = system (@cmd);
$retval = $? >> 8;
if ($retval != 0) {
  die "RPM building failed!\n";
}

# clean up macros file
unlink "$tmpdir/rpmrc", "$tmpdir/macros";

# if we did a build all, lets move the rpms into our current
# directory
my $bin_rpm = "./perl-${name}-${ver}-${release}.${build_arch}.rpm";

exit(0);

###############
# Subroutines #
###############
sub die_usage {
  usage();
  exit(1);
}

sub usage {
  print <<EOF;
cpanflute2 Version ${VERSION}

cpanflute2 [--arch=] [--buildall] [--create] [--descfile=...] [--email=...]
	[--epoch=...] [--outdir=...] [--name=...] [--noarch] [--noperlreqs]
	[--patch=...] [--perlver=...] [--release=...] [--test]
	[--tmpdir=...] [--version=...] (archive)
cpanflute2 --usage

	--arch=		Set package to this arch if it is not noarch.  Defaults
			to system default (normally i386).
	--buildall	Build binary RPM's also.
        --create        Have %setup macro create the base directory before
	                unpacking the tarball.
	--descfile=	Path to a text file whose contents should be
	                substituted for the description of the RPM.
        --email=        Email address placed in specfile.  Defaults to
	                ${defaults{'email'}}.
        --epoch=        Set specfile Epoch tag.
        --outdir=       Where to output the SRPM and or RPM.  Defaults
	                to ${defaults{'outdir'}}
        --name=		Name of CPAN module.  Defaults to parsing this
	                from the tarball filename.
	--noarch	Forces architecture noarch.
	--noperlreqs	If set PERL interpreter requirements for things like
                        large file support will not be added.  By default
			they are added.
	--patch=	Patch to use.
	--perlver=      Version of PERL to require.  Defaults to ${defaults{'perlver'}}.
	--release=	Release of RPM to build.  Defaults to ${defaults{'release'}}.
        --test          Run "make test" in %build.
        --tmpdir=	Temporary directory in which to build.  Defaults
	                to ${defaults{'tmpdir'}}.
        --version=	Version of CPAN module.  Defaults to parsing this
	                from the tarball filename.
        --installdirs=  Specify which INSTALLDIRS setting to use (see MakeMaker)
	(archive)	Name of gzipped tarball containing CPAN source.

EOF
}

sub get_default_build_arch {
  my $build_arch = qx(rpm --eval %{_build_arch});
  chomp $build_arch;

  return $build_arch;
}

#
# Read in description file and return its text.
sub read_desc_file {
  my $file = shift;

  open FILE, "<$file"
    or die "Can't open $file for reading: $!";

  local $/ = undef;
  my $ret = <FILE>;

  close FILE;
  return $ret;
}
