From 5d4604ed1458bc336f5a0dd02c4437fb6bc0b2e1 Mon Sep 17 00:00:00 2001 From: misaka00251 Date: Fri, 12 Sep 2025 18:03:18 +0800 Subject: [PATCH] Import from cpanspec project --- Build.PL | 34 ++ cpanget | 75 ++++ cpanspec | 1192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1301 insertions(+) create mode 100644 Build.PL create mode 100755 cpanget create mode 100755 cpanspec diff --git a/Build.PL b/Build.PL new file mode 100644 index 0000000..2efa8ab --- /dev/null +++ b/Build.PL @@ -0,0 +1,34 @@ +use strict; +use warnings; +use Module::Build; + +my $builder = Module::Build->new( + dist_name => 'cpanspec', + license => 'perl', + dist_author => 'Steven Pritchard ', + dist_version_from => 'cpanspec', + requires => { + 'Archive::Tar' => 0, + 'Archive::Zip' => 0, + 'IO::Uncompress::Bunzip2' => 0, + 'File::Basename' => 0, + 'FileHandle' => 0, + 'Getopt::Long' => 0, + 'locale' => 0, + 'LWP::UserAgent' => 0, + 'POSIX' => 0, + 'Parse::CPAN::Packages' => 0, + 'Pod::Simple::TextContent' => 0, + 'Pod::Usage' => 0, + 'Text::Autoformat' => 0, + 'YAML' => 0, + }, + script_files => [ + 'cpanspec', + 'cpanget', + ], +); + +$builder->create_build_script(); + +# vi: set ai et: diff --git a/cpanget b/cpanget new file mode 100755 index 0000000..936643f --- /dev/null +++ b/cpanget @@ -0,0 +1,75 @@ +#!/bin/sh + +set -e + +CPAN=${CPAN:-"http://www.cpan.org"} +packages=$HOME/.cpan/sources/modules/02packages.details.txt.gz + +quiet='' +only_url=0 + +help() { + cat <&2 + continue + fi + + if [ $only_url -eq 0 ] ; then + curl $quiet -R -o "$( basename $tar )" $CPAN/authors/id/$tar + else + echo "$module: $CPAN/authors/id/$tar" + fi +done + +# vi: set ai et: diff --git a/cpanspec b/cpanspec new file mode 100755 index 0000000..a4e9136 --- /dev/null +++ b/cpanspec @@ -0,0 +1,1192 @@ +#!/usr/bin/perl +# +# cpanspec - Generate a spec file for a CPAN module +# +# Copyright (C) 2004-2009 Steven Pritchard +# 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.67 2009/01/16 20:35:17 stevenpritchard Exp $ + +our $NAME="cpanspec"; +our $VERSION='1.78'; + +=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 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 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 instead of B. + +=item B<-f>, B<--force> + +Force overwriting an existing spec file. Normally B 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 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 environment variable, defaults to L. + +=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 + +=head1 SEE ALSO + +L, L, L + +=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)) { + 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|in|pod|cfg|inl)$/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|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); + + 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} =~ /^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} =~ /^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(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 () { + if (/^BuildRequires:\s*(\S+)\s*(\S+)?/) { + my $dep=$1; + my $version=0; + $version=$2 if (defined($2)); + $build_requires{$dep}=$version; + } + } + } + } + + print $spec <= %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 < %{__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 </dev/null \\; + +\%{_fixperms} $macro{buildroot}/* + +END + + if ($addlicense and !grep /copying|artistic|copyright|license/i, @doc) { + print $spec < COPYING +perldoc -t perlartistic > Artistic + +END + + push(@doc, "COPYING", "Artistic"); + } + + print $spec <close(); + + build_rpm($specfile) if ($buildsrpm or $buildrpm); + + push(@processed, $module); +} + +# vi: set ai et: