8 use CPANPLUS
::Backend
();
10 use Getopt
::Long
::Descriptive
qw( describe_options );
11 use JSON
::PP
qw( encode_json );
12 use Log
::Log4perl
qw(:easy);
15 # Readonly hash that maps CPAN style license strings to information
16 # necessary to generate a Nixpkgs style license attribute.
17 Readonly
::Hash
my %LICENSE_MAP => (
19 # The Perl 5 License (Artistic 1 & GPL 1 or later).
21 licenses
=> [qw( artistic1 gpl1Plus )]
24 # GNU Affero General Public License, Version 3.
26 licenses
=> [qw( agpl3Plus )],
30 # Apache Software License, Version 1.1.
32 licenses
=> ["Apache License 1.1"],
36 # Apache License, Version 2.0.
38 licenses
=> [qw( asl20 )]
41 # Artistic License, (Version 1).
43 licenses
=> [qw( artistic1 )]
46 # Artistic License, Version 2.0.
48 licenses
=> [qw( artistic2 )]
51 # BSD License (three-clause).
53 licenses
=> [qw( bsd3 )],
57 # FreeBSD License (two-clause).
59 licenses
=> [qw( bsd2 )]
62 # GNU Free Documentation License, Version 1.2.
64 licenses
=> [qw( fdl12Plus )]
67 # GNU Free Documentation License, Version 1.3.
69 licenses
=> [qw( fdl13Plus )]
72 # GNU General Public License, Version 1.
74 licenses
=> [qw( gpl1Plus )],
78 # GNU General Public License, Version 2. Note, we will interpret
79 # "gpl" alone as GPL v2+.
81 licenses
=> [qw( gpl2Plus )],
85 # GNU General Public License, Version 3.
87 licenses
=> [qw( gpl3Plus )],
91 # GNU Lesser General Public License, Version 2.1. Note, we will
92 # interpret "gpl" alone as LGPL v2.1+.
94 licenses
=> [qw( lgpl21Plus )],
98 # GNU Lesser General Public License, Version 3.0.
100 licenses
=> [qw( lgpl3Plus )],
104 # MIT (aka X11) License.
106 licenses
=> [qw( mit )]
109 # Mozilla Public License, Version 1.0.
111 licenses
=> [qw( mpl10 )]
114 # Mozilla Public License, Version 1.1.
116 licenses
=> [qw( mpl11 )]
121 licenses
=> [qw( openssl )]
124 # Q Public License, Version 1.0.
126 licenses
=> [qw( qpl )]
129 # Original SSLeay License.
131 licenses
=> ["Original SSLeay License"],
135 # Sun Internet Standards Source License (SISSL).
137 licenses
=> ["Sun Industry Standards Source License v1.1"],
143 licenses
=> [qw( zlib )]
146 # Other Open Source Initiative (OSI) approved license.
148 licenses
=> [qw( free )],
152 # Requires special permission from copyright holder.
154 licenses
=> [qw( unfree )],
158 # Not an OSI approved license, but not restricted. Note, we
159 # currently map this to unfreeRedistributable, which is a
160 # conservative choice.
162 licenses
=> [qw( unfreeRedistributable )],
166 # License not provided in metadata.
174 my ( $opt, $usage ) = describe_options
(
175 'usage: $0 %o MODULE',
176 [ 'maintainer|m=s', 'the package maintainer' ],
177 [ 'debug|d', 'enable debug output' ],
178 [ 'help', 'print usage message and exit' ]
186 my $module_name = $ARGV[0];
188 if ( !defined $module_name ) {
189 print STDERR
"Missing module name\n";
190 print STDERR
$usage->text;
194 return ( $opt, $module_name );
197 # Takes a Perl package attribute name and returns 1 if the name cannot
198 # be referred to as a bareword. This typically happens if the package
199 # name is a reserved Nix keyword.
203 return $pkg =~ /^(?
: assert
|
218 my $attr_name = $module->package_name;
219 if ( $attr_name eq "libwww-perl" ) {
223 $attr_name =~ s/-//g;
230 return ( $module->package_name, $module->package_version =~ s/^v(\d)/$1/r );
236 my $yaml_path = "$pkg_path/META.yml";
237 my $json_path = "$pkg_path/META.json";
240 if ( -r
$json_path ) {
241 $meta = CPAN
::Meta
->load_file($json_path);
243 elsif ( -r
$yaml_path ) {
244 $meta = CPAN
::Meta
->load_file($yaml_path);
247 WARN
("package has no META.yml or META.json");
253 # Map a module to the attribute corresponding to its package
254 # (e.g. HTML::HeadParser will be mapped to HTMLParser, because that
255 # module is in the HTML-Parser package).
257 my ( $cb, $module_name ) = @_;
258 my @modules = $cb->search( type
=> "name", allow
=> [$module_name] );
259 if ( scalar @modules == 0 ) {
262 $module_name =~ s/:://g;
265 my $module = $modules[0];
266 my $attr_name = pkg_to_attr
($module);
267 DEBUG
("mapped dep $module_name to $attr_name");
272 my ( $cb, $meta, $type ) = @_;
274 return if !defined $meta;
276 my $prereqs = $meta->effective_prereqs;
277 my $deps = $prereqs->requirements_for( $type, "requires" );
279 foreach my $n ( $deps->required_modules ) {
280 next if $n eq "perl";
282 my @core = Module
::CoreList
->find_modules(qr/^$n$/);
285 my $pkg = module_to_pkg
( $cb, $n );
287 # If the package name is reserved then we need to refer to it
288 # through the "self" variable.
289 $pkg = "self.\"$pkg\"" if is_reserved
($pkg);
297 return keys %{ { map { $_ => 1 } @_ } };
301 my ($cpan_license) = @_;
303 return if !defined $cpan_license;
307 # If the license is ambiguous then we'll print an extra warning.
308 # For example, "gpl_2" is ambiguous since it may refer to exactly
309 # "GPL v2" or to "GPL v2 or later".
312 # Whether the license is available inside `lib.licenses`.
315 my $nix_license = $LICENSE_MAP{$cpan_license};
316 if ( !$nix_license ) {
317 WARN
("Unknown license: $cpan_license");
318 $licenses = [$cpan_license];
322 $licenses = $nix_license->{licenses
};
323 $amb = $nix_license->{amb
};
324 $in_set = !$nix_license->{in_set
};
329 if ( @
$licenses == 0 ) {
331 # Avoid defining the license line.
334 my $lic = 'lib.licenses';
335 if ( @
$licenses == 1 ) {
336 $license_line = "$lic.$licenses->[0]";
339 $license_line = "with $lic; [ " . join( ' ', @
$licenses ) . " ]";
343 if ( @
$licenses == 1 ) {
344 $license_line = $licenses->[0];
347 $license_line = '[ ' . join( ' ', @
$licenses ) . ' ]';
351 INFO
("license: $cpan_license");
352 WARN
("License '$cpan_license' is ambiguous, please verify") if $amb;
354 return $license_line;
357 my ( $opt, $module_name ) = handle_opts
();
359 Log
::Log4perl
->easy_init(
361 level
=> $opt->debug ?
$DEBUG : $INFO,
366 my $cb = CPANPLUS
::Backend
->new;
368 my @modules = $cb->search( type
=> "name", allow
=> [$module_name] );
369 die "module $module_name not found\n" if scalar @modules == 0;
370 die "multiple packages that match module $module_name\n" if scalar @modules > 1;
371 my $module = $modules[0];
373 my ($pkg_name, $pkg_version) = get_pkg_name
$module;
374 my $attr_name = pkg_to_attr
$module;
376 INFO
( "attribute name: ", $attr_name );
377 INFO
( "module: ", $module->module );
378 INFO
( "version: ", $module->version );
379 INFO
( "package: ", $module->package, " (", "$pkg_name-$pkg_version", ", ", $attr_name, ")" );
380 INFO
( "path: ", $module->path );
382 my $tar_path = $module->fetch();
383 INFO
( "downloaded to: ", $tar_path );
384 INFO
( "sha-256: ", $module->status->checksum_value );
386 my $pkg_path = $module->extract();
387 INFO
( "unpacked to: ", $pkg_path );
389 my $meta = read_meta
($pkg_path);
391 DEBUG
( "metadata: ", encode_json
( $meta->as_struct ) ) if defined $meta;
393 my @runtime_deps = sort( uniq
( get_deps
( $cb, $meta, "runtime" ) ) );
394 INFO
("runtime deps: @runtime_deps");
396 my @build_deps = sort( uniq
(
397 get_deps
( $cb, $meta, "configure" ),
398 get_deps
( $cb, $meta, "build" ),
399 get_deps
( $cb, $meta, "test" )
402 # Filter out runtime dependencies since those are already handled.
403 my %in_runtime_deps = map { $_ => 1 } @runtime_deps;
404 @build_deps = grep { not $in_runtime_deps{$_} } @build_deps;
406 INFO
("build deps: @build_deps");
408 my $homepage = $meta ?
$meta->resources->{homepage
} : undef;
409 INFO
("homepage: $homepage") if defined $homepage;
411 my $description = $meta ?
$meta->abstract : undef;
412 if ( defined $description ) {
413 $description = uc( substr( $description, 0, 1 ) )
414 . substr( $description, 1 ); # capitalise first letter
415 $description =~ s/\.$//; # remove period at the end
416 $description =~ s/\s*$//;
417 $description =~ s/^\s*//;
418 $description =~ s/\n+/ /; # Replace new lines by space.
419 INFO
("description: $description");
422 #print(Data::Dumper::Dumper($meta->licenses) . "\n");
423 my $license = $meta ? render_license
( $meta->licenses ) : undef;
425 INFO
( "RSS feed: https://metacpan.org/feed/distribution/",
426 $module->package_name );
428 my $build_fun = -e
"$pkg_path/Build.PL"
429 && !-e
"$pkg_path/Makefile.PL" ?
"buildPerlModule" : "buildPerlPackage";
431 print STDERR
"===\n";
434 ${\(is_reserved($attr_name) ? "\"$attr_name\"" : $attr_name)} = $build_fun {
436 version = "$pkg_version";
438 url = "mirror://cpan/${\$module->path}/${\$module->package}";
439 sha256 = "${\$module->status->checksum_value}";
442 print <<EOF if scalar @build_deps > 0;
443 buildInputs = [ @build_deps ];
445 print <<EOF if scalar @runtime_deps > 0;
446 propagatedBuildInputs = [ @runtime_deps ];
451 print <<EOF if defined $homepage;
452 homepage = "$homepage";
454 print <<EOF if defined $description && $description ne "Unknown";
455 description = "$description";
457 print <<EOF if defined $license;
460 print <<EOF if $opt->maintainer;
461 maintainers = [ maintainers.${\$opt->maintainer} ];