1188 lines
33 KiB
Perl
Executable File
1188 lines
33 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
#
|
|
# cpanspec - Generate a spec file for a CPAN module
|
|
#
|
|
# Copyright (C) 2004-2008 Steven Pritchard <steve@kspei.com>
|
|
# This program is free software; you can redistribute it
|
|
# and/or modify it under the same terms as Perl itself.
|
|
#
|
|
# 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.
|
|
#
|
|
# $Id: cpanspec,v 1.62 2008/06/16 19:24:04 stevenpritchard Exp $
|
|
|
|
our $NAME="cpanspec";
|
|
our $VERSION='1.77';
|
|
|
|
=head1 NAME
|
|
|
|
cpanspec - Generate a spec file for a CPAN module
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
cpanspec [options] [file [...]]
|
|
|
|
Options:
|
|
--help -h Help message
|
|
--old -o Be more compatible with old RHL/FC releases
|
|
--license -l Include generated license texts if absent in source
|
|
--noprefix -n Don't add perl- prefix to package name
|
|
--force -f Force overwriting existing spec
|
|
--packager -p Name and email address of packager (for changelog)
|
|
--release -r Release of package (defaults to 1)
|
|
--epoch -e Epoch of package
|
|
--disttag -d Disttag (defaults to %{?dist})
|
|
--srpm -s Build a source rpm
|
|
--build -b Build source and binary rpms
|
|
--cpan -c CPAN mirror URL
|
|
--verbose -v Be more verbose
|
|
--prefer-macros -m Prefer macros over environment variables in the spec
|
|
|
|
Long options:
|
|
--follow Process build dependencies
|
|
--filter-requires Specify Requires to remove
|
|
--filter-provides Specify Provides to remove
|
|
--add-requires Add Requires for this item
|
|
--add-provides Add Provides for this item
|
|
--add-buildrequires Add BuildRequires for this item
|
|
--version Print the version number and exit
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
B<cpanspec> will generate a spec file to build a rpm from a CPAN-style
|
|
Perl module distribution.
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 4
|
|
|
|
=item B<-h>, B<--help>
|
|
|
|
Print a brief help message and exit.
|
|
|
|
=item B<-o>, B<--old>
|
|
|
|
Be more compatible with old RHL/FC releases. With this option enabled,
|
|
the generated spec file
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
Defines perl_vendorlib or perl_vendorarch.
|
|
|
|
=item *
|
|
|
|
Includes explicit dependencies for core Perl modules.
|
|
|
|
=item *
|
|
|
|
Uses C<%check || :> instead of just C<%check>.
|
|
|
|
=item *
|
|
|
|
Includes a hack to remove LD_RUN_PATH from Makefile.
|
|
|
|
=back
|
|
|
|
=item B<-l>, B<--license>
|
|
|
|
Generate COPYING and Artistic license texts if the source doesn't seem
|
|
to include them.
|
|
|
|
=item B<-n>, B<--noprefix>
|
|
|
|
Don't add I<perl-> prefix to the name of the package. This is useful
|
|
for perl-based applications (such as this one), so that the name of
|
|
the rpm is simply B<cpanspec> instead of B<perl-cpanspec>.
|
|
|
|
=item B<-f>, B<--force>
|
|
|
|
Force overwriting an existing spec file. Normally B<cpanspec> will
|
|
refuse to overwrite an existing spec file for safety. This option
|
|
removes that safety check. Please use with caution.
|
|
|
|
=item B<-p>, B<--packager>
|
|
|
|
The name and email address of the packager. Overrides the C<%packager>
|
|
macro in C<~/.rpmmacros>.
|
|
|
|
=item B<-r>, B<--release>
|
|
|
|
The release number of the package. Defaults to 1.
|
|
|
|
=item B<-e>, B<--epoch>
|
|
|
|
The epoch number of the package. By default, this is undefined, so
|
|
no epoch will be used in the generated spec.
|
|
|
|
=item B<-d>, B<--disttag>
|
|
|
|
Disttag (a string to append to the release number), used to
|
|
differentiate builds for various releases. Defaults to the
|
|
semi-standard (for Fedora) string C<%{?dist}>.
|
|
|
|
=item B<-s>, B<--srpm>
|
|
|
|
Build a source rpm from the generated spec file.
|
|
|
|
=item B<-b>, B<--build>
|
|
|
|
Build source and binary rpms from the generated spec file.
|
|
B<Please be aware that this is likely to fail!> Even if it succeeds,
|
|
the generated rpm will almost certainly need some work to make
|
|
rpmlint happy.
|
|
|
|
=item B<-c>, B<--cpan>
|
|
|
|
The URL to a CPAN mirror. If not specified with this option or the
|
|
B<CPAN> environment variable, defaults to L<http://www.cpan.org/>.
|
|
|
|
=item B<-v>, B<--verbose>
|
|
|
|
Be more verbose.
|
|
|
|
=item B<-m>, B<--prefer-macros>
|
|
|
|
Prefer the macro form of common spec constructs over the environment variable
|
|
form (e.g. %{buildroot} vs $RPM_BUILD_ROOT).
|
|
|
|
=item B<--follow>
|
|
|
|
Add build dependencies to the list of modules to process.
|
|
|
|
=item B<--filter-requires>
|
|
|
|
Specify Requires to remove.
|
|
|
|
=item B<--filter-provides>
|
|
|
|
Specify Provides to remove.
|
|
|
|
=item B<--add-requires>
|
|
|
|
Add Requires for this item.
|
|
|
|
=item B<--add-provides>
|
|
|
|
Add Provides for this item.
|
|
|
|
=item B<--add-buildrequires>
|
|
|
|
Add BuildRequires for this item.
|
|
|
|
=item B<--version>
|
|
|
|
Print the version number and exit.
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Steven Pritchard <steve@kspei.com>
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<perl(1)>, L<cpan2rpm(1)>, L<cpanflute2(1)>
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use FileHandle;
|
|
use Archive::Tar;
|
|
use Archive::Zip qw(:ERROR_CODES);
|
|
use POSIX;
|
|
use locale;
|
|
use Text::Autoformat;
|
|
use YAML qw(Load);
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
use File::Basename;
|
|
use LWP::UserAgent;
|
|
use Parse::CPAN::Packages;
|
|
use Pod::Simple::TextContent;
|
|
# Apparently gets pulled in by another module.
|
|
#use Cwd;
|
|
|
|
our %opt;
|
|
|
|
our $help=0;
|
|
our $compat=0;
|
|
our $addlicense=0;
|
|
our $noprefix=0;
|
|
our $force=0;
|
|
our $packager;
|
|
our $release=1;
|
|
our $epoch;
|
|
our $disttag='%{?dist}';
|
|
our $buildsrpm=0;
|
|
our $buildrpm=0;
|
|
our $verbose=0;
|
|
our $follow=0;
|
|
our $macros=0;
|
|
our $source;
|
|
our $cpan=$ENV{'CPAN'} || "http://www.cpan.org";
|
|
|
|
our $home=$ENV{'HOME'} || (getpwuid($<))[7];
|
|
die "Can't locate home directory. Please define \$HOME.\n"
|
|
if (!defined($home));
|
|
|
|
our $pkgdetails="$home/.cpan/sources/modules/02packages.details.txt.gz";
|
|
our $updated=0;
|
|
|
|
our $packages;
|
|
|
|
our @filter_requires;
|
|
our @filter_provides;
|
|
our @add_requires;
|
|
our @add_provides;
|
|
our @add_buildrequires;
|
|
|
|
# env. vars and their macro analogues
|
|
my @MACROS = (
|
|
|
|
# 0 is for the full expansions....
|
|
{
|
|
'optimize' => '$RPM_OPT_FLAGS',
|
|
'buildroot' => '$RPM_BUILD_ROOT',
|
|
},
|
|
|
|
# 1 is for the macros.
|
|
{
|
|
'optimize' => '%{optimize}',
|
|
'buildroot' => '%{buildroot}',
|
|
},
|
|
);
|
|
|
|
# this is set after the parameters are passed
|
|
our %macro;
|
|
|
|
sub print_version {
|
|
print "$NAME version $VERSION\n";
|
|
exit 0;
|
|
}
|
|
|
|
sub verbose(@) {
|
|
print STDERR @_, "\n" if ($verbose);
|
|
}
|
|
|
|
sub fetch($$) {
|
|
my ($url, $file)=@_;
|
|
my @locations=();
|
|
|
|
verbose("Fetching $file from $url...");
|
|
|
|
my $ua=LWP::UserAgent->new('env_proxy' => 1)
|
|
or die "LWP::UserAgent->new() failed: $!\n";
|
|
|
|
my $request;
|
|
LOOP: $request=HTTP::Request->new('GET' => $url)
|
|
or die "HTTP::Request->new() failed: $!\n";
|
|
|
|
my @buf=stat($file);
|
|
$request->if_modified_since($buf[9]) if (@buf);
|
|
|
|
# FIXME - Probably should do $ua->request() here and skip loop detection.
|
|
my $response=$ua->simple_request($request)
|
|
or die "LWP::UserAgent->simple_request() failed: $!\n";
|
|
|
|
push(@locations, $url);
|
|
if ($response->code eq "301" or $response->code eq "302") {
|
|
$url=$response->header('Location');
|
|
die "Redirect loop detected! " . join("\n ", @locations, $url) . "\n"
|
|
if (grep { $url eq $_ } @locations);
|
|
goto LOOP;
|
|
}
|
|
|
|
if ($response->is_success) {
|
|
my $fh=new FileHandle ">$file"
|
|
or die "Can't write to $file: $!\n";
|
|
print $fh $response->content;
|
|
$fh->close();
|
|
|
|
my $last_modified=$response->last_modified;
|
|
utime(time, $last_modified, $file) if ($last_modified);
|
|
} elsif ($response->code eq "304") {
|
|
verbose("$file is up to date.");
|
|
} else {
|
|
die "Failed to get $url: " . $response->status_line . "\n";
|
|
}
|
|
}
|
|
|
|
sub mkdir_p($) {
|
|
my $dir=shift;
|
|
|
|
my @path=split '/', $dir;
|
|
|
|
for (my $n=0;$n<@path;$n++) {
|
|
my $partial="/" . join("/", @path[0..$n]);
|
|
if (!-d $partial) {
|
|
verbose("mkdir($partial)");
|
|
mkdir $partial or die "mkdir($partial) failed: $!\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub update_packages() {
|
|
return 1 if ($updated);
|
|
|
|
verbose("Updating $pkgdetails...");
|
|
|
|
mkdir_p(dirname($pkgdetails)) if (!-d dirname($pkgdetails));
|
|
|
|
fetch("$cpan/modules/" . basename($pkgdetails), $pkgdetails);
|
|
|
|
$updated=1;
|
|
}
|
|
|
|
sub build_rpm($) {
|
|
my $spec=shift;
|
|
my $dir=getcwd();
|
|
|
|
my $rpmbuild=(-x "/usr/bin/rpmbuild" ? "/usr/bin/rpmbuild" : "/bin/rpm");
|
|
|
|
verbose("Building " . ($buildrpm ? "rpms" : "source rpm") . " from $spec");
|
|
|
|
# From Fedora CVS Makefile.common.
|
|
if (system($rpmbuild, "--define", "_sourcedir $dir",
|
|
"--define", "_builddir $dir",
|
|
"--define", "_srcrpmdir $dir",
|
|
"--define", "_rpmdir $dir",
|
|
($buildrpm ? "-ba" : ("-bs", "--nodeps")),
|
|
$spec) != 0) {
|
|
if ($? == -1) {
|
|
die "Failed to execute $rpmbuild: $!\n";
|
|
} elsif (WIFSIGNALED($?)) {
|
|
die "$rpmbuild died with signal " . WTERMSIG($?)
|
|
. (($? & 128) ? ", core dumped\n" : "\n");
|
|
} else {
|
|
die "$rpmbuild exited with value " . WEXITSTATUS($?) . "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub list_files($$) {
|
|
my $archive=$_[0];
|
|
my $type=$_[1];
|
|
|
|
if ($type eq 'tar') {
|
|
return $archive->list_files();
|
|
} elsif ($type eq 'zip') {
|
|
return map { $_->fileName(); } $archive->members();
|
|
}
|
|
}
|
|
|
|
sub extract($$$) {
|
|
my $archive=$_[0];
|
|
my $type=$_[1];
|
|
my $filename=$_[2];
|
|
|
|
if ($type eq 'tar') {
|
|
return $archive->get_content($filename);
|
|
} elsif ($type eq 'zip') {
|
|
return $archive->contents($filename);
|
|
}
|
|
}
|
|
|
|
sub get_description(%) {
|
|
my %args=@_;
|
|
my $pm="";
|
|
my ($summary, $description);
|
|
|
|
my $path=$args{module};
|
|
$path=~s,::,/,g;
|
|
my @pmfiles=("$args{path}/lib/$path.pod",
|
|
"$args{path}/lib/$path.pm");
|
|
if ($args{module} =~ /::/) {
|
|
my @tmp=split '/', $path;
|
|
my $last=pop @tmp;
|
|
push(@pmfiles, "$args{path}/lib/$last.pod",
|
|
"$args{path}/lib/$last.pm");
|
|
}
|
|
do {
|
|
push(@pmfiles, "$args{path}/$path.pod",
|
|
"$args{path}/$path.pm");
|
|
} while ($path=~s,^[^/]+/,,);
|
|
push(@pmfiles, "$args{path}/$args{module}")
|
|
if ($args{module} !~ /::/);
|
|
|
|
for my $file (@pmfiles) {
|
|
$pm=(grep { $_ eq $file or $_ eq "./$file" }
|
|
list_files($args{archive}, $args{type}))[0];
|
|
last if $pm;
|
|
}
|
|
|
|
if ($pm) {
|
|
verbose "Trying to fetch description from $pm...";
|
|
|
|
if (my $content=extract($args{archive}, $args{type}, $pm)) {
|
|
my $parser=Pod::Simple::TextContent->new()
|
|
or die "Pod::Simple::TextContent->new() failed: $!\n";
|
|
$parser->no_whining(1);
|
|
my $rendered="";
|
|
$parser->output_string(\$rendered);
|
|
$parser->parse_string_document($content);
|
|
if ($parser->content_seen and $rendered) {
|
|
if ($rendered=~/DESCRIPTION\s+(\S.*?)\n\n/s) {
|
|
$description=$1;
|
|
}
|
|
if ($rendered=~/NAME\s*$args{module}\s[-\s]*(\S[^\n]*)/s) {
|
|
if ($1 ne "SYNOPSIS") {
|
|
$summary=$1;
|
|
$summary=~s/[.\s]+$//;
|
|
$summary=~s/^(?:An?|The)\s+//i;
|
|
$summary=ucfirst($summary);
|
|
}
|
|
}
|
|
return($description, $summary) if (defined($description));
|
|
}
|
|
} else {
|
|
warn "Failed to read $pm from $args{filename}"
|
|
. ($args{type} eq 'tar'
|
|
? (": " . $args{archive}->error()) : "") . "\n";
|
|
}
|
|
}
|
|
|
|
if (my $readme=(sort {
|
|
length($a) <=> length($b) or $a cmp $b
|
|
} (grep /README/i, @{$args{files}}))[0]) {
|
|
verbose "Trying to fetch description from $readme...";
|
|
|
|
if (my $content=extract($args{archive}, $args{type},
|
|
"$args{path}/$readme")) {
|
|
$content=~s/\r//g; # Why people use DOS text, I'll never understand.
|
|
for my $string (split "\n\n", $content) {
|
|
$string=~s/^\n+//;
|
|
if ((my @tmp=split "\n", $string) > 2
|
|
and $string !~ /^[#\-=]/) {
|
|
return($string, undef);
|
|
}
|
|
}
|
|
} else {
|
|
warn "Failed to read $readme from $args{filename}"
|
|
. ($args{type} eq 'tar'
|
|
? (": " . $args{archive}->error()) : "") . "\n";
|
|
}
|
|
}
|
|
|
|
return(undef, undef);
|
|
}
|
|
|
|
sub check_rpm($) {
|
|
my $dep=shift;
|
|
|
|
my $rpm="/bin/rpm";
|
|
return undef if (!-x $rpm);
|
|
|
|
my @out=`$rpm -q --whatprovides "$dep"`;
|
|
|
|
if ($? != 0) {
|
|
#warn "backtick (rpm) failed with return value $?";
|
|
return undef;
|
|
}
|
|
|
|
return @out;
|
|
}
|
|
|
|
sub check_repo($) {
|
|
my $dep=shift;
|
|
|
|
my $repoquery="/usr/bin/repoquery";
|
|
return undef if (!-x $repoquery);
|
|
|
|
verbose("Running $repoquery to check for $dep. This may take a while...");
|
|
my @out=`$repoquery --whatprovides "$dep"`;
|
|
|
|
if ($? != 0) {
|
|
#warn "backtick (repoquery) failed with return value $?";
|
|
return undef;
|
|
}
|
|
|
|
return grep { /^\S+-[^-]+-[^-]+$/ } @out;
|
|
}
|
|
|
|
sub check_dep($) {
|
|
my $module=shift;
|
|
|
|
return (check_rpm("perl($module)") || check_repo("perl($module)"));
|
|
}
|
|
|
|
# Set locale to en_US.UTF8 so that dates in changelog will be correct
|
|
# if using another locale. Also ensures writing out UTF8. (Thanks to
|
|
# Roy-Magne Mo for pointing out the problem and providing a solution.)
|
|
setlocale(LC_ALL, "en_US.UTF-8");
|
|
|
|
GetOptions(
|
|
'help|h' => \$help,
|
|
'old|o' => \$compat,
|
|
'license|l' => \$addlicense,
|
|
'noprefix|n' => \$noprefix,
|
|
'force|f' => \$force,
|
|
'packager|p=s' => \$packager,
|
|
'release|r=i' => \$release,
|
|
'epoch|e=i' => \$epoch,
|
|
'disttag|d=s' => \$disttag,
|
|
'srpm|s' => \$buildsrpm,
|
|
'build|b' => \$buildrpm,
|
|
'cpan|c=s' => \$cpan,
|
|
'verbose|v' => \$verbose,
|
|
'follow' => \$follow,
|
|
'filter-requires=s' => \@filter_requires,
|
|
'filter-provides=s' => \@filter_provides,
|
|
'add-requires=s' => \@add_requires,
|
|
'add-provides=s' => \@add_provides,
|
|
'add-buildrequires=s' => \@add_buildrequires,
|
|
'version' => \&print_version,
|
|
'prefer-macros|m' => \$macros,
|
|
) or pod2usage({ -exitval => 1, -verbose => 0 });
|
|
|
|
pod2usage({ -exitval => 0, -verbose => 1 }) if ($help);
|
|
pod2usage({ -exitval => 1, -verbose => 0 }) if (!@ARGV);
|
|
|
|
if ($follow and $buildrpm) {
|
|
warn "Sorry, --follow and --build are mutually exclusive right now.\n"
|
|
. "We can't build when tracking deps right now. Ignoring --build.\n";
|
|
$buildrpm=0;
|
|
}
|
|
|
|
%macro = %{ $MACROS[$macros] };
|
|
|
|
my $prefix=$noprefix ? "" : "perl-";
|
|
|
|
$packager=$packager || `rpm --eval '\%packager'`;
|
|
|
|
chomp $packager;
|
|
|
|
if (!$packager or $packager eq "\%packager") {
|
|
die "\%packager not defined in ~/.rpmmacros."
|
|
. " Please add or use --packager option.\n";
|
|
}
|
|
|
|
our %corelist;
|
|
|
|
my $rpm=new FileHandle "rpm -q --provides perl|"
|
|
or warn "Failed to execute rpm: $!\n";
|
|
|
|
while (my $provides=<$rpm>) {
|
|
chomp $provides;
|
|
|
|
if ($provides=~/^perl\(([^\)]+)\)(?:\s+=\s+(\S+))\s*$/) {
|
|
$corelist{$1}=defined($2) ? $2 : 0;
|
|
}
|
|
}
|
|
|
|
my @args=@ARGV;
|
|
my @processed=();
|
|
|
|
for my $file (@args) {
|
|
my ($name,$version,$type);
|
|
|
|
if ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.(tar)\.(?:gz|bz2)$/) {
|
|
$name=$1;
|
|
$version=$2;
|
|
$type=$3;
|
|
} elsif ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.tgz$/) {
|
|
$name=$1;
|
|
$version=$2;
|
|
$type = 'tar';
|
|
} elsif ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.(zip)$/) {
|
|
$name=$1;
|
|
$version=$2;
|
|
$type=$3;
|
|
} else {
|
|
|
|
# keep things happy if we get "Foo-Bar" instead of "Foo::Bar"
|
|
$file =~ s/-/::/g;
|
|
|
|
# Look up $file in 02packages.details.txt.
|
|
update_packages();
|
|
$packages=Parse::CPAN::Packages->new($pkgdetails)
|
|
if (!defined($packages));
|
|
die "Parse::CPAN::Packages->new() failed: $!\n"
|
|
if (!defined($packages));
|
|
my ($m,$d);
|
|
if ($m=$packages->package($file) and $d=$m->distribution()) {
|
|
$source=$cpan . "/authors/id/" . $d->prefix();
|
|
$file=basename($d->filename());
|
|
fetch($source, $file);
|
|
$name=$d->dist();
|
|
$version=$d->version();
|
|
$version=~s/^v\.?//;
|
|
if ($file =~ /\.(tar)\.gz$/) {
|
|
$type=$1;
|
|
} elsif ($file =~ /\.tgz$/) {
|
|
$type='tar';
|
|
} elsif ($file =~ /\.(zip)$/) {
|
|
$type=$1;
|
|
} else {
|
|
warn "Failed to parse '$file', skipping...\n";
|
|
next;
|
|
}
|
|
} else {
|
|
warn "Failed to parse '$file' or find a module by that name, skipping...\n";
|
|
next;
|
|
}
|
|
}
|
|
|
|
my $module=$name;
|
|
$module=~s/-/::/g;
|
|
|
|
my $archive;
|
|
my $path;
|
|
if ($type eq 'tar') {
|
|
my $f=$file;
|
|
if ($file=~/\.bz2$/) {
|
|
eval {
|
|
use IO::Uncompress::Bunzip2;
|
|
};
|
|
|
|
if ($@) {
|
|
warn "Failed to load IO::Uncompress::Bunzip2: $@\n";
|
|
warn "Skipping $file...\n";
|
|
next;
|
|
}
|
|
|
|
$f=IO::Uncompress::Bunzip2->new($file);
|
|
if (!defined($f)) {
|
|
warn "IO::Uncompress::Bunzip2->new() failed on $file: $!\n";
|
|
next;
|
|
}
|
|
}
|
|
$archive=Archive::Tar->new($f, 1)
|
|
or die "Archive::Tar->new() failed: $!\n";
|
|
} elsif ($type eq 'zip') {
|
|
$archive=Archive::Zip->new() or die "Archive::Zip->new() failed: $!\n";
|
|
die "Read error on $file\n" unless ($archive->read($file) == AZ_OK);
|
|
}
|
|
|
|
my @files;
|
|
|
|
my $bogus=0;
|
|
for my $entry (list_files($archive, $type)) {
|
|
if ($entry !~ /^(?:.\/)?($name-(?:v\.?)?$version)(?:\/|$)/) {
|
|
warn "BOGUS PATH DETECTED: $entry\n";
|
|
$bogus++;
|
|
next;
|
|
} elsif (!defined($path)) {
|
|
$path=$1;
|
|
}
|
|
|
|
$entry=~s,^(?:.\/)?$name-(?:v\.?)?$version/,,;
|
|
next if (!$entry);
|
|
|
|
push(@files, $entry);
|
|
}
|
|
if ($bogus) {
|
|
warn "Skipping $file with $bogus path elements!\n";
|
|
next;
|
|
}
|
|
|
|
my $url="http://search.cpan.org/dist/$name/";
|
|
|
|
$source=$source || "http://www.cpan.org/modules/by-module/"
|
|
. ($module=~/::/ ? (split "::", $module)[0] : (split "-", $name)[0])
|
|
. "/" . basename($file);
|
|
$source=~s/$version/\%{version}/;
|
|
|
|
my ($description,$summary)=get_description(
|
|
archive => $archive,
|
|
type => $type,
|
|
filename => $file,
|
|
name => $name,
|
|
module => $module,
|
|
version => $version,
|
|
files => \@files,
|
|
path => $path,
|
|
);
|
|
|
|
if (defined($description) and $description) {
|
|
$description=autoformat $description, { "all" => 1,
|
|
"left" => 1,
|
|
"right" => 75,
|
|
"squeeze" => 0,
|
|
};
|
|
$description=~s/\n+$//s;
|
|
} else {
|
|
$description="$module Perl module";
|
|
}
|
|
|
|
$summary="$module Perl module" if (!defined($summary));
|
|
|
|
my @doc=sort { $a cmp $b } grep {
|
|
!/\//
|
|
and !/\.(pl|xs|h|c|pm|in|pod|cfg)$/i
|
|
and !/^\./
|
|
and $_ ne $path
|
|
and $_ ne "MANIFEST"
|
|
and $_ ne "MANIFEST.SKIP"
|
|
and $_ ne "INSTALL"
|
|
and $_ ne "SIGNATURE"
|
|
and $_ ne "META.yml"
|
|
and $_ ne "NINJA"
|
|
and $_ ne "configure"
|
|
and $_ ne "config.guess"
|
|
and $_ ne "config.sub"
|
|
and $_ ne "typemap"
|
|
and $_ ne "bin"
|
|
and $_ ne "lib"
|
|
and $_ ne "t"
|
|
and $_ ne "inc"
|
|
and $_ ne "autobuild.sh"
|
|
and $_ ne "pm_to_blib"
|
|
and $_ ne "install.sh"
|
|
} @files;
|
|
|
|
my $date=strftime("%a %b %d %Y", localtime);
|
|
|
|
my $noarch=!grep /\.(c|xs)$/i, @files;
|
|
my $vendorlib=($noarch ? "vendorlib" : "vendorarch");
|
|
my $lib="\%{perl_$vendorlib}";
|
|
|
|
if (@filter_requires) {
|
|
my $script="$name-filter-requires.sh";
|
|
verbose "Writing $script...";
|
|
my $sh;
|
|
if ($force) {
|
|
rename($script, "$script~") if (-e $script);
|
|
$sh=new FileHandle ">$script";
|
|
} else {
|
|
$sh=new FileHandle $script, O_WRONLY|O_CREAT|O_EXCL;
|
|
}
|
|
die "Failed to create $script: $!\n" if (!$sh);
|
|
|
|
print $sh "#!/bin/sh\n\n"
|
|
. "\@\@PERL_REQ\@\@ \"\$\@\" | sed -e '/^$filter_requires[0]\$/d'";
|
|
if (@filter_requires > 1) {
|
|
for my $dep (@filter_requires[1..$#filter_requires]) {
|
|
print $sh " \\\n -e '/^$dep\$/d'";
|
|
}
|
|
}
|
|
print $sh "\n";
|
|
}
|
|
|
|
if (@filter_provides) {
|
|
my $script="$name-filter-provides.sh";
|
|
verbose "Writing $script...";
|
|
my $sh;
|
|
if ($force) {
|
|
rename($script, "$script~") if (-e $script);
|
|
$sh=new FileHandle ">$script";
|
|
} else {
|
|
$sh=new FileHandle $script, O_WRONLY|O_CREAT|O_EXCL;
|
|
}
|
|
die "Failed to create $script: $!\n" if (!$sh);
|
|
|
|
print $sh "#!/bin/sh\n\n"
|
|
. "\@\@PERL_PROV\@\@ \"\$\@\" | sed -e '/^$filter_provides[0]\$/d'";
|
|
if (@filter_provides > 1) {
|
|
for my $dep (@filter_provides[1..$#filter_provides]) {
|
|
print $sh " \\\n -e '/^$dep\$/d'";
|
|
}
|
|
}
|
|
print $sh "\n";
|
|
}
|
|
|
|
my $specfile="$prefix$name.spec";
|
|
verbose "Writing $specfile...";
|
|
|
|
my $spec;
|
|
if ($force) {
|
|
rename($specfile, "$specfile~") if (-e $specfile);
|
|
$spec=new FileHandle ">$specfile";
|
|
} else {
|
|
$spec=new FileHandle "$specfile", O_WRONLY|O_CREAT|O_EXCL;
|
|
}
|
|
|
|
if (!$spec) {
|
|
warn "Failed to create $specfile: $!\n";
|
|
next;
|
|
}
|
|
|
|
print $spec qq[\%{!?perl_$vendorlib: \%define perl_$vendorlib \%(eval "\`\%{__perl} -V:install$vendorlib\`"; echo \$install$vendorlib)}\n\n]
|
|
if ($compat);
|
|
|
|
my $license="";
|
|
|
|
my $scripts=0;
|
|
my (%build_requires,%requires);
|
|
my ($yml,$meta);
|
|
if (grep /^META\.yml$/, @files
|
|
and $yml=extract($archive, $type, "$path/META.yml")) {
|
|
# Basic idea borrowed from Module::Depends.
|
|
my $meta;
|
|
eval { $meta=Load($yml); };
|
|
if ($@) {
|
|
warn "Error parsing $path/META.yml: $@";
|
|
goto SKIP;
|
|
}
|
|
|
|
%build_requires=%{$meta->{build_requires}} if ($meta->{build_requires});
|
|
%requires=%{$meta->{requires}} if ($meta->{requires});
|
|
if ($meta->{recommends}) {
|
|
for my $dep (keys(%{$meta->{recommends}})) {
|
|
$requires{$dep}=$requires{$dep}
|
|
|| $meta->{recommends}->{$dep};
|
|
}
|
|
}
|
|
|
|
# FIXME - I'm not sure this is sufficient...
|
|
if ($meta->{script_files} or $meta->{scripts}) {
|
|
$scripts=1;
|
|
}
|
|
|
|
if ($meta->{license}) {
|
|
# This list of licenses is from the Module::Build::API
|
|
# docs, cross referenced with the list of licenses in
|
|
# /usr/share/rpmlint/config.
|
|
if ($meta->{license} =~ /^perl$/i) {
|
|
$license="GPL+ or Artistic";
|
|
} elsif ($meta->{license} =~ /^apache$/i) {
|
|
$license="Apache Software License";
|
|
} elsif ($meta->{license} =~ /^artistic$/i) {
|
|
$license="Artistic";
|
|
} elsif ($meta->{license} =~ /^bsd$/i) {
|
|
$license="BSD";
|
|
} elsif ($meta->{license} =~ /^gpl$/i) {
|
|
$license="GPL";
|
|
} elsif ($meta->{license} =~ /^lgpl$/i) {
|
|
$license="LGPL";
|
|
} elsif ($meta->{license} =~ /^mit$/i) {
|
|
$license="MIT";
|
|
} elsif ($meta->{license} =~ /^mozilla$/i) {
|
|
$license="MPL";
|
|
} elsif ($meta->{license} =~ /^open_source$/i) {
|
|
$license="OSI-Approved"; # rpmlint will complain
|
|
} elsif ($meta->{license} =~ /^unrestricted$/i) {
|
|
$license="Distributable";
|
|
} elsif ($meta->{license} =~ /^restrictive$/i) {
|
|
$license="Non-distributable";
|
|
warn "License is 'restrictive'."
|
|
. " This package should not be redistributed.\n";
|
|
} else {
|
|
warn "Unknown license '" . $meta->{license} . "'!\n";
|
|
$license="CHECK(Distributable)";
|
|
}
|
|
}
|
|
SKIP:
|
|
}
|
|
|
|
if (my @licenses=grep /license|copyright|copying/i, @doc) {
|
|
if (!$license) {
|
|
$license="Distributable, see @licenses";
|
|
} elsif ($license=~/^(OSI-Approved|Distributable|Non-distributable)$/) {
|
|
$license.=", see @licenses";
|
|
}
|
|
}
|
|
$license="CHECK(GPL+ or Artistic)" if (!$license);
|
|
|
|
my $usebuildpl=0;
|
|
if (grep /^Build\.PL$/, @files) {
|
|
$build_requires{'Module::Build'}=0;
|
|
$usebuildpl=1;
|
|
} else {
|
|
$build_requires{'ExtUtils::MakeMaker'}=0;
|
|
}
|
|
|
|
if (!$usebuildpl) {
|
|
# This is an ugly hack to parse any PREREQ_PM in Makefile.PL.
|
|
if (open(CHILD, "-|") == 0) {
|
|
eval {
|
|
use subs 'WriteMakefile';
|
|
|
|
sub WriteMakefile(@) {
|
|
my %args=@_;
|
|
|
|
if (!defined($args{'PREREQ_PM'})) {
|
|
return;
|
|
}
|
|
|
|
# Versioned BuildRequires aren't reliably honored by
|
|
# rpmbuild, but we'll include them anyway as a hint to the
|
|
# packager.
|
|
for my $dep (keys(%{$args{'PREREQ_PM'}})) {
|
|
print "BuildRequires: $dep";
|
|
print " " . $args{'PREREQ_PM'}->{$dep}
|
|
if ($args{'PREREQ_PM'}->{$dep});
|
|
print "\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
local $/=undef;
|
|
|
|
my $makefilepl=extract($archive, $type, "$path/Makefile.PL")
|
|
or warn "Failed to extract $path/Makefile.PL";
|
|
|
|
open(STDERR, ">/dev/null");
|
|
eval "no warnings;
|
|
use subs qw(require die warn eval open close rename);
|
|
BEGIN { sub require { 1; } }
|
|
BEGIN { sub die { 1; } }
|
|
BEGIN { sub warn { 1; } }
|
|
BEGIN { sub eval { 1; } }
|
|
BEGIN { sub open { 1; } }
|
|
BEGIN { sub close { 1; } }
|
|
BEGIN { sub rename { 1; } }
|
|
$makefilepl";
|
|
|
|
exit 0;
|
|
} else {
|
|
while (<CHILD>) {
|
|
if (/^BuildRequires:\s*(\S+)\s*(\S+)?/) {
|
|
my $dep=$1;
|
|
my $version=0;
|
|
$version=$2 if (defined($2));
|
|
$build_requires{$dep}=$version;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
print $spec <<END;
|
|
Name: $prefix$name
|
|
Version: $version
|
|
Release: $release$disttag
|
|
END
|
|
|
|
print $spec "Epoch: $epoch\n" if (defined($epoch));
|
|
|
|
print $spec <<END;
|
|
Summary: $summary
|
|
License: $license
|
|
Group: Development/Libraries
|
|
URL: $url
|
|
Source0: $source
|
|
BuildRoot: \%{_tmppath}/\%{name}-\%{version}-\%{release}-root-\%(\%{__id_u} -n)
|
|
END
|
|
|
|
printf $spec "%-16s%s\n", "BuildArch:", "noarch" if ($noarch);
|
|
|
|
if (defined($requires{perl})) {
|
|
$build_requires{perl}=$build_requires{perl} || $requires{perl};
|
|
delete $requires{perl};
|
|
}
|
|
|
|
if (defined($build_requires{perl})) {
|
|
printf $spec "%-16s%s >= %s\n", "BuildRequires:", "perl",
|
|
(($build_requires{perl} lt "5.6.0" ? "0:" : "1:")
|
|
. $build_requires{perl}) if $build_requires{perl};
|
|
delete $build_requires{perl};
|
|
}
|
|
|
|
for my $dep (keys(%requires)) {
|
|
$build_requires{$dep}=$build_requires{$dep} || $requires{$dep};
|
|
}
|
|
|
|
for my $dep (sort(keys(%build_requires))) {
|
|
if (exists($corelist{$dep})) {
|
|
next if (!$compat);
|
|
} elsif ($follow) {
|
|
if ($dep ne $module and !(grep { $_ eq $dep } @processed, @args)) {
|
|
if (check_dep($dep)) {
|
|
verbose("$dep is available, skipping.");
|
|
} else {
|
|
verbose("$dep is not available, adding it to the list.");
|
|
push(@args, $dep);
|
|
}
|
|
}
|
|
}
|
|
printf $spec "%-16s%s", "BuildRequires:", "perl($dep)";
|
|
print $spec (" >= " . $build_requires{$dep})
|
|
if ($build_requires{$dep});
|
|
print $spec "\n";
|
|
}
|
|
|
|
for my $dep (@add_buildrequires) {
|
|
printf $spec "%-16s%s\n", "BuildRequires:", $dep;
|
|
}
|
|
|
|
for my $dep (sort(keys(%requires))) {
|
|
next if (!$compat and exists($corelist{$dep}));
|
|
printf $spec "%-16s%s", "Requires:", "perl($dep)";
|
|
print $spec (" >= " . $requires{$dep}) if ($requires{$dep});
|
|
print $spec "\n";
|
|
}
|
|
|
|
for my $dep (@add_requires) {
|
|
printf $spec "%-16s%s\n", "Requires:", $dep;
|
|
}
|
|
|
|
if (!$compat) {
|
|
print $spec <<END;
|
|
Requires: perl(:MODULE_COMPAT_\%(eval "`\%{__perl} -V:version`"; echo \$version))
|
|
END
|
|
}
|
|
|
|
for my $prov (@add_provides) {
|
|
printf $spec "%-16s%s\n", "Provides:", $prov;
|
|
}
|
|
|
|
if (@filter_requires) {
|
|
print $spec <<END
|
|
|
|
Source98: $name-filter-requires.sh
|
|
\%global real_perl_requires \%{__perl_requires}
|
|
\%define __perl_requires \%{_tmppath}/\%{name}-\%{version}-\%{release}-\%(\%{__id_u} -n)-filter-requires
|
|
END
|
|
}
|
|
|
|
if (@filter_provides) {
|
|
print $spec <<END
|
|
|
|
Source99: $name-filter-provides.sh
|
|
\%global real_perl_provides \%{__perl_provides}
|
|
\%define __perl_provides \%{_tmppath}/\%{name}-\%{version}-\%{release}-\%(\%{__id_u} -n)-filter-provides
|
|
END
|
|
}
|
|
|
|
my $buildpath=$path;
|
|
$buildpath=~s/$version/\%{version}/;
|
|
print $spec <<END;
|
|
|
|
\%description
|
|
$description
|
|
|
|
\%prep
|
|
\%setup -q@{[($noprefix ? "" : " -n $buildpath")]}
|
|
END
|
|
|
|
if (@filter_requires) {
|
|
print $spec <<'END';
|
|
|
|
sed -e 's,@@PERL_REQ@@,%{real_perl_requires},' %{SOURCE98} > %{__perl_requires}
|
|
chmod +x %{__perl_requires}
|
|
END
|
|
}
|
|
|
|
if (@filter_provides) {
|
|
print $spec <<'END';
|
|
|
|
sed -e 's,@@PERL_PROV@@,%{real_perl_provides},' %{SOURCE99} > %{__perl_provides}
|
|
chmod +x %{__perl_provides}
|
|
END
|
|
}
|
|
|
|
if (grep { $_ eq "pm_to_blib" } @files) {
|
|
print $spec <<'END';
|
|
|
|
rm -f pm_to_blib
|
|
END
|
|
}
|
|
|
|
print $spec <<END;
|
|
|
|
\%build
|
|
END
|
|
|
|
if ($usebuildpl) {
|
|
print $spec <<END;
|
|
\%{__perl} Build.PL installdirs=vendor@{[$noarch ? '' : qq{ optimize="$macro{optimize}"} ]}
|
|
./Build
|
|
END
|
|
} else {
|
|
print $spec <<END;
|
|
\%{__perl} Makefile.PL INSTALLDIRS=vendor@{[$noarch ? '' : qq{ OPTIMIZE="$macro{optimize}"}]}
|
|
END
|
|
|
|
print $spec
|
|
"\%{__perl} -pi -e 's/^\\tLD_RUN_PATH=[^\\s]+\\s*/\\t/' Makefile\n"
|
|
if ($compat and !$noarch);
|
|
|
|
print $spec <<END;
|
|
make \%{?_smp_mflags}
|
|
END
|
|
}
|
|
|
|
print $spec <<END;
|
|
|
|
\%install
|
|
rm -rf $macro{buildroot}
|
|
|
|
END
|
|
|
|
if ($usebuildpl) {
|
|
print $spec
|
|
"./Build install destdir=$macro{buildroot} create_packlist=0\n";
|
|
} else {
|
|
print $spec <<END;
|
|
make pure_install PERL_INSTALL_ROOT=$macro{buildroot}
|
|
|
|
find $macro{buildroot} -type f -name .packlist -exec rm -f {} \\;
|
|
END
|
|
}
|
|
|
|
if (!$noarch) {
|
|
print $spec <<END;
|
|
find $macro{buildroot} -type f -name '*.bs' -size 0 -exec rm -f {} \\;
|
|
END
|
|
}
|
|
|
|
print $spec <<END;
|
|
find $macro{buildroot} -depth -type d -exec rmdir {} 2>/dev/null \\;
|
|
|
|
\%{_fixperms} $macro{buildroot}/*
|
|
|
|
END
|
|
|
|
if ($addlicense and !grep /copying|artistic|copyright|license/i, @doc) {
|
|
print $spec <<END;
|
|
perldoc -t perlgpl > COPYING
|
|
perldoc -t perlartistic > Artistic
|
|
|
|
END
|
|
|
|
push(@doc, "COPYING", "Artistic");
|
|
}
|
|
|
|
print $spec <<END;
|
|
\%check@{[($compat ? ' || :' : '')]}
|
|
END
|
|
if ($usebuildpl) {
|
|
print $spec "./Build test\n";
|
|
} else {
|
|
print $spec "make test\n";
|
|
}
|
|
|
|
print $spec <<END;
|
|
|
|
\%clean
|
|
rm -rf $macro{buildroot}@{[
|
|
(@filter_requires ? ' %{__perl_requires}' : '') .
|
|
(@filter_provides ? ' %{__perl_provides}' : '')]}
|
|
|
|
\%files
|
|
\%defattr(-,root,root,-)
|
|
\%doc @doc
|
|
END
|
|
|
|
if ($scripts) {
|
|
print $spec "\%{_bindir}/*\n";
|
|
# FIXME - How do we auto-detect man pages?
|
|
}
|
|
|
|
if ($noarch) {
|
|
print $spec "$lib/*\n";
|
|
} else {
|
|
print $spec "$lib/auto/*\n$lib/" . (split /::/, $module)[0] . "*\n";
|
|
}
|
|
|
|
print $spec <<END;
|
|
\%{_mandir}/man3/*
|
|
|
|
\%changelog
|
|
* $date $packager $version-$release
|
|
- Specfile autogenerated by $NAME $VERSION.
|
|
END
|
|
|
|
$spec->close();
|
|
|
|
build_rpm($specfile) if ($buildsrpm or $buildrpm);
|
|
|
|
push(@processed, $module);
|
|
}
|
|
|
|
# vi: set ai et:
|