#!/usr/bin/perl -w # Take a perl tarball and make a specfile for it. Now with bundles. # # Copyright 2000,2001 Simon Wilkinson. All rights reserved. # # 10/18/2001 - # Added resolution of prereq_pm modules # # This program is free software; you can redistribute it # and/or modify it under the same terms as Perl itself. # use strict; use CPAN; use POSIX; use Sys::Hostname; use File::Basename; use Getopt::Long; use vars qw ($DEBUG $ARCH $builddir $seen @report); $ARCH = 'i386'; $DEBUG = 0; # Icky globals my $release; my $package; $seen = {}; sub usage { print STDERR <] [--builddir ] Where: is the release number of the RPM you wish to produce is the name of the module to build EOM exit(1); } my $ret=GetOptions("release" => \$release, "builddir=s" => \$builddir); $package=$ARGV[0]; usage() if !$package; $release=1 if !$release; $builddir=ExtractRpmMacro($ENV{HOME}."/.rpmmacros","_topdir") if !$builddir; $builddir=ExtractRpmMacro("/etc/rpm/macros","_topdir") if !$builddir; $builddir=getcwd() if !$builddir; die "Build directory $builddir doesn't look like an RPM build root\n" if ((! -d "$builddir/SPECS") || (! -d "$builddir/SOURCES")); process($package,$release); print join("\n",@report)."\n"; sub process { my ($infile,$release)=@_; # Convert our installation list into an unbundled one unbundle($infile); print "Building $infile\n"; cpan2rpm($infile,$builddir,$release); } # Given a Module, try to split it into its required components - this # currently only handles Bundles, but could also be extended to deal with # prereqs as well. sub unbundle { my ($item) = @_; if ($item=~/Bundle::/) { my $obj=CPAN::Shell->expand('Bundle',$item); foreach my $kid ($obj->contains) { process($kid,$release); } } } sub cpan2rpm($$$) { my ($infile,$builddir,$release) = @_; my $ret; my $obj=CPAN::Shell->expand('Module',$infile); print "CPAN tells us the following about $infile:\n",$obj->as_string if ($DEBUG); $ret=fetch_source($obj,$builddir); $ret=build_specfile($obj,$builddir,$release) if !$ret; return $ret; } # FIXME: Some error handling in the function below wouldn't go amiss ... sub fetch_source { my ($obj,$builddir)=@_; # Minor Sanity checks my $id=$obj->{ID}; return "Error: No file for $id\n" if $obj->cpan_file eq "N/A"; return "Error: $id says 'Contact Author'\n" if $obj->cpan_file =~ /^Contact Author/; return "Error: $id is contained within Perl itself!\n" if ($obj->cpan_file =~/perl-5\.\d?\.\d?\.tar\.gz/xo); # We do this so we can take advantage of CPAN's object caching. This is # pinched from the CPAN::Distribution::get method, which we can't use # directly, as it untars the package as well - which we let RPM do. my $dist = $CPAN::META->instance('CPAN::Distribution',$obj->cpan_file); my($local_wanted) = MM->catfile($CPAN::Config->{keep_source_where}, "authors", "id", split("/",$dist->{ID}) ); my $local_file = CPAN::FTP->localize("authors/id/$dist->{ID}", $local_wanted); $dist->{localfile} = $local_file; $dist->verifyMD5 if ($CPAN::META->has_inst('MD5')); # Find all the prereqs for this distribution, then build em. # TODO this should be somewhere else $dist->make; build_prereqs( $dist->prereq_pm()); my $infile=basename($obj->cpan_file); File::Copy::copy($local_file,"$builddir/SOURCES/$infile"); return undef; } sub build_prereqs($) { my ($prereqs) = @_; foreach my $prereq (keys %{$prereqs}) { process ($prereq, $release); } } sub build_specfile($$$) { my ($obj,$builddir,$release) = @_; my $source=basename($obj->cpan_file); # don't go through dependencies on something we've already dealt with return() if ($seen->{$source}); $seen->{$source} = 1; my ($name,$version)=($source=~/(.*)-(.*)\.tar\.gz/); return "Couldn't get a name for $source\n" if !$name; return "Couldn't get a version for $source\n" if !$version; my $summary="$name module for perl"; my $description=$obj->{description}; $description= $summary if !$description; open(SPEC, ">$builddir/SPECS/perl-$name.spec") or die "Couldn't open perl-$name.spec for writing."; print SPEC < $name-$version-filelist %files -f ${name}-${version}-filelist %defattr(-,root,root) %changelog EOF print SPEC "* ",POSIX::strftime("%a %b %d %Y",localtime()), " ",$ENV{USER}," <",$ENV{USER},"\@",hostname(),">\n"; print SPEC "- Spec file automatically generated by cpan2rpm\n"; close(SPEC); system("rpm -ba $builddir/SPECS/perl-$name.spec >/dev/null") == 0 or push (@report, "RPM of $source failed with : $!\n"); system("rpm -Uvh $builddir/RPMS/$ARCH/perl-$name-$version-$release.$ARCH.rpm") == 0 or warn "RPM of $source could not be installed: $!\n"; push (@report, "Built perl-$name-$version-$release.$ARCH.rpm"); } sub ExtractRpmMacro { my ($file,$macro) = @_; my $handle=new IO::File; if (!$handle->open($file)) { return undef; } while(<$handle>) { if (/\%$macro (.*)/) { return $1; } } return undef; } =head1 NAME cpan2rpm - fetch and convert CPAN packages to RPMs =head1 SYNOPSIS cpan2rpm --release =head1 DESCRIPTION cpan2rpm provides a quick way of creating RPM packages from perl modules published on CPAN. It interfaces with the perl CPAN module to fetch the file from the selected mirror, and then creates a spec file from the information in CPAN, and invokes RPM on that spec file. Files are created in the users RPM build root. =head1 OPTIONS =over 4 =item release Sets the release number of the created RPMs. =back =head1 SEE ALSO rpm(1) =head1 AUTHOR Simon Wilkinson