Make::Cache::Hash - Dependency file functions and hashing


Make-Cache documentation Contained in the Make-Cache distribution.

Index


Code Index:

NAME

Top

Make::Cache::Hash - Dependency file functions and hashing

SYNOPSIS

Top

Make::Cache::Hash::clear_cache();

hash{tgt}{dep} = Make::Cache::Hash::dfile_read(filename=>fn);

my $digest = Make::Cache::Hash::hash(filenames=>[], text=>[]);

DESCRIPTION

Top

Make::Cache::Hash contains functions for reading and writing make.d files, and for doing MD5 hashes on files.

FUNCTIONS

Top

dfile_read (filename=>in)

Read the specified filename. Return a hash reference, where the keys of the hash are the target (generated output) files, and the values are a hash of the dependent (required input) files. The filename itself is considered a output dependency.

hash

Return a MD5 hash on the specified list of filenames, and specified list of text. With the ignore_rcs parameter, which defaults as on, ignore any RCS/CVS/Perforce meta tags in the source.

clear_hash

Clear the internal cache used to accelerate the hash() function. Must be called anytime a file that has been hashed has changed.

newest

Return the mod time of the newest filename in the list of filenames passed. If any does not exist, return undef.

DISTRIBUTION

Top

The latest version is available from CPAN and from http://www.veripool.org/.

Copyright 2000-2010 by Wilson Snyder. This package is free software; you can redistribute it and/or modify it under the terms of either the GNU Lesser General Public License Version 3 or the Perl Artistic License Version 2.0.

AUTHORS

Top

Wilson Snyder <wsnyder@wsnyder.org>

SEE ALSO

Top

objcache, Make::Cache


Make-Cache documentation Contained in the Make-Cache distribution.

# See copyright, etc in below POD section.
######################################################################

package Make::Cache::Hash;
use IO::File;
use Digest::MD5;
use Carp;

use strict;
use vars qw ($Debug);

our $VERSION = '1.052';

#######################################################################

#######################################################################
# Dependency reading

sub dfile_read {
    my %params = (#filename=>"",	# File to be read
		  @_);
    # Read a dependency (.d) file into %Deps hash
    # Return hash reference of {targets}{depends} = 1
    # Mostly from SystemC::sp_makecheck

    $params{filename} or carp "Must specify filename=>,";
    my $fh = IO::File->new($params{filename}) or die "$0: %Error: $! depending $params{filename}\n";
    my $line = "";

    my %all_deps;
    while (defined (my $thisline = $fh->getline())) {
	chomp $thisline;
	$line .= $thisline;
	next if ($line =~ s/\\s*$/ /);
	next if ($line =~ /^\s*$/);
	if ($line =~ /^([^:]+):([^:]*)$/) {
	    my $tgtl = $1;  my $depl = $2;
	    my @tgts = ($params{filename});	# Imply the .d is a target
	    foreach my $tgt (split /\s+/,"$tgtl ") {
		next if $tgt eq "";
		push @tgts, $tgt;
	    }
	    foreach my $dep (split /\s+/,"$depl ") {
		next if $dep eq "";
		foreach my $tgt (@tgts) {
		    $all_deps{$tgt}{$dep} = 1;
		}
	    }
	} else {
	    die "$0: %Error: $params{filename}:$.: Strange dependency line: $line\n";
	}
	$line = "";
    }
    $fh->close;
    return (\%all_deps);
}

#######################################################################
# Hashing

sub hash {
    my %params = (filenames => [],	# Array ref of files to be hashed
		  text => [],		# Array ref of constant text to be hashed
		  defines => {},	# Hash ref of defines to search for and mark defined
		  ignore_rcs => 1,	# Ignore $ Id  comments in the text
		  @_);
    # Hash the given text and files, return the hash
    # If file doesn't exist or other error, warn and return undef.

    my $md5 = Digest::MD5->new;
    # We sort and eliminate duplicate filenames, so ordering won't result in differing hashes
    my %didfn;
    foreach my $depfile (sort @{$params{filenames}}) {
	next if $didfn{$depfile};
	$didfn{$depfile} = 1;

	my $filedigest = _hash_a_file(\%params, $depfile);
	return undef if !$filedigest;
	$md5->add($filedigest);
	$params{subhashes}{$depfile} = $filedigest if $Debug;
    }

    # We DON'T sort the text, order presumed to matter
    foreach my $text (@{$params{text}}) {
	$md5->add($text);
	$params{subhashes}{$text} = 'added' if $Debug;
    }

    my $digest = $md5->hexdigest;
    if ($Debug) {
	print "MD $md5\n";
	use Data::Dumper;
	print "Make::Cache::Hash $digest <--\n";
	print Dumper(\%params),"\n";
    }
    # Mystery: Passing a $md5 object to the caller resets the hash!
    return $digest;
}

use vars qw (%Hash_Cache);
sub clear_cache {
    %Hash_Cache = ();
}
sub _hash_a_file {
    my $self = shift;
    my $filename = shift;
    if ($Hash_Cache{$filename}) {
	print "HASHIT_Hit $filename\n" if $Debug;
	my $cache_ent = $Hash_Cache{$filename};
	foreach my $def (keys %{$self->{defines}}) {
	    (defined $cache_ent->{defines}{$def})
		or die "%Error: Badly cached $filename for define $def,";
	    if ($cache_ent->{defines}{$def}) {
		$self->{defines}{$def} = 1;  # We never reset it; any usage in any file is a "usage"
	    }
	}
	return $cache_ent->{digest};
    }
    print "HASHIT_New       $filename\n" if $Debug;

    my $md5 = Digest::MD5->new;
    my $fh = IO::File->new($filename);
    if (!$fh) {
	warn "$0: -Info: $! hashing $filename\n" if $Debug;
	return undef;
    }

    # Read the file.  Flwoop in one big chunk.
    local $/; undef $/;
    my $wholefile = <$fh>;
    #print "HASH_A_FILE $filename  >>>>>>>>\n$wholefile<<<<<<<\n";
    if ($self->{ignore_rcs}) {
	$wholefile =~ s/(\$ (I d | R evision | D ate | A uthor | H eader ) :)[^\n\"\$]+\$/$1/gx;
    }
    $md5->add($wholefile);
    $fh->close();

    my $cache_ent = {};
    foreach my $def (keys %{$self->{defines}}) {
	if ($wholefile =~ m/$def/m) {
	    $self->{defines}{$def} = 1;
	    $cache_ent->{defines}{$def} = 1;
	    #print "FOUNDDEF!!!!! $filename !!! '$def'\n" if $Debug
	} else {
	    $cache_ent->{defines}{$def} = 0;
	    #print "NoDEF         $filename !!! '$def'\n" if $Debug
	}
    }

    $cache_ent->{digest} = $md5->hexdigest;
    $Hash_Cache{$filename} = $cache_ent;
    print "\tDIGEST $filename\t$cache_ent->{digest}\n" if $Debug;
    return $cache_ent->{digest};
}

#######################################################################
# Modtimes

sub newest {
    my %params = (filenames => [],	# Array ref of files to be dated
		  @_);
    # Return the most recent modtime of the list of files
    # If one of them doesn't exist, return undef

    my $newest = undef;
    my $filename = shift;
    foreach my $depfile (@{$params{filenames}}) {
	my $omtime = (stat($depfile))[9];
	if (!defined $omtime) {
	    $newest = $filename = undef;
	    last;
	}
	if (!defined $newest || $omtime>$newest) {
	    $newest = $omtime;
	    $filename = $depfile;
	}
    }
    return ($newest, $filename) if wantarray;
    return $newest;
}

#######################################################################
1;
__END__

######################################################################