| Module-License-Report documentation | Contained in the Module-License-Report distribution. |
Module::License::Report::CPANPLUSModule - Abstraction of a CPAN module
Copyright 2005 Clotho Advanced Media, Inc., <cpan@clotho.com>
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
use Module::License::Report::CPANPLUS.pm
use Module::License::Report::CPANPLUSModule.pm
my $cp = Module::License::Report::CPANPLUS->new();
my $module = Module::License::Report::CPANPLUSModule->new($cp, 'Foo::Bar');
my $license = $module->license();
This is an extension of the CPANPLUS::Module API for use by Module::License::Report. It's unlikely that you want to use this directly.
The $cp argument is a Module::License::Report::CPANPLUS
instance. The $module_name should be of a form acceptable to
Module::License::Report::CPANPLUS::get_module().
Returns a boolean.
Returns a Module::License::Report::Object instance, or undef.
Searches the specified file for license and/or copyright information. This uses heuristics.
Loads and parses a META.yml file. Returns a hashref that has,
minimally, a license field.
Parses the CPAN DSLIP metadata. Returns a hashref that has,
minimally, a license field.
See http://cpan.uwinnipeg.ca/htdocs/faqs/dslip.html for more information.
Loads and parses a Makefile.PL file. Returns a hashref that has,
minimally, a license field.
The parsing is very simplistic.
Loads and parses a Build.PL file. Returns a hashref that has,
minimally, a license field.
The parsing is very simplistic.
Returns the name of the file that has the definitive VERSION.
This file might not exist.
This relies on parsing META.yml, Build.PL or Makefile.PL.
Returns the name of a .pod file that corresponds to version_from().
This file might not exist.
Returns the name of the file that is the most likely source of license or copyright information.
Returns a list of all files in the root of the distribution directory,
like README, Makefile.PL, etc.
Returns the module name that was specified in the constructor.
Returns the name of the package, like Foo-Bar.
Returns the version of the package, like 0.12.04_01.
Returns the path to the extracted distribution. If the distribution is not yet extracted, does that first.
Extracts the distribution archive (perhaps a .tar.gz or a .zip
file) and returns the path.
Downloads the distribution from CPAN.
Clotho Advanced Media Inc., cpan@clotho.com
Primary developer: Chris Dolan
| Module-License-Report documentation | Contained in the Module-License-Report distribution. |
package Module::License::Report::CPANPLUSModule; use warnings; use strict; use CPANPLUS::Internals::Constants; use File::Slurp qw(); use File::Spec qw(); use Module::License::Report::Object; use YAML qw(); our $VERSION = '0.02'; # This is a translation from CPAN "dslip" codes to Module::Build YAML codes # From: http://cpan.uwinnipeg.ca/htdocs/faqs/dslip.html # To: http://search.cpan.org/dist/Module-Build/lib/Module/Build.pm#license my %dslip_license_abbrevs = ( p => 'perl', g => 'gpl', l => 'lgpl', b => 'bsd', a => 'artistic', o => 'unrestricted', ); ### CHANGES HERE SHOULD BE REFLECTED IN ::Object POD! ### # This is an unordered list of possible sources for license information # Each entry has these fields: # name - Machine-readable codeword for the source - should not change ever # description - Human-readable description of the source # confidence - Number between 100 (high) and 0 (low) # sub - Anonymous function that returns (<licensename>, <filename>) # Note that the filename may be undef my @license_sources = ( { name => 'META.yml', description => 'META.yml license field', confidence => 100, sub => sub { my $self = shift; return $self->yml()->{license}, 'META.yml'; }, }, { name => 'DSLIP', description => 'CPAN license field', confidence => 95, sub => sub { my $self = shift; return $self->dslip()->{license}, undef; }, }, { name => 'Module', description => 'Copyright statement in module file', confidence => 50, sub => sub { my $self = shift; my $file = $self->version_from(); return $self->license_from_file($file), $file; }, }, { name => 'POD', description => 'Copyright statement in module pod file', confidence => 45, sub => sub { my $self = shift; my $file = $self->version_from_pod(); return $self->license_from_file($file), $file; }, }, { name => 'LicenseFile', description => 'Copyright statement in miscellaneous file', confidence => 25, sub => sub { my $self = shift; my $file = $self->license_filename(); return $self->license_from_file($file), $file; }, }, );
sub new { my $pkg = shift; my $cp = shift; # Module::License::Report::CPANPLUS instance my $name = shift; my $self = bless { cp => $cp, name => $name, mod => $cp->_module_by_name($name), }, $pkg; return $self->{mod} ? $self : (); }
sub verbose { my $self = shift; return $self->{cp}->{verbose}; }
sub license { my $self = shift; _announce("Find license for $self->{name}", $self->verbose()); for my $source (reverse sort {$a->{confidence} <=> $b->{confidence}} @license_sources) { _announce(" Try source $source->{name}", $self->verbose()); my ($license, $file) = $source->{sub}($self); my $result = { name => $license, source_file => $file, source_name => $source->{name}, source_desc => $source->{description}, confidence => $source->{confidence}, module => $self, }; if ($license) { return Module::License::Report::Object->new($result); } } return; }
sub license_from_file { my $self = shift; my $licensefile = shift; if ($licensefile) { my $filename = File::Spec->catfile($self->extract_dir(), $licensefile); if (-f $filename) { my $content = File::Slurp::read_file($filename); if ($content =~ m/=head\d\s+(?:licen[cs]e|licensing|copyright|legal)\b(.*?)(=head\\d.*|=cut.*|)\z/ixms) { my $licensetext = $1; # Check for any of the following phrases (Change spaces to \s+) my @phrases = ( 'under the same (?:terms|license) as Perl itself', ); my $regex = join q{|}, map {join '\\s+', split m/\s+/xms, $_} @phrases; if ($licensetext =~ m/$regex/ixms) { return 'perl'; } } } } return undef; ## no critic needs an explicit undef because of list context }
sub yml { my $self = shift; if (!$self->{yml}) { $self->{yml} = { license => undef, }; my $filename = File::Spec->catfile($self->extract_dir(), 'META.yml'); if (-f $filename) { my $yaml = File::Slurp::read_file($filename); my $meta = eval { YAML::Load($yaml) }; if (!$meta) { _announce('Failed to read META.yml', $self->verbose()); } else { for my $key (qw(license)) { if ($meta->{$key}) { $self->{yml}->{$key} = $meta->{$key}; } } } } } return $self->{yml}; }
sub dslip { my $self = shift; if (!$self->{dslip}) { $self->{dslip} = { license => undef, }; my $dslip_str = $self->{mod}->dslip(); if ($dslip_str) { my ($devel_stage, $support_level, $language_used, $interface_style, $public_license) = $dslip_str =~ m/(.)/gxms; if ($public_license) { $self->{dslip}->{license} = $dslip_license_abbrevs{$public_license}; } } } return $self->{dslip}; }
sub makefile { my $self = shift; if (!$self->{makefile}) { $self->{makefile} = {}; my $filename = File::Spec->catfile($self->extract_dir(), 'Makefile.PL'); if (-f $filename) { my $makefile = File::Slurp::read_file($filename); # Get main file from the MakeMaker command if ($makefile =~ m/([\'\"]?)VERSION_FROM\1\s*(?:=>|,)\s*(\"[^\"]+|\'[^\']+)/xms) { my $module_file = substr $2, 1; # remove leading quote $self->{makefile}->{version_from} = $module_file; } } } return $self->{makefile}; }
sub buildfile { my $self = shift; if (!$self->{buildfile}) { $self->{buildfile} = {}; my $filename = File::Spec->catfile($self->extract_dir(), 'Build.PL'); if (-f $filename) { my $buildfile = File::Slurp::read_file($filename); # Get main file from the Module::Build constructor if ($buildfile =~ m/([\'\"]?)module_name\1\s*(?:=>|,)\s*(\"[^\"]+|\'[^\']+)/xms) { my $module_name = substr $2, 1; # remove leading quote # This algorithm comes from Module::Build::Base::dist_version() v0.27_02 my $file = File::Spec->catfile('lib', split m/::/xms, $module_name) . '.pm'; $self->{buildfile}->{version_from} = $file; } elsif ($buildfile =~ m/([\'\"]?)dist_version_from\1\s*(?:=>|,)\s*(\"[^\"]+|\'[^\']+)/xms) { my $module_file = substr $2, 1; # remove leading quote $self->{buildfile}->{version_from} = $module_file; } } } return $self->{buildfile}; }
sub version_from { my $self = shift; my @candidates = ( $self->yml()->{version_from}, $self->buildfile()->{version_from}, $self->makefile()->{version_from}, ); for my $filename (@candidates) { if ($filename && -f File::Spec->catfile($self->extract_dir(), $filename)) { return $filename; } } return; }
sub version_from_pod { my $self = shift; my $version_from = $self->version_from(); my $version_pod; if ($version_from && $version_from =~ m/ \.pm \z /xms) { ($version_pod = $version_from) =~ s/ \.pm \z /.pod/xms; } return $version_pod; }
sub license_filename { my $self = shift; # Check files that are for-sure my @licenses = grep {m/\A (?:copyright|copying|license|gpl|lgpl|artistic) \b /ixms} $self->root_files(); if (@licenses > 0) { return $licenses[0]; } # Check doc files that might have copyright inline foreach my $file ((grep {m/\A readme/ixms} $self->root_files()), (grep {defined $_} $self->version_from(), $self->version_from_pod())) { my $filename = File::Spec->catfile($self->extract_dir(), $file); if (-f $filename) { my $content = File::Slurp::read_file($filename); if ($content =~ m/\b(?:licen[sc]e|licensing|copyright)\b/ixms) # [sc] is to catch a common typo { return $file; } } } return; }
sub root_files { my $self = shift; # Get list of files in the root of the distro my @files = grep {-f File::Spec->catfile($self->extract_dir(), $_)} File::Slurp::read_dir($self->extract_dir()); return @files; }
sub name { my $self = shift; return $self->{name}; }
sub package_name { my $self = shift; return $self->{mod}->package_name(); }
sub package_version { my $self = shift; return $self->{mod}->package_version(); }
sub extract_dir { my $self = shift; return $self->extract(); }
sub extract { my $self = shift; $self->fetch(); if (!$self->{mod}->status->extract) { #_announce('Extract module', $self->verbose()); $self->{mod}->extract; if ($self->verbose) { _announce('Extracted to ' . $self->{mod}->status()->extract(), $self->verbose()); } } return $self->{mod}->status->extract; }
sub fetch { my $self = shift; if (!$self->{mod}->status->fetch) { #_announce('Fetch module', $self->verbose()); $self->{mod}->fetch; } return $self->{mod}->status->fetch; } sub _announce { my $msg = shift; my $verbose = shift; if ($verbose) { print $msg,"\n"; } return; } 1; __END__