Files
cpanspec/cpanspec
Petr Šabata bf1eedf57c Filter out all META.* and *.ini files from %doc
META.json and DistZilla's dist.ini are pretty common these days and
we're not interested in them, so let's drop them.

Signed-off-by: Petr Šabata <contyk@redhat.com>
2014-12-05 15:05:58 +01:00

1325 lines
37 KiB
Perl
Executable File

#!/usr/bin/perl
#
# cpanspec - Generate a spec file for a CPAN module
#
# Copyright (C) 2004-2011 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.68 2009/01/22 01:48:06 stevenpritchard Exp $
our $NAME="cpanspec";
our $VERSION='1.79';
=head1 NAME
cpanspec - Generate a spec file for a CPAN module
cpanget - Download a CPAN module
=head1 SYNOPSIS
cpanspec [options] [module [...]]
cpanget [options] [module [...]]
Options:
--help -h Help message
--old -o Be more compatible with old RHL/FC releases
--perl-version -P Minimum perl version to support
--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
--download-only -D Only download the tarball from CPAN
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
The modules specified on the command line can be either filenames of tarballs,
or module names as downloadable from CPAN.
=head1 EXAMPLES
cpanspec Linux::Prctl
cpanspec ~/downloads/Acme-MetaSyntactic-0.99.tar.gz
cpanget Module::Build
=head1 DESCRIPTION
B<cpanspec> will generate a spec file to build a rpm from a CPAN-style
Perl module distribution.
B<cpanget> will download the tarball for a module from CPAN. This
implies --download-only and ignores most other options.
=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<-P>, B<--perl-version>
Include explicit dependencies for core modules when newer versions are
required than shipped with the specified version.
=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<-S>, B<--stdout>
Write the generated spec file to stdout instead of a file.
=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<-D>, B<--download-only>
Only download the upstream tarball, do not generate a specfile. Most
other options are ignored as they are only relevant when creating a
specfile or building a package.
=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;
use Module::CoreList;
# Apparently gets pulled in by another module.
#use Cwd;
our %opt;
our $help=0;
our $compat=0;
our $perl_version=0;
our $addlicense=0;
our $noprefix=0;
our $force=0;
our $stdout=0;
our $packager;
our $release=1;
our $epoch;
our $disttag='%{?dist}';
our $download_only=(basename($0) eq "cpanget") ? 1 : 0;
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' => '%{optflags}',
'buildroot' => '%{buildroot}',
},
);
# this is set after the parameters are passed
our %macro;
our %dep_blacklist = (
'Win32' => 1, # We're not on win32
'Win32::Process' => 1, # We're not on win32
'Test::Perl::Critic' => 1, # Too many dependencies
'ok' => 1, # Side effect of Test::use::ok
'BaseClass' => 1, # Common test class shipped by ourselves
'TestServer' => 1, # Likewise
);
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=which("rpmbuild","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=which("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=which("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)"));
}
sub which(@) {
for my $bin (@_) {
for my $path (split /:/, $ENV{PATH}) {
return "$path/$bin" if -x "$path/$bin";
}
}
die("Cannot find " . join(" or ", @_));
}
# 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,
'perl-version|P=s' => \$perl_version,
'license|l' => \$addlicense,
'noprefix|n' => \$noprefix,
'force|f' => \$force,
'stdout|S' => \$stdout,
'packager|p=s' => \$packager,
'release|r=i' => \$release,
'epoch|e=i' => \$epoch,
'disttag|d=s' => \$disttag,
'download-only|D' => \$download_only,
'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-";
my $rpm=which("rpm");
$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;
if($perl_version) {
die("Unknown perl version: $perl_version. (Known versions: " . join(", ", keys %Module::CoreList::version) . ")")
unless exists $Module::CoreList::version{$perl_version};
%corelist = %{ $Module::CoreList::version{$perl_version} };
}
else {
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=();
update_packages();
$packages=Parse::CPAN::Packages->new($pkgdetails)
if (!defined($packages));
die "Parse::CPAN::Packages->new() failed: $!\n"
if (!defined($packages));
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.
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);
next if $download_only;
$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)) {
next if ($type eq 'tar' and $entry eq 'pax_global_header');
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|ini?|pod|cfg|inl)$/i
and !/^\./
and $_ ne $path
and $_ ne "MANIFEST"
and $_ ne "MANIFEST.SKIP"
and $_ ne "INSTALL"
and $_ ne "SIGNATURE"
and !/^META\..+$/i
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|h|xs|inl)$/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);
map { s/\//\\\//g } @filter_requires;
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);
map { s/\//\\\//g } @filter_provides;
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 ($stdout) {
$spec = *STDOUT;
} elsif ($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="";
# TODO It's possible these need to be listed instead of just detected.
my $uses_autoinstall = grep /Module\/AutoInstall.pm/, @files;
my $scripts=(grep /^(?:bin|scripts?|tools)\//, @files);
my $makecontent;
if (grep /^Makefile\.PL$/, @files
and $makecontent=extract($archive, $type, "$path/Makefile.PL")) {
$scripts ||= $makecontent =~ 'install_script';
$scripts ||= ($makecontent =~ /EXE_FILES/ && $makecontent !~ /EXE_FILES.*\[\s*(qw\(\s*\)\s*)?\]/) ? 1 : 0;
}
if (grep /^Build\.PL$/, @files
and $makecontent=extract($archive, $type, "$path/Build.PL")) {
$scripts ||= $makecontent =~ 'script_files';
}
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});
%build_requires=(%build_requires, %{$meta->{configure_requires}}) if ($meta->{configure_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} =~ /^artistic_?2$/i) {
$license="Artistic 2.0";
} elsif ($meta->{license} =~ /^bsd$/i) {
$license="BSD";
} elsif ($meta->{license} =~ /^gpl$/i) {
$license="GPL+";
} elsif ($meta->{license} =~ /^lgpl$/i) {
$license="LGPLv2+";
} elsif ($meta->{license} =~ /^mit$/i) {
$license="MIT";
} elsif ($meta->{license} =~ /^mozilla$/i) {
$license="MPL";
} elsif ($meta->{license} =~ /^cc0$/i) {
$license="CC0";
} 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);
# Add build dependencies for modules used in tests.
my @tests=grep {
$_ eq "test.pl"
or $_ =~ /^t\/.*\.t$/
} @files;
use Module::ExtractUse;
my %bogus_dep=();
for my $testfile (@tests) {
my $extract_o_matic=Module::ExtractUse->new()
or die "Module::ExtractUse->new failed: $!\n";
my $test=extract($archive, $type, $path . '/' . $testfile);
if (!defined($test)) {
warn "Failed to extract $testfile from $archive: $!\n";
next;
}
$extract_o_matic->extract_use(\$test);
for my $dep ($extract_o_matic->array()) {
next if ($dep eq $module);
next if exists $build_requires{$dep};
next if exists $bogus_dep{$dep};
my $package = $packages->package($dep);
if (!defined($package)) {
$bogus_dep{$dep}++;
# Don't print this for common false positives
unless ($dep =~ /^(Config|base|5|lib)/) {
verbose("Module not on CPAN, not adding dependency: $dep");
}
next;
}
if ($package->distribution->dist eq $name) {
#verbose("Dependency $dep provided by ourselves, not adding dependency");
next;
}
$build_requires{$dep} ||= 0;
}
}
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(STDIN, ">/dev/null");
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})) {
$build_requires{perl} =~ s/^[<>=]+ *//;
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))) {
next if exists $dep_blacklist{$dep};
if (exists($corelist{$dep}) && $corelist{$dep} ge $build_requires{$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($uses_autoinstall) {
print $spec <<END
export PERL_AUTOINSTALL="--skipdeps"
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";
print $spec "\%{_mandir}/man1/*\n";
}
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: