File Coverage

blib/lib/Debian/Control/FromCPAN.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Debian::Control::FromCPAN - fill F from unpacked CPAN distribution
4              
5             =head1 SYNOPSIS
6              
7             my $c = Debian::Control::FromCPAN->new();
8             $c->discover_dependencies( { ... } );
9             $c->prune_perl_deps;
10              
11             Debian::Control::FromCPAN inherits from L.
12             =cut
13              
14             package Debian::Control::FromCPAN;
15              
16 2     2   33957870 use strict;
  2         8  
  2         200  
17 2     2   30 use warnings;
  2         19  
  2         285  
18              
19             our $VERSION = '0.77';
20              
21 2     2   144 use Carp qw(croak);
  2         5  
  2         351  
22              
23 2     2   25 use base 'Debian::Control';
  2         3  
  2         1557  
24              
25             use CPAN ();
26             use DhMakePerl::Utils qw( is_core_module find_cpan_module nice_perl_ver split_version_relation );
27             use File::Spec qw( catfile );
28             use Module::Depends ();
29              
30             use constant oldstable_perl_version => '5.10.1';
31              
32             =head1 METHODS
33              
34             =over
35              
36             =item discover_dependencies( [ { options hash } ] )
37              
38             Discovers module dependencies and fills the dependency fields in
39             F accordingly.
40              
41             Options:
42              
43             =over
44              
45             =item apt_contents
46              
47             An instance of L to be used when locating to which package
48             a required module belongs.
49              
50             =item dir
51              
52             The directory where the cpan distribution was unpacked.
53              
54             =item intrusive
55              
56             A flag indicating permission to use L for
57             discovering dependencies in case L fails. Since this requires
58             loading all Perl modules in the distribution (and running their BEGIN blocks
59             (and the BEGIN blocks of their dependencies, recursively), it is recommended to
60             use this only when dealing with trusted sources.
61              
62             =item require_deps
63              
64             If true, causes the method to die if some a package for some dependency cannot
65             be found. Otherwise only a warning is issued.
66              
67             =item verbose
68              
69             =item wnpp_query
70              
71             An instance of L to be used when checking for WNPP bugs of
72             depended upon packages.
73              
74             =back
75              
76             Returns a list of module names for which no suitable Debian packages were
77             found.
78              
79             =cut
80              
81             sub discover_dependencies {
82             my ( $self, $opts ) = @_;
83              
84             $opts //= {};
85             ref($opts) and ref($opts) eq 'HASH'
86             or die 'Usage: $obj->{ [ { opts hash } ] )';
87             my $apt_contents = delete $opts->{apt_contents};
88             my $dir = delete $opts->{dir};
89             my $intrusive = delete $opts->{intrusive};
90             my $require_deps = delete $opts->{require_deps};
91             my $verbose = delete $opts->{verbose};
92             my $wnpp_query = delete $opts->{wnpp_query};
93              
94             die "Unsupported option(s) given: " . join( ', ', sort( keys(%$opts) ) )
95             if %$opts;
96              
97             my $src = $self->source;
98             my $bin = $self->binary_tie->Values(0);
99              
100             local @INC = ( $dir, @INC );
101              
102             # try Module::Depends, but if that fails then
103             # fall back to Module::Depends::Intrusive.
104              
105             my $finder = Module::Depends->new->dist_dir($dir);
106             my $deps;
107             do {
108             no warnings;
109             local *STDERR;
110             open( STDERR, ">/dev/null" );
111             $deps = $finder->find_modules;
112             };
113              
114             my $error = $finder->error();
115             if ($error) {
116             if ($verbose) {
117             warn '=' x 70, "\n";
118             warn "Failed to detect dependencies using Module::Depends.\n";
119             warn "The error given was:\n";
120             warn "$error";
121             }
122              
123             if ( $intrusive ) {
124             warn "Trying again with Module::Depends::Intrusive ... \n"
125             if $verbose;
126             require Module::Depends::Intrusive;
127             $finder = Module::Depends::Intrusive->new->dist_dir($dir);
128             do {
129             no warnings;
130             local *STDERR;
131             open( STDERR, ">/dev/null" );
132             $deps = $finder->find_modules;
133             };
134              
135             if ( $finder->error ) {
136             if ($verbose) {
137             warn '=' x 70, "\n";
138             warn
139             "Could not find the "
140             . "dependencies for the requested module.\n";
141             warn "Generated error: " . $finder->error;
142              
143             warn "Please bug the module author to provide a"
144             . " proper META.yml file.\n"
145             . "Automatic find of"
146             . " dependencies failed. You may want to \n"
147             . "retry using the '--[b]depends[i]' options\n"
148             . "or just fill the dependency fields in debian/rules"
149             . " by hand\n";
150              
151             return;
152             }
153             }
154             }
155             else {
156             if ($verbose) {
157             warn "If you understand the security implications, try --intrusive.\n";
158             warn '=' x 70, "\n";
159             }
160             return;
161             }
162             }
163              
164             # run-time
165             my ( $debs, $missing )
166             = $self->find_debs_for_modules( $deps->{requires}, $apt_contents, $verbose );
167              
168             if (@$debs) {
169             if ($verbose) {
170             print "\n";
171             print "Needs the following debian packages: "
172             . join( ", ", @$debs ) . "\n";
173             }
174             $bin->Depends->add(@$debs);
175             if ( $bin->Architecture eq 'all' ) {
176             $src->Build_Depends_Indep->add(@$debs);
177             }
178             else {
179             $src->Build_Depends->add(@$debs);
180             }
181             }
182              
183             # build-time
184             my ( $b_debs, $b_missing ) = $self->find_debs_for_modules(
185             { %{ $deps->{build_requires} || {} },
186             %{ $deps->{test_requires} || {} },
187             %{ $deps->{configure_requires} || {} }
188             },
189             $apt_contents,
190             $verbose
191             );
192              
193             if (@$b_debs) {
194             if ($verbose) {
195             print "\n";
196             print "Needs the following debian packages during building: "
197             . join( ", ", @$b_debs ) . "\n";
198             }
199             if ( $self->is_arch_dep ) {
200             $src->Build_Depends->add(@$b_debs);
201             }
202             else {
203             $src->Build_Depends_Indep->add(@$b_debs);
204             }
205             }
206              
207             push @$missing, @$b_missing;
208              
209             if (@$missing) {
210             my ($missing_debs_str);
211             if ($apt_contents) {
212             $missing_debs_str
213             = "Needs the following modules for which there are no debian packages available:\n";
214             for (@$missing) {
215             my $bug
216             = $wnpp_query
217             ? ( $wnpp_query->bugs_for_package($_) )[0]
218             : undef;
219             $missing_debs_str .= " - $_";
220             $missing_debs_str .= " (" . $bug->type_and_number . ')'
221             if $bug;
222             $missing_debs_str .= "\n";
223             }
224             }
225             else {
226             $missing_debs_str = "The following Perl modules are required and not installed in your system:\n";
227             for (@$missing) {
228             my $bug
229             = $wnpp_query
230             ? ( $wnpp_query->bugs_for_package($_) )[0]
231             : undef;
232             $missing_debs_str .= " - $_";
233             $missing_debs_str .= " (" . $bug->type_and_number . ')'
234             if $bug;
235             $missing_debs_str .= "\n";
236             }
237             $missing_debs_str .= <
238             You do not have 'apt-file' currently installed, or have not ran
239             'apt-file update' - If you install it and run 'apt-file update' as
240             root, I will be able to tell you which Debian packages are those
241             modules in (if they are packaged).
242             EOF
243             }
244              
245             if ($require_deps) {
246             die $missing_debs_str;
247             }
248             else {
249             warn $missing_debs_str;
250             }
251              
252             }
253              
254             return @$missing;
255             }
256              
257             =item find_debs_for_modules I[, APT contents[, verbose ]]
258              
259             Scans the given hash of dependencies ( module => version ) and returns
260             matching Debian package dependency specification (as an instance of
261             L class) and a list of missing modules.
262              
263             Perl core is searched first, then installed packages, then the APT contents.
264              
265             =cut
266              
267             sub find_debs_for_modules {
268              
269             my ( $self, $dep_hash, $apt_contents, $verbose ) = @_;
270              
271             my $debs = Debian::Dependencies->new();
272              
273             my @missing;
274              
275             while ( my ( $module, $version ) = each %$dep_hash ) {
276              
277             my $ver_rel;
278              
279             ( $ver_rel, $version ) = split_version_relation($version) if $version;
280              
281             $version =~ s/^v// if $version;
282              
283             my $dep;
284              
285             require Debian::DpkgLists;
286             if ( my $ver = is_core_module( $module, $version ) ) {
287             $dep = Debian::Dependency->new( 'perl', $ver );
288             }
289             elsif ( my @pkgs = Debian::DpkgLists->scan_perl_mod($module) ) {
290             $dep = Debian::Dependency->new(
291             ( @pkgs > 1 )
292             ? [ map { { pkg => $_, ver => $version } } @pkgs ]
293             : ( $pkgs[0], $version )
294             );
295             }
296             elsif ($apt_contents) {
297             $dep = $apt_contents->find_perl_module_package( $module, $version );
298             }
299              
300              
301             $dep->rel($ver_rel) if $dep and $ver_rel and $dep->ver;
302              
303             my $mod_ver = join( " ", $module, $ver_rel // (), $version || () );
304             if ($dep) {
305             if ($verbose) {
306             if ( $dep->pkg and $dep->pkg eq 'perl' ) {
307             print "= $mod_ver is in core";
308             print " since " . $dep->ver if $dep->ver;
309             print "\n";
310             }
311             else {
312             print "+ $mod_ver found in $dep\n";
313             }
314             }
315              
316             my $target_perl_version = $^V;
317             $target_perl_version =~ s/^v//;
318             $target_perl_version = Dpkg::Version->new($target_perl_version);
319              
320             if ( $dep->pkg
321             and $dep->pkg eq 'perl'
322             and $dep->ver
323             and $dep->ver > $target_perl_version )
324             {
325             print " ! $dep is too new. Adding alternative dependency\n"
326             if $verbose;
327              
328             my $alt_dep;
329              
330             if ( my @pkgs = Debian::DpkgLists->scan_perl_mod($module) ) {
331             @pkgs = grep {
332             ( $_ ne 'perl-modules' )
333             and ( $_ ne 'perl-base' )
334             and ( $_ ne 'perl' )
335             } @pkgs;
336              
337             $alt_dep = Debian::Dependency->new(
338             ( @pkgs > 1 )
339             ? [ map { { pkg => $_, ver => $version } } @pkgs ]
340             : ( $pkgs[0], $version )
341             ) if @pkgs;
342             }
343              
344             if ( not $alt_dep and $apt_contents) {
345             $alt_dep
346             = $apt_contents->find_perl_module_package( $module,
347             $version );
348             }
349              
350             $alt_dep
351             //= Debian::Dependency->new(
352             $self->module_name_to_pkg_name($module),
353             '>=', $version );
354              
355             $dep = Debian::Dependency->new("$alt_dep | $dep");
356             #print " $dep\n";
357             }
358             }
359             else {
360             print "- $mod_ver not found in any package\n";
361             push @missing, $module;
362              
363             my $mod = find_cpan_module($module);
364             if ( $mod and $mod->distribution ) {
365             ( my $dist = $mod->distribution->base_id ) =~ s/-v?\d[^-]*$//;
366             my $pkg = $self->module_name_to_pkg_name($dist);
367              
368             print " CPAN contains it in $dist\n";
369             print " substituting package name of $pkg\n";
370              
371             $dep = Debian::Dependency->new( $pkg, $ver_rel, $version );
372             }
373             else {
374             print " - it seems it is not available even via CPAN\n";
375             }
376             }
377              
378             $debs->add($dep) if $dep;
379             }
380              
381             return $debs, \@missing;
382             }
383              
384             =item prune_simple_perl_dep
385              
386             Input:
387              
388             =over
389              
390             =item dependency object
391              
392             shall be a simple dependency (no alternatives)
393              
394             =item (optional) build dependency flag
395              
396             true value indicates the dependency is a build-time one
397              
398             =back
399              
400              
401             The following checks are made
402              
403             =over
404              
405             =item dependencies on C
406              
407             These are replaced with C as per Perl policy.
408              
409             =item dependencies on C and build-dependencies on C or
410             C
411              
412             These are removed, unless they specify a version greater than the one available
413             in C or the dependency relation is not C<< >= >> or C<<< >> >>>.
414              
415             =back
416              
417             Return value:
418              
419             =over
420              
421             =item undef
422              
423             if the dependency is redundant.
424              
425             =item pruned dependency
426              
427             otherwise. C replaced with C.
428              
429             =back
430              
431             =cut
432              
433             sub prune_simple_perl_dep {
434             my( $self, $dep, $build ) = @_;
435              
436             croak "No alternative dependencies can be given"
437             if $dep->alternatives;
438              
439             return $dep unless $dep->pkg =~ /^(?:perl|perl-base|perl-modules)$/;
440              
441             # perl-modules is replaced with perl
442             $dep->pkg('perl') if $dep->pkg eq 'perl-modules';
443              
444             my $unversioned = (
445             not $dep->ver
446             or $dep->rel =~ />/
447             and $dep->ver <= $self->oldstable_perl_version
448             );
449              
450             # if the dependency is considered unversioned, make sure there is no
451             # version
452             if ($unversioned) {
453             $dep->ver(undef);
454             $dep->rel(undef);
455             }
456              
457             # perl-base is (build-)essential
458             return undef
459             if $dep->pkg eq 'perl-base' and $unversioned;
460              
461             # perl is needed in build-dependencies (see Policy 4.2)
462             return $dep if $dep->pkg eq 'perl' and $build;
463              
464             # unversioned perl non-build-dependency is redundant, because it will be
465             # covered by ${perl:Depends}
466             return undef
467             if not $build
468             and $dep->pkg eq 'perl'
469             and $unversioned;
470              
471             return $dep;
472             }
473              
474             =item prune_perl_dep
475              
476             Similar to L, but supports alternative dependencies.
477             If any of the alternatives is redundant, the whole dependency is considered
478             redundant.
479              
480             =cut
481              
482             sub prune_perl_dep {
483             my( $self, $dep, $build ) = @_;
484              
485             return $self->prune_simple_perl_dep( $dep, $build )
486             unless $dep->alternatives;
487              
488             for my $simple ( @{ $dep->alternatives } ) {
489             my $pruned = $self->prune_simple_perl_dep( $simple, $build );
490              
491             # redundant alternative?
492             return undef unless $pruned;
493              
494             $simple = $pruned;
495             }
496              
497             return $dep;
498             }
499              
500             =item prune_perl_deps
501              
502             Remove redundant (build-)dependencies on perl, perl-modules and perl-base.
503              
504             =cut
505              
506             sub prune_perl_deps {
507             my $self = shift;
508              
509             # remove build-depending on ancient perl versions
510             for my $perl ( qw( perl perl-base perl-modules ) ) {
511             for ( qw( Build_Depends Build_Depends_Indep ) ) {
512             my @ess = $self->source->$_->remove($perl);
513             # put back non-redundant ones (possibly modified)
514             for my $dep (@ess) {
515             my $pruned = $self->prune_perl_dep( $dep, 1 );
516              
517             $self->source->$_->add($pruned) if $pruned;
518             }
519             }
520             }
521              
522             # remove depending on ancient perl versions
523             for my $perl ( qw( perl perl-base perl-modules ) ) {
524             for my $pkg ( $self->binary_tie->Values ) {
525             for my $rel ( qw(Depends Recommends Suggests) ) {
526             my @ess = $pkg->$rel->remove($perl);
527             for my $dep (@ess) {
528             my $pruned = $self->prune_perl_dep( $dep, 0 );
529              
530             $pkg->$rel->add($pruned) if $pruned;
531             }
532             }
533             }
534             }
535             }
536              
537             =back
538              
539             =head1 CLASS METHODS
540              
541             =over
542              
543             =item module_name_to_pkg_name
544              
545             Receives a perl module name like C and returns a suitable Debian
546             package name for it, like C.
547              
548             =cut
549              
550             sub module_name_to_pkg_name {
551             my ( $self, $module ) = @_;
552              
553             my $pkg = lc $module;
554              
555             # ensure policy compliant names and versions (from Joeyh)...
556             $pkg =~ s/[^-.+a-zA-Z0-9]+/-/g;
557              
558             $pkg =~ s/--+/-/g;
559              
560             $pkg = 'lib' . $pkg unless $pkg =~ /^lib/;
561             $pkg .= '-perl';
562              
563             return $pkg;
564             }
565              
566             =back
567              
568             =head1 COPYRIGHT & LICENSE
569              
570             Copyright (C) 2009, 2010, 2012 Damyan Ivanov L
571              
572             This program is free software; you can redistribute it and/or modify it under
573             the terms of the GNU General Public License version 2 as published by the Free
574             Software Foundation.
575              
576             This program is distributed in the hope that it will be useful, but WITHOUT ANY
577             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
578             PARTICULAR PURPOSE.
579              
580             =cut
581              
582             1;
583              
584