File Coverage

blib/lib/Software/LicenseUtils.pm
Criterion Covered Total %
statement 66 66 100.0
branch 24 36 66.6
condition 5 7 71.4
subroutine 12 12 100.0
pod 5 5 100.0
total 112 126 88.8


line stmt bran cond sub pod time code
1 7     7   303052 use strict;
  7         62  
  7         172  
2 7     7   28 use warnings;
  7         12  
  7         156  
3 7     7   28 use Carp;
  7         12  
  7         464  
4              
5             # ABSTRACT: little useful bits of code for licensey things
6             $Software::LicenseUtils::VERSION = '0.104002';
7             use File::Spec;
8 7     7   33 use IO::Dir;
  7         11  
  7         141  
9 7     7   2400 use Module::Load;
  7         99038  
  7         298  
10 7     7   2859  
  7         5931  
  7         41  
11             #pod =method guess_license_from_pod
12             #pod
13             #pod my @guesses = Software::LicenseUtils->guess_license_from_pod($pm_text);
14             #pod
15             #pod Given text containing POD, like a .pm file, this method will attempt to guess
16             #pod at the license under which the code is available. This method will return
17             #pod either a list of Software::License classes names (as strings) or false.
18             #pod
19             #pod This method looks for a POD heading like 'license', 'copyright', or 'legal'.
20             #pod
21             #pod Calling this method in scalar context is a fatal error.
22             #pod
23             #pod =cut
24              
25             my $_v = qr/(?:v(?:er(?:sion|\.))?(?: |\.)?)/i;
26             my @phrases = (
27             "under the same (?:terms|license) as perl $_v?6" => [],
28             'under the same (?:terms|license) as (?:the )?perl' => 'Perl_5',
29             'affero g' => 'AGPL_3',
30             "GNU (?:general )?public license,? $_v?([123])" => sub { "GPL_$_[0]" },
31             'GNU (?:general )?public license' => [ map {"GPL_$_"} (1..3) ],
32             "GNU (?:lesser|library) (?:general )?public license,? $_v?([23])\\D" => sub {
33             $_[0] == 2 ? 'LGPL_2_1' : $_[0] == 3 ? 'LGPL_3_0' : ()
34             },
35             'GNU (?:lesser|library) (?:general )?public license' => [ qw(LGPL_2_1 LGPL_3_0) ],
36             '(?:the )?2[-\s]clause (?:Free)?BSD' => 'FreeBSD',
37             'BSD license' => 'BSD',
38             'FreeBSD license' => 'FreeBSD',
39             "Artistic license $_v?(\\d)" => sub { "Artistic_$_[0]_0" },
40             'Artistic license' => [ map { "Artistic_$_\_0" } (1..2) ],
41             "LGPL,? $_v?(\\d)" => sub {
42             $_[0] == 2 ? 'LGPL_2_1' : $_[0] == 3 ? 'LGPL_3_0' : ()
43             },
44             'LGPL' => [ qw(LGPL_2_1 LGPL_3_0) ],
45             "GPL,? $_v?(\\d)" => sub { "GPL_$_[0]" },
46             'GPL' => [ map { "GPL_$_" } (1..3) ],
47             'FreeBSD' => 'FreeBSD',
48             'BSD' => 'BSD',
49             'Artistic' => [ map { "Artistic_$_\_0" } (1..2) ],
50             'MIT' => 'MIT',
51             'has dedicated the work to the Commons' => 'CC0_1_0',
52             'waiving all of his or her rights to the work worldwide under copyright law' => 'CC0_1_0',
53             'has waived all copyright and related or neighboring rights to' => 'CC0_1_0',
54             'apache(?: |-)1.1' => "Apache_1_1",
55             "Apache Software License(\\s)+Version 1.1" => "Apache_1_1",
56             'apache(?: |-)2.0' => "Apache_2_0",
57             "Apache License(\\s)+Version 2.0" => "Apache_2_0",
58             'No license is granted to other entities' => 'None',
59             );
60              
61             my %meta_keys = ();
62             my %meta1_keys = ();
63             my %meta2_keys = ();
64             my %spdx_expression = ();
65              
66             # find all known Software::License::* modules and get identification data
67             #
68             # XXX: Grepping over @INC is dangerous, as it means that someone can change the
69             # behavior of your code by installing a new library that you don't load. rjbs
70             # is not a fan. On the other hand, it will solve a real problem. One better
71             # solution is to check "core" licenses first, then fall back, and to skip (but
72             # warn about) bogus libraries. Another is, at least when testing S-L itself,
73             # to only scan lib/ blib. -- rjbs, 2013-10-20
74             for my $lib (map { "$_/Software/License" } @INC) {
75             next unless -d $lib;
76             for my $file (IO::Dir->new($lib)->read) {
77             next unless $file =~ m{\.pm$};
78              
79             # if it fails, ignore it
80             eval {
81             (my $mod = $file) =~ s{\.pm$}{};
82             my $class = "Software::License::$mod";
83             load $class;
84             $meta_keys{ $class->meta_name }{$mod} = undef;
85             $meta1_keys{ $class->meta_name }{$mod} = undef;
86             $meta_keys{ $class->meta2_name }{$mod} = undef;
87             $meta2_keys{ $class->meta2_name }{$mod} = undef;
88             if (defined $class->spdx_expression) {
89             $spdx_expression{ $class->spdx_expression }{$class} = undef;
90             }
91             my $name = $class->name;
92             unshift @phrases, qr/\Q$name\E/, [$mod];
93             if ((my $name_without_space = $name) =~ s/\s+\(.+?\)//) {
94             unshift @phrases, qr/\Q$name_without_space\E/, [$mod];
95             }
96             };
97             }
98             }
99              
100             my ($class, $pm_text) = @_;
101             die "can't call guess_license_* in scalar context" unless wantarray;
102 32     32 1 15341 return unless $pm_text =~ /
103 32 50       83 (
104 32 50       209 =head \d \s+
105             (?:licen[cs]e|licensing|copyright|legal)\b
106             )
107             /ixmsg;
108              
109             my $header = $1;
110              
111 32         79 if (
112             $pm_text =~ m/
113 32 50       482 \G
114             (
115             .*?
116             )
117             (=head\\d.*|=cut.*|)
118             \z
119             /ixms
120             ) {
121             my $license_text = "$header$1";
122              
123 32         86 for (my $i = 0; $i < @phrases; $i += 2) {
124             my ($pattern, $license) = @phrases[ $i .. $i+1 ];
125 32         81 $pattern =~ s{\s+}{\\s+}g
126 3025         5447 unless ref $pattern eq 'Regexp';
127 3025 100       8834 if ( $license_text =~ /\b$pattern\b/i ) {
128             my $match = $1;
129 3025 100       23243 # if ( $osi and $license_text =~ /All rights reserved/i ) {
130 31         74 # warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
131             # }
132             my @result = (ref $license||'') eq 'CODE' ? $license->($match)
133             : (ref $license||'') eq 'ARRAY' ? @$license
134 31 100 100     201 : $license;
    100 100        
135              
136             return unless @result;
137             return map { "Software::License::$_" } sort @result;
138 31 50       61 }
139 31         114 }
  31         192  
140             }
141              
142             return;
143             }
144 1         5  
145             #pod =method guess_license_from_meta
146             #pod
147             #pod my @guesses = Software::LicenseUtils->guess_license_from_meta($meta_str);
148             #pod
149             #pod Given the content of the META.(yml|json) file found in a CPAN distribution, this
150             #pod method makes a guess as to which licenses may apply to the distribution. It
151             #pod will return a list of zero or more Software::License instances or classes.
152             #pod
153             #pod =cut
154              
155             my ($class, $meta_text) = @_;
156             die "can't call guess_license_* in scalar context" unless wantarray;
157              
158 26     26 1 10356 my ($license_text) = $meta_text =~ m{\b["']?license["']?\s*:\s*["']?([a-z_0-9]+)["']?}gm;
159 26 50       56  
160             return unless $license_text and my $license = $meta_keys{ $license_text };
161 26         192  
162             return map { "Software::License::$_" } sort keys %$license;
163 26 50 33     123 }
164              
165 26         88 {
  28         125  
166             no warnings 'once';
167             *guess_license_from_meta_yml = \&guess_license_from_meta;
168             }
169 7     7   5940  
  7         16  
  7         3065  
170             #pod =method guess_license_from_meta_key
171             #pod
172             #pod my @guesses = Software::LicenseUtils->guess_license_from_meta_key($key, $v);
173             #pod
174             #pod This method returns zero or more Software::License classes known to use C<$key>
175             #pod as their META key. If C<$v> is supplied, it specifies whether to treat C<$key>
176             #pod as a v1 or v2 meta entry. Any value other than 1 or 2 will raise an exception.
177             #pod
178             #pod =cut
179              
180             my ($self, $key, $v) = @_;
181              
182             my $src = (! defined $v) ? \%meta_keys
183             : $v eq '1' ? \%meta1_keys
184 3     3 1 307 : $v eq '2' ? \%meta2_keys
185             : Carp::croak("illegal META version: $v");
186 3 50       13  
    100          
    50          
187             return unless $src->{$key};
188             return map { "Software::License::$_" } sort keys %{ $src->{$key} };
189             }
190              
191 3 100       14 my %short_name = (
192 2         4 'GPL-1' => 'Software::License::GPL_1',
  2         14  
  2         8  
193             'GPL-2' => 'Software::License::GPL_2',
194             'GPL-3' => 'Software::License::GPL_3',
195             'LGPL-2' => 'Software::License::LGPL_2',
196             'LGPL-2.1' => 'Software::License::LGPL_2_1',
197             'LGPL-3' => 'Software::License::LGPL_3_0',
198             'LGPL-3.0' => 'Software::License::LGPL_3_0',
199             'Artistic' => 'Software::License::Artistic_1_0',
200             'Artistic-1' => 'Software::License::Artistic_1_0',
201             'Artistic-2' => 'Software::License::Artistic_2_0',
202             );
203              
204             #pod =method new_from_short_name
205             #pod
206             #pod my $license_object = Software::LicenseUtils->new_from_short_name( {
207             #pod short_name => 'GPL-1',
208             #pod holder => 'X. Ample'
209             #pod }) ;
210             #pod
211             #pod Create a new L<Software::License> object from the license specified
212             #pod with C<short_name>. Known short license names are C<GPL-*>, C<LGPL-*> ,
213             #pod C<Artistic> and C<Artistic-*>
214             #pod
215             #pod =cut
216              
217             my ( $class, $arg ) = @_;
218              
219             Carp::croak "no license short name specified"
220             unless defined $arg->{short_name};
221             my $short = delete $arg->{short_name};
222 1     1 1 848 Carp::croak "Unknown license with short name $short"
223             unless $short_name{$short};
224              
225 1 50       4 my $lic_file = my $lic_class = $short_name{$short} ;
226 1         3 $lic_file =~ s!::!/!g;
227             require "$lic_file.pm";
228 1 50       4 return $lic_class->new( $arg );
229             }
230 1         3  
231 1         5 #pod =method new_from_spdx_expression
232 1         9 #pod
233 1         11 #pod my $license_object = Software::LicenseUtils->new_from_spdx_expression( {
234             #pod spdx_expression => 'MPL-2.0',
235             #pod holder => 'X. Ample'
236             #pod }) ;
237             #pod
238             #pod Create a new L<Software::License> object from the license specified
239             #pod with C<spdx_expression>. Some licenses doesn't have an spdx
240             #pod identifier (for example L<Software::License::Perl_5>), so you can pass
241             #pod spdx identifier but also expressions.
242             #pod Known spdx license identifiers are C<BSD>, C<MPL-1.0>.
243             #pod
244             #pod =cut
245              
246             my ( $class, $arg ) = @_;
247              
248             Carp::croak "no license spdx name specified"
249             unless defined $arg->{spdx_expression};
250             my $spdx = delete $arg->{spdx_expression};
251             Carp::croak "Unknown license with spdx name $spdx"
252 1     1 1 502 unless $spdx_expression{$spdx};
253              
254             my ($lic_file) = my ($lic_class) = keys %{$spdx_expression{$spdx}} ;
255 1 50       6 $lic_file =~ s!::!/!g;
256 1         4 require "$lic_file.pm";
257             return $lic_class->new( $arg );
258 1 50       5 }
259              
260 1         3 1;
  1         5  
261 1         6  
262 1         7  
263 1         12 =pod
264              
265             =encoding UTF-8
266              
267             =head1 NAME
268              
269             Software::LicenseUtils - little useful bits of code for licensey things
270              
271             =head1 VERSION
272              
273             version 0.104002
274              
275             =head1 PERL VERSION
276              
277             This module is part of CPAN toolchain, or is treated as such. As such, it
278             follows the agreement of the Perl Toolchain Gang to require no newer version of
279             perl than v5.8.1. This version may change by agreement of the Toolchain Gang,
280             but for now is governed by the L<Lancaster
281             Consensus|https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>
282             of 2013.
283              
284             =head1 METHODS
285              
286             =head2 guess_license_from_pod
287              
288             my @guesses = Software::LicenseUtils->guess_license_from_pod($pm_text);
289              
290             Given text containing POD, like a .pm file, this method will attempt to guess
291             at the license under which the code is available. This method will return
292             either a list of Software::License classes names (as strings) or false.
293              
294             This method looks for a POD heading like 'license', 'copyright', or 'legal'.
295              
296             Calling this method in scalar context is a fatal error.
297              
298             =head2 guess_license_from_meta
299              
300             my @guesses = Software::LicenseUtils->guess_license_from_meta($meta_str);
301              
302             Given the content of the META.(yml|json) file found in a CPAN distribution, this
303             method makes a guess as to which licenses may apply to the distribution. It
304             will return a list of zero or more Software::License instances or classes.
305              
306             =head2 guess_license_from_meta_key
307              
308             my @guesses = Software::LicenseUtils->guess_license_from_meta_key($key, $v);
309              
310             This method returns zero or more Software::License classes known to use C<$key>
311             as their META key. If C<$v> is supplied, it specifies whether to treat C<$key>
312             as a v1 or v2 meta entry. Any value other than 1 or 2 will raise an exception.
313              
314             =head2 new_from_short_name
315              
316             my $license_object = Software::LicenseUtils->new_from_short_name( {
317             short_name => 'GPL-1',
318             holder => 'X. Ample'
319             }) ;
320              
321             Create a new L<Software::License> object from the license specified
322             with C<short_name>. Known short license names are C<GPL-*>, C<LGPL-*> ,
323             C<Artistic> and C<Artistic-*>
324              
325             =head2 new_from_spdx_expression
326              
327             my $license_object = Software::LicenseUtils->new_from_spdx_expression( {
328             spdx_expression => 'MPL-2.0',
329             holder => 'X. Ample'
330             }) ;
331              
332             Create a new L<Software::License> object from the license specified
333             with C<spdx_expression>. Some licenses doesn't have an spdx
334             identifier (for example L<Software::License::Perl_5>), so you can pass
335             spdx identifier but also expressions.
336             Known spdx license identifiers are C<BSD>, C<MPL-1.0>.
337              
338             =head1 AUTHOR
339              
340             Ricardo Signes <rjbs@semiotic.systems>
341              
342             =head1 COPYRIGHT AND LICENSE
343              
344             This software is copyright (c) 2022 by Ricardo Signes.
345              
346             This is free software; you can redistribute it and/or modify it under
347             the same terms as the Perl 5 programming language system itself.
348              
349             =cut