diff --git a/Changes b/Changes new file mode 100644 index 0000000..f7c7381 --- /dev/null +++ b/Changes @@ -0,0 +1,58 @@ +Revision history for perl-generators. + +1.10 Fri Jul 1 2016 +- Update Makefile.PL to be able change requires/provides namespace for + software collection + +1.09 Thu Jun 23 2016 +- Fix regression in parsing of heredoc + +1.08 Fri Mar 18 2016 +- Accept square brackets for list of dependencies +- Fixed generator to produce provides from code like this + "package IRI {}" (BZ#1318658) + +1.07 Tue Oct 20 2015 +- Return perl version as normalized perl(:VERSION) symbol + + This changes two things how "require 5.006" is handled: + 1) The version is "5.006" is normalized to RPM-friendly format "5.6.0" + without any epoch number. If the version cannot be normalized, the + dependency is not exported. + 2) The requirement is exported as version of "perl(:VERSION)" RPM + symbol instead of "perl" RPM symbol. + + +1.06 Tue Oct 5 2015 +- Do not process results that contain direct method calls + +1.05 Fri Oct 2 2015 +- Fixed parsing of "use base" to find out a bareword (BZ#1267267) +- Update parsing of provides version when 'use version' is called + +1.04 Tue Jul 28 2015 +- Update parsing of provide's version +- Add changes released in rpm 4.12.0 + +1.03 Mon Feb 2 2015 +- Update parsing of here-doc and quoted section which should be skipped +- Update tests suite + +1.02 Fri Dec 12 2014 +- Fix BZ#1172716 - update regex to properly match the module name +- Update tests suite +- Update the steps to get package version + +1.01 Tue Oct 21 2014 +- Fixed BZ#1160263 - do not catch numeric substring in the + variable name for VERSION +- Update requires generator to: + - ignore variables in statement + - .pm files are not added with 'use' + - argument of 'aliased' is added + +1.00 Tue May 22 2014 +- First version +- Take the Perl generators and fileattrs file from rpm-build 4.11.2 + and create perl-generators package + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..b477146 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,31 @@ +Changes +Makefile.PL +MANIFEST This list of files +MANIFEST.SKIP +t/01_basic.t +t/02_list.t +t/03_anymoose.t +t/04_provides.t +t/05_whitespace.t +t/06_variables.t +t/07_multiline.t +t/08_heredoc.t +t/data/anymoose +t/data/basic +t/data/heredoc +t/data/list +t/data/multiline +t/data/provides +t/data/todo +t/data/variables +t/data/whitespace +t/test.t +t/testdata +template/bin/perl.prov +template/bin/perl.req +template/fileattrs/perl.attr +template/fileattrs/perllib.attr +template/t/lib/PerlNS.pm +TODO +META.yml Module YAML meta-data (added by MakeMaker) +META.json Module JSON meta-data (added by MakeMaker) diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..f05a8a1 --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,5 @@ +^(./|)bin/ +^(./|)fileattrs/ +^(./|)t/lib/ +MYMETA.* +Makefile$ diff --git a/META.json b/META.json new file mode 100644 index 0000000..97d77fd --- /dev/null +++ b/META.json @@ -0,0 +1,48 @@ +{ + "abstract" : "unknown", + "author" : [ + "Jitka Plesnikova " + ], + "dynamic_config" : 1, + "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150005", + "license" : [ + "open_source" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "generators", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : { + "requires" : { + "Test::More" : "0", + "strict" : "0", + "warnings" : "0" + } + }, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0" + } + }, + "runtime" : { + "requires" : { + "Fedora::VSP" : "0", + "version" : "0" + } + } + }, + "release_status" : "stable", + "resources" : { + "homepage" : "http://jplesnik.fedorapeople.org/generators/" + }, + "version" : "1.10", + "x_serialization_backend" : "JSON::PP version 2.27300" +} diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..7ebcb16 --- /dev/null +++ b/META.yml @@ -0,0 +1,28 @@ +--- +abstract: unknown +author: + - 'Jitka Plesnikova ' +build_requires: + Test::More: '0' + strict: '0' + warnings: '0' +configure_requires: + ExtUtils::MakeMaker: '0' +dynamic_config: 1 +generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150005' +license: open_source +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: generators +no_index: + directory: + - t + - inc +requires: + Fedora::VSP: '0' + version: '0' +resources: + homepage: http://jplesnik.fedorapeople.org/generators/ +version: '1.10' +x_serialization_backend: 'CPAN::Meta::YAML version 0.016' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..8395d05 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,68 @@ +use strict; +use warnings; + +use ExtUtils::MakeMaker; +use File::Copy; +use File::Find; + +my $perl_ns = "perl"; +my $suffix = ""; + +if (defined $ENV{'PERL_NS'} && $ENV{'PERL_NS'} !~ /^$/) { + $perl_ns = $ENV{'PERL_NS'} . "-" . $perl_ns; + $suffix = "." . $ENV{'PERL_NS'}; +} + +my @directories_to_search = ("template"); +File::Find::find({ + wanted => \&wanted, + untaint => 1, + no_chdir => 1, + }, @directories_to_search); + +sub wanted { + my $tmpl = $File::Find::name; + my $dir = $File::Find::dir; + if ( -f $tmpl) { + my $file = $tmpl; + $file =~ s/template\///; + $dir =~ s/template\///; + system("mkdir $dir") if (! -d $dir); + $file = $file.$suffix if ($file =~ m/bin/); + copy("$tmpl", "$file") or die "Copy failed: $!"; + system("sed -i 's/__PERL_NS__/$perl_ns/' $file"); + system("sed -i 's/__PERL_SUFFIX__/$suffix/' $file"); + system("chmod 755 $file") if ($file =~ m/bin/); + } +} + +WriteMakefile( + 'NAME' => 'generators', + 'VERSION' => '1.10', + 'AUTHOR' => 'Jitka Plesnikova ', + 'LICENSE' => 'gpl', + 'EXE_FILES' => [ "bin/perl.prov$suffix", "bin/perl.req$suffix" ], + 'BUILD_REQUIRES' => { + }, + 'PREREQ_PM' => { + 'Fedora::VSP' => 0, + 'version' => 0, + }, + TEST_REQUIRES => { + 'Test::More' => 0, + 'strict' => 0, + 'warnings' => 0, + }, + 'CONFIGURE_REQUIRES' => { + 'ExtUtils::MakeMaker' => 0, + }, + 'META_MERGE' => { + 'resources' => { + 'homepage' => + 'http://jplesnik.fedorapeople.org/generators/', + }, + }, + 'test' => { + 'TESTS' => 't/*.t', + }, +); diff --git a/TODO b/TODO new file mode 100644 index 0000000..af74109 --- /dev/null +++ b/TODO @@ -0,0 +1,21 @@ +The following definitions of requires are not process properly: + +# Only the first module is found +use POE qw(System::Wheel Client::HTTP); + +use Test::Requires qw(TestRequiresArray1 TestRequiresArray2); +use Test::Requires {TestRequiresHash1 => 0.1, TestRequiresHash2 => 0.2}; + +# The modules defined by 'with' are not found +use Moose; +with 'Foo::Bar' => { -version => 0.01 }, + 'Bar::Baz' => { -version => 0.03 }; + +# Do not process 'no' yet +no File::Path; + +# Does not find modules defined at 'eval' +eval{ require File::Spec; 1} or die; + +https://metacpan.org/source/ETHER/Module-Runtime-Conflicts-0.001/lib/Module/Runtime/Conflicts.pm +BEGIN { use Test::More } diff --git a/t/01_basic.t b/t/01_basic.t new file mode 100644 index 0000000..80de332 --- /dev/null +++ b/t/01_basic.t @@ -0,0 +1,40 @@ +use strict; +use warnings; +use Test::More tests => 2; +use Test::Simple; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $files = "t/data/basic"; +my @requires = qx($PERL_REQ $files); +my @provides = qx($PERL_PROV $files); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(:VERSION) >= 5.6.1\n", + "$perl_ns(:VERSION) >= 5.6.0\n", + "$perl_ns(:VERSION) >= 5.10.0\n", + "$perl_ns(Alpha) >= 0.12\n", + "$perl_ns(Alpha::One)\n", + "$perl_ns(Alpha::Two)\n", + "$perl_ns(Beta)\n", + "$perl_ns(Epsilon)\n", + "$perl_ns(Eta)\n", + "$perl_ns(ExtUtils::MM_Unix)\n", + "$perl_ns(Gamma)\n", + "$perl_ns(Iota)\n", + "$perl_ns(Kappa)\n", + "$perl_ns(Lambda)\n", + "$perl_ns(Theta)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); + diff --git a/t/02_list.t b/t/02_list.t new file mode 100644 index 0000000..a0b8206 --- /dev/null +++ b/t/02_list.t @@ -0,0 +1,43 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/list"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(Alpha)\n", + "$perl_ns(Beta)\n", + "$perl_ns(Gamma)\n", + "$perl_ns(Delta)\n", + "$perl_ns(Epsilon)\n", + "$perl_ns(Some::Crazy::Module)\n", + "$perl_ns(Another::Crazy::Module)\n", + "$perl_ns(aliased)\n", + "$perl_ns(base)\n", + "$perl_ns(parent)\n", + "$perl_ns(Theta)\n", + "$perl_ns(Kappa::Lambda)\n", + "$perl_ns(Mu::Nu)\n", + "$perl_ns(Try)\n", + "$perl_ns(This)\n", + "$perl_ns(One)\n", + "$perl_ns(constant)\n", + "$perl_ns(TARGET_CLASS)\n", + "$perl_ns(XML::XQL::Element)\n", + "$perl_ns(Class::Accessor::Fast)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); diff --git a/t/03_anymoose.t b/t/03_anymoose.t new file mode 100644 index 0000000..e1a8090 --- /dev/null +++ b/t/03_anymoose.t @@ -0,0 +1,26 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/anymoose"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(Any::Moose) >= 0.18\n", + "$perl_ns(Mouse)\n", + "$perl_ns(Mouse::Role)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); diff --git a/t/04_provides.t b/t/04_provides.t new file mode 100644 index 0000000..1de1805 --- /dev/null +++ b/t/04_provides.t @@ -0,0 +1,49 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/provides"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Requires +my @expectedrequires = ("$perl_ns(version)\n"); +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only '$perl_ns(version)' is required."); + +# +# Provides +my @expectedprovides = ( + "$perl_ns(Alpha)\n", + "$perl_ns(Alpha::Beta123) = 1.23\n", + "$perl_ns(Beta) = 1.00\n", + "$perl_ns(CGI::Apache) = 1.7\n", + "$perl_ns(Config::General)\n", + "$perl_ns(DBI) = 1.633\n", + "$perl_ns(Delta) = 0.50\n", + "$perl_ns(DynaLoader) = 1.03\n", + "$perl_ns(ExtUtils::Install) = 1.8\n", + "$perl_ns(ExtUtils::MM_Unix)\n", + "$perl_ns(FindBin) = 1.9\n", + "$perl_ns(Gamma) = 2.00\n", + "$perl_ns(Iota) = 1\n", + "$perl_ns(Kappa::Lambda) = 5.43\n", + "$perl_ns(Omega) = 9.87\n", + "$perl_ns(Template) = 3.45\n", + "$perl_ns(Test::Kwalitee::Extra) = 0.3.0\n", + "$perl_ns(Test::Pod::No404s) = 0.02\n", + "$perl_ns(Theta)\n", + "$perl_ns(XML::Grove) = 0.46\n", + "$perl_ns(Xi)\n", + "$perl_ns(Zeta) = 0.05\n", + "$perl_ns(Module::ExtractUse) = 0.33\n", + "$perl_ns(Module::Info)\n", + "$perl_ns(IRI) = 0.004\n", +); + +is_deeply([ sort @provides ], [ sort @expectedprovides ], "All expected provides were found."); diff --git a/t/05_whitespace.t b/t/05_whitespace.t new file mode 100644 index 0000000..fa261a9 --- /dev/null +++ b/t/05_whitespace.t @@ -0,0 +1,24 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/whitespace"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(Use::WhiteSpace)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); diff --git a/t/06_variables.t b/t/06_variables.t new file mode 100644 index 0000000..9b1baae --- /dev/null +++ b/t/06_variables.t @@ -0,0 +1,20 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/variables"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +is(scalar(@requires), 0, 'No package is required'); diff --git a/t/07_multiline.t b/t/07_multiline.t new file mode 100644 index 0000000..6ca89af --- /dev/null +++ b/t/07_multiline.t @@ -0,0 +1,25 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/multiline"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +is(scalar(@provides), 0, 'No package is provided'); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(At::The::End)\n", + "$perl_ns(overload)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); diff --git a/t/08_heredoc.t b/t/08_heredoc.t new file mode 100644 index 0000000..0a7ed71 --- /dev/null +++ b/t/08_heredoc.t @@ -0,0 +1,35 @@ +use strict; +use warnings; +use Test::More tests => 2; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my $file = "t/data/heredoc"; +my @requires = qx($PERL_REQ $file); +my @provides = qx($PERL_PROV $file); + +# +# Provides +my @expectedprovides = ( + "$perl_ns(More::Then::Two::Mark)\n", + "$perl_ns(Not::In::Heredoc)\n", + "$perl_ns(THAT)\n", +); + +is_deeply([ sort @provides ], [ sort @expectedprovides ], "Only expected provides were found."); + +# +# Requires +my @expectedrequires = ( + "$perl_ns(Bitwise::Operator)\n", + "$perl_ns(constant)\n", + "$perl_ns(More::Then::Two::Mark)\n", + "$perl_ns(Not::Hang)\n", + "$perl_ns(Not::In::Heredoc)\n", + "$perl_ns(THAT)\n", +); + +is_deeply([ sort @requires ], [ sort @expectedrequires ], "Only expected requires were found."); diff --git a/t/data/anymoose b/t/data/anymoose new file mode 100644 index 0000000..b4711eb --- /dev/null +++ b/t/data/anymoose @@ -0,0 +1,4 @@ +use Any::Moose; +use Any::Moose qw(Role); +use Any::Moose 'Alpha'; +use Any::Moose 0.18 'Role::Alpha'; diff --git a/t/data/basic b/t/data/basic new file mode 100644 index 0000000..8ba52c7 --- /dev/null +++ b/t/data/basic @@ -0,0 +1,28 @@ +# Inversioned Alpha should not be listed +use Alpha; +use Alpha 0.12; + +use Alpha::One; +use qw(Alpha::Two); +use Beta (); +require Gamma; +use ExtUtils::MM_Unix; + +eval { require Delta }; + +# TODO: Don't recognize more than one 'use' or 'require' at one line +use Epsilon; use Zeta; + +require v5.6.1; +require 5.006_001; +use 5.010; + +# 'use' does not accept module name with .pm +use 'Ignore.pm'; + +require Eta; +require 'Theta.pm'; +require "Iota.pm"; +require q !Kappa.pm!; +require qq{Lambda.pm}; + diff --git a/t/data/heredoc b/t/data/heredoc new file mode 100644 index 0000000..520c2b0 --- /dev/null +++ b/t/data/heredoc @@ -0,0 +1,177 @@ +# +# 'authentication' should not be found, because it is part of "= <<" block +twitter_api_method suggestion_categories => ( + path => 'users/suggestions', + method => 'GET', + params => [], + required => [], + returns => 'ArrayRef', + description => <<'' +Returns the list of suggested user categories. The category slug can be used in +the C API method get the users in that category . Does not +require authentication +package authentication; + +); + +%hash = ( + "text" => << 'EOT' +use and require is a horrible stuff +package EOT +EOT +); + +$template = <get_diff($log) ); + return << "HERE"; +package Use::Template; +use Template; +HERE +} + +$pod .= << 'HERE'; +package Simply::Pod; +use Simply::Pod; +HERE + +push @LIB, <note_template( $log, << "HERE" ); +perldelta: $section [pending] +use +HERE + +write_file( File::Spec->catfile( $fulldir, 'bazmarkhian.al' ), <<'EOT' ); +package Foo; +use Foo; +1; +EOT + +is (eval <<'END', 1, 'lexical topic compiles') or diag $@; + package Experimental + ; + use experimental 'lexical_topic'; + my $_ = 1; + is($_, 1, '$_ is 1'); +END + + +# Multiple here-docs does not properly. The skipping finish on the first tag +myfunc(<< "THIS", 23, <<'THAT'); +package THIS; +use THIS; +THIS +package THAT; +use THAT; +THAT + +print <<< 'test'; +package More::Then::Two::Mark; +use More::Then::Two::Mark; + +print 20 << 20; +print 20<<20; +use Bitwise::Operator; + +use constant COPYRIGHT_SHORT => <>}, + Parse::RecDescent::_tracefirst($_[1]), + q{' . $self->{"name"} .'}, + $tracelevel) +} + +$trase = q!<; +use parent 'Epsilon'; +use parent qw{Theta}; + +# 'Zeta', 'Iota' should not be found +use parent -norequire, qw(Zeta Iota); + +use aliased "Some::Crazy::Module"; + +# 'ShorterName' should not be found +use aliased "Another::Crazy::Module" => "ShorterName"; + +use base Kappa::Lambda; + +use base Mu::Nu::; + +use base qw/ + Try +/; + +use base qw ( + This + One ); + +use parent qw[Class::Accessor::Fast]; + +# Two examples from perl-Sys-Info-Base +# "__PACKAGE__" should not be found +use base __PACKAGE__->load_subclass('Sys::Info::Driver::%s::Device::CPU'); + +# It is not possible to filter constant which is used as a module +use constant TARGET_CLASS => __PACKAGE__->load_subclass('Sys::Info::Driver::%s::OS'); +use base TARGET_CLASS; + +# Do not ignore line which contains '->' in a coment +use base 'XML::XQL::Element'; # L -> L diff --git a/t/data/multiline b/t/data/multiline new file mode 100644 index 0000000..0faff69 --- /dev/null +++ b/t/data/multiline @@ -0,0 +1,15 @@ +# Only a module 'overload' should be reported from the file +use overload + '+' => \&myadd, + '-' => \&mysub; + +# 'these' should not be found as requirement +my $lwp_note = " Sa-update will use curl, wget or fetch to download updates. + Because LWP does not support IPv6, sa-update as of 3.4.0 will + use these standard programs to download rule updates leaving LWP + as a fallback if none of the programs are found. + + *IMPORTANT NOTE*: You only need one of these programs."; + + +use At::The::End diff --git a/t/data/provides b/t/data/provides new file mode 100644 index 0000000..2498c6b --- /dev/null +++ b/t/data/provides @@ -0,0 +1,87 @@ +package Alpha; + +package Beta; +$Beta::VERSION = '1.00'; + +package Gamma; +our $VERSION = '2.00'; + +package Delta; +our $VERSION = 0.01; +$VERSION = 0.50; + +package Test::Pod::No404s; +$Test::Pod::No404s::VERSION = '0.02'; + +package FindBin; +our $VERSION = $VERSION = sprintf("%d.%02d", q$Revision: 1.9 $ =~ /(\d+)\.(\d+)/); + +package ExtUtils::Install; +$VERSION = substr q$Revision: 1.8 $, 10; + +package CGI::Apache; +$VERSION = (qw$Revision: 1.7 $)[1]; + +package DynaLoader; +$VERSION = $VERSION = "1.03"; # avoid typo warning + +# version should not be found +package Config::General; +$Config::General::VERSION == 2.33; + +package Alpha::Beta123; +our $VERSION = $Alpha::Beta123::VERSION=1.23; + +package Template; +$Template::VERSION ="3.45"; + +package Zeta; +$VERSION = "1.01"; +$VERSION = $VERSION + 0.05; + +package Theta; +$tmp = 1.56 +$VERSION = $tmp; + +package Iota; +$Iota::VERSION = 1; + +package Omega; +$VERSION="9.87_65"; + +package Test::Kwalitee::Extra; +our $VERSION = 'v0.3.0'; + +package XML::Grove; +$VERSION = '0.46alpha'; + +package Kappa::Lambda v5.43; + +# version should not be found + package Xi; + $VERSION =~ s/1.24/1.00/; + +package ExtUtils::MM_Unix; + +# Multiline module definition are ignored for now +package # hide form PAUSE + DBIx::Class::CDBICompat::AbstractSearch; + +package + Sigma + 1.27 + ; + +package DBI; +our $XS_VERSION = our $VERSION = "1.633"; # ==> ALSO update the version in the pod text below! + +package Module::ExtractUse; +use version; our $VERSION=version->new('0.33'); + +# Do not process this kind of version definition +package Module::Info; +$VERSION = eval 'use version; 1' ? 'version'->new('0.35') : '0.35'; + +package IRI { + our $VERSION = '0.004'; +} diff --git a/t/data/todo b/t/data/todo new file mode 100644 index 0000000..7ae99f7 --- /dev/null +++ b/t/data/todo @@ -0,0 +1,15 @@ +# Examples of currently ignore requires + +eval { require Alpha }; +require Beta if $something > $somethingelse; + +# Parameters for module 'if' are not handle properly +use if $] < 5.008, "Gamma"; +use if WANTED, Delta => qw(Epsilon); + +use $alfa; +use @beta 1.00; +use %charlie qw//; +use Delta.pm; +require *delta; + diff --git a/t/data/variables b/t/data/variables new file mode 100644 index 0000000..fa40ddd --- /dev/null +++ b/t/data/variables @@ -0,0 +1,4 @@ +use $alfa; +use @beta 1.00; +use %charlie qw//; +require *delta; diff --git a/t/data/whitespace b/t/data/whitespace new file mode 100644 index 0000000..1aec654 --- /dev/null +++ b/t/data/whitespace @@ -0,0 +1,5 @@ +# TODO: Stop ignoring 'require' with whitespaces at the beginning + require Require::WhiteSpace; + +# 'use' prefixed by whitespace should be find + use Use::WhiteSpace diff --git a/t/test.t b/t/test.t new file mode 100644 index 0000000..32f999c --- /dev/null +++ b/t/test.t @@ -0,0 +1,30 @@ +use strict; +use warnings; + +use Test::More tests => 17; + +use lib 't/lib'; +use PerlNS qw($PERL_NAMESPACE $PERL_PROV $PERL_REQ); + +my $perl_ns = $PERL_NAMESPACE; + +my @requires = qx($PERL_REQ t/testdata); +my @provides = qx($PERL_PROV t/testdata); + +# +# Provides +ok(grep(/$perl_ns\(Test\)/, @provides), 'Test module is provided'); +ok(grep(/$perl_ns\(TMP::tmp\)/, @provides), 'TMP::tmp module is provided'); + +# +# Requires +my @list_requires = ("Test::Simple", "POE", "Any::Moose", "aliased", + "Moose", "Test::Requires", "base", "Exporter", "parent", "DBI", + "File::Copy", "Carp", "Use::WhiteSpace", +); +foreach my $mod (@list_requires) { + ok(grep(/^$perl_ns\($mod\)$/,@requires), "$mod module is required") +} + +ok(grep(/$perl_ns\(:VERSION\) >= 5\.6\.1/, @requires), "'$perl_ns(:VERSION) >= 5.6.1' is required"); +ok(grep(/$perl_ns\(version\) >= 0.77/, @requires), "'$perl_ns(version) >= 0.77' is required"); diff --git a/t/testdata b/t/testdata new file mode 100644 index 0000000..ac51e84 --- /dev/null +++ b/t/testdata @@ -0,0 +1,91 @@ +################################################### +# Examples of provides +################################################### + +package Test; + +package TMP::tmp; + +################################################### +# Examples of requires +################################################### + +################################################### +# Simple 'use' + +use Test::Simple; + +################################################### +# Simple 'use' with whitespaces at the beginning + + use Use::WhiteSpace; + +################################################### +# 'use' with version + +use v5.6.1; +use version 0.77; + +# TODO +use Meow v1.2.3; + +################################################### +# 'use' with parameter +# TODO +# The extra parameters are found only for 'base' and 'parent' + +use overload + + = \+ + +use POE qw(System::Wheel Client::HTTP); +use Any::Moose 'Role'; + +use aliased "Some::Crazy::Module"; +use aliased "Another::Crazy::Module" => "ShorterName"; + +use Moose; +# Is not find yet +with 'Foo::Bar'; # => { -version => 0.01 }, + 'Bar::Baz'; # => { -version => 0.03 }; + +use Test::Requires qw(TestRequiresArray1 TestRequiresArray2); +use Test::Requires {TestRequiresHash1 => 0.1, TestRequiresHash2 => 0.2}; + + +################################################### +# 'use' with 'base' or 'parent + +use base 'Exporter'; +use parent qw(DBI); + +# TODO +# It is not parse correctly yet, because it does not remove anything from list +# of found modules. +# use parent 'NotRequiredByParent1'; +# use parent -norequire, 'NotRequiredByParent1', 'NotRequiredByParent2'; +# +# Do not process 'no' yet +# no File::Path; +# +# use if $[ < 5.8.8, POE => qw(Client::TCP Server::TCP) + +################################################### +# 'require' + +require File::Copy (); +require Carp; + +################################################### +# 'require' with whitespaces at the beginning + +# TODO + require Require::WhiteSpace; + + +# TODO +# Does not find modules defined at 'eval' +eval{ require File::Spec; 1} or die; + +1 + + diff --git a/template/bin/perl.prov b/template/bin/perl.prov new file mode 100755 index 0000000..3dc2cfa --- /dev/null +++ b/template/bin/perl.prov @@ -0,0 +1,203 @@ +#!/usr/bin/perl + +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License . +# There is NO WARRANTY, to the extent permitted by law. + +# This script was originally written by Ken Estes Mail.com +# kestes@staff.mail.com + +# a simple script to print the proper name for Perl libraries. + +# It does not parse the perl grammar but instead just lex it looking for +# what we want. It takes special care to ignore comments and pod's. + +# The filenames to scan are either passed on the command line or if +# that is empty they are passed via stdin. + +# If there are lines in the file which match the pattern +# (m/^\s*\$VERSION\s*=\s+/) +# then these are taken to be the version numbers of the modules. +# Special care is taken with a few known idioms for specifying version +# numbers of files under rcs/cvs control. + +# If there are strings in the file which match the pattern +# m/^\s*\$RPM_Provides\s*=\s*["'](.*)['"]/i +# then these are treated as additional names which are provided by the +# file and are printed as well. + +my $perl_ns = "__PERL_NS__"; + +if ("@ARGV") { + foreach (@ARGV) { + process_file($_); + } +} else { + + # notice we are passed a list of filenames NOT as common in unix the + # contents of the file. + + foreach (<>) { + process_file($_); + } +} + + +foreach $module (sort keys %require) { + if (length($require{$module}) == 0) { + print "$perl_ns($module)\n"; + } else { + + # I am not using rpm3.0 so I do not want spaces around my + # operators. Also I will need to change the processing of the + # $RPM_* variable when I upgrade. + + print "$perl_ns($module) = $require{$module}\n"; + } +} + +exit 0; + + + +sub process_file { + + my ($file) = @_; + chomp $file; + + if (!open(FILE, $file)) { + warn("$0: Warning: Could not open file '$file' for reading: $!\n"); + return; + } + + my ($package, $version, $incomment, $inover) = (); + + while () { + + # skip the here-docs "<<" blocks + # assume that <<12 means bitwise operation + if (((m/^\s*(?:'[^']*?'|"[^"]*?"|[^"'#]*?)*?[^"'#<@]<<[\\]?(\w+)\s*/ && + ($1 !~ m/^\d+$/)) || + m/^\s*(?:'[^']*?'|"[^"]*?"|[^"'#]*?)*?[^"'#<@]<<\s*('[^']*?'|"[^"]*?"|`[^`]*?`)\s*/ + ) && + ! m/q[qxwr]?\s*[{([#|!\/][^})\]#|!\/]*?<<[^<]/ + ) { + $tag = $1; + $tag =~ s/['"`]//g; + while () { + chomp; + ( $_ eq $tag ) && last; + } + $_ = ; + } + + # skip q{} quoted sections - just hope we don't have curly brackets + # within the quote, nor an escaped hash mark that isn't a comment + # marker, such as occurs right here. Draw the line somewhere. + if ( m/^.*\Wq[qxwr]?\s*([{([#|!\/])[^})\]#|!\/]*$/ && ! m/^\s*(package)\s/ ) { + $tag = $1; + $tag =~ tr/{\(\[\#|!\//})]#|!\//; + $tag = quotemeta($tag); + while () { + ( $_ =~ m/$tag/ ) && last; + } + } + + # skip the documentation + + # we should not need to have item in this if statement (it + # properly belongs in the over/back section) but people do not + # read the perldoc. + + if (/^=(head[1-4]|pod|for|item)/) { + /^=cut/ && next while ; + } + + if (/^=over/) { + /^=back/ && next while ; + } + + # skip the data section + if (m/^__(DATA|END)__$/) { + last; + } + + # not everyone puts the package name of the file as the first + # package name so we report all namespaces except some common + # false positives as if they were provided packages (really ugly). + + if (m/^\s*package\s+([_:a-zA-Z0-9]+)\s*v?([0-9._]+)?\s*(;|{)/) { + $package = $1; + $version = defined($2) ? $2 : undef; + if ($package eq 'main') { + undef $package; + undef $version; + } else { + # If $package already exists in the $require hash, it means + # the package definition is broken up over multiple blocks. + # In that case, don't stomp a previous $VERSION we might have + # found. (See BZ#214496.) + $require{$package} = $version unless (exists $require{$package}); + } + } + + # after we found the package name take the first assignment to + # $VERSION as the version number. Exporter requires that the + # variable be called VERSION so we are safe. + + # here are examples of VERSION lines from the perl distribution + + #FindBin.pm:$VERSION = $VERSION = sprintf("%d.%02d", q$Revision: 1.9 $ =~ /(\d+)\.(\d+)/); + #ExtUtils/Install.pm:$VERSION = substr q$Revision: 1.9 $, 10; + #CGI/Apache.pm:$VERSION = (qw$Revision: 1.9 $)[1]; + #DynaLoader.pm:$VERSION = $VERSION = "1.03"; # avoid typo warning + #General.pm:$Config::General::VERSION = 2.33; + # + # or with the new "our" pragma you could (read will) see: + # + # our $VERSION = '1.00' + if ($package && m/^(?:\s*use\s+version\s*;)?\s*(?:[^#=]*=[^=~>]|)?\s*(?:our\s+)?\$(?:\Q$package\E::)?VERSION\s*=([^=~>]\s*[^;]*)/) { + my $version_str = $1; + + # first see if the version string contains the string + # '$Revision' this often causes bizarre strings and is the most + # common method of non static numbering. + + if ($version_str =~ m/\$Revision: (\d+[.0-9]+)/) { + $version = $1; + } elsif ($version_str =~ m/\b['"]?v?(\d+(?:\.[.0-9]+)?)(_\d*|[a-zA-Z]*)?['"]?\b/) { + + # look for a static number hard coded in the script + + $version = $1; + } + $require{$package} = $version; + } + + # Allow someone to have a variable that defines virtual packages + # The variable is called $RPM_Provides. It must be scoped with + # "our", but not "local" or "my" (just would not make sense). + # + # For instance: + # + # $RPM_Provides = "blah bleah" + # + # Will generate provides for "blah" and "bleah". + # + # Each keyword can appear multiple times. Don't + # bother with datastructures to store these strings, + # if we need to print it print it now. + + if (m/^\s*(our\s+)?\$RPM_Provides\s*=\s*["'](.*)['"]/i) { + foreach $_ (split(/\s+/, $2)) { + print "$_\n"; + } + } + + } + + close(FILE) || + die("$0: Could not close file: '$file' : $!\n"); + + return; +} diff --git a/template/bin/perl.req b/template/bin/perl.req new file mode 100755 index 0000000..9e2e016 --- /dev/null +++ b/template/bin/perl.req @@ -0,0 +1,355 @@ +#!/usr/bin/perl + +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License . +# There is NO WARRANTY, to the extent permitted by law. + +# This script was originally written by Ken Estes Mail.com +# kestes@staff.mail.com + +# a simple script used to generate dependencies of Perl modules and scripts. + +# It does not parse the perl grammar but instead just lex it looking for +# what we want. It takes special care to ignore comments and pod's. + +# The filenames to scan are either passed on the command line or if +# that is empty they are passed via stdin. + +# If there are strings in the file which match the pattern +# m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i +# then these are treated as additional names which are required by the +# file and are printed as well. + +my $perl_ns = "__PERL_NS__"; + +$HAVE_VERSION = 0; +eval { require version; $HAVE_VERSION = 1; }; +use Fedora::VSP (); + + +if ("@ARGV") { + foreach (@ARGV) { + process_file($_); + } +} else { + + # notice we are passed a list of filenames NOT as common in unix the + # contents of the file. + + foreach (<>) { + process_file($_); + } +} + + +foreach $perlver (sort keys %perlreq) { + print "$perl_ns(:VERSION) >= $perlver\n"; +} +foreach $module (sort keys %require) { + if (length($require{$module}) == 0) { + print "$perl_ns($module)\n"; + } else { + + # I am not using rpm3.0 so I do not want spaces around my + # operators. Also I will need to change the processing of the + # $RPM_* variable when I upgrade. + + print "$perl_ns($module) >= $require{$module}\n"; + } +} + +exit 0; + + + +sub add_require { + my ($module, $newver) = @_; + + # __EXAMPLE__ is not valid requirement + return if ($module =~ m/^__[A-Z]+__$/o); + + # To prevent that module does not end with '::' + # Example: use base Object::Event::; + $module =~ s/::$//; + + my $oldver = $require{$module}; + if ($oldver) { + $require{$module} = $newver + if ($HAVE_VERSION && $newver && version->new($oldver) < $newver); + } + else { + $require{$module} = $newver; + } +} + +sub process_file { + + my ($file) = @_; + chomp $file; + + if (!open(FILE, $file)) { + warn("$0: Warning: Could not open file '$file' for reading: $!\n"); + return; + } + + while () { + + # skip the here-docs "<<" blocks + # assume that <<12 means bitwise operation + if (((m/^\s*(?:'[^']*?'|"[^"]*?"|[^"'#]*?)*?[^"'#<@]<<[\\]?(\w+)\s*/ && + ($1 !~ m/^\d+$/)) || + m/^\s*(?:'[^']*?'|"[^"]*?"|[^"'#]*?)*?[^"'#<@]<<\s*('[^']*?'|"[^"]*?"|`[^`]*?`)\s*/ + ) && + ! m/q[qxwr]?\s*[{([#|!\/][^})\]#|!\/]*?<<[^<]/ + ) { + $tag = $1; + $tag =~ s/['"`]//g; + if ($_ =~ m/^\s*use\s(constant)\s/) { add_require($1, undef) } + while () { + chomp; + ( $_ eq $tag ) && last; + } + $_ = ; + } + + # skip q{} quoted sections - just hope we don't have curly brackets + # within the quote, nor an escaped hash mark that isn't a comment + # marker, such as occurs right here. Draw the line somewhere. + if ( m/^.*\Wq[qxwr]?\s*([{([#|!\/])[^})\]#|!\/]*$/ && ! m/^\s*(require|use)\s/ ) { + $tag = $1; + $tag =~ tr/{\(\[\#|!\//})]#|!\//; + $tag = quotemeta($tag); + while () { + ( $_ =~ m/$tag/ ) && last; + } + } + + # skip the documentation + + # we should not need to have item in this if statement (it + # properly belongs in the over/back section) but people do not + # read the perldoc. + + if (/^=(head[1-4]|pod|for|item)/) { + /^=cut/ && next while ; + } + + if (/^=over/) { + /^=back/ && next while ; + } + + # skip the data section + if (m/^__(DATA|END)__$/) { + last; + } + + # Each keyword can appear multiple times. Don't + # bother with datastructures to store these strings, + # if we need to print it print it now. + # + # Again allow for "our". + if (m/^\s*(our\s+)?\$RPM_Requires\s*=\s*["'](.*)['"]/i) { + foreach $_ (split(/\s+/, $2)) { + print "$_\n"; + } + } + + my $modver_re = qr/[.0-9]+/; + my $begin_re = qr#qw\s*[(\/'"!|{\[]\s*|qq?\s*[(\/'"!|{\[]\s*|['"]#; + my $end_re = qr#[)\/"'!|}\]]#; + + # Skip multiline print and assign statements + if ( m/\$\S+\s*=\s*(")([^"\\]|(\\.))*$/ || + m/\$\S+\s*=\s*(')([^'\\]|(\\.))*$/ || + m/print\s+(")([^"\\]|(\\.))*$/ || + m/print\s+(')([^'\\]|(\\.))*$/ ) { + + my $quote = $1; + while () { + m/^([^\\$quote]|(\\.))*$quote/ && last; + } + $_ = ; + } + + if ( + +# ouch could be in a eval, perhaps we do not want these since we catch +# an exception they must not be required + +# eval { require Term::ReadLine } or die $@; +# eval "require Term::Rendezvous;" or die $@; +# eval { require Carp } if defined $^S; # If error/warning during compilation, + + + (m/^(\s*) # we hope the inclusion starts the line + (require|use)\s+(?!\{) # do not want 'do {' loops + # quotes around name are always legal + (?:$begin_re?\s*([\w:\/\.]+?)\s*$end_re?| + ([\w:\.]+?))[^\w]*? + [\t; \n] + # the syntax for 'use' allows version requirements + \s*($modver_re)?\s* + # catch parameter like '-norequire,' + (-[\w,]+)?\s* + # the latter part is for "use base qw(Foo)" and friends special case + (?:$begin_re\s* + ([^)\/"'\$!|}]*?) + \s*$end_re| + (?:qw<|qq?<)([^>]*?)>|([\w\:]+)|)\s*(.*) + /x) + ) { + my ($whitespace, $statement, $module, $version, $params, $list, $rest) = ($1, $2, $3, $5, $6, $7 || $8 || $9, $10); + $version = undef if ($version eq ''); + + # Ignore line which contains direct method calls + # use base __PACKAGE__->subroutine(...); + $list = "" if ($list =~ /^[^;#]*?->/ || $rest =~ /^[^;#]*?->/); + + # + # Executed in case that multiline q{} quoted sections is used for + # list of modules + if (defined($list) && $list =~ /^q[qxwr]?$/) { + $list = ""; + if ($rest =~ m/^\s*([{([#|!\/])\s*([^})\]#|!\/]*)$/) { + $tag = $1; + $list = $2; + chomp($list); + $tag =~ tr/{\(\[\#|!\//})]#|!\//; + $tag = quotemeta($tag); + while () { + my $line = $_; + chomp($line); + if ($line =~ m/^\s*(.*?)$tag/) { + $list .= ' ' . $1 if ($1 ne ''); + last; + } else { $list .= ' ' . $line; } + } + } + } + + # we only consider require statements that are flushed against + # the left edge. any other require statements give too many + # false positives, as they are usually inside of an if statement + # as a fallback module or a rarely used option + + ($whitespace ne "" && $statement eq "require") && next; + + # if there is some interpolation of variables just skip this + # dependency, we do not want + # do "$ENV{LOGDIR}/$rcfile"; + + ($module =~ m/\$/) && next; + + # ignore variables + ($module =~ m/^\s*[\$%@\*]/) && next; + + # skip if the phrase was "use of" -- shows up in gimp-perl, et al. + next if $module eq 'of'; + + # if the module ends in a comma we probably caught some + # documentation of the form 'check stuff,\n do stuff, clean + # stuff.' there are several of these in the perl distribution + + ($module =~ m/[,>]$/) && next; + + # if the module name starts in a dot it is not a module name. + # Is this necessary? Please give me an example if you turn this + # back on. + + # ($module =~ m/^\./) && next; + + # if the module starts with /, it is an absolute path to a file + if ($module =~ m(^/)) { + print "$module\n"; + next; + } + + # sometimes people do use POSIX qw(foo), or use POSIX(qw(foo)) etc. + # we can strip qw.*$, as well as (.*$: + $module =~ s/qw.*$//; + $module =~ s/\(.*$//; + + # if the module ends with .pm, strip it to leave only basename. + # .pm files are not accepted by 'use' + ($module =~ s/\.pm$// && $statement eq 'use' ) && next; + + # some perl programmers write 'require URI/URL;' when + # they mean 'require URI::URL;' + + ($module =~ s/\//::/ && $statement eq 'use' ) && next; + + # trim off trailing parentheses if any. Sometimes people pass + # the module an empty list. + + $module =~ s/\(\s*\)$//; + + if ( $module =~ m/^(v?[0-9._]+)$/ ) { + # if module is a number then both require and use interpret that + # to mean that a particular version of perl is specified + + my $rpm_ver = Fedora::VSP::vsp($1); + if (defined $rpm_ver) { + $perlreq{"$rpm_ver"} = 1; + next; + } + + }; + + # ph files do not use the package name inside the file. + # perlmodlib documentation says: + + # the .ph files made by h2ph will probably end up as + # extension modules made by h2xs. + + # so do not expend much effort on these. + + + # there is no easy way to find out if a file named systeminfo.ph + # will be included with the name sys/systeminfo.ph so only use the + # basename of *.ph files + + ($module =~ m/\.ph$/) && next; + + # use base|parent qw(Foo) dependencies + # use aliased qw(Foo::Bar) dependencies + if ($statement eq "use" && ($module eq "base" || $module eq "aliased")) { + add_require($module, $version); + if (defined($list) && $list ne "") { + add_require($_, undef) for split(' ', $list); + } + next; + } + if ($statement eq "use" && $module eq "parent") { + add_require($module, $version); + if (defined($list) && $list ne "" && $params !~ /-norequire/) { + add_require($_, undef) for split(' ', $list); + } + next; + } + + # use Any::Moose dependencies + # Mouse or Mouse::Role will be added + if ($statement eq "use" && $module eq "Any::Moose") { + add_require($module, $version); + if (defined($list) && $list ne "") { + if (grep { !/^Role$/ } split(' ', $list)) { + add_require('Mouse::Role', undef); + } else { + add_require('Mouse', undef); + } + } else { + add_require('Mouse', undef); + } + next; + } + + add_require($module, $version); + } # use|require regex + + } # while () + + close(FILE) || + die("$0: Could not close file: '$file' : $!\n"); + + return; +} diff --git a/template/fileattrs/perl.attr b/template/fileattrs/perl.attr new file mode 100644 index 0000000..be96382 --- /dev/null +++ b/template/fileattrs/perl.attr @@ -0,0 +1,3 @@ +%__perl_requires %{_rpmconfigdir}/perl.req__PERL_SUFFIX__ +%__perl_magic ^.*[Pp]erl .*$ +%__perl_flags exeonly diff --git a/template/fileattrs/perllib.attr b/template/fileattrs/perllib.attr new file mode 100644 index 0000000..b00abfd --- /dev/null +++ b/template/fileattrs/perllib.attr @@ -0,0 +1,5 @@ +%__perllib_provides %{_rpmconfigdir}/perl.prov__PERL_SUFFIX__ +%__perllib_requires %{_rpmconfigdir}/perl.req__PERL_SUFFIX__ +%__perllib_magic ^Perl[[:digit:]] module source.* +%__perllib_path \\.pm$ +%__perllib_flags magic_and_path diff --git a/template/t/lib/PerlNS.pm b/template/t/lib/PerlNS.pm new file mode 100644 index 0000000..c82c505 --- /dev/null +++ b/template/t/lib/PerlNS.pm @@ -0,0 +1,14 @@ +package PerlNS; + +use strict; +use warnings; +use Exporter; # 'import'; + +our @ISA = qw/Exporter/; +our @EXPORT = qw/$PERL_NAMESPACE $PERL_PROV $PERL_REQ/; + +our $PERL_NAMESPACE = "__PERL_NS__"; +our $PERL_PROV = "bin/perl.prov" . "__PERL_SUFFIX__"; +our $PERL_REQ = "bin/perl.req" . "__PERL_SUFFIX__"; + +1;