| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Module::CPANTS::Kwalitee::License; | 
| 2 | 7 |  |  | 7 |  | 3886 | use warnings; | 
|  | 7 |  |  |  |  | 12 |  | 
|  | 7 |  |  |  |  | 291 |  | 
| 3 | 7 |  |  | 7 |  | 35 | use strict; | 
|  | 7 |  |  |  |  | 14 |  | 
|  | 7 |  |  |  |  | 173 |  | 
| 4 | 7 |  |  | 7 |  | 36 | use File::Spec::Functions qw(catfile); | 
|  | 7 |  |  |  |  | 15 |  | 
|  | 7 |  |  |  |  | 326 |  | 
| 5 | 7 |  |  | 7 |  | 2726 | use Software::LicenseUtils; | 
|  | 7 |  |  |  |  | 372708 |  | 
|  | 7 |  |  |  |  | 8538 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '1.01'; | 
| 8 |  |  |  |  |  |  | $VERSION =~ s/_//; ## no critic | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 35 |  |  | 35 | 1 | 108 | sub order { 100 } | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | ################################################################## | 
| 13 |  |  |  |  |  |  | # Analyse | 
| 14 |  |  |  |  |  |  | ################################################################## | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | sub analyse { | 
| 17 | 11 |  |  | 11 | 1 | 54 | my $class = shift; | 
| 18 | 11 |  |  |  |  | 24 | my $me = shift; | 
| 19 | 11 |  |  |  |  | 239 | my $distdir = $me->distdir; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | # check META.yml | 
| 22 | 11 |  |  |  |  | 206 | my $yaml = $me->d->{meta_yml}; | 
| 23 | 11 |  |  |  |  | 210 | $me->d->{license} = ''; | 
| 24 | 11 | 100 |  |  |  | 123 | if ($yaml) { | 
| 25 | 3 | 50 | 33 |  |  | 37 | if ($yaml->{license} and $yaml->{license} ne 'unknown') { | 
| 26 | 3 |  |  |  |  | 20 | my $license = $yaml->{license}; | 
| 27 | 3 | 50 |  |  |  | 10 | $license = join ',', @$license if ref $license eq 'ARRAY'; | 
| 28 | 3 |  |  |  |  | 61 | $me->d->{license_from_yaml} = $license; | 
| 29 | 3 |  |  |  |  | 102 | $me->d->{license} = $license.' defined in META.yml'; | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  | # use "files_array" to exclude files listed in "no_index". | 
| 33 | 11 |  | 50 |  |  | 203 | my $files = $me->d->{files_array} || []; | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | # check if there's a LICEN[CS]E file | 
| 36 |  |  |  |  |  |  | # (also accept LICENSE.txt etc; RT #114247) | 
| 37 | 11 | 50 |  |  |  | 91 | if (my ($file) = grep {$_ =~ /^(?:LICEN[CS]E|COPYING)\b/} @$files) { | 
|  | 19 |  |  |  |  | 123 |  | 
| 38 | 0 |  |  |  |  | 0 | $me->d->{license} .= " defined in $file"; | 
| 39 | 0 |  |  |  |  | 0 | $me->d->{external_license_file} = $file; | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | # check pod | 
| 43 | 11 |  |  |  |  | 46 | my %licenses; | 
| 44 | 11 |  |  |  |  | 46 | foreach my $file (grep { /\.(?:pm|pod|pl|PL)$/ } sort @$files ) { | 
|  | 19 |  |  |  |  | 166 |  | 
| 45 | 9 | 50 |  |  |  | 46 | next if $file =~ /(?:Makefile|Build)\.PL$/; | 
| 46 | 9 |  |  |  |  | 57 | my $path = catfile($distdir, $file); | 
| 47 | 9 | 50 |  |  |  | 168 | next unless -r $path; # skip if not readable | 
| 48 | 9 | 50 |  |  |  | 339 | open my $fh, '<', $path or next; | 
| 49 | 9 |  |  |  |  | 32 | my $in_pod = 0; | 
| 50 | 9 |  |  |  |  | 47 | my $pod = ''; | 
| 51 | 9 |  |  |  |  | 37 | my $pod_head = ''; | 
| 52 | 9 |  |  |  |  | 37 | my @possible_licenses; | 
| 53 |  |  |  |  |  |  | my @unknown_license_texts; | 
| 54 | 9 |  |  |  |  | 0 | my $uc_head; | 
| 55 | 9 |  |  |  |  | 172 | while(<$fh>) { | 
| 56 | 33 |  |  |  |  | 83 | my $first_four = substr($_, 0, 4); | 
| 57 | 33 | 50 | 66 |  |  | 326 | if ($first_four eq '=hea' && (($uc_head = uc $_) =~ /(?:LICEN[CS]E|LICEN[CS]ING|COPYRIGHT|LEGAL)/)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 58 | 0 |  |  |  |  | 0 | $me->d->{license_in_pod} = 1; | 
| 59 | 0 |  | 0 |  |  | 0 | $me->d->{license} ||= "defined in POD ($file)"; | 
| 60 | 0 | 0 |  |  |  | 0 | if ($in_pod) { | 
| 61 | 0 |  |  |  |  | 0 | my @guessed = Software::LicenseUtils->guess_license_from_pod("=head1 LICENSE\n$pod\n\n=cut\n"); | 
| 62 | 0 | 0 |  |  |  | 0 | if (@guessed) { | 
| 63 | 0 |  |  |  |  | 0 | push @possible_licenses, @guessed; | 
| 64 |  |  |  |  |  |  | } else { | 
| 65 | 0 |  |  |  |  | 0 | push @unknown_license_texts, "$pod_head$pod"; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 0 |  |  |  |  | 0 | $in_pod = 1; | 
| 70 | 0 |  |  |  |  | 0 | $pod_head = $_; | 
| 71 | 0 |  |  |  |  | 0 | $pod = ''; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  | elsif ($first_four eq '=hea' or $first_four eq '=cut') { | 
| 74 | 2 | 50 |  |  |  | 6 | if ($in_pod) { | 
| 75 | 0 |  |  |  |  | 0 | my @guessed = Software::LicenseUtils->guess_license_from_pod("=head1 LICENSE\n$pod\n\n=cut\n"); | 
| 76 | 0 | 0 |  |  |  | 0 | if (@guessed) { | 
| 77 | 0 |  |  |  |  | 0 | push @possible_licenses, @guessed; | 
| 78 |  |  |  |  |  |  | } else { | 
| 79 | 0 |  |  |  |  | 0 | push @unknown_license_texts, "$pod_head$pod"; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  | } | 
| 82 | 2 |  |  |  |  | 3 | $in_pod = 0; | 
| 83 | 2 |  |  |  |  | 5 | $pod = ''; | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  | elsif ($in_pod) { | 
| 86 | 0 |  |  |  |  | 0 | $pod .= $_; | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | } | 
| 89 | 9 | 50 |  |  |  | 37 | if ($pod) { | 
| 90 | 0 |  |  |  |  | 0 | my @guessed = Software::LicenseUtils->guess_license_from_pod("=head1 LICENSE\n$pod\n\n=cut\n"); | 
| 91 | 0 | 0 |  |  |  | 0 | if (@guessed) { | 
| 92 | 0 |  |  |  |  | 0 | push @possible_licenses, @guessed; | 
| 93 |  |  |  |  |  |  | } else { | 
| 94 | 0 |  |  |  |  | 0 | push @unknown_license_texts, "$pod_head$pod"; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  | } | 
| 97 | 9 | 50 |  |  |  | 38 | if (@possible_licenses) { | 
| 98 | 0 |  |  |  |  | 0 | @possible_licenses = map { s/^Software::License:://; $_ } @possible_licenses; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 99 | 0 |  | 0 |  |  | 0 | push @{$licenses{$_} ||= []}, $file for @possible_licenses; | 
|  | 0 |  |  |  |  | 0 |  | 
| 100 | 0 |  |  |  |  | 0 | $me->d->{files_hash}{$file}{license} = join ',', @possible_licenses; | 
| 101 |  |  |  |  |  |  | } else { | 
| 102 | 9 | 50 |  |  |  | 119 | $me->d->{unknown_license_texts}{$file} = join "\n", @unknown_license_texts if @unknown_license_texts; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | } | 
| 105 | 11 | 50 |  |  |  | 48 | if (%licenses) { | 
| 106 | 0 |  |  |  |  | 0 | $me->d->{licenses} = \%licenses; | 
| 107 | 0 |  |  |  |  | 0 | my @possible_licenses = keys %licenses; | 
| 108 | 0 | 0 |  |  |  | 0 | if (@possible_licenses == 1) { | 
| 109 | 0 |  |  |  |  | 0 | my ($type) = @possible_licenses; | 
| 110 | 0 |  |  |  |  | 0 | $me->d->{license_type} = $type; | 
| 111 | 0 |  |  |  |  | 0 | $me->d->{license_file} = join ',', @{$licenses{$type}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 11 |  |  |  |  | 48 | return; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | ################################################################## | 
| 119 |  |  |  |  |  |  | # Kwalitee Indicators | 
| 120 |  |  |  |  |  |  | ################################################################## | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub kwalitee_indicators{ | 
| 123 |  |  |  |  |  |  | return [ | 
| 124 |  |  |  |  |  |  | { | 
| 125 |  |  |  |  |  |  | name => 'meta_yml_has_license', | 
| 126 |  |  |  |  |  |  | error => q{This distribution does not have a license defined in META.yml.}, | 
| 127 |  |  |  |  |  |  | remedy => q{Define the license if you are using in Build.PL. If you are using MakeMaker (Makefile.PL) you should upgrade to ExtUtils::MakeMaker version 6.31.}, | 
| 128 |  |  |  |  |  |  | is_extra => 1, | 
| 129 |  |  |  |  |  |  | code => sub { | 
| 130 | 11 |  |  | 11 |  | 81 | my $d = shift; | 
| 131 | 11 |  |  |  |  | 29 | my $yaml = $d->{meta_yml}; | 
| 132 | 11 | 100 | 66 |  |  | 68 | ($yaml->{license} and $yaml->{license} ne 'unknown') ? 1 : 0 }, | 
| 133 |  |  |  |  |  |  | details => sub { | 
| 134 | 0 |  |  | 0 |  | 0 | my $d = shift; | 
| 135 | 0 |  |  |  |  | 0 | my $yaml = $d->{meta_yml}; | 
| 136 | 0 | 0 |  |  |  | 0 | return "No META.yml." unless $yaml; | 
| 137 | 0 | 0 |  |  |  | 0 | return "No license was found in META.yml." unless $yaml->{license}; | 
| 138 | 0 |  |  |  |  | 0 | return "Unknown license was found in META.yml."; | 
| 139 |  |  |  |  |  |  | }, | 
| 140 |  |  |  |  |  |  | }, | 
| 141 |  |  |  |  |  |  | { | 
| 142 |  |  |  |  |  |  | name => 'has_human_readable_license', | 
| 143 |  |  |  |  |  |  | error => q{This distribution does not have a license defined in the documentation or in a file called LICENSE}, | 
| 144 |  |  |  |  |  |  | remedy => q{Add a section called "LICENSE" to the documentation, or add a file named LICENSE to the distribution.}, | 
| 145 |  |  |  |  |  |  | code => sub { | 
| 146 | 11 |  |  | 11 |  | 65 | my $d = shift; | 
| 147 | 11 | 50 | 33 |  |  | 105 | return $d->{external_license_file} || $d->{license_in_pod} ? 1 : 0; | 
| 148 |  |  |  |  |  |  | }, | 
| 149 |  |  |  |  |  |  | details => sub { | 
| 150 | 0 |  |  | 0 |  | 0 | my $d = shift; | 
| 151 | 0 |  |  |  |  | 0 | return "Neither LICENSE file nor LICENSE section in pod was found."; | 
| 152 |  |  |  |  |  |  | }, | 
| 153 |  |  |  |  |  |  | }, | 
| 154 |  |  |  |  |  |  | { | 
| 155 |  |  |  |  |  |  | name => 'has_separate_license_file', | 
| 156 |  |  |  |  |  |  | error => q{This distribution does not have a LICENSE or LICENCE file in its root directory.}, | 
| 157 |  |  |  |  |  |  | remedy => q{This is not a critical issue. Currently mainly informative for the CPANTS authors. It might be removed later.}, | 
| 158 |  |  |  |  |  |  | is_experimental => 1, | 
| 159 | 11 | 50 |  | 11 |  | 75 | code => sub { shift->{external_license_file} ? 1 : 0 }, | 
| 160 |  |  |  |  |  |  | details => sub { | 
| 161 | 0 |  |  | 0 |  | 0 | my $d = shift; | 
| 162 | 0 |  |  |  |  | 0 | return "LICENSE file was found."; | 
| 163 |  |  |  |  |  |  | }, | 
| 164 |  |  |  |  |  |  | }, | 
| 165 |  |  |  |  |  |  | { | 
| 166 |  |  |  |  |  |  | name => 'has_license_in_source_file', | 
| 167 |  |  |  |  |  |  | error => q{Does not have license information in any of its source files}, | 
| 168 |  |  |  |  |  |  | remedy => q{Add =head1 LICENSE and the text of the license to the main module in your code.}, | 
| 169 |  |  |  |  |  |  | code => sub { | 
| 170 | 11 |  |  | 11 |  | 72 | my $d = shift; | 
| 171 | 11 | 50 |  |  |  | 36 | return $d->{license_in_pod} ? 1 : 0; | 
| 172 |  |  |  |  |  |  | }, | 
| 173 |  |  |  |  |  |  | details => sub { | 
| 174 | 0 |  |  | 0 |  | 0 | my $d = shift; | 
| 175 | 0 |  |  |  |  | 0 | return "LICENSE section was not found in the pod."; | 
| 176 |  |  |  |  |  |  | }, | 
| 177 |  |  |  |  |  |  | }, | 
| 178 |  |  |  |  |  |  | { | 
| 179 |  |  |  |  |  |  | name => 'has_known_license_in_source_file', | 
| 180 |  |  |  |  |  |  | error => q{Does not have license information in any of its source files, or the information is not recognized by Software::License}, | 
| 181 |  |  |  |  |  |  | remedy => q{Add =head1 LICENSE and/or the proper text of the well-known license to the main module in your code.}, | 
| 182 |  |  |  |  |  |  | is_extra => 1, | 
| 183 |  |  |  |  |  |  | code => sub { | 
| 184 | 11 |  |  | 11 |  | 68 | my $d = shift; | 
| 185 | 11 | 50 |  |  |  | 41 | return 0 unless $d->{license_in_pod}; | 
| 186 | 0 |  |  |  |  | 0 | my @files_with_licenses = grep {$d->{files_hash}{$_}{license}} keys %{$d->{files_hash}}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 187 | 0 | 0 |  |  |  | 0 | return @files_with_licenses ? 1 : 0; | 
| 188 |  |  |  |  |  |  | }, | 
| 189 |  |  |  |  |  |  | details => sub { | 
| 190 | 0 |  |  | 0 |  | 0 | my $d = shift; | 
| 191 | 0 |  |  |  |  | 0 | return "LICENSE section was not found in the pod, or the license information was not recognized by Software::License."; | 
| 192 |  |  |  |  |  |  | }, | 
| 193 |  |  |  |  |  |  | }, | 
| 194 | 8 |  |  | 8 | 1 | 376 | ]; | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | q{Favourite record of the moment: | 
| 199 |  |  |  |  |  |  | Lili Allen - Allright, still}; | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | __END__ |