Hello,

if anyone is interested, here is what I am testing at the moment. Added cache_cb to M::SD as described before and using it from PAR-Packer. PAR-Packer gets a cd|cachedeps (filename) option.
I use it like 'pp -cd deps.dat -more_options  script.pl' or
'pp -cd deps.dat -o test.exe -e "use Tk; tkinit; MainLoop;" 'Works fine for me.
Thanks you for any comments or suggestions.

Cheers Christoph
--- C:\DOKUME~1\chris\LOKALE~1\Temp\ScanDeps.pm-revBASE.svn001.tmp.pm   Fr Jul  
3 16:08:10 2009
+++ C:\devel\M-SD_wc\trunk\lib\Module\ScanDeps.pm       Fr Jul  3 13:27:10 2009
@@ -2,7 +2,8 @@
 use 5.006;
 use strict;
 use vars qw( $VERSION @EXPORT @EXPORT_OK @ISA $CurrentPackage @IncludeLibs 
$ScanFileRE );
-
+warn "testing cached M::SD\n";
+use warnings;
 $VERSION   = '0.91';
 @EXPORT    = qw( scan_deps scan_deps_runtime );
 @EXPORT_OK = qw( scan_line scan_chunk add_deps scan_deps_runtime 
path_to_inc_name );
@@ -499,7 +500,7 @@
     return $inc_name;
 }
 
-my $Keys = 'files|keys|recurse|rv|skip|first|execute|compile|warn_missing';
+my $Keys = 
'files|keys|recurse|rv|skip|first|execute|compile|warn_missing|cache_cb';
 sub scan_deps {
     my %args = (
         rv => {},
@@ -560,8 +561,12 @@
 
 sub scan_deps_static {
     my ($args) = @_;
-    my ($files, $keys, $recurse, $rv, $skip, $first, $execute, $compile, 
$_skip) =
-        @$args{qw( files keys recurse rv skip first execute compile _skip )};
+    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 || {}} };
@@ -573,66 +578,62 @@
           and $file ne lc($file) and $_skip->{lc($file)}++;
         next unless $file =~ $ScanFileRE;
 
-        local *FH;
-        open FH, $file or die "Cannot open $file: $!";
+        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},
+                 );
 
-        $SeenTk = 0;
-        # Line-by-line scanning
-        LINE:
-        while (<FH>) {
-            chomp(my $line = $_);
-            foreach my $pm (scan_line($line)) {
-                last LINE if $pm eq '__END__';
+            my $preload = _get_preload($pm) or next;
 
-                # 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/;
-                }
-
-                if ($pm eq '__POD__') {
-                    while (<FH>) { last if (/^=cut/) }
-                    next LINE;
-                }
-
-                $pm = 'CGI/Apache.pm' if $file =~ /^Apache(?:\.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},
-                );
-            }
+            add_deps(
+                     used_by => $key,
+                     rv      => $args->{rv},
+                     modules => $preload,
+                     skip    => $args->{skip},
+                     warn_missing => $args->{warn_missing},
+                 );
         }
-        close FH;
-
-        # }}}
     }
 
     # Top-level recursion handling {{{
+   
     while ($recurse) {
         my $count = keys %$rv;
         my @files = sort grep -T $_->{file}, values %$rv;
         scan_deps_static({
-            files   => [ map $_->{file}, @files ],
-            keys    => [ map $_->{key},  @files ],
-            rv      => $rv,
-            skip    => $skip,
-            recurse => 0,
-            _skip   => $_skip,
+            files    => [ map $_->{file}, @files ],
+            keys     => [ map $_->{key},  @files ],
+            rv       => $rv,
+            skip     => $skip,
+            recurse  => 0,
+            cache_cb => $cache_cb, 
+            _skip    => $_skip,
         }) or ($args->{_deep} and return);
         last if $count == keys %$rv;
     }
@@ -689,6 +690,43 @@
     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__';
+
+            # 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/;
+            }
+            if ($pm eq '__POD__') {
+                while (<$FH>) {
+                    last if (/^=cut/);
+                }
+                next LINE;
+            }
+            $SeenTk || do{$SeenTk = 1 if $pm =~ /Tk\.pm$/;};
+            # the following line does not make much sense here ???
+            # $file is an absolute path and will never match
+            #$pm = 'CGI/Apache.pm' if $file =~ /^Apache(?:\.pm)$/;
+            $found{$pm}++;
+        }
+    }
+    close $FH or die "Cannot close $file: $!";
+    return keys %found;
+}
+
 sub scan_line {
     my $line = shift;
     my %found;
@@ -708,7 +746,8 @@
         # use VERSION:
         if (/^\s*(?:use|require)\s+([\d\._]+)/) {
           # include feaure.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)
+          if (version->new($1) >= version->new("5.9.5")) {
+              # seems to catch 5.9, too (but not 5.9.4)
             return "feature.pm";
           }
         }
@@ -881,6 +920,7 @@
             }
         }
     }
+
     $rv->{$module} ||= {
         file => $file,
         key  => $module,
@@ -939,6 +979,7 @@
                 $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  => 
"auto/$path/$_->{name}",
                            file   => $_->{file}, used_by => $module,
                            type   => $type );
--- C:\DOKUME~1\chris\LOKALE~1\Temp\Packer.pm-revBASE.svn000.tmp.pm     Fr Jul  
3 16:05:38 2009
+++ C:\devel\PAR-Packer_wc\trunk\lib\PAR\Packer.pm      Do Jul  2 22:13:45 2009
@@ -35,7 +35,9 @@
 use Module::ScanDeps ();
 use PAR ();
 use PAR::Filter ();
-
+use Storable;
+use Digest::MD5;
+use Carp;
 use constant OPTIONS => {
     'a|addfile:s@'   => 'Additional files to pack',
     'A|addlist:s@'   => 'File containing list of additional files to pack',
@@ -69,6 +71,7 @@
     'vv|verbose2',   => 'Verbosity level 2',
     'vvv|verbose3',  => 'Verbosity level 3',
     'z|compress:i'   => 'Compression level',
+    'cd|cachedeps:s' => 'Cache detected dependencies in a file',
 };
 
 my $ValidOptions = {};
@@ -657,7 +660,14 @@
     my $root = '';
     $root = "$Config{archname}/" if ($opt->{m});
     $self->{pack_attrib}{root} = '';
-
+    my $cachefile = '';
+    my $use_cache = 0;
+    my @cache_cb;
+    $cachefile = $opt->{cachedeps} if ($opt->{cachedeps});
+    if ($cachefile){
+        _init_cache($cachefile);
+        @cache_cb = (cache_cb => \&_cache_cb);
+    }
     my $par_file = $self->{par_file};
     my (@modules, @data, @exclude);
 
@@ -726,12 +736,14 @@
       ? $self->_obj_function($fe, 'scan_deps_runtime')
       : $self->_obj_function($fe, 'scan_deps');
 
+
     $scan_dispatch->(
-        rv      => \%map,
-        files   => \...@files,
-        execute => $opt->{x},
-        compile => $opt->{c},
-        skip    => \%skip,
+                     rv      => \%map,
+                     files   => \...@files,
+                     execute => $opt->{x},
+                     compile => $opt->{c},
+                     skip    => \%skip,
+                     @cache_cb,
         ($opt->{n}) ? () : (
             recurse => 1,
             first   => 1,
@@ -873,7 +885,9 @@
 
     $dep_manifest->{'MANIFEST'} = [ string => "<<placeholder>>" ];
     $dep_manifest->{'META.yml'} = [ string => "<<placeholder>>" ];
-
+    if ($cachefile){
+        _store_cache($cachefile);
+    }
     return ($dep_manifest);
 }
 
@@ -1650,6 +1664,70 @@
     unlink $self->{parl} if $self->{parl_is_temporary};
 }
 
+{
+my $cache;
+sub _init_cache{
+    my $file = shift;
+    eval{$cache = retrieve($file)};
+    warn $@ if ($@);
+    unless ($cache){
+        warn "Couldn't retrieve data from file $file. Building new cache.";
+        $cache = {};
+    }
+}
+
+sub _store_cache{
+    my $file = shift;
+    Storable::nstore($cache, $file)
+          or warn "Could not store cache to file $file!";
+}
+
+sub _cache_cb{
+    my %args = @_;
+    if ( $args{action} eq 'read' ){
+        return _read_cache( %args );
+    }
+    elsif ( $args{action} eq 'write' ){
+        return _write_cache( %args );
+    }
+    croak "action must be read or write\n";
+}
+
+sub _read_cache{
+    my %args = @_;
+    my ($key, $file, $mod_aref) = @args{qw/key file modules/};
+    return 0 unless (exists $cache->{$key});
+    ### we have an entry - check MD5
+    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];
+    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;
 
 =head1 SEE ALSO

Reply via email to