Module-ScanDeps-1.20/0000755000372000037200000000000012604223352014534 5ustar roderichroderichModule-ScanDeps-1.20/inc/0000755000372000037200000000000012604223352015305 5ustar roderichroderichModule-ScanDeps-1.20/inc/Module/0000755000372000037200000000000012604223352016532 5ustar roderichroderichModule-ScanDeps-1.20/inc/Module/Install.pm0000644000372000037200000003021712604223221020474 0ustar roderichroderich#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.006; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.16'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::getcwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::getcwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::getcwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split /\n/, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; binmode FH; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; binmode FH; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; binmode FH; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; binmode FH; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp { _version($_[1]) <=> _version($_[2]); } # Cloned from Params::Util::_CLASS sub _CLASS { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2012 Adam Kennedy. Module-ScanDeps-1.20/inc/Module/Install/0000755000372000037200000000000012604223352020140 5ustar roderichroderichModule-ScanDeps-1.20/inc/Module/Install/Scripts.pm0000644000372000037200000000101112604223222022112 0ustar roderichroderich#line 1 package Module::Install::Scripts; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub install_script { my $self = shift; my $args = $self->makemaker_args; my $exe = $args->{EXE_FILES} ||= []; foreach ( @_ ) { if ( -f $_ ) { push @$exe, $_; } elsif ( -d 'script' and -f "script/$_" ) { push @$exe, "script/$_"; } else { die("Cannot find script '$_'"); } } } 1; Module-ScanDeps-1.20/inc/Module/Install/Win32.pm0000644000372000037200000000340312604223222021374 0ustar roderichroderich#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Module-ScanDeps-1.20/inc/Module/Install/Makefile.pm0000644000372000037200000002743712604223222022224 0ustar roderichroderich#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-separated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # This previous attempted to inherit the version of # ExtUtils::MakeMaker in use by the module author, but this # was found to be untenable as some authors build releases # using future dev versions of EU:MM that nobody else has. # Instead, #toolchain suggests we use 6.59 which is the most # stable version on CPAN at time of writing and is, to quote # ribasushi, "not terminally fucked, > and tested enough". # TODO: We will now need to maintain this over time to push # the version up as new versions are released. $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 544 Module-ScanDeps-1.20/inc/Module/Install/WriteAll.pm0000644000372000037200000000237612604223222022225 0ustar roderichroderich#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; Module-ScanDeps-1.20/inc/Module/Install/Fetch.pm0000644000372000037200000000462712604223222021534 0ustar roderichroderich#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Module-ScanDeps-1.20/inc/Module/Install/Metadata.pm0000644000372000037200000004330212604223222022214 0ustar roderichroderich#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; my $value = @_ ? shift : 1; if ( $self->{values}->{dynamic_config} ) { # Once dynamic we never change to static, for safety return 0; } $self->{values}->{dynamic_config} = $value ? 1 : 0; return 1; } # Convenience command sub static_config { shift->dynamic_config(0); } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the really old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) [\s|;]* /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashes delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Module-ScanDeps-1.20/inc/Module/Install/Can.pm0000644000372000037200000000615712604223222021204 0ustar roderichroderich#line 1 package Module::Install::Can; use strict; use Config (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.16'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # Check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; require File::Spec; my $abs = File::Spec->catfile($dir, $cmd); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # Can our C compiler environment build XS files sub can_xs { my $self = shift; # Ensure we have the CBuilder module $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 ); # Do we have the configure_requires checker? local $@; eval "require ExtUtils::CBuilder;"; if ( $@ ) { # They don't obey configure_requires, so it is # someone old and delicate. Try to avoid hurting # them by falling back to an older simpler test. return $self->can_cc(); } # Do we have a working C compiler my $builder = ExtUtils::CBuilder->new( quiet => 1, ); unless ( $builder->have_compiler ) { # No working C compiler return 0; } # Write a C file representative of what XS becomes require File::Temp; my ( $FH, $tmpfile ) = File::Temp::tempfile( "compilexs-XXXXX", SUFFIX => '.c', ); binmode $FH; print $FH <<'END_C'; #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int main(int argc, char **argv) { return 0; } int boot_sanexs() { return 1; } END_C close $FH; # Can the C compiler access the same headers XS does my @libs = (); my $object = undef; eval { local $^W = 0; $object = $builder->compile( source => $tmpfile, ); @libs = $builder->link( objects => $object, module_name => 'sanexs', ); }; my $result = $@ ? 0 : 1; # Clean up all the build files foreach ( $tmpfile, $object, @libs ) { next unless defined $_; 1 while unlink; } return $result; } # Can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 236 Module-ScanDeps-1.20/inc/Module/Install/Base.pm0000644000372000037200000000214712604223222021350 0ustar roderichroderich#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.16'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 Module-ScanDeps-1.20/script/0000755000372000037200000000000012604223352016040 5ustar roderichroderichModule-ScanDeps-1.20/script/scandeps.pl0000644000372000037200000001505412604220754020205 0ustar roderichroderich#!/usr/bin/perl $VERSION = '0.76'; use strict; use Config; use Getopt::Long qw(:config bundling no_ignore_case); use Module::ScanDeps; use ExtUtils::MakeMaker; use subs qw( _name _modtree ); my $usage = "Usage: $0 [ -B ] [ -V ] [ -T ] [ -x [ --xargs STRING ] | -c ] [ -R ] [-C FILE ] [ -e STRING | FILE ... ]\n"; my %opts; GetOptions(\%opts, "B|bundle", "C|cachedeps=s", "c|compile", "e|eval=s", "xargs=s", "R|no-recurse", "T|modtree", "V|verbose", "x|execute", ) or die $usage; my (%map, %skip); my $core = $opts{B}; my $verbose = $opts{V}; my $eval = $opts{e}; my $recurse = $opts{R} ? 0 : 1; my $modtree = {} unless $opts{T}; # i.e. disable it unless explicitly requested if ($eval) { require File::Temp; my ($fh, $filename) = File::Temp::tempfile( UNLINK => 1 ); print $fh $eval, "\n" or die $!; close $fh; push @ARGV, $filename; } if ($opts{x} && defined $opts{xargs}) { require Text::ParseWords; $opts{x} = [ Text::ParseWords::shellwords($opts{xargs}) ]; } die $usage unless @ARGV; my @files = @ARGV; while (<>) { next unless /^package\s+([\w:]+)/; $skip{$1}++; } my $map = scan_deps( files => \@files, recurse => $recurse, $opts{x} ? ( execute => $opts{x} ) : $opts{c} ? ( compile => 1 ) : (), $opts{V} ? ( warn_missing => 1 ) : (), $opts{C} ? ( cache_file => $opts{C}) : (), ); my $len = 0; my @todo; my (%seen, %dist, %core, %bin); foreach my $key (sort keys %$map) { my $mod = $map->{$key}; my $name = $mod->{name} = _name($key); print "# $key [$mod->{type}]\n" if $verbose; if ($mod->{type} eq 'shared') { $key =~ s!auto/!!; $key =~ s!/[^/]+$!!; $key =~ s!/!::!; $bin{$key}++; } next unless $mod->{type} eq 'module'; next if $skip{$name}; my $privPath = "$Config::Config{privlibexp}/$key"; my $archPath = "$Config::Config{archlibexp}/$key"; $privPath =~ s|\\|\/|og; $archPath =~ s|\\|\/|og; if ($mod->{file} eq $privPath or $mod->{file} eq $archPath) { next unless $core; $core{$name}++; } elsif (my $dist = _modtree->{$name}) { $seen{$name} = $dist{$dist->package}++; } $len = length($name) if $len < length($name); $mod->{used_by} ||= []; push @todo, $mod; } $len += 2; print "# Legend: [C]ore [X]ternal [S]ubmodule [?]NotOnCPAN\n" if $verbose; foreach my $mod (sort { "@{$a->{used_by}}" cmp "@{$b->{used_by}}" or $a->{key} cmp $b->{key} } @todo) { my $version = MM->parse_version($mod->{file}); if (!$verbose) { printf "%-${len}s => '$version',", "'$mod->{name}'" if $version; } else { printf "%-${len}s => '0', # ", "'$mod->{name}'"; my @base = map(_name($_), @{$mod->{used_by}}); print $seen{$mod->{name}} ? 'S' : ' '; print $bin{$mod->{name}} ? 'X' : ' '; print $core{$mod->{name}} ? 'C' : ' '; print _modtree && !_modtree->{$mod->{name}} ? '?' : ' '; print " # "; print "@base" if @base; } print "\n"; } warn "No modules found!\n" unless @todo; sub _name { my $str = shift; $str =~ s!/!::!g; $str =~ s!.pm$!!i; $str =~ s!^auto::(.+)::.*!$1!; return $str; } sub _modtree { $modtree ||= eval { require CPANPLUS::Backend; CPANPLUS::Backend->new->module_tree; } || {}; } 1; __END__ =head1 NAME scandeps.pl - Scan file prerequisites =head1 SYNOPSIS % scandeps.pl *.pm # Print PREREQ_PM section for *.pm % scandeps.pl -e 'STRING' # Scan an one-liner % scandeps.pl -B *.pm # Include core modules % scandeps.pl -V *.pm # Show autoload/shared/data files % scandeps.pl -R *.pm # Don't recurse % scandeps.pl -C CACHEFILE # use CACHEFILE to cache dependencies =head1 DESCRIPTION F is a simple-minded utility that prints out the C section needed by modules. If the option C<-T> is specified and you have B installed, modules that are part of an earlier module's distribution with be denoted with C; modules without a distribution name on CPAN are marked with C. Also, if the C<-B> option is specified, module belongs to a perl distribution on CPAN (and thus uninstallable by C or C) are marked with C. Finally, modules that has loadable shared object files (usually needing a compiler to install) are marked with C; with the C<-V> flag, those files (and all other files found) will be listed before the main output. Additionally, all module files that the scanned code depends on but were not found (and thus not scanned recursively) are listed. These may include genuinely missing modules or false positives. That means, modules your code does not depend on (on this particular platform) but that were picked up by the heuristic anyway. =head1 OPTIONS =over 4 =item B<-e>, B<--eval>=I Scan I as a string containing perl code. =item B<-c>, B<--compile> Compiles the code and inspects its C<%INC>, in addition to static scanning. =item B<-x>, B<--execute> Executes the code and inspects its C<%INC>, in addition to static scanning. You may use B<--xargs> to specify C<@ARGV> when executing the code. =item B<--xargs>=I If B<-x> is given, splits the C using the function C from L and passes the result as C<@ARGV> when executing the code. =item B<-B>, B<--bundle> Include core modules in the output and the recursive search list. =item B<-R>, B<--no-recurse> Only show dependencies found in the files listed and do not recurse. =item B<-V>, B<--verbose> Verbose mode: Output all files found during the process; show dependencies between modules and availability. Additionally, warns of any missing dependencies. If you find missing dependencies that aren't really dependencies, you have probably found false positives. =item B<-C>, B<--cachedeps>=I Use CACHEFILE to speed up the scanning process by caching dependencies. Creates CACHEFILE if it does not exist yet. =item B<-T>, B<--modtree> Retrieves module information from CPAN if you have B installed. =back =head1 SEE ALSO L, L, L =head1 ACKNOWLEDGMENTS Simon Cozens, for suggesting this script to be written. =head1 AUTHORS Audrey Tang Eautrijus@autrijus.orgE =head1 COPYRIGHT Copyright 2003, 2004, 2005, 2006 by Audrey Tang Eautrijus@autrijus.orgE. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =cut Module-ScanDeps-1.20/t/0000755000372000037200000000000012604223352014777 5ustar roderichroderichModule-ScanDeps-1.20/t/6-file-glob.t0000644000372000037200000000055612604220754017200 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 2; use Module::ScanDeps; use lib qw(t/data); my $map = scan_deps( files => ['t/data/file-glob-no.pl'], recurse => 1, ); ok(not exists $map->{'File/Glob.pm'}); $map = scan_deps( files => ['t/data/file-glob-yes.pl'], recurse => 1, ); ok(exists $map->{'File/Glob.pm'}); __END__ Module-ScanDeps-1.20/t/4-static_functional_interface_options_fake.t0000644000372000037200000004010212604220754025617 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 546; use lib qw(t t/data/static); use Utils; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # RECURSE OPTION TESTS ############################################################## # Using the following dependency tree # # M # /|\ # / | \ # / | \ # / M \ # / / \ \ # / / \ \ # M M M M # \ \ / / # \ \ / / # \ M / # \ | / # \ | / # M # # With dependencies always going from the top downwards ############################################################## my @roots1 = qw(t/data/static/outer_diamond_N.pm); my $expected_rv1 = { "inner_diamond_N.pm" => { file => generic_abs_path("t/data/static/inner_diamond_N.pm"), key => "inner_diamond_N.pm", type => "module", used_by => ["outer_diamond_N.pm"], }, "outer_diamond_E.pm" => { file => generic_abs_path("t/data/static/outer_diamond_E.pm"), key => "outer_diamond_E.pm", type => "module", used_by => ["outer_diamond_N.pm"], }, "outer_diamond_N.pm" => { file => generic_abs_path("t/data/static/outer_diamond_N.pm"), key => "outer_diamond_N.pm", type => "module", uses => ["inner_diamond_N.pm", "outer_diamond_E.pm", "outer_diamond_W.pm"], }, "outer_diamond_W.pm" => { file => generic_abs_path("t/data/static/outer_diamond_W.pm"), key => "outer_diamond_W.pm", type => "module", used_by => ["outer_diamond_N.pm"], }, }; my $rv1 = scan_deps( files => \@roots1, recurse => 0, ); compare_scandeps_rvs($rv1, $expected_rv1, \@roots1); ############################################################## # Using the following dependency tree # # InputA.pl InputB.pl InputC.pl # / \ \ / # / \ \ / # / \ \ / # TestA.pm TestB.pm TestC.pm / # \ / # \ / # TestD.pm # ############################################################## my @roots2 = qw(t/data/static/InputA.pl t/data/static/InputB.pl t/data/static/InputC.pl); my $expected_rv2 = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl"], # No "TestC.pm" used_by entry }, }; my $rv2 = scan_deps( files => \@roots2, recurse => 0, ); compare_scandeps_rvs($rv2, $expected_rv2, \@roots2); ############################################################## # SKIP OPTION TESTS ############################################################## # Dependency tree for tests # # InputA.pl InputB.pl InputC.pl # / \ \ / # / \ \ / # / \ \ / # TestA.pm TestB.pm TestC.pm / # \ / # \ / # TestD.pm # ############################################################## my @roots_ABC = qw(t/data/static/InputA.pl t/data/static/InputB.pl t/data/static/InputC.pl); ############################################################## my $expected_rv_ABC_skip_TestA = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, # It's OK to have this despite TestA.pm being skipped since this entry only shows # InputA.pl has been parsed and shown to depend on TestA.pm "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], uses => ["TestD.pm"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl", "TestC.pm"], }, }; my $rv3 = scan_deps( files => \@roots_ABC, skip => { generic_abs_path("t/data/static/TestA.pm") => 1 }, recurse => 1, ); compare_scandeps_rvs($rv3, $expected_rv_ABC_skip_TestA, \@roots_ABC); ############################################################## my $expected_rv_ABC_skip_TestC = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, # It's OK to have this despite TestC.pm being skipped since this entry only shows # InputB.pl has been parsed and shown to depend on TestC.pm "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl"], # No TestC used_by }, }; my $rv4 = scan_deps( files => \@roots_ABC, skip => { generic_abs_path("t/data/static/TestC.pm") => 1 }, recurse => 1, ); compare_scandeps_rvs($rv4, $expected_rv_ABC_skip_TestC, \@roots_ABC); ############################################################## # Test multiple skip entries my $expected_rv_ABC_skip_TestA_TestC = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, # It's OK to have this despite TestA.pm being skipped since this entry only shows # InputA.pl has been parsed and shown to depend on TestA.pm "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, # It's OK to have this despite TestC.pm being skipped since this entry only shows # InputB.pl has been parsed and shown to depend on TestC.pm "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl"], # No TestC used_by }, }; my $rv5 = scan_deps( files => \@roots_ABC, skip => { generic_abs_path("t/data/static/TestA.pm") => 1, generic_abs_path("t/data/static/TestC.pm") => 1, }, recurse => 1, ); compare_scandeps_rvs($rv5, $expected_rv_ABC_skip_TestA_TestC, \@roots_ABC); ############################################################## my @roots_AB = qw(t/data/static/InputA.pl t/data/static/InputB.pl); my $expected_rv_AB_skip_TestC = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, # It's OK to have this despite TestC.pm being skipped since this entry only shows # InputB.pl has been parsed and shown to depend on TestC.pm "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], }, # # No TestD entry # }; my $rv6 = scan_deps( files => \@roots_AB, skip => { generic_abs_path("t/data/static/TestC.pm") => 1 }, recurse => 1, ); compare_scandeps_rvs($rv6, $expected_rv_AB_skip_TestC, \@roots_AB); ############################################################## my $expected_rv_AB_skip_TestD = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], }, # # No TestD entry # }; my $rv7 = scan_deps( files => \@roots_AB, skip => { "t/data/static/TestD.pm" => 1 }, recurse => 1, ); #is_deeply($rv7, $expected_rv_AB_skip_TestD); compare_scandeps_rvs($rv7, $expected_rv_AB_skip_TestD, \@roots_AB); __END__ Module-ScanDeps-1.20/t/5-pluggable_fake.t0000644000372000037200000000103512604220754020260 0ustar roderichroderich#!/usr/bin/perl use Module::ScanDeps; use strict; use warnings; use Test::More qw(no_plan); # no_plan because the number of objects in the dependency tree (and hence the number of tests) can change use Test::Requires qw( Module::Pluggable ); use lib qw(t t/data/pluggable); use Utils; my $rv = scan_deps( files => ['t/data/pluggable/Foo.pm'], recurse => 1, ); my @deps = qw(Module/Pluggable.pm Foo/Plugin/Bar.pm Foo/Plugin/Baz.pm); generic_scandeps_rv_test($rv, ['t/data/pluggable/Foo.pm'], \@deps); __END__ Module-ScanDeps-1.20/t/1-static_functional_interface_real.t0000644000372000037200000000174112604220754024064 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use lib 't'; use Test::More qw(no_plan); # no_plan because the number of objects in the dependency tree (and hence the number of tests) can change use Utils; my $rv; my $root; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # Tests static dependency scanning on a real set of modules. # This exercises the scanning functionality but because the # majority of files scanned aren't fixed, the checks are # necessarily loose. ############################################################## $root = $0; my @deps = qw( Carp.pm Config.pm Exporter.pm Test/More.pm strict.pm vars.pm ); # Functional i/f $rv = scan_deps($root); generic_scandeps_rv_test($rv, [$0], \@deps); __END__ Module-ScanDeps-1.20/t/17-private_methods.t0000644000372000037200000000034612604220754020614 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 1; use Module::ScanDeps (); my @deps = Module::ScanDeps::_get_preload('Event.pm'); ok(grep {$_ eq 'Event/idle.pm'} @deps) or diag(join(', ',@deps)); Module-ScanDeps-1.20/t/0-pod.t0000644000372000037200000000014012604220754016101 0ustar roderichroderichuse strict; use Test::More; use Test::Requires { "Test::Pod" => "1.00" }; all_pod_files_ok(); Module-ScanDeps-1.20/t/16-scan_line.t0000644000372000037200000000100712604220754017344 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 3; use Module::ScanDeps qw/scan_line/; { my $chunk=<<'EOT'; use strict; EOT my @array=scan_line($chunk);@array=sort @array; is_deeply(\@array,[sort qw{strict.pm}]); } { my $chunk=<<'EOT'; require 5.10; EOT my @array=scan_line($chunk);@array=sort @array; is_deeply(\@array,[sort qw{feature.pm}]); } {# RT#48151 my $chunk=<<'EOT'; require __PACKAGE__ . "SomeExt.pm"; EOT eval { scan_line($chunk); }; is($@,''); } Module-ScanDeps-1.20/t/rt90869.t0000644000372000037200000000053112604220754016233 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More; use Module::ScanDeps qw(scan_deps); use lib qw(t/data/static); my @expected_modules = qw( TestA TestB TestC ); plan tests => scalar @expected_modules; my $rv = scan_deps("t/data/rt90869.pl"); foreach (@expected_modules) { ok(exists $rv->{"$_.pm"}, "expected module $_ found"); } Module-ScanDeps-1.20/t/10-case-insensitive-keys.t0000644000372000037200000000413412604220754021631 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use File::Spec; use Test::More; BEGIN { if(!File::Spec->case_tolerant()) { plan skip_all => 'Test irrelevant on case intolerant systems'; } else { plan tests => 43; } } use lib qw(t t/data/case-insensitive-keys); use Utils; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # Static dependency check of scripts that reference the same # module but in different cases ############################################################## my @roots1 = qw(t/data/case-insensitive-keys/this_case.pl t/data/case-insensitive-keys/that_case.pl); my $expected_rv1 = { "Test.pm" => { file => generic_abs_path("t/data/case-insensitive-keys/Test.pm"), key => "Test.pm", type => "module", used_by => ["this_case.pl", "that_case.pl"], }, "that_case.pl" => { file => generic_abs_path("t/data/case-insensitive-keys/that_case.pl"), key => "that_case.pl", type => "data", uses => ["Test.pm"], }, "this_case.pl" => { file => generic_abs_path("t/data/case-insensitive-keys/this_case.pl"), key => "this_case.pl", type => "data", uses => ["Test.pm"], }, }; # Functional i/f my $rv1 = scan_deps(@roots1); #use Data::Dump qw(dump); #print dump($rv1), "\n"; compare_scandeps_rvs($rv1, $expected_rv1, \@roots1); # Check that only one entry for Cwd is created. my @roots2 = qw(t/data/case-insensitive-keys/Test2.pm); my $rv2 = scan_deps(files => \@roots2); my @keys = grep { lc($_) eq "cwd.pm" } keys %$rv2; ok($#keys == 0, "contains only one match"); __END__ Module-ScanDeps-1.20/t/12-ScanFileRE.t0000644000372000037200000000333012604220754017321 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use File::Temp; use Test::More tests => 8; use lib 't/data/ScanFileRE'; BEGIN { use_ok( 'Module::ScanDeps' ); } # Test that ScanFileRE is applied to the input files my ($fh, $filename) = File::Temp::tempfile( UNLINK => 1, SUFFIX => '.na' ); ok(defined $Module::ScanDeps::ScanFileRE, "ScanFileRE is accessible outside Module::ScanDeps"); ok($filename !~ $Module::ScanDeps::ScanFileRE, "$filename does not match"); my $rv = scan_deps(files => [$filename]); ok( !(scalar grep { /\Q$filename\E/ } keys %$rv), "ScanFileRE removed non-matching input files" ); my ($fh2, $filename2) = File::Temp::tempfile( UNLINK => 1 ); ok($filename2 =~ $Module::ScanDeps::ScanFileRE, "$filename2 does match"); my $rv2 = scan_deps(files => [$filename2]); my $basename = $filename2; $basename =~ s/^.*(?:\/|\\)([^\\\/]+)$/$1/; ok( (scalar grep { /\Q$basename\E/ } keys %$rv2) == 1, "ScanFileRE did not remove matching input files" ); # The next two tests rely on t/data/ScanFileRE/auto/example/example.h using t/data/ScanFileRE/example_too.pm # Test that the default ScanFileRE is applied to the used files $rv = scan_deps(files => ['t/data/ScanFileRE/example.pm'], recurse => 1); ok( !(scalar grep { /example_too\.pm/ } keys %$rv), "ScanFileRE only scanned matching files in the dependency tree" ); # Test that ScanFileRE can be changed to now pick up all files in the dependency tree $Module::ScanDeps::ScanFileRE = qr/.*/; $rv = scan_deps(files => ['t/data/ScanFileRE/example.pm'], recurse => 1); ok( (scalar grep { /example_too\.pm/ } keys %$rv), "M::SD recognised the new ScanFileRE and scanned all files in the dependency tree" ); __END__ Module-ScanDeps-1.20/t/7-check-dynaloader.t0000644000372000037200000000532412604220754020534 0ustar roderichroderich#!perl use strict; use Test::More; use Config (); use Module::ScanDeps; use DynaLoader; use File::Temp; plan skip_all => "No dynamic loading available in your version of perl" unless $Config::Config{usedl}; my @try_mods = qw( Cwd File::Glob Data::Dumper List::Util Time::HiRes Compress::Raw::Zlib ); my @dyna_mods = grep { my $mod = $_; eval("require $mod; 1") && grep { $_ eq $mod } @DynaLoader::dl_modules } @try_mods; plan skip_all => "No dynamic module found (tried @try_mods)" unless @dyna_mods; diag "dynamic modules used for test: @dyna_mods"; plan tests => 4 * 2 * @dyna_mods; foreach my $module (@dyna_mods) { # cf. DynaLoader.pm my @modparts = split(/::/,$module); my $modfname = defined &DynaLoader::mod2fname ? DynaLoader::mod2fname(\@modparts) : $modparts[-1]; my $auto_path = join('/', 'auto', @modparts, "$modfname.$Config::Config{dlext}"); check_bundle_path($module, $auto_path, ".pl", <<"...", use $module; 1; ... sub { scan_deps( files => [ $_[0] ], recurse => 0); } ); check_bundle_path($module, $auto_path, ".pm", <<"...", package Bar; use $module; 1; ... sub { scan_deps_runtime( files => [ $_[0] ], recurse => 0, compile => 1); } ); check_bundle_path($module, $auto_path, ".pl", <<"...", # no way in hell can this detected by static analysis :) my \$req = join("", qw( r e q u i r e )); eval "\$req $module"; exit(0); ... sub { scan_deps_runtime( files => [ $_[0] ], recurse => 0, execute => 1); } ); check_bundle_path($module, $auto_path, ".pl", <<"...", # no way in hell can this detected by static analysis :) my \$req = join("", qw( r e q u i r e )); eval "\$req \$_" foreach \@ARGV; exit(0); ... sub { scan_deps_runtime( files => [ $_[0] ], recurse => 0, execute => [ $module ]); } ); } exit(0); # NOTE: check_bundle_path runs 2 tests sub check_bundle_path { my ($module, $auto_path, $suffix, $code, $scan) = @_; my ($fh, $filename) = File::Temp::tempfile( UNLINK => 1, SUFFIX => $suffix ); print $fh $code, "\n" or die $!; close $fh; my $rv = $scan->($filename); my ( $entry ) = grep { /^\Q$auto_path\E$/ } keys %$rv; ok( $entry, "$module: found some key that looks like it pulled in its shared lib (auto_path=$auto_path)" ); # Actually we accept anything that ends with $auto_path. ok($rv->{$entry}->{file} =~ m{/\Q$auto_path\E$}, "$module: the full bundle path we got ($rv->{$entry}->{file}) looks legit" ); } Module-ScanDeps-1.20/t/2-static_functional_interface_fake.t0000644000372000037200000002414012604220754024046 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 304; use lib qw(t t/data/static); use Utils; use version; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # Static dependency check of a script that doesn't use # anything ############################################################## my @roots1 = qw(t/data/static/null.pl); my $expected_rv1 = { "null.pl" => { file => generic_abs_path("t/data/static/null.pl"), key => "null.pl", type => "data", }, }; # Functional i/f my $rv1 = scan_deps(@roots1); compare_scandeps_rvs($rv1, $expected_rv1, \@roots1); ############################################################## # Static dependency check of a circular dependency: # ___ # |/_ \ # M _M # \____/| # ############################################################## my @roots2 = qw(t/data/static/egg.pm); my $expected_rv2 = { "chicken.pm" => { file => generic_abs_path("t/data/static/chicken.pm"), key => "chicken.pm", type => "module", used_by => ["egg.pm"], uses => ["egg.pm"], }, "egg.pm" => { file => generic_abs_path("t/data/static/egg.pm"), key => "egg.pm", type => "module", used_by => ["chicken.pm"], uses => ["chicken.pm"], }, }; # Functional i/f my $rv2 = scan_deps(@roots2); compare_scandeps_rvs($rv2, $expected_rv2, \@roots2); ############################################################## # Static dependency check of the following dependency tree # # M # /|\ # / | \ # / | \ # / M \ # / / \ \ # / / \ \ # M M M M # \ \ / / # \ \ / / # \ M / # \ | / # \ | / # M # # With dependencies always going from the top downwards ############################################################## my @roots3 = qw(t/data/static/outer_diamond_N.pm); my $expected_rv3 = { "inner_diamond_E.pm" => { file => generic_abs_path("t/data/static/inner_diamond_E.pm"), key => "inner_diamond_E.pm", type => "module", used_by => ["inner_diamond_N.pm"], uses => ["inner_diamond_S.pm"], }, "inner_diamond_N.pm" => { file => generic_abs_path("t/data/static/inner_diamond_N.pm"), key => "inner_diamond_N.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["inner_diamond_E.pm", "inner_diamond_W.pm"], }, "inner_diamond_S.pm" => { file => generic_abs_path("t/data/static/inner_diamond_S.pm"), key => "inner_diamond_S.pm", type => "module", used_by => ["inner_diamond_W.pm", "inner_diamond_E.pm"], uses => ["outer_diamond_S.pm"], }, "inner_diamond_W.pm" => { file => generic_abs_path("t/data/static/inner_diamond_W.pm"), key => "inner_diamond_W.pm", type => "module", used_by => ["inner_diamond_N.pm"], uses => ["inner_diamond_S.pm"], }, "outer_diamond_E.pm" => { file => generic_abs_path("t/data/static/outer_diamond_E.pm"), key => "outer_diamond_E.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["outer_diamond_S.pm"], }, "outer_diamond_N.pm" => { file => generic_abs_path("t/data/static/outer_diamond_N.pm"), key => "outer_diamond_N.pm", type => "module", uses => ["inner_diamond_N.pm", "outer_diamond_E.pm", "outer_diamond_W.pm"], }, "outer_diamond_S.pm" => { file => generic_abs_path("t/data/static/outer_diamond_S.pm"), key => "outer_diamond_S.pm", type => "module", used_by => ["outer_diamond_E.pm", "outer_diamond_W.pm", "inner_diamond_S.pm"], }, "outer_diamond_W.pm" => { file => generic_abs_path("t/data/static/outer_diamond_W.pm"), key => "outer_diamond_W.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["outer_diamond_S.pm"], }, }; # Functional i/f my $rv3 = scan_deps(@roots3); compare_scandeps_rvs($rv3, $expected_rv3, \@roots3); ############################################################## # Static dependency check of the following dependency tree # (i.e. multiple inputs) # # InputA.pl InputB.pl InputC.pl # / \ \ / # / \ \ / # / \ \ / # TestA.pm TestB.pm TestC.pm / # \ / # \ / # TestD.pm # ############################################################## my @roots4 = qw(t/data/static/InputA.pl t/data/static/InputB.pl t/data/static/InputC.pl); my $expected_rv4 = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], uses => ["TestD.pm"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl", "TestC.pm"], }, }; # Functional i/f my $rv4 = scan_deps(@roots4); compare_scandeps_rvs($rv4, $expected_rv4, \@roots4); ############################################################## # Static dependency check of the following dependency tree # Tests the .pm only lists the .pl once in it's used_by entries # # Duplicator.pl # / \ # / \ # / \ # \ / # \ / # \ / # Duplicated.pm # ############################################################## my @roots5 = qw(t/data/static/Duplicator.pl); my $expected_rv5 = { "Duplicated.pm" => { file => generic_abs_path("t/data/static/Duplicated.pm"), key => "Duplicated.pm", type => "module", used_by => ["Duplicator.pl"], }, "Duplicator.pl" => { file => generic_abs_path("t/data/static/Duplicator.pl"), key => "Duplicator.pl", type => "data", uses => ["Duplicated.pm"], }, }; # Functional i/f my $rv5 = scan_deps(@roots5); compare_scandeps_rvs($rv5, $expected_rv5, \@roots5); ############################################################## # Static dependency check of a module that does a # use 5.010; # Note that this doesn't test as much as the other tests # since feature.pm ropes in all kinds of things. ############################################################## SKIP: { skip "Skipping 'use VERSION' tests on pre-5.10.0", 2 if version->new($]) < version->new("5.10.0"); my @roots1 = qw(t/data/static/useVERSION.pm); # Functional i/f my $rv1 = scan_deps(@roots1); ok(exists $rv1->{"useVERSION.pm"}, "use VERSION: source file included"); ok(exists $rv1->{"feature.pm"}, "use VERSION: feature.pm included"); } __END__ Module-ScanDeps-1.20/t/data/0000755000372000037200000000000012604223352015710 5ustar roderichroderichModule-ScanDeps-1.20/t/data/file-glob-no.pl0000644000372000037200000000022012604220754020514 0ustar roderichroderichmy $serial_asn1; my $i; package Foo; sub length {1}; package main; bless $serial_asn1 => 'Foo'; for ($i=0; $i<$serial_asn1->length; $i++) { } Module-ScanDeps-1.20/t/data/file-glob-yes.pl0000644000372000037200000000021012604220754020677 0ustar roderichroderichmy $serial_asn1; my $i; package Foo; sub length {1}; package main; bless $serial_asn1 => 'Foo'; for ($i=0; $i++,$_=<*.txt>; $i++) { } Module-ScanDeps-1.20/t/data/duplicated_entries/0000755000372000037200000000000012604223352021557 5ustar roderichroderichModule-ScanDeps-1.20/t/data/duplicated_entries/Scoped/0000755000372000037200000000000012604223352022774 5ustar roderichroderichModule-ScanDeps-1.20/t/data/duplicated_entries/Scoped/Package.pm0000644000372000037200000000004712604220754024671 0ustar roderichroderichpackage Scoped::Package; 1; __END__Module-ScanDeps-1.20/t/data/duplicated_entries/use_scoped_package.pl0000644000372000037200000000005112604220754025717 0ustar roderichroderich#!/usr/bin/perl use Scoped::Package; Module-ScanDeps-1.20/t/data/static/0000755000372000037200000000000012604223352017177 5ustar roderichroderichModule-ScanDeps-1.20/t/data/static/Duplicated.pm0000644000372000037200000000004212604220754021612 0ustar roderichroderichpackage Duplicated; 1; __END__Module-ScanDeps-1.20/t/data/static/useVERSION.pm0000644000372000037200000000006012604220754021376 0ustar roderichroderichpackage useVERSION; use 5.010; 1; __END__ Module-ScanDeps-1.20/t/data/static/TestD.pm0000644000372000037200000000003512604220754020561 0ustar roderichroderichpackage TestD; 1; __END__Module-ScanDeps-1.20/t/data/static/inner_diamond_W.pm0000644000372000037200000000007712604220754022640 0ustar roderichroderichpackage inner_diamond_W; use inner_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/null.pl0000644000372000037200000000000012604220754020477 0ustar roderichroderichModule-ScanDeps-1.20/t/data/static/chicken.pm0000644000372000037200000000005312604220754021142 0ustar roderichroderichpackage chicken; use egg; 1; __END__Module-ScanDeps-1.20/t/data/static/Duplicator.pl0000644000372000037200000000004612604220754021645 0ustar roderichroderichuse Duplicated.pm; use Duplicated.pm;Module-ScanDeps-1.20/t/data/static/inner_diamond_S.pm0000644000372000037200000000007712604220754022634 0ustar roderichroderichpackage inner_diamond_S; use outer_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/InputA.pl0000644000372000037200000000003412604220754020734 0ustar roderichroderichuse TestA.pm; use TestB.pm;Module-ScanDeps-1.20/t/data/static/outer_diamond_E.pm0000644000372000037200000000007712604220754022641 0ustar roderichroderichpackage outer_diamond_E; use outer_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/outer_diamond_W.pm0000644000372000037200000000007712604220754022663 0ustar roderichroderichpackage outer_diamond_W; use outer_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/TestA.pm0000644000372000037200000000003512604220754020556 0ustar roderichroderichpackage TestA; 1; __END__Module-ScanDeps-1.20/t/data/static/inner_diamond_E.pm0000644000372000037200000000007712604220754022616 0ustar roderichroderichpackage inner_diamond_E; use inner_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/TestC.pm0000644000372000037200000000005312604220754020560 0ustar roderichroderichpackage TestC; use TestD; 1; __END__Module-ScanDeps-1.20/t/data/static/TestB.pm0000644000372000037200000000003512604220754020557 0ustar roderichroderichpackage TestB; 1; __END__Module-ScanDeps-1.20/t/data/static/outer_diamond_N.pm0000644000372000037200000000015312604220754022645 0ustar roderichroderichpackage outer_diamond_N; use outer_diamond_E; use outer_diamond_W; use inner_diamond_N; 1; __END__Module-ScanDeps-1.20/t/data/static/egg.pm0000644000372000037200000000005312604220754020300 0ustar roderichroderichpackage egg; use chicken; 1; __END__Module-ScanDeps-1.20/t/data/static/InputC.pl0000644000372000037200000000001512604220754020735 0ustar roderichroderichuse TestD.pm;Module-ScanDeps-1.20/t/data/static/inner_diamond_N.pm0000644000372000037200000000012512604220754022621 0ustar roderichroderichpackage inner_diamond_N; use inner_diamond_E; use inner_diamond_W; 1; __END__Module-ScanDeps-1.20/t/data/static/outer_diamond_S.pm0000644000372000037200000000004712604220754022654 0ustar roderichroderichpackage outer_diamond_S; 1; __END__Module-ScanDeps-1.20/t/data/static/InputB.pl0000644000372000037200000000001512604220754020734 0ustar roderichroderichuse TestC.pm;Module-ScanDeps-1.20/t/data/ScanFileRE/0000755000372000037200000000000012604223352017623 5ustar roderichroderichModule-ScanDeps-1.20/t/data/ScanFileRE/example_too.pm0000644000372000037200000000003412604220754022475 0ustar roderichroderichpackage example_too; 1; Module-ScanDeps-1.20/t/data/ScanFileRE/example.pm0000644000372000037200000000003012604220754021610 0ustar roderichroderichpackage example; 1; Module-ScanDeps-1.20/t/data/ScanFileRE/auto/0000755000372000037200000000000012604223352020573 5ustar roderichroderichModule-ScanDeps-1.20/t/data/ScanFileRE/auto/example/0000755000372000037200000000000012604223352022226 5ustar roderichroderichModule-ScanDeps-1.20/t/data/ScanFileRE/auto/example/example.h0000644000372000037200000000002012604220754024025 0ustar roderichroderichuse example_too;Module-ScanDeps-1.20/t/data/use_lib.pl0000644000372000037200000000011012604220754017662 0ustar roderichroderich#!/usr/bin/perl use lib "t/data/check_path_to_inc_name"; use Some; Module-ScanDeps-1.20/t/data/pluggable/0000755000372000037200000000000012604223352017652 5ustar roderichroderichModule-ScanDeps-1.20/t/data/pluggable/Foo.pm0000644000372000037200000000005612604220754020737 0ustar roderichroderichpackage Foo; use Module::Pluggable; 1; Module-ScanDeps-1.20/t/data/pluggable/Foo/0000755000372000037200000000000012604223352020375 5ustar roderichroderichModule-ScanDeps-1.20/t/data/pluggable/Foo/Plugin/0000755000372000037200000000000012604223352021633 5ustar roderichroderichModule-ScanDeps-1.20/t/data/pluggable/Foo/Plugin/Bar.pm0000644000372000037200000000004112604220754022673 0ustar roderichroderichpackage Foo::Plugin::Bar; 1; Module-ScanDeps-1.20/t/data/pluggable/Foo/Plugin/Baz.pm0000644000372000037200000000004112604220754022703 0ustar roderichroderichpackage Foo::Plugin::Baz; 1; Module-ScanDeps-1.20/t/data/case-insensitive-keys/0000755000372000037200000000000012604223352022132 5ustar roderichroderichModule-ScanDeps-1.20/t/data/case-insensitive-keys/this_case.pl0000644000372000037200000000001712604220754024432 0ustar roderichroderichuse Test; 1; Module-ScanDeps-1.20/t/data/case-insensitive-keys/Test.pm0000644000372000037200000000003412604220754023407 0ustar roderichroderichpackage Test; 1; __END__Module-ScanDeps-1.20/t/data/case-insensitive-keys/that_case.pl0000644000372000037200000000001712604220754024423 0ustar roderichroderichuse test; 1; Module-ScanDeps-1.20/t/data/case-insensitive-keys/Test2.pm0000644000372000037200000000005512604220754023474 0ustar roderichroderichpackage Test2; use Cwd; $foo->cwd->foo(); 1; Module-ScanDeps-1.20/t/data/check_path_to_inc_name/0000755000372000037200000000000012604223352022334 5ustar roderichroderichModule-ScanDeps-1.20/t/data/check_path_to_inc_name/Scoped/0000755000372000037200000000000012604223352023551 5ustar roderichroderichModule-ScanDeps-1.20/t/data/check_path_to_inc_name/Scoped/Package.pm0000644000372000037200000000004712604220754025446 0ustar roderichroderichpackage Scoped::Package; 1; __END__Module-ScanDeps-1.20/t/data/check_path_to_inc_name/Some.pm0000644000372000037200000000003412604220754023575 0ustar roderichroderichpackage Some; 1; __END__Module-ScanDeps-1.20/t/data/check_path_to_inc_name/use_scoped_package.pl0000644000372000037200000000005112604220754026474 0ustar roderichroderich#!/usr/bin/perl use Scoped::Package; Module-ScanDeps-1.20/t/data/rt90869.pl0000644000372000037200000000032412604220754017314 0ustar roderichroderich# some forms of "use autouse ..." use autouse TestA => qw(foo bar); use autouse "TestB", qw(foo bar); # "use if ..." (note the function call in COND) sub frobnicate { 1 } use if frobnicate(), TestC => qw(quux); Module-ScanDeps-1.20/t/Utils.pm0000644000372000037200000001632012604220754016442 0ustar roderichroderichpackage Utils; use strict; use warnings; use vars qw( $VERSION @ISA @EXPORT ); require Exporter; use Module::ScanDeps qw(path_to_inc_name); use Test::More; @ISA=qw(Exporter); $VERSION = '0.1'; @EXPORT = qw( generic_scandeps_rv_test compare_scandeps_rvs generic_abs_path ); my $test = Test::More->builder; sub import { my($self) = shift; my $pack = caller; $test->exported_to($pack); $self->export_to_level(1, $self, @EXPORT); } sub generic_scandeps_rv_test { my $rv = shift; my $array_ref = shift; my @input_keys = sort @$array_ref; $array_ref = shift; my @known_deps = sort @$array_ref; my @used_by; my ($used_by_ok, $i); # sanity check input foreach my $input (@input_keys) { !(grep {$_ eq $input} @known_deps) or die "\@input_keys overlaps with \@known_deps\n"; } $test->ok(ref($rv) eq "HASH", "\$rv is a ref") or return; # check all input files and known deps correspond to an entry in rv map {$_ = path_to_inc_name($_, 1)} @input_keys; map {$_ =~ s|\\|\/|go} (@input_keys, @known_deps); $test->ok(exists $rv->{$_}, "$_ is in rv") foreach (@input_keys, @known_deps); # Check general properties of the keys foreach my $key (keys %$rv) { $test->ok(exists($rv->{$key}{key}) && $key eq $rv->{$key}{key}, "For $key: the sub-key matches"); $test->ok(exists($rv->{$key}{file}) && $rv->{$key}{file} =~ /(?:^|[\/\\])\Q$key\E$/ && File::Spec->file_name_is_absolute($rv->{$key}{file}), "For $key: the file has been verified"); $test->ok(exists($rv->{$key}{type}) && $rv->{$key}{type} =~ /^(?:module|autoload|data|shared)$/, "For $key: the type matches module|autoload|data|shared"); if (exists($rv->{$key}{used_by})) { @used_by = sort @{$rv->{$key}{used_by}}; if (scalar @used_by > 0) { $used_by_ok = 1; if (scalar @used_by > 1) { for ($i=0; $i<$#used_by; $i++) { if ($used_by[$i] eq $used_by[$i+1]) { # relies on @used_by being sorted earlier $used_by_ok = 0; last; } } } $test->ok($used_by_ok, "$key\'s used_by has no duplicates"); $used_by_ok = 1; foreach my $used_by (@used_by) { $used_by_ok &= exists($rv->{$used_by}); } $test->ok($used_by_ok, "All entries in $key\'s used_by are themselves described in \$rv"); # check corresponding uses field foreach my $used_by (@used_by) { if (exists($rv->{$used_by}{uses})) { $test->ok(scalar(grep { $_ eq $key } @{$rv->{$used_by}{uses}}), "\$rv contains a matching uses field for the used_by entry $used_by for key $key"); } else { $test->ok(0, "\$rv contains a matching uses field for the used_by entry $used_by for key $key"); } } } else { $test->ok(0, "$key\'s used_by exists and isn't empty"); } } else { $test->ok((grep {$_ eq $key} @input_keys) | ($key =~ m/Plugin/o), "used-by not defined so $key must be one of the input files or is a plugin"); } if (exists($rv->{$key}{uses})) { # check corresponding used_by field foreach my $uses (@{$rv->{$key}{uses}}) { if (exists($rv->{$uses}{used_by})) { $test->ok(scalar(grep { $_ eq $key } @{$rv->{$uses}{used_by}}), "\$rv contains a matching used_by field for the uses entry $uses for key $key"); } else { $test->ok(0, "\$rv contains a matching used_by field for the uses entry $uses for key $key"); } } } } } sub compare_scandeps_rvs { my $rv_to_test = shift; my $rv_to_match = shift; my $array_ref = shift; my @input_keys = @$array_ref; my (@used_by_test, @used_by_match); my (@uses_test, @uses_match); my ($used_by_ok, $uses_ok); my ($compare_ok, $i); generic_scandeps_rv_test($rv_to_match, \@input_keys, []); # validate test data $test->ok(ref($rv_to_test) eq "HASH", "\$rv_to_test is a ref") or return; my @rv_to_match_keys = sort keys %{$rv_to_match}; my @rv_to_test_keys = sort keys %{$rv_to_test}; $test->cmp_ok(scalar @rv_to_test_keys, '==', scalar @rv_to_match_keys, "Number of keys in \$rv_to_test == Number of keys in \$rv_to_match") or return; $compare_ok = 1; for ($i=0; $i<=$#rv_to_match_keys; $i++) { $compare_ok &= ($rv_to_match_keys[$i] eq $rv_to_test_keys[$i]); } $test->ok($compare_ok, "Keys in \$rv_to_test all eq keys in \$rv_to_match"); foreach my $key (@rv_to_match_keys) { $test->ok(exists($rv_to_test->{$key}{key}) && $rv_to_test->{$key}{key} eq $rv_to_match->{$key}{key}, "For $key: sub-key matches the expected"); $test->ok(exists($rv_to_test->{$key}{file}) && $rv_to_test->{$key}{file} eq $rv_to_match->{$key}{file}, "For $key: file matches the expected"); $test->ok(exists($rv_to_test->{$key}{type}) && $rv_to_test->{$key}{type} eq $rv_to_match->{$key}{type}, "For $key: type matches the expected"); if (exists($rv_to_match->{$key}{used_by})) { $test->ok(exists($rv_to_test->{$key}{used_by}), "For $key: used_by exists as expected") or next; @used_by_test = sort @{$rv_to_test->{$key}{used_by}}; # order isn't important @used_by_match = sort @{$rv_to_match->{$key}{used_by}}; # order isn't important $test->cmp_ok(scalar @used_by_test, '==', scalar @used_by_match, "For $key: number of used_by in \$rv_to_test == Number of used_by in \$rv_to_match") or next; $used_by_ok = 1; for ($i=0; $i < scalar @used_by_match; $i++) { $used_by_ok &= ($used_by_match[$i] eq $used_by_test[$i]); } $test->ok($used_by_ok, "For $key: used_by in \$rv_to_test all eq used_by in \$rv_to_match"); } if (exists($rv_to_match->{$key}{uses})) { $test->ok(exists($rv_to_test->{$key}{uses}), "For $key: uses exists as expected") or next; @uses_test = sort @{$rv_to_test->{$key}{uses}}; # order isn't important @uses_match = sort @{$rv_to_match->{$key}{uses}}; # order isn't important $test->cmp_ok(scalar @uses_test, '==', scalar @uses_match, "For $key: number of uses in \$rv_to_test == Number of uses in \$rv_to_match") or next; $uses_ok = 1; for ($i=0; $i < scalar @uses_match; $i++) { $uses_ok &= ($uses_match[$i] eq $uses_test[$i]); } $test->ok($uses_ok, "For $key: uses in \$rv_to_test all eq uses in \$rv_to_match"); } } } sub generic_abs_path { my $file = shift @_; $file = File::Spec->rel2abs($file); $file =~ s|\\|\/|go; return $file; } 1; # Marks the end of any code. Any symbols after this are ignored. Use for documentation __END__ Module-ScanDeps-1.20/t/14-scan_chunk.t0000644000372000037200000000237712604220754017536 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 7; use Module::ScanDeps qw/scan_chunk/; { my $chunk=<<'EOT'; use strict; EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{strict.pm}]); } { my $chunk=<<'EOT'; use base qw(strict); EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{base.pm strict.pm}]); } { my $chunk=<<'EOT'; use parent qw(strict); EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{parent.pm strict.pm}]); } { my $chunk=<<'EOT'; use parent::doesnotexists qw(strict); EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{parent/doesnotexists.pm}]); } { my $chunk=<<'EOT'; use Mojo::Base 'strict'; EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{Mojo/Base.pm strict.pm}],'Mojo::Base'); } { my $chunk=<<'EOT'; use Catalyst qw/-Debug ConfigLoader Session::State::Cookie/ EOT #-Debug should be skipped my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{Catalyst.pm Catalyst/Plugin/ConfigLoader.pm Catalyst/Plugin/Session/State/Cookie.pm}]); } { my $chunk=<<'EOT'; use I18N::LangTags 0.30 (); EOT my @array=sort(scan_chunk($chunk)); is_deeply(\@array,[sort qw{I18N/LangTags.pm}]); } Module-ScanDeps-1.20/t/9-check_path_to_inc_name.t0000644000372000037200000000265712604220754021773 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Cwd; use Test::More tests => 7; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps', qw(path_to_inc_name scan_deps) ); } my $name; my $basepath; my $warn = 1; # Absolute path tests $basepath = cwd().'/t/data/check_path_to_inc_name/'; $name = 'Some.pm'; is(path_to_inc_name($basepath.$name, $warn), $name, "$name correctly returned by path_to_inc_name($basepath$name)"); $name = 'Scoped/Package.pm'; is(path_to_inc_name($basepath.$name, $warn), $name, "$name correctly returned by path_to_inc_name($basepath$name)"); # Relative path tests $basepath = 't/data/check_path_to_inc_name/'; $name = 'Some.pm'; is(path_to_inc_name($basepath.$name, $warn), $name, "$name correctly returned by path_to_inc_name($basepath$name)"); $name = 'Scoped/Package.pm'; is(path_to_inc_name($basepath.$name, $warn), $name, "$name correctly returned by path_to_inc_name($basepath$name)"); # script test $basepath = 't/data/check_path_to_inc_name/'; $name = 'use_scoped_package.pl'; is(path_to_inc_name($basepath.$name, $warn), $name, "$name correctly returned by path_to_inc_name($basepath$name)"); # 'use lib ...' my $rv = scan_deps("t/data/use_lib.pl"); ok(exists $rv->{"Some.pm"}, "'use lib ...' correctly interpreted"); __END__ Module-ScanDeps-1.20/t/8-check_duplicated_entries.t0000644000372000037200000000243112604220754022340 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More tests => 30; use lib qw(t t/data/duplicated_entries); use Utils; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } my @roots = qw(t/data/duplicated_entries/use_scoped_package.pl t/data/duplicated_entries/Scoped/Package.pm); my $expected_rv = { "use_scoped_package.pl" => { file => generic_abs_path("t/data/duplicated_entries/use_scoped_package.pl"), key => "use_scoped_package.pl", type => "data", uses => ["Scoped/Package.pm"], }, "Scoped/Package.pm" => { file => generic_abs_path("t/data/duplicated_entries/Scoped/Package.pm"), key => "Scoped/Package.pm", type => "module", used_by => ["use_scoped_package.pl"], }, }; # Functional i/f my $rv = scan_deps(@roots); compare_scandeps_rvs($rv, $expected_rv, \@roots); __END__ Module-ScanDeps-1.20/t/14-static_functional_cached.t0000644000372000037200000003536412604220754022424 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More 'no_plan'; use lib qw(t t/data/static); use Utils; use version; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # Static dependency check of a script that doesn't use # anything with basic cache_cb test added ############################################################## my @roots1 = qw(t/data/static/null.pl); my $expected_rv1 = { "null.pl" => { file => generic_abs_path("t/data/static/null.pl"), key => "null.pl", type => "data", }, }; expected_cache_cb_args({key => 'null.pl', file => 't/data/static/null.pl', }); my $rv1 = scan_deps(files => \@roots1, cache_cb => \&cache_cb ); compare_scandeps_rvs($rv1, $expected_rv1, \@roots1); ### check if we can use M::SD::Cache my $skip_cache_tests = 1; eval {require Module::ScanDeps::Cache;}; unless ($@){ $skip_cache_tests = Module::ScanDeps::Cache::prereq_missing(); warn $skip_cache_tests, "\n"; } my $cache_file = 'deps_cache.dat'; for my $t(qw/write_cache use_cache/){ SKIP: { skip "Skipping M:SD::Cache tests" , 289 if $skip_cache_tests; ############################################################## # Static dependency check of a circular dependency: # ___ # |/_ \ # M _M # \____/| # ############################################################## my @roots2 = qw(t/data/static/egg.pm); my $expected_rv2 = { "chicken.pm" => { file => generic_abs_path("t/data/static/chicken.pm"), key => "chicken.pm", type => "module", used_by => ["egg.pm"], uses => ["egg.pm"], }, "egg.pm" => { file => generic_abs_path("t/data/static/egg.pm"), key => "egg.pm", type => "module", used_by => ["chicken.pm"], uses => ["chicken.pm"], }, }; # Functional i/f my $rv2 = scan_deps(files => \@roots2, cache_file => $cache_file, recurse => 1, ); compare_scandeps_rvs($rv2, $expected_rv2, \@roots2); ############################################################## # Static dependency check of the following dependency tree # # M # /|\ # / | \ # / | \ # / M \ # / / \ \ # / / \ \ # M M M M # \ \ / / # \ \ / / # \ M / # \ | / # \ | / # M # # With dependencies always going from the top downwards ############################################################## my @roots3 = qw(t/data/static/outer_diamond_N.pm); my $expected_rv3 = { "inner_diamond_E.pm" => { file => generic_abs_path("t/data/static/inner_diamond_E.pm"), key => "inner_diamond_E.pm", type => "module", used_by => ["inner_diamond_N.pm"], uses => ["inner_diamond_S.pm"], }, "inner_diamond_N.pm" => { file => generic_abs_path("t/data/static/inner_diamond_N.pm"), key => "inner_diamond_N.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["inner_diamond_E.pm", "inner_diamond_W.pm"], }, "inner_diamond_S.pm" => { file => generic_abs_path("t/data/static/inner_diamond_S.pm"), key => "inner_diamond_S.pm", type => "module", used_by => ["inner_diamond_W.pm", "inner_diamond_E.pm"], uses => ["outer_diamond_S.pm"], }, "inner_diamond_W.pm" => { file => generic_abs_path("t/data/static/inner_diamond_W.pm"), key => "inner_diamond_W.pm", type => "module", used_by => ["inner_diamond_N.pm"], uses => ["inner_diamond_S.pm"], }, "outer_diamond_E.pm" => { file => generic_abs_path("t/data/static/outer_diamond_E.pm"), key => "outer_diamond_E.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["outer_diamond_S.pm"], }, "outer_diamond_N.pm" => { file => generic_abs_path("t/data/static/outer_diamond_N.pm"), key => "outer_diamond_N.pm", type => "module", uses => ["inner_diamond_N.pm", "outer_diamond_E.pm", "outer_diamond_W.pm"], }, "outer_diamond_S.pm" => { file => generic_abs_path("t/data/static/outer_diamond_S.pm"), key => "outer_diamond_S.pm", type => "module", used_by => ["outer_diamond_E.pm", "outer_diamond_W.pm", "inner_diamond_S.pm"], }, "outer_diamond_W.pm" => { file => generic_abs_path("t/data/static/outer_diamond_W.pm"), key => "outer_diamond_W.pm", type => "module", used_by => ["outer_diamond_N.pm"], uses => ["outer_diamond_S.pm"], }, }; # Functional i/f my $rv3 = scan_deps(cache_file => $cache_file, recurse => 1, files => \@roots3); compare_scandeps_rvs($rv3, $expected_rv3, \@roots3); ############################################################## # Static dependency check of the following dependency tree # (i.e. multiple inputs) # # InputA.pl InputB.pl InputC.pl # / \ \ / # / \ \ / # / \ \ / # TestA.pm TestB.pm TestC.pm / # \ / # \ / # TestD.pm # ############################################################## my @roots4 = qw(t/data/static/InputA.pl t/data/static/InputB.pl t/data/static/InputC.pl); my $expected_rv4 = { "InputA.pl" => { file => generic_abs_path("t/data/static/InputA.pl"), key => "InputA.pl", type => "data", uses => ["TestA.pm", "TestB.pm"], }, "InputB.pl" => { file => generic_abs_path("t/data/static/InputB.pl"), key => "InputB.pl", type => "data", uses => ["TestC.pm"], }, "InputC.pl" => { file => generic_abs_path("t/data/static/InputC.pl"), key => "InputC.pl", type => "data", uses => ["TestD.pm"], }, "TestA.pm" => { file => generic_abs_path("t/data/static/TestA.pm"), key => "TestA.pm", type => "module", used_by => ["InputA.pl"], }, "TestB.pm" => { file => generic_abs_path("t/data/static/TestB.pm"), key => "TestB.pm", type => "module", used_by => ["InputA.pl"], }, "TestC.pm" => { file => generic_abs_path("t/data/static/TestC.pm"), key => "TestC.pm", type => "module", used_by => ["InputB.pl"], uses => ["TestD.pm"], }, "TestD.pm" => { file => generic_abs_path("t/data/static/TestD.pm"), key => "TestD.pm", type => "module", used_by => ["InputC.pl", "TestC.pm"], }, }; # Functional i/f my $rv4 = scan_deps(cache_file => $cache_file, recurse => 1, files => \@roots4); compare_scandeps_rvs($rv4, $expected_rv4, \@roots4); ############################################################## # Static dependency check of the following dependency tree # Tests the .pm only lists the .pl once in it's used_by entries # # Duplicator.pl # / \ # / \ # / \ # \ / # \ / # \ / # Duplicated.pm # ############################################################## my @roots5 = qw(t/data/static/Duplicator.pl); my $expected_rv5 = { "Duplicated.pm" => { file => generic_abs_path("t/data/static/Duplicated.pm"), key => "Duplicated.pm", type => "module", used_by => ["Duplicator.pl"], }, "Duplicator.pl" => { file => generic_abs_path("t/data/static/Duplicator.pl"), key => "Duplicator.pl", type => "data", uses => ["Duplicated.pm"], }, }; # Functional i/f my $rv5 = scan_deps(cache_file => $cache_file, recurse => 1, files => \@roots5); compare_scandeps_rvs($rv5, $expected_rv5, \@roots5); } ### SKIP block wrapping M::SD::Cache tests } ### end of for (qw/write_cache use_cache/) ### cache testing helper functions ### { my ($cb_args, $expecting_write); sub expected_cache_cb_args{ $cb_args = shift; } sub cache_cb{ my %args = @_; is($args{key}, $cb_args->{key}, "check arg 'key' in cache_cb."); is($args{file}, $cb_args->{file}, "check arg 'file' in cache_cb."); if ( $expecting_write ){ is($args{action}, 'write', "expecting write action"); } if ($args{action} eq 'read'){ $expecting_write = 1; return 0; } elsif ( $args{action} eq 'write' ){ $expecting_write = 0; return 1 } my $action = $args{action}; ok( 0, "wrong action: got [$action] must be 'read' or 'write'"); } }### end cache testing helper functions ### ### test Module::ScanDeps::Cache.pm SKIP: { skip "Skipping M:SD::Cache tests" , 9 if $skip_cache_tests; my %files = ('file1.pl' => "use TestModule;\n", 'file2.pl' => "use TestModule;\n", 'file3.pl' => "use TestModule;\n return 0;\n"); for my $name (keys %files){ open my $fh, '>', $name or die "Can not open file $name: $!"; print $fh $files{$name}; close $fh or die "Can not close file $name: $!"; } my $cb = Module::ScanDeps::Cache::get_cache_cb(); my $mod = []; my $ret = $cb->(key => 'testfile', file => 'file1.pl', action => 'read', modules => $mod ); is( $ret, 0, "File not present in cache"); $ret = $cb->(key => 'testfile', file => 'file1.pl', modules => [qw /TestModule.pm/], action => 'write', ); is( $ret, 1, "Writing file to cache"); $ret = $cb->(key => 'testfile', file => 'file1.pl', action => 'read', modules => $mod ); is( $ret, 1, "File is present in cache"); is( $mod->[0], 'TestModule.pm', "cache_cb sets modules 1"); $mod = []; $ret = $cb->(key => 'testfile', file => 'file2.pl', action => 'read', modules => $mod ); is( $ret, 1, "Identical file returns the same dependencies from cache"); is( $mod->[0], 'TestModule.pm', "cache_cb sets modules 2"); $mod = []; $ret = $cb->(key => 'testfile', file => 'file3.pl', action => 'read', modules => $mod ); is( $ret, 0, "No cached deps returned for file with different content"); is( @$mod, 0, "cache_cb does not set modules if no deps found"); eval {$cb->(action => 'foo')}; ok ($@ =~ /must be read or write/, "cache_cb dies on wrong action"); for my $name (keys %files){ unlink $name or die "Could not unlink file $name: $!"; } } unlink( $cache_file ); __END__ Module-ScanDeps-1.20/t/13-static_prefork_test.t0000644000372000037200000000207012604220754021465 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Requires qw( prefork ); use lib 't'; use Utils; BEGIN { # Mwuahahaha! delete $INC{"prefork.pm"}; %prefork:: = (); plan 'no_plan'; # no_plan because the number of objects in the dependency tree (and hence the number of tests) can change } my $rv; my $root; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## BEGIN { use_ok( 'Module::ScanDeps' ); } ############################################################## # Tests static dependency scanning with the prefork module. # This was broken until Module::ScanDeps 0.85 ############################################################## $root = $0; use prefork "less"; my @deps = qw( Carp.pm Config.pm Exporter.pm Test/More.pm strict.pm vars.pm prefork.pm less.pm ); # Functional i/f $rv = scan_deps($root); generic_scandeps_rv_test($rv, [$0], \@deps); __END__ Module-ScanDeps-1.20/t/3-static_oo_interface_real.t0000644000372000037200000000216612604220754022343 0ustar roderichroderich#!/usr/bin/perl use strict; use warnings; use Test::More; my $rv; ############################################################## # Tests static dependency scanning on a real set of modules. # This exercises the scanning functionality but because the # majority of files scanned aren't fixed, the checks are # necessarily loose. ############################################################## my @deps = qw( Carp.pm Config.pm Exporter.pm Test/More.pm constant.pm strict.pm vars.pm Module/ScanDeps.pm ); plan tests => @deps + 3; ############################################################## # Tests compilation of Module::ScanDeps ############################################################## use_ok( 'Module::ScanDeps' ); my $obj = Module::ScanDeps->new; $obj->set_file($0); $obj->calculate_info; ok($rv = $obj->get_files); foreach my $mod (@deps) { ok(grep {$_->{store_as} eq $mod } @{$rv->{modules}}); }; use File::Basename qw/basename/; my $basename = basename($0); ok(not(grep {$_->{store_as} =~ /\Q$basename\E/} @{$rv->{modules}})); __END__ Module-ScanDeps-1.20/Makefile.PL0000644000372000037200000000152312604220754016512 0ustar roderichroderich#!/usr/bin/perl use strict; use inc::Module::Install 1.00; name 'Module-ScanDeps'; all_from 'lib/Module/ScanDeps.pm'; repository 'https://www.openfoundry.org/svn/par/Module-ScanDeps/trunk'; license 'perl'; perl_version '5.008001'; requires 'File::Temp' => '0'; requires 'File::Spec' => '0'; requires 'Getopt::Long' => '0'; requires 'Module::Metadata' => '0'; requires 'Text::ParseWords' => '0'; requires 'version' => '0'; test_requires 'Test::More' => '0'; test_requires 'Test::Requires' => '0'; install_script 'script/scandeps.pl'; no_index package => qw( Module::ScanDeps::Cache Module::ScanDeps::DataFeed ); WriteAll; Module-ScanDeps-1.20/lib/0000755000372000037200000000000012604223352015302 5ustar roderichroderichModule-ScanDeps-1.20/lib/Module/0000755000372000037200000000000012604223352016527 5ustar roderichroderichModule-ScanDeps-1.20/lib/Module/ScanDeps/0000755000372000037200000000000012604223352020227 5ustar roderichroderichModule-ScanDeps-1.20/lib/Module/ScanDeps/Cache.pm0000644000372000037200000000467712604220754021611 0ustar roderichroderichpackage Module::ScanDeps::Cache; use strict; use warnings; my $has_DMD5; eval { require Digest::MD5 }; $has_DMD5 = 1 unless $@; my $has_Storable; eval { require Storable }; $has_Storable = 1 unless $@; my $cache; my $cache_file; my $cache_dirty; sub prereq_missing{ my @missing; push @missing, 'Digest::MD5' unless $has_DMD5; push @missing, 'Storable' unless $has_Storable; return @missing; } sub init_from_file{ my $c_file = shift; return 0 if prereq_missing(); eval{$cache = Storable::retrieve($c_file)}; #warn $@ if ($@); unless ($cache){ warn "Couldn't retrieve data from file $c_file. Building new cache.\n"; $cache = {}; } $cache_file = $c_file; return 1; } sub store_cache{ my $c_file = shift || $cache_file; # no need to store to the file we retrieved from # unless we have seen changes written to the cache return unless ($cache_dirty || $c_file ne $cache_file); Storable::nstore($cache, $c_file) or warn "Could not store cache to file $c_file!"; } sub get_cache_cb{ return sub{ my %args = @_; if ( $args{action} eq 'read' ){ return _read_cache( %args ); } elsif ( $args{action} eq 'write' ){ return _write_cache( %args ); } die "action in cache_cb must be read or write!"; }; } ### check for existence of the entry ### check for identity of the file ### pass cached value in $mod_aref ### return true in case of a hit sub _read_cache{ my %args = @_; my ($key, $file, $mod_aref) = @args{qw/key file modules/}; return 0 unless (exists $cache->{$key}); my $entry = $cache->{$key}; my $checksum = _file_2_md5($file); if ($entry->{checksum} eq $checksum){ @$mod_aref = @{$entry->{modules}}; return 1; } return 0; } sub _write_cache{ my %args = @_; my ($key, $file, $mod_aref) = @args{qw/key file modules/}; my $entry = $cache->{$key} ||= {}; my $checksum = _file_2_md5($file); $entry->{checksum} = $checksum; $entry->{modules} = [@$mod_aref]; $cache_dirty = 1; return 1; } sub _file_2_md5{ my $file = shift; open my $fh, '<', $file or die "can't open $file: $!"; my $md5 = Digest::MD5->new; $md5->addfile($fh); close $fh or die "can't close $file: $!"; return $md5->hexdigest; } 1; Module-ScanDeps-1.20/lib/Module/ScanDeps.pm0000644000372000037200000015035112604220754020575 0ustar roderichroderichpackage Module::ScanDeps; use 5.008001; use strict; use warnings; use vars qw( $VERSION @EXPORT @EXPORT_OK @ISA $CurrentPackage @IncludeLibs $ScanFileRE ); $VERSION = '1.20'; @EXPORT = qw( scan_deps scan_deps_runtime ); @EXPORT_OK = qw( scan_line scan_chunk add_deps scan_deps_runtime path_to_inc_name ); use Config; require Exporter; our @ISA = qw(Exporter); use constant dl_ext => ".$Config{dlext}"; use constant lib_ext => $Config{lib_ext}; use constant is_insensitive_fs => ( -s $0 and (-s lc($0) || -1) == (-s uc($0) || -1) and (-s lc($0) || -1) == -s $0 ); use version; use Cwd (); use File::Path (); use File::Temp (); use File::Spec (); use File::Basename (); use FileHandle; use Module::Metadata; $ScanFileRE = qr/(?:^|\\|\/)(?:[^.]*|.*\.(?i:p[ml]|t|al))$/; =head1 NAME Module::ScanDeps - Recursively scan Perl code for dependencies =head1 SYNOPSIS Via the command-line program L: % scandeps.pl *.pm # Print PREREQ_PM section for *.pm % scandeps.pl -e "use utf8" # Read script from command line % scandeps.pl -B *.pm # Include core modules % scandeps.pl -V *.pm # Show autoload/shared/data files Used in a program; use Module::ScanDeps; # standard usage my $hash_ref = scan_deps( files => [ 'a.pl', 'b.pl' ], recurse => 1, ); # shorthand; assume recurse == 1 my $hash_ref = scan_deps( 'a.pl', 'b.pl' ); # App::Packer::Frontend compatible interface # see App::Packer::Frontend for the structure returned by get_files my $scan = Module::ScanDeps->new; $scan->set_file( 'a.pl' ); $scan->set_options( add_modules => [ 'Test::More' ] ); $scan->calculate_info; my $files = $scan->get_files; =head1 DESCRIPTION This module scans potential modules used by perl programs, and returns a hash reference; its keys are the module names as appears in C<%INC> (e.g. C); the values are hash references with this structure: { file => '/usr/local/lib/perl5/5.8.0/Test/More.pm', key => 'Test/More.pm', type => 'module', # or 'autoload', 'data', 'shared' used_by => [ 'Test/Simple.pm', ... ], uses => [ 'Test/Other.pm', ... ], } One function, C, is exported by default. Other functions such as (C, C, C, C) are exported upon request. Users of B may also use this module as the dependency-checking frontend, by tweaking their F like below: use Module::ScanDeps; ... my $packer = App::Packer->new( frontend => 'Module::ScanDeps' ); ... Please see L for detailed explanation on the structure returned by C. =head2 B $rv_ref = scan_deps( files => \@files, recurse => $recurse, rv => \%rv, skip => \%skip, compile => $compile, execute => $execute, ); $rv_ref = scan_deps(@files); # shorthand, with recurse => 1 This function scans each file in C<@files>, registering their dependencies into C<%rv>, and returns a reference to the updated C<%rv>. The meaning of keys and values are explained above. If C<$recurse> is true, C will call itself recursively, to perform a breadth-first search on text files (as defined by the -T operator) found in C<%rv>. If the C<\%skip> is specified, files that exists as its keys are skipped. This is used internally to avoid infinite recursion. If C<$compile> or C<$execute> is true, runs C in either compile-only or normal mode, then inspects their C<%INC> after termination to determine additional runtime dependencies. If C<$execute> is an array reference, passes C<@$execute> as arguments to each file in C<@files> when it is run. If performance of the scanning process is a concern, C can be set to a filename. The scanning results will be cached and written to the file. This will speed up the scanning process on subsequent runs. Additionally, an option C is recognized. If set to true, C issues a warning to STDERR for every module file that the scanned code depends but that wasn't found. Please note that this may also report numerous false positives. That is why by default, the heuristic silently drops all dependencies it cannot find. =head2 B Like B, but skips the static scanning part. =head2 B @modules = scan_line($line); Splits a line into chunks (currently with the semicolon characters), and return the union of C calls of them. If the line is C<__END__> or C<__DATA__>, a single C<__END__> element is returned to signify the end of the program. Similarly, it returns a single C<__POD__> if the line matches C; the caller is responsible for skipping appropriate number of lines until C<=cut>, before calling C again. =head2 B $module = scan_chunk($chunk); @modules = scan_chunk($chunk); Apply various heuristics to C<$chunk> to find and return the module name(s) it contains. In scalar context, returns only the first module or C. =head2 B $rv_ref = add_deps( rv => \%rv, modules => \@modules ); $rv_ref = add_deps( @modules ); # shorthand, without rv Resolves a list of module names to its actual on-disk location, by finding in C<@INC> and C<@Module::ScanDeps::IncludeLibs>; modules that cannot be found are skipped. This function populates the C<%rv> hash with module/filename pairs, and returns a reference to it. =head2 B $perl_name = path_to_inc_name($path, $warn) Assumes C<$path> refers to a perl file and does it's best to return the name as it would appear in %INC. Returns undef if no match was found and a prints a warning to STDERR if C<$warn> is true. E.g. if C<$path> = perl/site/lib/Module/ScanDeps.pm then C<$perl_name> will be Module/ScanDeps.pm. =head1 NOTES =head2 B<@Module::ScanDeps::IncludeLibs> You can set this global variable to specify additional directories in which to search modules without modifying C<@INC> itself. =head2 B<$Module::ScanDeps::ScanFileRE> You can set this global variable to specify a regular expression to identify what files to scan. By default it includes all files of the following types: .pm, .pl, .t and .al. Additionally, all files without a suffix are considered. For instance, if you want to scan all files then use the following: C<$Module::ScanDeps::ScanFileRE = qr/./> =head1 CAVEATS This module intentionally ignores the B hack on FreeBSD -- the additional directory is removed from C<@INC> altogether. The static-scanning heuristic is not likely to be 100% accurate, especially on modules that dynamically load other modules. Chunks that span multiple lines are not handled correctly. For example, this one works: use base 'Foo::Bar'; But this one does not: use base 'Foo::Bar'; =cut my $SeenTk; my %SeenRuntimeLoader; # Pre-loaded module dependencies {{{ my %Preload = ( 'AnyDBM_File.pm' => [qw( SDBM_File.pm )], 'AnyEvent.pm' => 'sub', 'Authen/SASL.pm' => 'sub', 'B/Hooks/EndOfScope.pm' => [qw( B/Hooks/EndOfScope/PP.pm B/Hooks/EndOfScope/XS.pm )], 'Bio/AlignIO.pm' => 'sub', 'Bio/Assembly/IO.pm' => 'sub', 'Bio/Biblio/IO.pm' => 'sub', 'Bio/ClusterIO.pm' => 'sub', 'Bio/CodonUsage/IO.pm' => 'sub', 'Bio/DB/Biblio.pm' => 'sub', 'Bio/DB/Flat.pm' => 'sub', 'Bio/DB/GFF.pm' => 'sub', 'Bio/DB/Taxonomy.pm' => 'sub', 'Bio/Graphics/Glyph.pm' => 'sub', 'Bio/MapIO.pm' => 'sub', 'Bio/Matrix/IO.pm' => 'sub', 'Bio/Matrix/PSM/IO.pm' => 'sub', 'Bio/OntologyIO.pm' => 'sub', 'Bio/PopGen/IO.pm' => 'sub', 'Bio/Restriction/IO.pm' => 'sub', 'Bio/Root/IO.pm' => 'sub', 'Bio/SearchIO.pm' => 'sub', 'Bio/SeqIO.pm' => 'sub', 'Bio/Structure/IO.pm' => 'sub', 'Bio/TreeIO.pm' => 'sub', 'Bio/LiveSeq/IO.pm' => 'sub', 'Bio/Variation/IO.pm' => 'sub', 'Catalyst.pm' => sub { return ('Catalyst/Runtime.pm', 'Catalyst/Dispatcher.pm', _glob_in_inc('Catalyst/DispatchType', 1)); }, 'Catalyst/Engine.pm' => 'sub', 'CGI/Application/Plugin/Authentication.pm' => [qw( CGI/Application/Plugin/Authentication/Store/Cookie.pm )], 'CGI/Application/Plugin/AutoRunmode.pm' => [qw( Attribute/Handlers.pm )], 'charnames.pm' => sub { _find_in_inc('unicore/Name.pl') ? 'unicore/Name.pl' : 'unicode/Name.pl' }, 'Class/Load.pm' => [qw( Class/Load/PP.pm )], 'Class/MakeMethods.pm' => 'sub', 'Class/MethodMaker.pm' => 'sub', 'Config/Any.pm' =>'sub', 'Crypt/Random.pm' => sub { _glob_in_inc('Crypt/Random/Provider', 1); }, 'Crypt/Random/Generator.pm' => sub { _glob_in_inc('Crypt/Random/Provider', 1); }, 'Date/Manip.pm' => [qw( Date/Manip/DM5.pm Date/Manip/DM6.pm )], 'Date/Manip/Base.pm' => sub { _glob_in_inc('Date/Manip/Lang', 1); }, 'Date/Manip/TZ.pm' => sub { return (_glob_in_inc('Date/Manip/TZ', 1), _glob_in_inc('Date/Manip/Offset', 1)); }, 'DateTime/Format/Builder/Parser.pm' => 'sub', 'DateTime/Locale.pm' => 'sub', 'DateTime/TimeZone.pm' => 'sub', 'DBI.pm' => sub { grep !/\bProxy\b/, _glob_in_inc('DBD', 1); }, 'DBIx/Class.pm' => 'sub', 'DBIx/SearchBuilder.pm' => 'sub', 'DBIx/Perlish.pm' => [qw( attributes.pm )], 'DBIx/ReportBuilder.pm' => 'sub', 'Device/ParallelPort.pm' => 'sub', 'Device/SerialPort.pm' => [ qw( termios.ph asm/termios.ph sys/termiox.ph sys/termios.ph sys/ttycom.ph ) ], 'diagnostics.pm' => sub { # shamelessly taken and adapted from diagnostics.pm use Config; my($privlib, $archlib) = @Config{qw(privlibexp archlibexp)}; if ($^O eq 'VMS') { require VMS::Filespec; $privlib = VMS::Filespec::unixify($privlib); $archlib = VMS::Filespec::unixify($archlib); } for ( "pod/perldiag.pod", "Pod/perldiag.pod", "pod/perldiag-$Config{version}.pod", "Pod/perldiag-$Config{version}.pod", "pods/perldiag.pod", "pods/perldiag-$Config{version}.pod", ) { return $_ if _find_in_inc($_); } for ( "$archlib/pods/perldiag.pod", "$privlib/pods/perldiag-$Config{version}.pod", "$privlib/pods/perldiag.pod", ) { return $_ if -f $_; } return 'pod/perldiag.pod'; }, 'Email/Send.pm' => 'sub', 'Event.pm' => [ map "Event/$_.pm", qw(idle io signal timer var)], 'ExtUtils/MakeMaker.pm' => sub { grep /\bMM_/, _glob_in_inc('ExtUtils', 1); }, 'File/Basename.pm' => [qw( re.pm )], 'File/BOM.pm' => [qw( Encode/Unicode.pm )], 'File/HomeDir.pm' => 'sub', 'File/Spec.pm' => sub { require File::Spec; map { my $name = $_; $name =~ s!::!/!g; "$name.pm" } @File::Spec::ISA; }, 'Gtk2.pm' => [qw( Cairo.pm )], # Gtk2.pm does: eval "use Cairo;" 'HTTP/Message.pm' => [ qw( URI/URL.pm URI.pm ) ], 'Image/ExifTool.pm' => sub { return( (map $_->{name}, _glob_in_inc('Image/ExifTool', 0)), # also *.pl files qw( File/RandomAccess.pm ), ); }, 'Image/Info.pm' => sub { return( _glob_in_inc('Image/Info', 1), qw( Image/TIFF.pm ), ); }, 'IO.pm' => [ qw( IO/Handle.pm IO/Seekable.pm IO/File.pm IO/Pipe.pm IO/Socket.pm IO/Dir.pm ) ], 'IO/Socket.pm' => [qw( IO/Socket/UNIX.pm )], 'JSON.pm' => sub { # add JSON/PP*.pm, JSON/PP/*.pm # and ignore other JSON::* modules (e.g. JSON/Syck.pm, JSON/Any.pm); # but accept JSON::XS, too (because JSON.pm might use it if present) return( grep /^JSON\/(PP|XS)/, _glob_in_inc('JSON', 1) ); }, 'Locale/Maketext/Lexicon.pm' => 'sub', 'Locale/Maketext/GutsLoader.pm' => [qw( Locale/Maketext/Guts.pm )], 'Log/Any.pm' => 'sub', 'Log/Log4perl.pm' => 'sub', 'Log/Report/Dispatcher.pm' => 'sub', 'LWP/MediaTypes.pm' => [qw( LWP/media.types )], 'LWP/Parallel.pm' => sub { _glob_in_inc( 'LWP/Parallel', 1 ), qw( LWP/ParallelUA.pm LWP/UserAgent.pm LWP/RobotPUA.pm LWP/RobotUA.pm ), }, 'LWP/Parallel/UserAgent.pm' => [qw( LWP/Parallel.pm )], 'LWP/UserAgent.pm' => sub { return( qw( URI/URL.pm URI/http.pm LWP/Protocol/http.pm ), _glob_in_inc("LWP/Authen", 1), _glob_in_inc("LWP/Protocol", 1), ); }, 'Mail/Audit.pm' => 'sub', 'Math/BigInt.pm' => 'sub', 'Math/BigFloat.pm' => 'sub', 'Math/Symbolic.pm' => 'sub', 'MIME/Decoder.pm' => 'sub', 'MIME/Types.pm' => [qw( MIME/types.db )], 'Module/Build.pm' => 'sub', 'Module/Pluggable.pm' => sub { _glob_in_inc('$CurrentPackage/Plugin', 1); }, 'Moose.pm' => sub { _glob_in_inc('Moose', 1), _glob_in_inc('Class/MOP', 1), }, 'MooseX/AttributeHelpers.pm' => 'sub', 'MooseX/POE.pm' => sub { _glob_in_inc('MooseX/POE', 1), _glob_in_inc('MooseX/Async', 1), }, 'Mozilla/CA.pm' => [qw( Mozilla/CA/cacert.pem )], 'MozRepl.pm' => sub { qw( MozRepl/Log.pm MozRepl/Client.pm Module/Pluggable/Fast.pm ), _glob_in_inc('MozRepl/Plugin', 1), }, 'Module/Implementation.pm' => \&_warn_of_runtime_loader, 'Module/Runtime.pm' => \&_warn_of_runtime_loader, 'Net/DNS/RR.pm' => 'sub', 'Net/FTP.pm' => 'sub', 'Net/HTTPS.pm' => [qw( IO/Socket/SSL.pm Net/SSL.pm )], 'Net/Server.pm' => 'sub', 'Net/SSH/Perl.pm' => 'sub', 'Package/Stash.pm' => [qw( Package/Stash/PP.pm Package/Stash/XS.pm )], 'Pango.pm' => [qw( Cairo.pm )], # Pango.pm does: eval "use Cairo;" 'PAR/Repository.pm' => 'sub', 'PAR/Repository/Client.pm' => 'sub', 'Params/Validate.pm' => 'sub', 'Parse/AFP.pm' => 'sub', 'Parse/Binary.pm' => 'sub', 'PDF/API2/Resource/Font.pm' => 'sub', 'PDF/API2/Basic/TTF/Font.pm' => sub { _glob_in_inc('PDF/API2/Basic/TTF', 1); }, 'PDF/Writer.pm' => 'sub', 'PDL/NiceSlice.pm' => 'sub', 'Perl/Critic.pm' => 'sub', #not only Perl/Critic/Policy 'PerlIO.pm' => [ 'PerlIO/scalar.pm' ], 'Pod/Usage.pm' => sub { # from Pod::Usage (as of 1.61) $] >= 5.005_58 ? 'Pod/Text.pm' : 'Pod/PlainText.pm' }, 'POE.pm' => [qw( POE/Kernel.pm POE/Session.pm )], 'POE/Component/Client/HTTP.pm' => sub { _glob_in_inc('POE/Component/Client/HTTP', 1), qw( POE/Filter/HTTPChunk.pm POE/Filter/HTTPHead.pm ), }, 'POE/Kernel.pm' => sub { _glob_in_inc('POE/XS/Resource', 1), _glob_in_inc('POE/Resource', 1), _glob_in_inc('POE/XS/Loop', 1), _glob_in_inc('POE/Loop', 1), }, 'POSIX.pm' => sub { map $_->{name}, _glob_in_inc('auto/POSIX/SigAction', 0), # *.al files _glob_in_inc('auto/POSIX/SigRt', 0), # *.al files }, 'PPI.pm' => 'sub', 'Regexp/Common.pm' => 'sub', 'RPC/XML/ParserFactory.pm' => sub { _glob_in_inc('RPC/XML/Parser', 1); }, 'SerialJunk.pm' => [ qw( termios.ph asm/termios.ph sys/termiox.ph sys/termios.ph sys/ttycom.ph ) ], 'SOAP/Lite.pm' => sub { _glob_in_inc('SOAP/Transport', 1), _glob_in_inc('SOAP/Lite', 1), }, 'Socket/GetAddrInfo.pm' => 'sub', 'SQL/Parser.pm' => sub { _glob_in_inc('SQL/Dialects', 1); }, 'SQL/Translator/Schema.pm' => sub { _glob_in_inc('SQL/Translator', 1); }, 'Sub/Exporter/Progressive.pm' => [qw( Sub/Exporter.pm )], 'SVK/Command.pm' => sub { _glob_in_inc('SVK', 1); }, 'SVN/Core.pm' => sub { _glob_in_inc('SVN', 1), map $_->{name}, _glob_in_inc('auto/SVN', 0), # *.so, *.bs files }, 'Template.pm' => 'sub', 'Term/ReadLine.pm' => 'sub', 'Test/Deep.pm' => 'sub', 'threads/shared.pm' => [qw( attributes.pm )], # anybody using threads::shared is likely to declare variables # with attribute :shared 'Tk.pm' => sub { $SeenTk = 1; qw( Tk/FileSelect.pm Encode/Unicode.pm ); }, 'Tk/Balloon.pm' => [qw( Tk/balArrow.xbm )], 'Tk/BrowseEntry.pm' => [qw( Tk/cbxarrow.xbm Tk/arrowdownwin.xbm )], 'Tk/ColorEditor.pm' => [qw( Tk/ColorEdit.xpm )], 'Tk/DragDrop/Common.pm' => sub { _glob_in_inc('Tk/DragDrop', 1), }, 'Tk/FBox.pm' => [qw( Tk/folder.xpm Tk/file.xpm )], 'Tk/Getopt.pm' => [qw( Tk/openfolder.xpm Tk/win.xbm )], 'Tk/Toplevel.pm' => [qw( Tk/Wm.pm )], 'Unicode/UCD.pm' => [qw( utf8_heavy.pl )], 'URI.pm' => sub { grep !/urn/, _glob_in_inc('URI', 1) }, 'utf8_heavy.pl' => \&_unicore, 'Win32/EventLog.pm' => [qw( Win32/IPC.pm )], 'Win32/Exe.pm' => 'sub', 'Win32/TieRegistry.pm' => [qw( Win32API/Registry.pm )], 'Win32/SystemInfo.pm' => [qw( Win32/cpuspd.dll )], 'Wx.pm' => [qw( attributes.pm )], 'XML/Parser.pm' => sub { _glob_in_inc('XML/Parser/Style', 1), _glob_in_inc('XML/Parser/Encodings', 1), }, 'XML/SAX.pm' => [qw( XML/SAX/ParserDetails.ini ) ], 'XMLRPC/Lite.pm' => sub { _glob_in_inc('XMLRPC/Transport', 1),; }, 'YAML.pm' => [qw( YAML/Loader.pm YAML/Dumper.pm )], 'YAML/Any.pm' => sub { # try to figure out what YAML::Any would have used my $impl = eval "use YAML::Any; YAML::Any->implementation;"; unless ($@) { $impl =~ s!::!/!g; return "$impl.pm"; } _glob_in_inc('YAML', 1); # fallback }, ); # }}} sub path_to_inc_name($$) { my $path = shift; my $warn = shift; my $inc_name; if ($path =~ m/\.pm$/io) { die "$path doesn't exist" unless (-f $path); my $module_info = Module::Metadata->new_from_file($path); die "Module::Metadata error: $!" unless defined($module_info); $inc_name = $module_info->name(); if (defined($inc_name)) { $inc_name =~ s|\:\:|\/|og; $inc_name .= '.pm'; } else { warn "# Couldn't find include name for $path\n" if $warn; } } else { # Bad solution! (my $vol, my $dir, $inc_name) = File::Spec->splitpath($path); } return $inc_name; } my $Keys = 'files|keys|recurse|rv|skip|first|execute|compile|warn_missing|cache_cb|cache_file'; sub scan_deps { my %args = ( rv => {}, (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1) ); if (!defined($args{keys})) { $args{keys} = [map {path_to_inc_name($_, $args{warn_missing})} @{$args{files}}]; } my $cache_file = $args{cache_file}; my $using_cache; if ($cache_file) { require Module::ScanDeps::Cache; $using_cache = Module::ScanDeps::Cache::init_from_file($cache_file); if( $using_cache ){ $args{cache_cb} = Module::ScanDeps::Cache::get_cache_cb(); }else{ my @missing = Module::ScanDeps::Cache::prereq_missing(); warn join(' ', "Can not use cache_file: Needs Modules [", @missing, "]\n",); } } my ($type, $path); foreach my $input_file (@{$args{files}}) { if ($input_file !~ $ScanFileRE) { warn "Skipping input file $input_file because it matches \$Module::ScanDeps::ScanFileRE\n" if $args{warn_missing}; next; } $type = _gettype($input_file); $path = $input_file; if ($type eq 'module') { # necessary because add_deps does the search for shared libraries and such add_deps( used_by => undef, rv => $args{rv}, modules => [path_to_inc_name($path, $args{warn_missing})], skip => undef, warn_missing => $args{warn_missing}, ); } else { _add_info( rv => $args{rv}, module => path_to_inc_name($path, $args{warn_missing}), file => $path, used_by => undef, type => $type, ); } } scan_deps_static(\%args); if ($args{execute} or $args{compile}) { scan_deps_runtime( rv => $args{rv}, files => $args{files}, execute => $args{execute}, compile => $args{compile}, skip => $args{skip} ); } if ( $using_cache ){ Module::ScanDeps::Cache::store_cache(); } # do not include the input files themselves as dependencies! delete $args{rv}{$_} foreach @{$args{files}}; return ($args{rv}); } sub scan_deps_static { my ($args) = @_; my ($files, $keys, $recurse, $rv, $skip, $first, $execute, $compile, $cache_cb, $_skip) = @$args{qw( files keys recurse rv skip first execute compile cache_cb _skip )}; $rv ||= {}; $_skip ||= { %{$skip || {}} }; foreach my $file (@{$files}) { my $key = shift @{$keys}; next if $_skip->{$file}++; next if is_insensitive_fs() and $file ne lc($file) and $_skip->{lc($file)}++; next unless $file =~ $ScanFileRE; my @pm; my $found_in_cache; if ($cache_cb){ my $pm_aref; # cache_cb populates \@pm on success $found_in_cache = $cache_cb->(action => 'read', key => $key, file => $file, modules => \@pm, ); unless( $found_in_cache ){ @pm = scan_file($file); $cache_cb->(action => 'write', key => $key, file => $file, modules => \@pm, ); } }else{ # no caching callback given @pm = scan_file($file); } foreach my $pm (@pm){ add_deps( used_by => $key, rv => $args->{rv}, modules => [$pm], skip => $args->{skip}, warn_missing => $args->{warn_missing}, ); my @preload = _get_preload($pm) or next; add_deps( used_by => $key, rv => $args->{rv}, modules => \@preload, skip => $args->{skip}, warn_missing => $args->{warn_missing}, ); } } # Top-level recursion handling {{{ # prevent utf8.pm from being scanned $_skip->{$rv->{"utf8.pm"}{file}}++ if $rv->{"utf8.pm"}; while ($recurse) { my $count = keys %$rv; my @files = sort grep { defined $_->{file} && -T $_->{file} } values %$rv; scan_deps_static({ files => [ map $_->{file}, @files ], keys => [ map $_->{key}, @files ], rv => $rv, skip => $skip, recurse => 0, cache_cb => $cache_cb, _skip => $_skip, }); last if $count == keys %$rv; } # }}} return $rv; } sub scan_deps_runtime { my %args = ( perl => $^X, rv => {}, (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1) ); my ($files, $rv, $execute, $compile, $skip, $perl) = @args{qw( files rv execute compile skip perl )}; $files = (ref($files)) ? $files : [$files]; if ($compile) { foreach my $file (@$files) { next unless $file =~ $ScanFileRE; my ($inchash, $dl_shared_objects, $incarray) = ({}, [], []); _compile_or_execute($perl, $file, undef, $inchash, $dl_shared_objects, $incarray); _merge_rv(_make_rv($inchash, $dl_shared_objects, $incarray), $rv); } } elsif ($execute) { foreach my $file (@$files) { $execute = [] unless ref $execute; # make sure it's an array ref my ($inchash, $dl_shared_objects, $incarray) = ({}, [], []); _compile_or_execute($perl, $file, $execute, $inchash, $dl_shared_objects, $incarray); _merge_rv(_make_rv($inchash, $dl_shared_objects, $incarray), $rv); } } return ($rv); } sub scan_file{ my $file = shift; my %found; my $FH; open $FH, $file or die "Cannot open $file: $!"; $SeenTk = 0; # Line-by-line scanning LINE: while (<$FH>) { chomp(my $line = $_); foreach my $pm (scan_line($line)) { last LINE if $pm eq '__END__'; if ($pm eq '__POD__') { while (<$FH>) { last if (/^=cut/); } next LINE; } # Skip Tk hits from Term::ReadLine and Tcl::Tk my $pathsep = qr/\/|\\|::/; if ($pm =~ /^Tk\b/) { next if $file =~ /(?:^|${pathsep})Term${pathsep}ReadLine\.pm$/; next if $file =~ /(?:^|${pathsep})Tcl${pathsep}Tk\W/; } $SeenTk ||= $pm =~ /Tk\.pm$/; $found{$pm}++; } } close $FH or die "Cannot close $file: $!"; return keys %found; } sub scan_line { my $line = shift; my %found; return '__END__' if $line =~ /^__(?:END|DATA)__$/; return '__POD__' if $line =~ /^=\w/; $line =~ s/\s*#.*$//; $line =~ s/[\\\/]+/\//g; foreach (split(/;/, $line)) { s/^\s*//; if (/^package\s+(\w+)/) { $CurrentPackage = $1; $CurrentPackage =~ s{::}{/}g; return; } # use VERSION: if (/^(?:use|require)\s+v?(\d[\d\._]*)/) { # include feature.pm if we have 5.9.5 or better if (version->new($1) >= version->new("5.9.5")) { # seems to catch 5.9, too (but not 5.9.4) return "feature.pm"; } } if (my ($pragma, $args) = /^use \s+ (autouse|if) \s+ (.+)/x) { # NOTE: There are different ways the MODULE may # be specified for the "autouse" and "if" pragmas, e.g. # use autouse Module => qw(func1 func2); # use autouse "Module", qw(func1); # To avoid to parse them ourself, we simply try to eval the # string after the pragma (in a list context). The MODULE # should be the first ("autouse") or second ("if") element # of the list. my $module; { no strict; no warnings; if ($pragma eq "autouse") { ($module) = eval $args; } else { # The syntax of the "if" pragma is # use if COND, MODULE => ARGUMENTS # The COND may contain undefined functions (i.e. undefined # in Module::ScanDeps' context) which would throw an # exception. Sneak "1 || " in front of COND so that # COND will not be evaluated. This will work in most # cases, but there are operators with lower precedence # than "||" which will cause this trick to fail. (undef, $module) = eval "1 || $args"; } # punt if there was a syntax error return if $@ or !defined $module; }; $module =~ s{::}{/}g; return ("$pragma.pm", "$module.pm"); } if (my ($how, $libs) = /^(use \s+ lib \s+ | (?:unshift|push) \s+ \@INC \s+ ,) (.+)/x) { my $archname = defined($Config{archname}) ? $Config{archname} : ''; my $ver = defined($Config{version}) ? $Config{version} : ''; foreach my $dir (do { no strict; no warnings; eval $libs }) { next unless defined $dir; my @dirs = $dir; push @dirs, "$dir/$ver", "$dir/$archname", "$dir/$ver/$archname" if $how =~ /lib/; foreach (@dirs) { unshift(@INC, $_) if -d $_; } } next; } $found{$_}++ for scan_chunk($_); } return sort keys %found; } # short helper for scan_chunk my %LoaderRegexp; # cache sub _build_loader_regexp { my $loaders = shift; my $prefix = (@_ && $_[0]) ? $_[0].'::' : ''; my $loader = join '|', map quotemeta($_), split /\s+/, $loaders; my $regexp = qr/^\s* use \s+ ($loader)(?!\:) \b \s* (.*)/sx; # WARNING: This doesn't take the prefix into account $LoaderRegexp{$loaders} = $regexp; return $regexp } # short helper for scan_chunk sub _extract_loader_dependency { my $loader = shift; my $loadee = shift; my $prefix = (@_ && $_[0]) ? $_[0].'::' : ''; my $loader_file = $loader; $loader_file =~ s/::/\//; $loader_file .= ".pm"; return [ $loader_file, map { my $mod="$prefix$_"; $mod =~ s{::}{/}g; "$mod.pm" } grep { length and !/^q[qw]?$/ and !/-/ } split /[^\w:-]+/, $loadee #should skip any module name that contains '-', not split it in two ]; } sub scan_chunk { my $chunk = shift; # Module name extraction heuristics {{{ my $module = eval { $_ = $chunk; s/^\s*//; # TODO: There's many more of these "loader" type modules on CPAN! # scan for the typical module-loader modules my $loaders = "asa base parent prefork POE encoding maybe only::matching Mojo::Base"; # grab pre-calculated regexp or re-build it (and cache it) my $loader_regexp = $LoaderRegexp{$loaders} || _build_loader_regexp($loaders); if ($_ =~ $loader_regexp) { # $1 == loader, $2 == loadee my $retval = _extract_loader_dependency($1, $2); return $retval if $retval; } $loader_regexp = $LoaderRegexp{"Catalyst"} || _build_loader_regexp("Catalyst", "Catalyst::Plugin"); if ($_ =~ $loader_regexp) { # $1 == loader, $2 == loadee my $retval = _extract_loader_dependency($1, $2, "Catalyst::Plugin"); return $retval if $retval; } return [ 'Class/Autouse.pm', map { s{::}{/}g; "$_.pm" } grep { length and !/^:|^q[qw]?$/ } split(/[^\w:]+/, $1) ] if /^use \s+ Class::Autouse \b \s* (.*)/sx or /^Class::Autouse \s* -> \s* autouse \s* (.*)/sx; return $1 if /^(?:use|no|require) \s+ ([\w:\.\-\\\/\"\']+)/x; return $1 if /^(?:use|no|require) \s+ \( \s* ([\w:\.\-\\\/\"\']+) \s* \)/x; if ( s/^eval\s+\"([^\"]+)\"/$1/ or s/^eval\s*\(\s*\"([^\"]+)\"\s*\)/$1/) { return $1 if /^\s* (?:use|no|require) \s+ ([\w:\.\-\\\/\"\']*)/x; } if (/(<[^>]*[^\$\w>][^>]*>)/) { my $diamond = $1; return "File/Glob.pm" if $diamond =~ /[*?\[\]{}~\\]/; } return "DBD/$1.pm" if /\b[Dd][Bb][Ii]:(\w+):/; # check for stuff like # decode("klingon", ...) # open FH, "<:encoding(klingon)", ... if (my ($args) = /\b(?:open|binmode)\b(.*)/) { my @mods; push @mods, qw( PerlIO.pm PerlIO/encoding.pm Encode.pm ), _find_encoding($1) if $args =~ /:encoding\((.*?)\)/; push @mods, qw( PerlIO.pm PerlIO/via.pm ) if $args =~ /:via\(/; return \@mods if @mods; } if (/\b(?:en|de)code\(\s*['"]?([-\w]+)/) { return [qw( Encode.pm ), _find_encoding($1)]; } return $1 if /\b do \s+ ([\w:\.\-\\\/\"\']*)/x; if ($SeenTk) { my @modules; while (/->\s*([A-Z]\w+)/g) { push @modules, "Tk/$1.pm"; } while (/->\s*Scrolled\W+([A-Z]\w+)/g) { push @modules, "Tk/$1.pm"; push @modules, "Tk/Scrollbar.pm"; } if (/->\s*setPalette/g) { push @modules, map { "Tk/$_.pm" } qw( Button Canvas Checkbutton Entry Frame Label Labelframe Listbox Menubutton Menu Message Radiobutton Scale Scrollbar Spinbox Text ); } return \@modules; } # Module::Runtime return $1 if /\b(?:require_module|use_module|use_package_optimistically) \s* \( \s* ([\w:"']+)/x; # Test::More return $1 if /\b(?:require_ok|use_ok) \s* \( \s* ([\w:"']+)/x; return; }; # }}} return unless defined($module); return wantarray ? @$module : $module->[0] if ref($module); $module =~ s/^['"]//; return unless $module =~ /^\w/; $module =~ s/\W+$//; $module =~ s/::/\//g; return if $module =~ /^(?:[\d\._]+|'.*[^']|".*[^"])$/; $module .= ".pm" unless $module =~ /\./; return $module; } sub _find_encoding { return unless $] >= 5.008 and eval { require Encode; %Encode::ExtModule }; my $mod = $Encode::ExtModule{ Encode::find_encoding($_[0])->name } or return; $mod =~ s{::}{/}g; return "$mod.pm"; } sub _add_info { my %args = @_; my ($rv, $module, $file, $used_by, $type) = @args{qw/rv module file used_by type/}; return unless defined($module) and defined($file); # Ensure file is always absolute $file = File::Spec->rel2abs($file); $file =~ s|\\|\/|go; # Avoid duplicates that can arise due to case differences that don't actually # matter on a case tolerant system if (File::Spec->case_tolerant()) { foreach my $key (keys %$rv) { if (lc($key) eq lc($module)) { $module = $key; last; } } if (defined($used_by)) { if (lc($used_by) eq lc($module)) { $used_by = $module; } else { foreach my $key (keys %$rv) { if (lc($key) eq lc($used_by)) { $used_by = $key; last; } } } } } $rv->{$module} ||= { file => $file, key => $module, type => $type, }; if (defined($used_by) and $used_by ne $module) { push @{ $rv->{$module}{used_by} }, $used_by if ( (!File::Spec->case_tolerant() && !grep { $_ eq $used_by } @{ $rv->{$module}{used_by} }) or ( File::Spec->case_tolerant() && !grep { lc($_) eq lc($used_by) } @{ $rv->{$module}{used_by} })); # We assume here that another _add_info will be called to provide the other parts of $rv->{$used_by} push @{ $rv->{$used_by}{uses} }, $module if ( (!File::Spec->case_tolerant() && !grep { $_ eq $module } @{ $rv->{$used_by}{uses} }) or ( File::Spec->case_tolerant() && !grep { lc($_) eq lc($module) } @{ $rv->{$used_by}{uses} })); } } # This subroutine relies on not being called for modules that have already been visited sub add_deps { my %args = ((@_ and $_[0] =~ /^(?:modules|rv|used_by|warn_missing)$/) ? @_ : (rv => (ref($_[0]) ? shift(@_) : undef), modules => [@_])); my $rv = $args{rv} || {}; my $skip = $args{skip} || {}; my $used_by = $args{used_by}; foreach my $module (@{ $args{modules} }) { my $file = _find_in_inc($module) or _warn_of_missing_module($module, $args{warn_missing}), next; next if $skip->{$file}; if (exists $rv->{$module}) { _add_info( rv => $rv, module => $module, file => $file, used_by => $used_by, type => undef ); next; } my $type = _gettype($file); _add_info( rv => $rv, module => $module, file => $file, used_by => $used_by, type => $type ); if ($module =~ /(.*?([^\/]*))\.p[mh]$/i) { my ($path, $basename) = ($1, $2); foreach (_glob_in_inc("auto/$path")) { next if $_->{file} =~ m{\bauto/$path/.*/}; # weed out subdirs next if $_->{name} =~ m{/\.(?:exists|packlist)$}; my ($ext,$type); $ext = lc($1) if $_->{name} =~ /(\.[^.]+)$/; if (defined $ext) { next if $ext eq lc(lib_ext()); $type = 'shared' if $ext eq lc(dl_ext()); $type = 'autoload' if ($ext eq '.ix' or $ext eq '.al'); } $type ||= 'data'; _add_info( rv => $rv, module => $_->{name}, file => $_->{file}, used_by => $module, type => $type ); } ### Now, handle module and distribution share dirs # convert 'Module/Name' to 'Module-Name' my $modname = $path; $modname =~ s|/|-|g; # TODO: get real distribution name related to module name my $distname = $modname; foreach (_glob_in_inc("auto/share/module/$modname")) { _add_info( rv => $rv, module => $_->{name}, file => $_->{file}, used_by => $module, type => 'data' ); } foreach (_glob_in_inc("auto/share/dist/$distname")) { _add_info( rv => $rv, module => $_->{name}, file => $_->{file}, used_by => $module, type => 'data' ); } } } # end for modules return $rv; } sub _find_in_inc { my $file = shift; return unless defined $file; foreach my $dir (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) { return "$dir/$file" if -f "$dir/$file"; } # absolute file names return $file if -f $file; return; } sub _glob_in_inc { my $subdir = shift; my $pm_only = shift; my @files; require File::Find; $subdir =~ s/\$CurrentPackage/$CurrentPackage/; foreach my $inc (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) { my $dir = "$inc/$subdir"; next unless -d $dir; File::Find::find( sub { return unless -f; return if $pm_only and !/\.p[mh]$/i; (my $name = $File::Find::name) =~ s!^\Q$inc\E/!!; push @files, $pm_only ? $name : { file => $File::Find::name, name => $name }; }, $dir ); } return @files; } my $unicore_stuff; sub _unicore { $unicore_stuff ||= [ map $_->{name}, _glob_in_inc('unicore', 0) ]; return @$unicore_stuff; } # App::Packer compatibility functions sub new { my ($class, $self) = @_; return bless($self ||= {}, $class); } sub set_file { my $self = shift; my $script = shift; my ($vol, $dir, $file) = File::Spec->splitpath($script); $self->{main} = { key => $file, file => $script, }; } sub set_options { my $self = shift; my %args = @_; foreach my $module (@{ $args{add_modules} }) { $module =~ s/::/\//g; $module .= '.pm' unless $module =~ /\.p[mh]$/i; my $file = _find_in_inc($module) or _warn_of_missing_module($module, $args{warn_missing}), next; $self->{files}{$module} = $file; } } sub calculate_info { my $self = shift; my $rv = scan_deps( 'keys' => [ $self->{main}{key}, sort keys %{ $self->{files} }, ], files => [ $self->{main}{file}, map { $self->{files}{$_} } sort keys %{ $self->{files} }, ], recurse => 1, ); my $info = { main => { file => $self->{main}{file}, store_as => $self->{main}{key}, }, }; my %cache = ($self->{main}{key} => $info->{main}); foreach my $key (sort keys %{ $self->{files} }) { my $file = $self->{files}{$key}; $cache{$key} = $info->{modules}{$key} = { file => $file, store_as => $key, used_by => [ $self->{main}{key} ], }; } foreach my $key (sort keys %{$rv}) { my $val = $rv->{$key}; if ($cache{ $val->{key} }) { defined($val->{used_by}) or next; push @{ $info->{ $val->{type} }->{ $val->{key} }->{used_by} }, @{ $val->{used_by} }; } else { $cache{ $val->{key} } = $info->{ $val->{type} }->{ $val->{key} } = { file => $val->{file}, store_as => $val->{key}, used_by => $val->{used_by}, }; } } $self->{info} = { main => $info->{main} }; foreach my $type (sort keys %{$info}) { next if $type eq 'main'; my @val; if (UNIVERSAL::isa($info->{$type}, 'HASH')) { foreach my $val (sort values %{ $info->{$type} }) { @{ $val->{used_by} } = map $cache{$_} || "!!$_!!", @{ $val->{used_by} }; push @val, $val; } } $type = 'modules' if $type eq 'module'; $self->{info}{$type} = \@val; } } sub get_files { my $self = shift; return $self->{info}; } sub add_preload_rule { my ($pm, $rule) = @_; die qq[a preload rule for "$pm" already exists] if $Preload{$pm}; $Preload{$pm} = $rule; } # scan_deps_runtime utility functions # compile $file if $execute is undef, # otherwise execute $file with arguments @$execute sub _compile_or_execute { my ($perl, $file, $execute, $inchash, $dl_shared_objects, $incarray) = @_; my ($fh, $instrumented_file) = File::Temp::tempfile(); # spoof $0 (to $file) so that FindBin works as expected # NOTE: We don't directly assign to $0 as it has magic (i.e. # assigning has side effects and may actually fail, cf. perlvar(1)). # Instead we alias *0 to a package variable holding the correct value. local $ENV{MSD_ORIGINAL_FILE} = $file; print $fh <<'...'; BEGIN { my $_0 = $ENV{MSD_ORIGINAL_FILE}; *0 = \$_0; } ... my (undef, $data_file) = File::Temp::tempfile(); local $ENV{MSD_DATA_FILE} = $data_file; # NOTE: When compiling the block will run as the last CHECK block; # when executing the block will run as the first END block and # the programs continues. print $fh $execute ? "END\n" : "CHECK\n", <<'...'; { # save %INC etc so that requires below don't pollute them my %_INC = %INC; my @_INC = @INC; my @_dl_shared_objects = @DynaLoader::dl_shared_objects; my @_dl_modules = @DynaLoader::dl_modules; require Cwd; require DynaLoader; require Data::Dumper; require B; require Config; while (my ($k, $v) = each %_INC) { # NOTES: # (1) An unsuccessful "require" may store an undefined value into %INC. # (2) If a key in %INC was located via a CODE or ARRAY ref or # blessed object in @INC the corresponding value in %INC contains # the ref from @INC. # (3) Some modules (e.g. Moose) fake entries in %INC, e.g. # "Class/MOP/Class/Immutable/Moose/Meta/Class.pm" => "(set by Moose)" # On some architectures (e.g. Windows) Cwd::abs_path() will throw # an exception for such a pathname. if (defined $v && !ref $v && -e $v) { $_INC{$k} = Cwd::abs_path($v); } else { delete $_INC{$k}; } } # drop refs from @_INC @_INC = grep { !ref $_ } @_INC; my $dlext = $Config::Config{dlext}; my @so = grep { defined $_ && -e $_ } Module::ScanDeps::DataFeed::_dl_shared_objects(); my @bs = @so; my @shared_objects = ( @so, grep { s/\Q.$dlext\E$/\.bs/ && -e $_ } @bs ); my $data_file = $ENV{MSD_DATA_FILE}; open my $fh, ">", $data_file or die "Couldn't open $data_file: $!\n"; print $fh Data::Dumper->Dump( [ \%_INC, \@_INC, \@shared_objects ], [qw( *inchash *incarray *dl_shared_objects )]); print $fh "1;\n"; close $fh; sub Module::ScanDeps::DataFeed::_dl_shared_objects { if (@_dl_shared_objects) { return @_dl_shared_objects; } elsif (@_dl_modules) { return map { Module::ScanDeps::DataFeed::_dl_mod2filename($_) } @_dl_modules; } return; } sub Module::ScanDeps::DataFeed::_dl_mod2filename { my $mod = shift; return if $mod eq 'B'; return unless defined &{"$mod\::bootstrap"}; my $dl_ext = $Config::Config{dlext}; # cf. DynaLoader.pm my @modparts = split(/::/, $mod); my $modfname = defined &DynaLoader::mod2fname ? DynaLoader::mod2fname(\@modparts) : $modparts[-1]; my $modpname = join('/', @modparts); foreach my $dir (@_INC) { my $file = "$dir/auto/$modpname/$modfname.$dl_ext"; return $file if -r $file; } return; } } # END or CHECK ... # append the file to compile or execute { open my $in, "<", $file or die "Couldn't open $file: $!"; print $fh qq[#line 1 "$file"\n], <$in>; close $in; } close $fh; # run the instrumented file my @cmd = ($perl); push @cmd, "-c" unless $execute; push @cmd, map { "-I$_" } @IncludeLibs; push @cmd, $instrumented_file; push @cmd, @$execute if $execute; my $rc = system(@cmd); _extract_info($data_file, $inchash, $dl_shared_objects, $incarray) if $rc == 0; unlink($instrumented_file, $data_file); die $execute ? "SYSTEM ERROR in executing $file @$execute: $rc" : "SYSTEM ERROR in compiling $file: $rc" unless $rc == 0; } # create a new hashref, applying fixups sub _make_rv { my ($inchash, $dl_shared_objects, $inc_array) = @_; my $rv = {}; my @newinc = map(quotemeta($_), @$inc_array); my $inc = join('|', sort { length($b) <=> length($a) } @newinc); # don't pack lib/c:/ or lib/C:/ $inc = qr/$inc/i if(is_insensitive_fs()); require File::Spec; foreach my $key (keys(%$inchash)) { my $newkey = $key; $newkey =~ s"^(?:(?:$inc)/?)""sg if File::Spec->file_name_is_absolute($newkey); $rv->{$newkey} = { 'used_by' => [], 'file' => $inchash->{$key}, 'type' => _gettype($inchash->{$key}), 'key' => $key }; } foreach my $dl_file (@$dl_shared_objects) { my $key = $dl_file; $key =~ s"^(?:(?:$inc)/?)""s; $rv->{$key} = { 'used_by' => [], 'file' => $dl_file, 'type' => 'shared', 'key' => $key }; } return $rv; } sub _extract_info { my ($fname, $inchash, $dl_shared_objects, $incarray) = @_; use vars qw(%inchash @dl_shared_objects @incarray); unless (do $fname) { die "error extracting info from DataFeed file: ", $@ || "can't read $fname: $!"; } $inchash->{$_} = $inchash{$_} for keys %inchash; @$dl_shared_objects = @dl_shared_objects; @$incarray = @incarray; } sub _gettype { my $name = shift; my $dlext = quotemeta(dl_ext()); return 'autoload' if $name =~ /(?:\.ix|\.al)$/i; return 'module' if $name =~ /\.p[mh]$/i; return 'shared' if $name =~ /\.$dlext$/i; return 'data'; } # merge all keys from $rv_sub into the $rv mega-ref sub _merge_rv { my ($rv_sub, $rv) = @_; my $key; foreach $key (keys(%$rv_sub)) { my %mark; if ($rv->{$key} and _not_dup($key, $rv, $rv_sub)) { warn "Different modules for file '$key' were found.\n" . " -> Using '" . _abs_path($rv_sub->{$key}{file}) . "'.\n" . " -> Ignoring '" . _abs_path($rv->{$key}{file}) . "'.\n"; $rv->{$key}{used_by} = [ grep (!$mark{$_}++, @{ $rv->{$key}{used_by} }, @{ $rv_sub->{$key}{used_by} }) ]; @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} }; $rv->{$key}{file} = $rv_sub->{$key}{file}; } elsif ($rv->{$key}) { $rv->{$key}{used_by} = [ grep (!$mark{$_}++, @{ $rv->{$key}{used_by} }, @{ $rv_sub->{$key}{used_by} }) ]; @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} }; } else { $rv->{$key} = { used_by => [ @{ $rv_sub->{$key}{used_by} } ], file => $rv_sub->{$key}{file}, key => $rv_sub->{$key}{key}, type => $rv_sub->{$key}{type} }; @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} }; } } } sub _not_dup { my ($key, $rv1, $rv2) = @_; if (File::Spec->case_tolerant()) { return lc(_abs_path($rv1->{$key}{file})) ne lc(_abs_path($rv2->{$key}{file})); } else { return _abs_path($rv1->{$key}{file}) ne _abs_path($rv2->{$key}{file}); } } sub _abs_path { return join( '/', Cwd::abs_path(File::Basename::dirname($_[0])), File::Basename::basename($_[0]), ); } sub _warn_of_runtime_loader { my $module = shift; return if $SeenRuntimeLoader{$module}++; $module =~ s/\.pm$//; $module =~ s|/|::|g; warn "# Use of runtime loader module $module detected. Results of static scanning may be incomplete.\n"; return; } sub _warn_of_missing_module { my $module = shift; my $warn = shift; return if not $warn; return if not $module =~ /\.p[ml]$/; warn "# Could not find source file '$module' in \@INC or \@IncludeLibs. Skipping it.\n" if not -f $module; } sub _get_preload1 { my $pm = shift; my $preload = $Preload{$pm} or return(); if ($preload eq 'sub') { $pm =~ s/\.p[mh]$//i; return _glob_in_inc($pm, 1); } elsif (UNIVERSAL::isa($preload, 'CODE')) { return $preload->($pm); } return @$preload; } sub _get_preload { my ($pm, $seen) = @_; $seen ||= {}; $seen->{$pm}++; my @preload; foreach $pm (_get_preload1($pm)) { next if $seen->{$pm}; $seen->{$pm}++; push @preload, $pm, _get_preload($pm, $seen); } return @preload; } 1; __END__ =head1 SEE ALSO L is a bundled utility that writes C section for a number of files. An application of B is to generate executables from scripts that contains prerequisite modules; this module supports two such projects, L and L. Please see their respective documentations on CPAN for further information. =head1 AUTHORS Audrey Tang Ecpan@audreyt.orgE To a lesser degree: Steffen Mueller Esmueller@cpan.orgE Parts of heuristics were deduced from: =over 4 =item * B by ActiveState Tools Corp L =item * B by IndigoStar, Inc L =back The B function is contributed by Edward S. Peschko. L is the official website for this module. You can write to the mailing list at Epar@perl.orgE, or send an empty mail to Epar-subscribe@perl.orgE to participate in the discussion. Please submit bug reports to Ebug-Module-ScanDeps@rt.cpan.orgE. =head1 COPYRIGHT Copyright 2002-2008 by Audrey Tang Ecpan@audreyt.orgE; 2005-2010 by Steffen Mueller Esmueller@cpan.orgE. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =cut Module-ScanDeps-1.20/Changes0000644000372000037200000006665412604222701016045 0ustar roderichroderich1.20 2015-10-04 - Fix RT #107304: Newer versions of File::Path cause warning "_Inline for _Inline: No such file or directory at Module/ScanDeps.pm line 1339." - drop the dubious call to rmtree() - Fix RT106142: Preload dependencies for PDL and PDL::NiceSlice - adopted from a patch by Shawn Laffan, thanks Shawn! - Fix RT#106144: Preload dependencies for File::BOM) - adopted from a patch by Shawn Laffan, thanks Shawn! - Revise our stance on utf8.pm: - A line of "use utf8;" just means "this file is encoded in UTF-8" and should _not_ result in scanning utf8.pm which will pull in the whole Unicode shebang (propery tables and what not). Yes, utf8.pm _doesn contain "require utf8_heavy.pl", but only inside an AUTOLOAD() that is _not_ triggered by calling functions like utf8::is_utf8(). - OTOH the innocently looking one-liner perl -ne 'print if /\pN/' implicitly loads utf8.pm and triggers the AUTOLAD(). - So prevent utf8.pm from being scanned and make utf8_heavy.pl the indicator for "I need the Unicode stuff" instead. - Cache the results of _get_preload('utf8_heavy.pl'). - Make %Preload "transitive" so that given my %Preload = ( 'Foo.pm' => [ 'Bar.pm' ], 'Bar.pm' => [ 'Quux.pm' ], ... ); scan_deps_static() register a dependency on Bar.pm _and_ Quux.pm when it saw "use Foo;" - Minor changes: - drop dubious %Preload of utf8.pm for SOAP::Lite and XML::Parser::Expat - drop code for Perl < 5.008 as we require 5.8.1 already - rework the implementation of -x/-c - add add_preload_rule() to dynamically add a %Preload rule - recognize constructs like "open FH, '<:via(Foo)', ..." - upgrade to Module::Install 1.16 1.19 2015-05-27 - add %Preload rule for LWP::MediaTypes: data file LWP/media.types - add %Preload entry for MIME::Types: data file MIME/types.db - add %Preload rule for AnyEvent - always add Encode.pm when fix encountering constructs like decode("klingon", ...) open FH, "<:encoding(klingon)", .. - add license - update OpenFoundry repository URL 1.18 2015-01-19 - Fix RT #101569: Incorrect module parsing if Moose is included 1.17 2014-10-31 - scandeps.pl: die if an option is not recognized - Reformat Changes file according to CPAN::Changes::Spec - Modify %Preload rule: let Unicode::UCD explicitly imply utf8.pm. This fixes PAR::Packer's self test. Previously Unicode::UCD implied utf8.pm implicitly because it contains calls to some utf8::foo() functions. - Add %Preload rule: Mozilla::CA requires its cacert.pem file - Recognize "do filename" constructs even if "do" isn't at the start of a chunk. - Upgrade to Module::Install 1.14 1.16 2014-09-28 - Fix RT#98938: recognize Module::Runtime module-loading functions - Fix a nasty typo that broke scandeps.pl option -E $ scandeps -E "some string" Unknown option: E Can't open some string: No such file or directory at scandeps.pl line 49. - also scandeps.pl: die if an option is not recognized - Remove some overzealous heuristics from scan_chunk() - they were looking for Foo::Bar->something Foo::Bar::whatever(...) _anywhere_ in programs to infer a dependency on Foo/Bar.pm. BEWARE: This might break some use cases, i.e. missing some dependencies. On the other hand, this causes hard to investigate problems like the one starting at http://www.mail-archive.com/par@perl.org/msg05531.html. While the former can easily be worked around by the user itself (just add a missing dependecy explicitly, e.g. using "pp -M ...") and typically can be solved in general by adding a %Preload rule, the latter just wastes people's times. - Recognize Test::More require_ok() and use_ok() - makes 3-static_oo_interface_real.t pass again (fallout from the above) - Upgrade to Module::Install 1.12 - Add option -T to request information from CPAN - don't access CPAN behind the user's back just because they have CPANPLUS installed (it was in the Perl core from 5.10 to 5.18) - it might not even have been configured (e.g. in a corporate internet) - only do this when explicitly requested 1.15 2014-08-23 - Fix RT #98203: Migrate from deprecated Module::Build::ModuleInfo to Module::Metadata - thanx Petr Pisar (ppisar@redhat.com) for the hint - add long option names to scandeps.pl - implement option --xargs for scandeps.pl - fix wrong version numbers in Changes 1.14 2014-08-03 - Fix RT #92860 (t/7-check-dynaloader.t doesn't handle systems with mod2fname), also RT #97519 (Fix for t/7-check-dynaloader.t on systems with DynaLoader::mod2fname) - applied patch from Brian Fraser (fraserbn@gmail.com), thanks! - lib/Module/ScanDeps/DataFeed.pm: apply here, too 1.13 2013-12-21 - Fix recognition of (open() arguments) "<:encoding(klingon)", implies modules PerlIO and PerlIO::encoding. 1.12 2013-12-01 - Fix RT #90869: Use of uninitialized value $module in substitution (s///) - Fix RT #87775: typo fixes, thanks dsteinbrunner@pobox.com - new %Preload rule for B::Hooks::EndOfScope - new %Preload rule for Pod::Usage - add a fake %Preload rule that warns if use of Module::Implementation or Module::Runtime is detected (coz' they're doing runtime loading) - change some tests to use Test::Requires instead of homegrown stuff; hence add it to "test_requires" - clean up some uses of Test::More 1.11 2013-09-28 - Fix RT #89000: test broken by indirect base.pm disuse - delete base.pm from list of expected deps, patch by Andrew Main (zefram@fysh.org) - new %Preload rule for Net::HTTPS (e.g. used by LWP::Protocol::https) - look for IO::Socket::SSL or Net::SSL - new %Preload rule for YAML::Any - try to figure out what YAML::Any would have used (using YAML::Any->implementation) - as fallback, include anything below YAML 1.10 2012-10-20 - add %Preload rule for Params::Validate to detect its PP and XS implementations - Fix RT #80276 Module DateTime::Format::ISO8601 generates error after being packaged - caused by failing to pack DateTime::Format::Builder::Parser::XXX modules needed by DateTime::Format::Builder::Parser - add a corresponding %Preload rule - update to Module::Install 1.06 1.09 2012-09-09 - teach Module::ScanDeps about "use if ..." constructs - fixes CPAN Testers failures for PAR::Packer with perl 5.17.1 and up (Roderich Schupp) - RT #79003: t/7-check-dynaloader.t failing when /usr/lib != /usr/lib64 - scrap the test for "$entry{file} starts with $expected_prefix" as its assumptions are flawed (Roderich Schupp) - Mojo::Base is a loader (Alexandr Ciornii) - Special case for Class::Load (Alexandr Ciornii) 1.08 2012-02-21 - RT #73785: scandeps -c fails on modules that depend on Getopt::Euclid - for "scandeps -c ..." switch from an INIT block to a CHECK block and call the augmented script with "perl -c" instaed of "perl" - RT#72954 ":encoding(UTF-8)" doesn't imply a dependency on Encode.pm - if scan_chunk sees ":encoding(FOO)" or similar, it goes to some length to find the "external" Encode module to handle FOO; but it forgets that Encode.pm itself is needed at runtime (esp. if FOO is an encoding "internally" handled by Encode.pm, e.g. "UTF-8") - %Preload: add rules for Gtk2.pm and Pango.pm - %Preload: fix a problem with Image::ExifTool 1.07 2011-11-29 - RT #72796: dynaloader test fails when the .so files are in the system lib dirs and local::lib is involved? Relax a check in t/7-check-dynaloader.t - Update Module::Install to 1.04 1.06 2011-11-28 - RT #72211: pp includes way too much modules (when using 'use strict;')? Rework regexes to detect "use MODULE ...": the following line from unicore/mktables my $unihan = 'Unihan properties are by default not enabled in the Perl core. Instead use CPAN: Unicode::Unihan'; would erroneously detect a dependency on CPAN.pm (which will in turn pull in a lot of modules) - Bump Perl version requirement to 5.8.1 (Schwern: The End Of 5.6 Is Nigh!) - Rewrite t/7-check-dynaloader.t to look for more candidates of dynamic modules that might be used as test cases 1.05 2011-11-02 - RT #72082: $FindBin::Bin issue on Moudel::ScanDeps 1.04 Make FindBin work (at least with option -c or -x) by spoofing $0 in the temp script generated for M:SD::DataFeed - RT #70134: patch suggestions for Module::ScanDeps 1.04: additional preload rules, used_via_preload attribute Add suggested %Preload rules from the attached patch (thanks, Markus Jansen) - Add %Preload rules for MozRepl - Special case for Package::Stash (Alexandr Ciornii) - Special case for Moose (Alexandr Ciornii) 1.04 2011-07-21 - Brown paper bag bug: fix option -x (execute) (broken by changes for -c) - While we're at it: honor option -I with -c 1.03 2011-07-18 - RT #69213: ScanDeps incompatible with AnyEvent (Perl 5.14, AnyEvent 5.34, PAR 1.00.2) For option -c (compile) M:SD used to wrap the file in one big sub and appended an END block where it dumps %INC etc; the outer sub causes problems with certain contructs. Instead we now use an INIT block prepended to the file. - RT #69471: Problem with "eval { require SomeModule }" constructions Module::ScanDeps::DataFeed now omits %INC pairs with an undefined value (these may be created by an unsuccessful "require" under certain conditions). Also omit CODE refs from @INC. - Fix for failing CPAN Testers report http://www.cpantesters.org/cpan/report/4208fa16-a5d1-11e0-a0bc-c71a7862a918: Perl 5.15.0 got rid of Shell.pm - Fix for failing CPAN Testers report http://www.cpantesters.org/cpan/report/772147dc-6c1f-1014-baf2-318eb63ba09a: - regex meta characters in filenames break consistency check - Simplify Module::ScanDeps::DataFeed somewhat by localizing %INC around "require Module::ScanDeps::DataFeed" and by using Data::Dumper for the actual dump. - Don't create the tempfiles for DataFeed in the working directory. - Purge all pod from Module::ScanDeps::DataFeed, advise the CPAN indexer not to bother with it; same for Module::ScanDeps::Cache. 1.02 2011-04-03 - %Preload: add _all_ *.pl file below .../unicore for utf8.pm 1.01 2011-03-26 - %Preload: add "unicore/version" for Unicode/UCD.pm (because it contains a call openunicode(..., "version")) 1.00 2011-02-19 - RT #65855: Special handling for POSIX requested (Roderich Schupp) - RT #65252: Temp files left when execute fails (Roderich Schupp) - add a %Preload rule for Log::Report::Dispatcher (Roderich Schupp) cf. http://www.nntp.perl.org/group/perl.par/2011/01/msg4871.html - add %Preload rule for Date::Manip (Roderich Schupp) - speed up scanning *significantly* by not re-constructing regexen for every line of input and reducing the no. of sub calls (Steffen Mueller) - add Eric Roode to AUTHORS (Steffen Mueller) - RT #61027: "use lib" does not work (Roderich Schupp) scan_line(): When handling "use lib '/some/dir'" we add "/some/dir/ARCHNAME", "/some/dir/VER" and "/some/dir/VER/ARCHNAME", but forgot to add "/some/dir" itself. While we're at it, improve parsing the argument list of "use lib". Simply eval the string, this should at least make all forms of quoted strings work correctly. - fix URI special case (clkao) - fix a regression reported by CPAN Testers (Roderich Schupp) - finally: bump version to 1.00 0.98 2010-07-26 - Make %Preload entry for "utf8.pm" lazy (Roderich Schupp) - Upgrade to Module::Install 1.00 (Roderich Schupp) - RT #58093: Par-Packer not including all dependencies (unicore/Heavy.pl) (Roderich Schupp) - Add %Preload rule for RPC::XML (Roderich Schupp) - RT #57494: add %Preload rule for JSON.pm (Roderich Schupp) 0.97 2010-04-10 - Pack the content of module/distribution sharedirs is automatically. (kmx) - RT #56020 - add data files used by Unicode::UCD (Roderich Schupp) - RT #55746 - remove bogus "... if %Config::Config" condition (Roderich Schupp) - Add special case for CGI::Application::Plugin::AutoRunmode (Alexandr Ciornii) - Add special case for CGI::Application::Plugin::Authentication (Alexandr Ciornii) - Add special case for DBIx::Perlish (Alexandr Ciornii) 0.96 2009-11-13 - perl 5.6.1 compatibility (Alexandr Ciornii) - Test for "use module version;" (Alexandr Ciornii) 0.95 2009-10-16 - Fix "uninitialized value" warnings (Dave Rolsky) - Add special case for Perl::Critic (Alexandr Ciornii) - Add special case for Event (Alexandr Ciornii) - Add special case for Wx.pm (Alexandr Ciornii) - Add special case for Log::Any 0.94 2009-08-10 - Add tests for scan_line (Alexandr Ciornii) - RT#48151 fixed, "require __PACKAGE__" should not die (Alexandr Ciornii) - OS/2 fixes (Ilya Zakharevich) 0.93 2009-07-19 - Implement caching of dependencies (Christoph Lamprecht) 0.92 2009-07-19 - Fix bug with {type} being set to unexpected values in some cases (Christoph Lamprecht) - Add tests for scan_chunk (Alexandr Ciornii) - Add special case for parent.pm (Alexandr Ciornii) - Fix for "use parent::something" (Alexandr Ciornii) - Add special case for Catalyst.pm (Alexandr Ciornii) 0.91 2009-06-22 - Add special case for Tk's setPalette call (Christoph Lamprecht) 0.90 2009-05-09 - Add special case for DateTime::Locale - Add special case for PAR::Repository and PAR::Repository::Client 0.89 2008-11-03 - Distribution fixes. - Do not use base Exporter. - Detection of 'asa' and 'only::matching'. 0.88 2008-10-28 - Add special case for File::HomeDir. 0.87 2008-10-28 - Add special case for PPI. 0.86 2008-10-23 - Fix the 'use prefork "Foo"' static detection. - Fix the detection of any of the module-loader modules such as prefork, autouse, etc. if invoked as 'use prefork"Foo"' (note the lack of a space). - Slightly refactor the loader-module scanning. (see above) - Support for "use maybe 'foo';" - Use (arch|priv)libexp instead of (arch|priv)lib in scandeps.pl (Mark Stosberg) - Update to Module::Install 0.77 0.85 2008-08-01 - Add special case for Net::Server. 0.84 2008-05-13 - Add special case for Class::MethodMaker. 0.83 2008-03-23 - Add special case for Image::ExifTool. 0.82 2008-01-08 - Add Test::More to build requirements (Alexandr Ciornii) - Add dependency on version.pm - Now correctly identifies feature.pm as a dependency if "use 5.10.0;" (and up) is found. 0.81 2007-12-07 - Fix for the case-insensitive-file-system-test. 0.80 2007-11-30 - Fix to avoid duplicated entries arising from used_by references with case differences. - Do not report input files themselves as dependencies. (Regression from 0.74 onwards) - Remove warning from ScanFileRE tests. 0.78 2007-11-17 - Fix ScanFileRE heuristics to allow for scanning files without suffixes. 0.77 2007-09-20 - Add support for prefork.pm (similar to how base.pm is detected). - Added uses field to hash descriptions returned by scan_deps + tests (Adrian Issott) - Added ScanFileRE to restrict the files scanned to .pl, .pm, .al and .t but allow the user to override + tests (Adrian Issott) 0.76 2007-07-21 - Fix special case for Term::ReadLine (should not rope in Tk) - New special case for Tcl::Tk (should not rope in Tk either!) - New special case for threads::shared ==> rope in attributes.pm - Fix to avoid duplicated entries that can arise due to case differences that don't actually matter on case-tolerant systems (Adrian Issott) - M::SD warnings now go to STDERR not STDOUT (Adrian Issott) - Fixed bug #24162: scandeps.(bat|pl) doesn't correctly identify Core Modules on Windows (Adrian Issott) - Now finds shared libraries for modules specified as input files. - Tests for finding shared libraries. 0.75 2007-06-24 - Fix special cases for POE. (Roderich Schupp) - Added exported path_to_inc_name subroutine (Adrian Issott) - Added Module::Build::ModuleInfo dependency (Adrian Issott) - Fixed bug where input files weren't scoped properly - Add new "check-for-dynaloader" test. (Eric Wilhelm) 0.74 2007-04-26 - Same as 0.73_01, but not a developer release. 0.73_01 2007-03-28 - Fixed bug "scan_deps doesn't show ALL the dependencies" - Ensured all file entries are given by absolute paths - Added a number of test artificial dependency trees as test data mainly for "scan_deps doesn't show ALL the dependencies" bug - Added tests for scandeps recurse option (all pass) - Added tests for scandeps skip option (all pass) - Added tests to show a duplicated dependency is in fact only shown once (all pass) - Added Utils.pm test module containing generic_scandeps_rv_test and compare_scandeps_rvs subroutines (Adrian Issott) 0.73 2007-03-25 - Now being a little cleverer for detecting globs in diamond operators. (Requiring a meta character within the <>.) 0.72 2007-02-03 - Case-insensitive @INC removal for case-insensitive filesystems (Eric Wilhelm) 0.71 2007-01-04 - Added special cases for Catalyst Class::MakeMethods Config::Any DBIx::Class Email::Send Log::Log4perl SQL::Translator - print() the "# Legend..." line instead of warn()ing it. 0.70 2006-11-21 - Added special case for Image::Info. 0.69 2006-11-07 - Additional corner cases for LWP::UserAgent and LWP::Parallel::UserAgent and friends. 0.68 2006-10-25 - Added special case for PerlIO.pm. If PerlIO.pm is needed, require PerlIO::scalar, too, because it can be used "under the hood". (Roderich Schupp) - Added some File::Spec'ness. (Steffen Mueller) - Refactored the %Preload mapping code into _get_preload so that the PAR -M %Preload fix would work. (Steffen Mueller) 0.67 2006-10-24 - Added @IncludeLibs which is used alongside @INC for searching modules. (David Romano) - Won't pick up Tk as a dependency for Term::ReadLine any more. You can stop laughing now! 0.66 2006-09-24 - Fixed another bug in Module::ScanDeps::Datafeed which would break run- and compile-time dependency scanners if $ENV{PERL5LIB} entries had trailing backslashes. Thanks to Steven Mackenzie for pointing this out. - Added some documentation and comments to M::SD::Datafeed for the sake of future maintainers. 0.65 2006-09-24 - Fixed bug in Module::ScanDeps::Datafeed which would die() in 0.64. 0.64 2006-09-22 - Upgraded to Module::Install 0.64 - Added warning of missing modules when -V is in effect (scandeps.pl). - Added warning of missing modules if "warn_missing=>1" specified as an option to scan_deps. 0.63 2006-08-27 - Upgraded to Module::Install 0.63 0.62 2006-07-16 - Better diagnostics.pm support for searching the related .pod file. 0.61 2006-06-30 - Now presenting more helpful (and correct) error messages when multiple versions of a module (files) are found. - Corrected a POD error. - Added test for POD correctness. 0.60 2006-05-23 - Fixed bug that prevented "use encoding 'utf-8';" from being picked up. This was because the -8 was stripped and thus, the encoding wasn't recognized. 0.59 2006-05-03 - Recovering 5.005 compatibility. (Hopefully!) - Using Module::Install 0.62 - Added a dependency on File::Temp for pre 5.6 perls. - Fixed broken Module::Pluggable support. 0.58 2006-04-16 - Added dependency for Test::Deep - Added dependency for Math::Symbolic 0.57 2006-03-03 - Applied Stephen Schulze's patch which fixes the problem that modules are reported as depended upon only once. 0.56 2006-02-20 - Added special dependency for Tk::Getopt. Suggested by Slaven Rezic. 0.55 2006-02-17 - Applied Roderich Schupp's patch to fix a problem with 'autouse'. - Now using Module::Install 0.56 0.54 2006-01-11 - Switch to File::Temp::tempfile() for more robust temporary file creation. Contributed by: Jesse Vincent - Update to latest Module::Install _again_ to fix Cygwin installation. Reported by: Matt S Trout 0.53 2006-01-10 - Update to latest Module::Install; no functional changes. 0.52 2005-12-12 - Support for autouse.pm. - Support for Tk::DragDrop. Reported by: Renee Baecker. 0.51 2005-01-08 - scandeps.pl is now usable without CPANPLUS.pm installed. Reported by: Rafael Garcia-Suarez 0.50 2004-10-03 - LWP::Authen::* is now bundled with LWP::UserAgent. Reported by: Marcus Rueckert - Properly sign the release with newer EU::MM. 0.49 2004-09-26 - Adds Class::Autouse support, as requested by Adam Kennedy. 0.48 2004-09-07 - Skip auto/ files too if explicitly specified. - Also check for lower-cased keys in %skip, if operating under a case-insensitive file system. 0.47 2004-09-07 - First version under svk management. - Support for Mail::Audit plugins; prompted by Andrew Lee. - Support for modules that use Module::Plugin; prompted by Brian Cassidy. - scandeps.pl now reports module versions, courtesy of Dan Friedman. - Delayed loading of CPANPLUS on scandeps.pl. 0.46 2004-07-02 - Doc fixes; update signature test; add Alan to authors. - add POE heuristics from: http://search.cpan.org/dist/POE/lib/POE/Preprocessor.pm 0.44 2004-06-08 - Consistently recognize .ph files and upper-cased .p[mh] files. - Support for PDF::Writer. - Patfch from Roderich Shupps to fix absolute filename detection on non-Unix systems. 0.43 2004-06-02 - Add preliminary support for BioPerl, as suggested by Nathan Haigh. - Support for Net::SSH::Perl was incorrectly specified. - Add some support for PDF::API2 -- note you still have to explicitly require "PDF::API2::Basic::TTF::Font" to get TrueType support. - add heuristics for Devel::ParallelPort, as reported by Jouke Visser. 0.42 2004-04-30 - add support for DBIx::SearchBuilder and DBIx::ReportBuilder. - oops, typo - add PerlIO.pm to :encoding. 0.41 2004-04-18 - correctly handle SVN::Core, courtesy of Robert Spiers. - handles SVK::Command properly. - add support for Parse::Binary-based modules 0.40 2004-02-23 - Malcolm Nooning noticed that _execute() and _compile() checks were failing under directories that contain spaces, due to a qw() misuse. - Add heuristics for XML::SAX and XML::Parser::Expat, reported by Darek Adamkiewicz and Iain Cass. 0.39 2004-01-25 - Merged Edward's patch to make DataFeed.pm work with pre-5.8.3 perl versions. 0.38 2004-01-08 - Switching back to ExtUtils::MakeMaker, hoping to make ActiveState's cpanrun happy. 0.37 2003-12-31 - Win32 does not take Cwd::abs_path() for filenames. - Detection for __END__ blocks was wrong in _compile(). 0.36 2003-12-31 - sorry, "scandeps.pl -r" should be "-x". 0.35 2003-12-31 - New "-c" and "-r" flags to scandeps.pl for additional compile- and runtime-checking of dependencies. - New "compile" and "execute" flags to scan_deps() for runtime scanning, using scan_deps_runtime(). - integrated Edward S. Peschko's massive runtime detection patch, as scan_deps_runtime(). 0.34 2003-12-30 - changes. 0.33 2003-12-21 - Upgrades to Module::Install 0.30 framework. - Nik's got a CPAN ID. 0.32 2003-10-26 - Support for Locale::Maketext::Guts, reported by Jouke Visser. - Support for XML::Parser, reported by Jan Dubois. - Support for :encoding(), encoding.pm, and encode()/decode(). 0.31 2003-10-17 - Jesse Schoch reports that LWP::Protocol::https is not properly detected. 0.30 2003-09-20 - "use base" was still incorrectly parsed. 0.29 2003-09-17 - Simon Andrews points out that Math::BigInt's heuristics is badly coded. Fixed, and added heuristics for Math::BigFloat. - More defense against hash randomisation by sorting all keys() and values(). 0.28 2003-08-17 - Move ScanDeps.pm to lib/Module/. - Suggestion from Matt Sergeant to recognize A::B from A::B::C only on functions like A::B::C(). - This be 0.27 for real. - "use base" was improperly detected. 0.27 2003-08-16 - more patch from Roderich Schupp: handles "use base" and fixed Tk::Scrolled. - add $SeenTk to control Tk-specific heuristics. - add_deps now takes (skip => \%skip) properly. - scan_chunk() can now return more than one files in list context. - bump version. 0.26 2003-08-11 - add link to http://par.perl.org/ and the mailing list. - don't append ".pm" to require '' lines if it already has an extension. (this is required for Win32API::Registry to work with .pc files.) 0.25 2003-08-10 - tidy up the source a little. - POD and END sections was also scanned. bad. - PAR::read_file() should not imply dependency on PAR.pm. 0.24 2003-08-10 - Add support for SOAP::Lite, XMLRPC::Lite and Win32::SystemInfo. 0.23 2003-08-08 - @File::Spec::ISA was crippled during scanning, thanks to Roderich Schupp for pointing out. 0.22 2003-08-07 - huge patch to include almost all heuristics deducible from PerlApp: Authen::SASL, Crypt::Random, DBI, File::Spec, HTTP::Message, Math::BigInt, MIME::Decoder, Net::DNS::RR, Net::FTP, Net::SSH::Perl, SQL::Parser, Template, Term::ReadLine, URI, XML::Parser::Expat, diagnostics. - now accepts uppercased "DBI:" in DSN strings. - fixed a typo on Tk::FBox's xpm file. 0.21 2003-07-30 - Jouke reports that Win32.pm pulls all Win32::* DLLs. - oops. - scandeps.pl now take -e to scan a perl expression - anydbm implies SDBM. - Bruce Winter says that this fix for SerialJunks is needed on his Red Hat Linux oh well. 0.19 2003-03-22 - Jess Robinson reported that the fix was not -w safe. 0.18 2003-03-20 - added logic for "utf8" and "charnames" needed by Germain Garand. - added logic for "Devel::SerialPort" needed by Bruce Winter. - POSIX.pm no longer pulls in utf8.pm anymore. - .ph files are now fully supported. - take unshift/push @INC into account, too. - add Nik to authors. - Nik Clayton's patch to properly handle 'use lib'. - IO.pm dependencies, courtesy of Jerry Veldhuis. 0.14 2003-01-19 - s/UNIVERSA/UNIVERSAL/; - test explicitly for a hashref for safety. - try to fix D.H.'s bug report about broken 5.6 and pseudohashfications. - add lathos and obra to authors. - mention scandeps.pl earlier in pod. 0.13 2003-01-18 - much more improved scandeps, as suggested by jesse vincent. - add #! for core; explains the symbols. - use cpanplus to tell apart redundant modules if possible. 0.12 2003-01-18 - adds script/scandeps.pl - new year. - add CAVEATS about the fact that we don't probe beyond @INC, as requested by crazyinsomniac. - M::B heuristics. - reflect SEE ALSO in README. 0.10 2002-11-04 - Now featuring an object-oriented syntax, conformant with App::Packer::Frontend. - added corresponding documentation and tests. 0.03 2002-11-03 - add AUTHORS. - last minute fix from merlyn's bug report. - New presets for Locale::Maketext::Lexicon, Term::ReadLine, Regexp::Common, File::Spec, ExtUtils::MakeMaker. - New heuristics for Module::Name->method, Module::Name::sub - Strings in comments were erroneously checked. Fixed. - Mention PerlApp as a source of inspiration. - Regexp::Common. 0.02 2002-11-02 - now performs testing by looking at the test file itself. - displays correct message when connection fails. - backported to 5.001. - was looking in POD sections; fixed. - thorough comments and documentations. - oops, Makefile shouldn't be in RCS. - written-from-scratch version of dependency finding algorithm. Module-ScanDeps-1.20/MANIFEST0000644000372000037200000000413212604223317015666 0ustar roderichroderichAUTHORS Changes inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Scripts.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm lib/Module/ScanDeps.pm lib/Module/ScanDeps/Cache.pm Makefile.PL MANIFEST This list of files META.yml README script/scandeps.pl t/0-pod.t t/1-static_functional_interface_real.t t/10-case-insensitive-keys.t t/12-ScanFileRE.t t/13-static_prefork_test.t t/14-scan_chunk.t t/14-static_functional_cached.t t/16-scan_line.t t/17-private_methods.t t/2-static_functional_interface_fake.t t/3-static_oo_interface_real.t t/4-static_functional_interface_options_fake.t t/5-pluggable_fake.t t/6-file-glob.t t/7-check-dynaloader.t t/8-check_duplicated_entries.t t/9-check_path_to_inc_name.t t/data/case-insensitive-keys/Test.pm t/data/case-insensitive-keys/Test2.pm t/data/case-insensitive-keys/that_case.pl t/data/case-insensitive-keys/this_case.pl t/data/check_path_to_inc_name/Scoped/Package.pm t/data/check_path_to_inc_name/Some.pm t/data/check_path_to_inc_name/use_scoped_package.pl t/data/duplicated_entries/Scoped/Package.pm t/data/duplicated_entries/use_scoped_package.pl t/data/file-glob-no.pl t/data/file-glob-yes.pl t/data/pluggable/Foo.pm t/data/pluggable/Foo/Plugin/Bar.pm t/data/pluggable/Foo/Plugin/Baz.pm t/data/rt90869.pl t/data/ScanFileRE/auto/example/example.h t/data/ScanFileRE/example.pm t/data/ScanFileRE/example_too.pm t/data/static/chicken.pm t/data/static/Duplicated.pm t/data/static/Duplicator.pl t/data/static/egg.pm t/data/static/inner_diamond_E.pm t/data/static/inner_diamond_N.pm t/data/static/inner_diamond_S.pm t/data/static/inner_diamond_W.pm t/data/static/InputA.pl t/data/static/InputB.pl t/data/static/InputC.pl t/data/static/null.pl t/data/static/outer_diamond_E.pm t/data/static/outer_diamond_N.pm t/data/static/outer_diamond_S.pm t/data/static/outer_diamond_W.pm t/data/static/TestA.pm t/data/static/TestB.pm t/data/static/TestC.pm t/data/static/TestD.pm t/data/static/useVERSION.pm t/data/use_lib.pl t/rt90869.t t/Utils.pm wip/scan_dlls.pl Module-ScanDeps-1.20/README0000644000372000037200000000155712604220754015427 0ustar roderichroderichThis is the README file for Module::ScanDeps, a module to recursively scan Perl programs for dependencies. An application of Module::ScanDeps is to generate executables from scripts that contains necessary modules; this module supports two such projects, PAR and App::Packer. Please see their respective documentations on CPAN for further information. * Installation Module::ScanDeps uses the standard perl module install process: cpansign -v # optional; see SIGNATURE for details perl Makefile.PL make # or 'nmake' on Win32 make test make install * Copyright Copyright 2002-2008 by Audrey Tang ; 2005-2009 by Steffen Mueller . All rights reserved. You can redistribute and/or modify this bundle under the same terms as Perl itself. See . Module-ScanDeps-1.20/wip/0000755000372000037200000000000012604223352015333 5ustar roderichroderichModule-ScanDeps-1.20/wip/scan_dlls.pl0000644000372000037200000001574712604220754017653 0ustar roderichroderich#!/usr/bin/perl # recursively find NEEDED (in the ELF sense) shared libraries # for a given share library or for all installed Perl "glue" libraries use strict; use warnings; use File::Spec; use File::Find; use File::Basename; package DLL { use strict; use warnings; use Capture::Tiny qw(:all); our ($show_system_libs, $show_perl_libs); # default: don't show my @dll_path = File::Spec->path; # Windows # my @dll_path = qw(/lib /lib/x86_64-linux-gnu /usr/lib /usr/lib/x86_64-linux-gnu); # + $ENV{LD_LIBRARY_PATH} if set # Linux (Debian multi-arch) # maybe use "gcc -print-search-dirs" (pathnames may need canonicalization) # install: /usr/lib/gcc/x86_64-linux-gnu/4.9/ # programs: =/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/bin/ # libraries: =/usr/lib/gcc/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/4.9/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib/:/lib/x86_64-linux-gnu/4.9/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/4.9/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/lib/:/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../:/lib/:/usr/lib/ require Tie::CPHash; tie my %cache, "Tie::CPHash"; sub name { shift->{name} } sub path { shift->{path} } sub find # class method { my ($class, $name) = @_; unless ($cache{$name}) { my $found; foreach (@dll_path) { my $path = File::Spec->catfile($_, $name); $found = $path, last if -e $path; } $cache{$name} = bless { name => $name, path => $found, }, $class; } return $cache{$name}; } sub needed { my ($self, $path) = @_; if (ref $self) { return @{ $self->{needed} } if $self->{needed}; $path = $self->{path}; die "can't find DLL $self->{name}" unless defined $path; } else { die __PACKAGE__."->needed: argument PATH missing" unless defined $path; } my ($out, $err, $exit) = capture { system(qw( objdump -ax ), $path) }; die qq["objdump -ax $path" failed: $err] unless $exit == 0; my @needed = map { __PACKAGE__->find($_) } $out =~ /^\s*DLL Name:\s*(\S+)/gm; # Windows # $out =~ /^\s*NEEDED\s+(\S+)/gm; # Linux $self->{needed} = \@needed if ref $self; return @needed; } sub depends { my ($self, $path) = @_; if (ref $self) { $path = $self->{path}; die "can't find DLL $self->{name}" unless defined $path; } else { die __PACKAGE__."->depends argument PATH missing" unless defined $path; } tie my %seen, "Tie::CPHash"; $seen{$self->name} = $self if ref $self; _depends(\%seen, $self->needed($path)); return values %seen; } sub _depends { my ($seen, @needed) = @_; foreach (@needed) { next if $seen->{$_->name}; if (defined $_->path) { next if $_->is_system_lib && !$show_system_libs; next if $_->is_perl_lib && !$show_perl_libs; } $seen->{$_->name} = $_; _depends($seen, $_->needed) if defined $_->path; } } sub canon_path { my ($self) = @_; return unless defined $_->path; return $_->{canon_path} ||= _canon_path($_->path); } sub _canon_path { my ($path, $no_file) = @_; my ($vol, $dirs, $file) = File::Spec->splitpath($path, $no_file); $dirs =~ s{[/\\]$}{}; my $foo = join("/", $vol, File::Spec->splitdir($dirs), $file); return lc $foo; } my $system_root = _canon_path($ENV{SystemRoot}, 1); sub is_system_lib { my ($self) = @_; my $canon_path = $_->canon_path or return; return length $canon_path > length $system_root && substr($canon_path, 0, length $system_root) eq $system_root; } tie my %perl_libs, "Tie::CPHash"; { local $show_system_libs = 0; local $show_perl_libs = 1; $perl_libs{$_->name} = $_ foreach __PACKAGE__->depends($^X); }; sub is_perl_lib { $perl_libs{shift->name} ? 1 : 0 } } # return a list of installed (ie. found below some directory in @INC) glue DLLs sub find_all_installed_glue_dlls { my @dlls; find(sub { push @dlls, $File::Find::name if /\.dll/i; }, grep { my $auto; !ref $_ && -d ($auto = File::Spec->catdir($_, "auto")) ? $auto : () } @INC); return @dlls; } # guess the Perl module from the pathname of a glue DLL sub guess_module_from_glue_dll { my ($path) = @_; # module Foo::Bar::Quux typically installs its glue DLL as # .../auto/Foo/Bar/Quux/Quux.dll or # .../auto/Foo/Bar/Quux/Quux.xs.dll my ($vol, $dirs, $file) = File::Spec->splitpath($path); $dirs =~ s{[/\\]$}{}; $dirs =~ s{^(?:.*?[/\\])?auto[/\\]}{} or warn(qq[DLL "$path": path doesn't contain "auto"\n]), return; return join("::", File::Spec->splitdir($dirs)); } my $show_lib_path = 0; sub show_lib { my ($dll) = @_; if ($show_lib_path) { printf "\t%s => %s\n", $dll->name, $dll->path || "(not found)"; } else { printf "\t%s\n", $dll->name; } } if (@ARGV) { foreach (@ARGV) { print $_, "\n"; show_lib($_) foreach DLL->depends($_); } } else { my %mod2dll; my @non_mod_dlls; foreach (find_all_installed_glue_dlls()) { my $mod = guess_module_from_glue_dll($_); push(@non_mod_dlls, $_), next unless $mod; $mod2dll{$mod} = $_; } foreach my $mod (sort keys %mod2dll) { my $dll = $mod2dll{$mod}; my @deps = DLL->depends($dll) or next; # suppress glue DLLs w/o dependencies print "$mod ($dll)\n"; show_lib($_) foreach @deps; } print "\n"; foreach my $dll (sort @non_mod_dlls) { print "$dll\n"; show_lib($_) foreach DLL->depends($dll); } } Module-ScanDeps-1.20/AUTHORS0000644000372000037200000000323112604220754015606 0ustar roderichroderichHere is a list of people and their CPAN id, extracted from the ChangeLog file and the mailing list archives. These people have either submitted patches or suggestions, or their bug reports or comments have inspired the appropriate patches. Corrections, additions, deletions welcome: Adam Kennedy (ADAMK) Adrian Issott Alan Stewart Alexandr Ciornii (CHORNY) Andrew Lee Brian Cassidy (BRICAS) Bruce Winter Chris Dolan (CDOLAN,CLOTHO) Christoph Lamprecht (LAMPRECHT) D. H. (PODMASTER) Dan Friedman (LAMECH) Darek Adamkiewicz (DADAMK) David Romano Dominique Quatravaux (DOMQ) Edward S. Peschko Eric J. Roode (ROODE) Eric Wilhelm Germain Garand (GGARAND) Iain Cass Indy Singh Jan Dubois (JDB) Jerry Veldhuis Jess Robinson Jesse Schoch Jesse Vincent (JESSE) Johan Vromans Jouke Visser (JOUKE) Malcolm Nooning Marcus Rueckert Mark Stosberg (MARKSTOS) Markus Jansen Matt S Trout (MSTROUT) Matt Sergeant (MSERGEANT) Nadim Ibn Hamouda El Khemir (NKH) Nathan Haigh Nik Clayton (NIKC) Rafael Garcia-Suarez (RGARCIA) Randal L. Schwartz (MERLYN) Renee Baecker (RENEEB) Robert Spier (RSPIER) Roderich Schupp (RSCHUPP) Scott Stanton Seth L. Blumberg (METAL) Simon Andrews Simon Cozens (SIMON) Slaven Rezic (SREZIC) Steffen Mueller (SMUELLER) Stephen Schulze Steve Pick Steven Mackenzie Module-ScanDeps-1.20/META.yml0000644000372000037200000000150112604223222015776 0ustar roderichroderich--- abstract: 'Recursively scan Perl code for dependencies' author: - 'Audrey Tang ' build_requires: ExtUtils::MakeMaker: 6.59 Test::More: 0 Test::Requires: 0 configure_requires: ExtUtils::MakeMaker: 6.59 distribution_type: module dynamic_config: 1 generated_by: 'Module::Install version 1.16' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Module-ScanDeps no_index: directory: - inc - t package: - Module::ScanDeps::Cache - Module::ScanDeps::DataFeed requires: File::Spec: 0 File::Temp: 0 Getopt::Long: 0 Module::Metadata: 0 Text::ParseWords: 0 perl: 5.8.1 version: 0 resources: license: http://dev.perl.org/licenses/ repository: https://www.openfoundry.org/svn/par/Module-ScanDeps/trunk version: '1.20'