ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/Cache/Cache.pm
Revision: 1.14
Committed: Tue Oct 18 14:59:28 2011 UTC (13 years, 6 months ago) by muzaffar
Content type: text/plain
Branch: MAIN
CVS Tags: V2_2_5_pre2, V2_2_5_pre1, V2_2_4, V2_2_4_pre9, V2_2_4_pre8, V2_2_4_pre7, V2_2_4_pre6, V2_2_4_pre5, V2_2_4_pre4, V2_2_4_pre3, V2_2_4_pre2, V2_2_4_pre1, HEAD
Changes since 1.13: +0 -3 lines
Log Message:
removed cvs $id statement

File Contents

# User Rev Content
1 sashby 1.2 #____________________________________________________________________
2     # File: Cache.pm
3     #____________________________________________________________________
4     #
5     # Author: Shaun Ashby <Shaun.Ashby@cern.ch>
6     # (with contribution from Lassi.Tuura@cern.ch)
7     # Copyright: 2003 (C) Shaun Ashby
8     #
9     #--------------------------------------------------------------------
10 sashby 1.5
11     =head1 NAME
12    
13     Cache::Cache - A generic directory cache object.
14    
15     =head1 SYNOPSIS
16 sashby 1.6
17     my $cacheobject=Cache::Cache->new();
18 sashby 1.5
19     =head1 DESCRIPTION
20    
21     A package to provide caching of directory information. Directory timestamps
22     are tracked on further reading of an existing cache and lists of modified
23     directories and BuildFiles can be obtained.
24    
25     =head1 METHODS
26    
27     =over
28    
29     =cut
30    
31 sashby 1.2 package Cache::Cache;
32     require 5.004;
33    
34     use Exporter;
35 muzaffar 1.9 use Utilities::AddDir;
36 sashby 1.2 @ISA=qw(Exporter);
37     #
38 sashby 1.5
39     =item C<new()>
40    
41 muzaffar 1.11 Create a new Cache::Cache object. The name of the cache is B<DirCache.db.gz> by default.
42 sashby 1.5
43     =cut
44    
45 sashby 1.2 sub new()
46     ###############################################################
47     # new #
48     ###############################################################
49     # modified : Thu Nov 27 16:45:27 2003 / SFA #
50     # params : #
51     # : #
52     # function : #
53     # : #
54     ###############################################################
55     {
56     my $proto=shift;
57     my $class=ref($proto) || $proto;
58     my $self=
59     {
60 muzaffar 1.11 CACHENAME => "DirCache.db.gz", # Name of global file/dir cache;
61 sashby 1.2 BFCACHE => {}, # BuildFile cache;
62     DIRCACHE => {}, # Source code cache;
63 muzaffar 1.9 EXTRASUFFIX => {}, # path with extra suffix;
64 sashby 1.2 STATUS => 0, # Status of cache: 1 => something changed. If so, force save;
65     VERBOSE => 0 # Verbose mode (0/1);
66     };
67    
68     bless $self,$class;
69     return $self;
70     }
71    
72 sashby 1.5 =item C<getdir($path)>
73    
74 sashby 1.6 Return a list of directories starting from $path.
75 sashby 1.5
76     =cut
77    
78 sashby 1.2 sub getdir()
79     {
80     my $self=shift;
81 muzaffar 1.9 my $path=shift;
82 muzaffar 1.12 my $ignore=shift || 'CVS|\\..*';
83     my $match=shift || ".+";
84 muzaffar 1.9
85 sashby 1.2 opendir (DIR, $path) || die "$path: cannot read: $!\n";
86 sashby 1.4 # Skip .admin and CVS subdirectories too.
87 sashby 1.3 # Also skip files that look like backup files or files being modified with emacs:
88 muzaffar 1.12 my @items = map { "$path/$_" } grep ((-d "$path/$_") && ($_=~/^($match)$/) && ($_!~/^($ignore)$/),readdir(DIR));
89 sashby 1.2 closedir (DIR);
90     return @items;
91     }
92    
93 sashby 1.6 =item C<prune($path)>
94    
95     Recursively remove directories from the cache starting at $path.
96    
97     =cut
98    
99 sashby 1.2 sub prune()
100     {
101     my $self=shift;
102 muzaffar 1.9 my $path = shift;
103     my $skipparent = shift || 0;
104     my $suffix = shift || "";
105     $self->extra_suffix($path,$suffix) if ($suffix);
106     if (!$skipparent)
107     {
108     my $parent = $path;
109     $parent =~ s|(.*)/[^/]+$|$1|;
110     if ($parent ne $path && exists $self->{DIRCACHE}{$parent})
111     {
112     my ($time, @subs) = @{$self->{DIRCACHE}{$parent}};
113     $self->{DIRCACHE}{$parent} = [ $time, grep ($_ ne $path, @subs) ];
114     $self->{ADDEDDIR}{$parent}=1;
115     $self->cachestatus(1);
116     }
117     }
118     if (exists $self->{ADDEDDIR}{$path}){delete $self->{ADDEDDIR}{$path};}
119     return if ! exists $self->{DIRCACHE}{$path};
120 sashby 1.2 $self->cachestatus(1);
121 muzaffar 1.9 foreach my $bf ("$ENV{SCRAM_BUILDFILE}.xml","$ENV{SCRAM_BUILDFILE}")
122     {
123     if (exists $self->{BFCACHE}{"${path}/${bf}"})
124     {
125     if (!-f "${path}/${bf}") {$self->{REMOVEDBF}{"${path}/${bf}"}=1;}
126     delete $self->{BFCACHE}{"${path}/${bf}"};
127     if (exists $self->{ADDEDBF}{"${path}/${bf}"}){delete $self->{ADDEDBF}{"${path}/${bf}"};}
128     last;
129     }
130     }
131     if (!-d $path) {$self->{REMOVEDDIR}{$path}=1;}
132     my (undef, @subs) = @{$self->{DIRCACHE}{$path}};
133     delete $self->{DIRCACHE}{$path};
134 sashby 1.2 foreach my $sub (@subs)
135     {
136 muzaffar 1.9 $self->prune($sub,1);
137 sashby 1.2 }
138     }
139    
140 sashby 1.6 =item C<checktree($path, $required, $dofiles)>
141    
142     A timestamp checking routine. Starting from $path, check all timestamps of
143     directories and their files. Skip all files unless $dofiles is 1.
144    
145     =cut
146    
147 sashby 1.2 sub checktree()
148     {
149 muzaffar 1.9 my ($self, $path, $required) = @_;
150 sashby 1.2 # Check if this path needs to be checked. If it exists, has the same mode
151     # and the same time stamp, it's up to date and doesn't need to be checked.
152     # Otherwise if it is a directory whose time-stamp has changed, rescan it.
153     # If the path has be removed, prune it from the cache. Note that we skip
154     # non-directories unless $dofiles is set. Considering only directories is
155     # dramatically faster.
156    
157     # NB: We stat each path only once ever. The special "_" file handle uses
158     # the results from the last stat we've made. See man perlfunc/stat.
159     if (! stat($path))
160     {
161     die "$path: $!\n" if $required;
162     $self->prune($path);
163     return;
164     }
165    
166     # If the entry in the cache is not the same mode or time, force an update.
167     # Otherwise use the cache as the list of items we need to change.
168 muzaffar 1.9 my $cached = $self->{DIRCACHE}{$path};
169 sashby 1.2 my @items = ();
170 muzaffar 1.13 my $matchdir='[a-zA-Z0-9][a-zA-Z0-9-_]*';
171 sashby 1.2
172     if (! -d _)
173     {
174 muzaffar 1.9 $self->prune($path);
175     return;
176 sashby 1.2 }
177 muzaffar 1.9 elsif (! $cached)
178 sashby 1.2 {
179     # When a directory is added, this block is activated
180 muzaffar 1.9 $self->{ADDEDDIR}{$path}=1;
181 muzaffar 1.12 $self->{DIRCACHE}{$path} = [ (stat(_))[9], @items = $self->getdir($path,'',$matchdir) ];
182 sashby 1.2 $required = 1;
183     $self->cachestatus(1);
184     }
185 muzaffar 1.9 elsif ($cached->[0] != (stat(_))[9])
186 sashby 1.2 {
187 muzaffar 1.9 my $ntime = (stat(_))[9];
188 sashby 1.2 # When a subdirectory is removed, this block is activated
189     #
190     # This is a parent directory. We store this as any
191     # update can be taken recursively from this dir:
192 muzaffar 1.9 #$self->modified_parentdirs($path);
193 sashby 1.2 # Current subdirs:
194 muzaffar 1.12 my %curdirs = map { $_ => 1 } $self->getdir($path,'',$matchdir);
195 muzaffar 1.9 my %olddirs = ();
196     for (my $i = 1; $i <= $#$cached; $i++)
197     {
198     my $d = $cached->[$i];
199     $olddirs{$d}=1;
200     if (!exists $curdirs{$d})
201     {
202     $self->prune($d,1);
203     }
204     }
205 sashby 1.2
206 muzaffar 1.9 foreach my $d (keys %curdirs)
207     {
208     if (!exists $olddirs{$d})
209 sashby 1.2 {
210 muzaffar 1.9 if ($self->extra_suffix($d))
211     {
212     delete $curdirs{$d};
213     }
214 sashby 1.2 }
215 muzaffar 1.9 }
216    
217     $self->{ADDEDDIR}{$path}=1;
218 sashby 1.2 $self->cachestatus(1);
219 muzaffar 1.9 @items = keys %curdirs;
220     $required = 0;
221     $self->{DIRCACHE}{$path} = [ $ntime, @items ];
222 sashby 1.2 }
223     else
224     {
225 muzaffar 1.9 (undef, @items) = @{$self->{DIRCACHE}{$path}};
226 sashby 1.2 $required = 0;
227     }
228 muzaffar 1.9 if (($self->{cachereset}) && (!exists $self->{ADDEDDIR}{$path}))
229     {
230     $self->{ADDEDDIR}{$path}=1;
231     $self->cachestatus(1);
232     }
233 sashby 1.2
234 muzaffar 1.9 my $bfcachedir=$ENV{LOCALTOP}."/".$ENV{SCRAM_TMP}."/".$ENV{SCRAM_ARCH}."/cache/bf/${path}";
235 muzaffar 1.11 my $cbf="${bfcachedir}/$ENV{SCRAM_BUILDFILE}";
236 muzaffar 1.9 my $bftime=0;
237     my $bf="${path}/$ENV{SCRAM_BUILDFILE}";
238     foreach my $ext (".xml","")
239     {
240     my $bfn="$bf$ext";
241     if (! stat ($bfn))
242     {
243     if (exists $self->{BFCACHE}{$bfn})
244     {
245     $self->{REMOVEDBF}{$bfn}=1;
246     delete $self->{BFCACHE}{$bfn};
247 muzaffar 1.11 Utilities::AddDir::adddir($bfcachedir);
248     open(BF,">${cbf}");close(BF);
249 muzaffar 1.9 $self->cachestatus(1);
250     }
251     }
252     else
253     {
254     $bftime = (stat(_))[9];
255     if ((! exists $self->{BFCACHE}{$bfn}) ||
256     ($bftime != $self->{BFCACHE}{$bfn}))
257     {
258 muzaffar 1.11 if ((!-f "${cbf}") || (exists $self->{BFCACHE}{$bfn}))
259     {
260     Utilities::AddDir::adddir($bfcachedir);
261     open(BF,">${cbf}");close(BF);
262     }
263     $self->{ADDEDBF}{$bfn}=1;
264     delete $self->{BFCACHE}{$bf};
265 muzaffar 1.9 $self->{BFCACHE}{$bfn}=$bftime;
266     if ($ext eq ""){$self->{nonxml}+=1;}
267     $self->cachestatus(1);
268     }
269     elsif($self->{cachereset})
270     {
271     $self->{ADDEDBF}{$bfn}=1;
272     if ($ext eq ""){$self->{nonxml}+=1;}
273 muzaffar 1.11 if (!-f "${cbf}")
274 muzaffar 1.9 {
275 muzaffar 1.11 Utilities::AddDir::adddir($bfcachedir);
276     open(BF,">${cbf}");close(BF);
277 muzaffar 1.9 }
278     $self->cachestatus(1);
279     }
280     last;
281     }
282     }
283 muzaffar 1.11 if (exists $self->{ExtraDirCache})
284     {
285     eval {$self->{ExtraDirCache}->DirCache($self,$path);};
286     }
287 sashby 1.2 # Process sub-directories
288     foreach my $item (@items)
289     {
290 muzaffar 1.9 $self->checktree($item, $required);
291 sashby 1.2 }
292     }
293    
294 sashby 1.6 =item C<clean_cache_recursive($startdir)>
295    
296     Recursive remove cached data for directories under $startdir.
297    
298     =cut
299    
300 sashby 1.2 sub clean_cache_recursive()
301     {
302     my $self=shift;
303     my ($startdir) = @_;
304     my $children = $self->{DIRCACHE}->{$startdir};
305    
306     for (my $i = 2; $i <= $#$children; $i++)
307     {
308     # Remove all children:
309     $self->schedremoval($children->[$i]);
310     $self->clean_cache_recursive($children->[$i]);
311     }
312    
313     delete $self->{DIRCACHE}->{$startdir};
314     return $self;
315     }
316    
317 sashby 1.6 =item C<dirtree($dir,$dofiles)>
318    
319     Starting from $dir, scan the directory tree. Ignore files unless $dofiles is set. This
320     function just calls checktree().
321    
322     =cut
323    
324 sashby 1.2 sub dirtree()
325     {
326     my $self=shift;
327 muzaffar 1.9 my ($dir) = @_;
328 sashby 1.2
329     # Get the directory tree:
330 muzaffar 1.9 $self->checktree($dir, 1);
331 sashby 1.2 return $self;
332     }
333    
334 sashby 1.6 =item C<checkfiles()>
335    
336     Function to actually run the timestamp checks. This is only run from
337     SCRAM::CMD::build().
338    
339     =cut
340    
341 sashby 1.2 sub checkfiles()
342     {
343     my $self=shift;
344 muzaffar 1.9 $self->{cachereset}=shift || 0;
345 sashby 1.2 # Scan config dir for top-level data, then start from src:
346 muzaffar 1.9 my @scandirs=($ENV{SCRAM_CONFIGDIR}, $ENV{SCRAM_SOURCEDIR});
347 sashby 1.2 # Loop over all directories that need scanning (normally just src and config):
348 muzaffar 1.9 $self->{nonxml}=0;
349 muzaffar 1.11 eval ("use SCRAM::Plugins::DirCache;");
350     if(!$@) {$self->{ExtraDirCache} = SCRAM::Plugins::DirCache->new();}
351 sashby 1.2 foreach my $scand (@scandirs)
352     {
353     $self->logmsg("SCRAM: Scanning $scand [dofiles set to ".$dofiles."]\n");
354     # Check the directory tree:
355 muzaffar 1.9 $self->dirtree($scand);
356 sashby 1.2 }
357 muzaffar 1.9 if ($self->cachestatus())
358 sashby 1.2 {
359 muzaffar 1.9 foreach my $bf ("$ENV{SCRAM_BUILDFILE}.xml","$ENV{SCRAM_BUILDFILE}")
360     {
361     if (exists $self->{BFCACHE}{"$ENV{SCRAM_CONFIGDIR}/${bf}"})
362     {
363     $self->{ADDEDBF}{"$ENV{SCRAM_CONFIGDIR}/${bf}"}=1;
364     last;
365     }
366 sashby 1.2 }
367     }
368 muzaffar 1.11 delete $self->{ExtraDirCache};
369 muzaffar 1.9 if ($self->{nonxml} > 0)
370 sashby 1.2 {
371 muzaffar 1.11 #print STDERR "**** WARNING: ",$self->{nonxml}," non-xml based $ENV{SCRAM_BUILDFILE} were read.\n";
372 sashby 1.2 }
373     return $self;
374     }
375    
376 sashby 1.6 =item C<dircache()>
377    
378     Return a reference to the directory cache hash.
379    
380     =cut
381    
382 sashby 1.2 sub dircache()
383     {
384     my $self=shift;
385     # Return the file cache:
386     return $self->{DIRCACHE};
387     }
388    
389 sashby 1.6 =item C<added_dirs($path)>
390    
391     Add $path to the list of directories added since last scan, or return
392     the list of added directories if no argument given.
393    
394     =cut
395    
396 sashby 1.2 sub added_dirs()
397     {
398     my $self=shift;
399     my ($path) = @_;
400    
401     # If we have a path to add, add it.
402     if ($path)
403     {
404     if (exists($self->{ADDEDDIRS}))
405     {
406     push(@{$self->{ADDEDDIRS}}, $path);
407     }
408     else
409     {
410     $self->{ADDEDDIRS} = [ $path ];
411     }
412     }
413     else
414     {
415     # Otherwise, return the array of added dirs:
416     my @addeddirs = @{$self->{ADDEDDIRS}};
417     delete $self->{ADDEDDIRS};
418     return \@addeddirs;
419     }
420     }
421    
422 sashby 1.6 =item C<modified_parentdirs($path)>
423    
424     Add a directory $path to the list of parent directories (directories
425     having subdirectories), or return a reference to the list.
426     Storing this parent allows any update to be taken recursively from this
427     location.
428    
429     =cut
430    
431 sashby 1.2 sub modified_parentdirs()
432     {
433     my $self=shift;
434     my ($path) = @_;
435    
436     # If we have a path to add, add it.
437     # Don't bother if it's the main source dir as we don't
438     # want to rescan everything from src (that would be silly):
439     if ($path && $path ne $ENV{SCRAM_SOURCEDIR})
440     {
441     if (exists($self->{MODPARENTDIRS}))
442     {
443     push(@{$self->{MODPARENTDIRS}}, $path);
444     }
445     else
446     {
447     $self->{MODPARENTDIRS} = [ $path ];
448     }
449     }
450     else
451     {
452     # Otherwise, return the array of added dirs:
453     my @moddeddirs = @{$self->{MODPARENTDIRS}};
454     delete $self->{MODPARENTDIRS};
455     return \@moddeddirs;
456     }
457     }
458    
459 sashby 1.6 =item C<schedremoval($d)>
460    
461     Add directory $d to list of directories that should be removed
462     recursively from the cache.
463     If no arguments given, return a reference to a list of
464     directories to be removed.
465    
466     =cut
467    
468 sashby 1.2 sub schedremoval()
469     {
470     my $self=shift;
471     my ($d)=@_;
472    
473     if ($d)
474     {
475     if (exists($self->{REMOVEDATA}))
476     {
477     push(@{$self->{REMOVEDATA}},$d);
478     }
479     else
480     {
481     $self->{REMOVEDATA} = [ $d ];
482     }
483     }
484     else
485     {
486     my $remove = [ @{$self->{REMOVEDATA}} ];
487     $self->{REMOVEDATA} = [];
488     return $remove;
489     }
490     }
491    
492 sashby 1.6 =item C<filestatus()>
493    
494     Return a true or false value depending on whether
495     a BuildFile was changed or not.
496    
497     =cut
498    
499 sashby 1.2 sub filestatus()
500     {
501     my $self=shift;
502     # Here we want to return a true or false value depending on whether
503     # or not a buildfile was changed:
504     return $self->{STATUSSRC};
505     }
506    
507 sashby 1.6 =item C<configstatus()>
508    
509     Return a true or false value depending on whether
510     a file in the config directory was changed or not.
511    
512     =cut
513    
514 sashby 1.2 sub configstatus()
515     {
516     my $self=shift;
517     # Here we want to return a true or false value depending on whether or not a file
518     # in config dir was changed:
519     return $self->{STATUSCONFIG};
520     }
521    
522 sashby 1.6 =item C<bf_for_scanning()>
523    
524     Return a list of BuildFiles to re-read. Note that this is only done
525     if the status was changed (i.e. not necessary to read through the list
526     of BuildFiles to know whether something changed as the flag B<STATUSSRC>
527     is set as the source tree is checked).
528     If B<STATUSCONFIG> is true, all BuildFiles are marked to be read.
529    
530     =cut
531    
532 sashby 1.2 sub bf_for_scanning()
533     {
534     my $self=shift;
535     my $MODIFIED = [];
536 muzaffar 1.9 map { push(@$MODIFIED, $_) } @{$self->{ADDEDBF}};
537 sashby 1.2 return $MODIFIED;
538     }
539    
540 sashby 1.6 =item C<paths()>
541    
542     Return a reference to an array of directories for the current source tree.
543    
544     =cut
545    
546 sashby 1.2 sub paths()
547     {
548     my $self=shift;
549     my $paths = {};
550    
551 muzaffar 1.9 my $ALLDIRS = [];
552     map { push(@$ALLDIRS, $_) } keys %{$self->{DIRCACHE}};
553     return $ALLDIRS;
554 sashby 1.2 }
555    
556 sashby 1.6 =item C<verbose()>
557    
558     Turn verbosity for the cache on or off.
559    
560     =cut
561    
562 sashby 1.2 sub verbose()
563     {
564     my $self=shift;
565     # Turn on verbose mode:
566     @_ ? $self->{VERBOSE} = shift
567     : $self->{VERBOSE}
568     }
569    
570 sashby 1.6 =item C<cachestatus()>
571    
572     Set or return the cache status to indicate whether or not a file
573     timestamp has changed since the last pass.
574    
575     =cut
576    
577 sashby 1.2 sub cachestatus()
578     {
579     my $self=shift;
580     # Set/return the status of the cache:
581     @_ ? $self->{STATUS} = shift
582     : $self->{STATUS}
583     }
584    
585 sashby 1.6 =item C<logmsg(@message)>
586    
587     Print a message to B<STDERR>. This is only used in
588     checktree(), checkfiles() and paths().
589    
590     =cut
591    
592 sashby 1.2 sub logmsg()
593     {
594     my $self=shift;
595     # Print a message to STDOUT if VERBOSE is true:
596     print STDERR @_ if $self->verbose();
597     }
598    
599 sashby 1.6 =item C<name()>
600    
601     Set or return the name of the cache. Normally set
602 muzaffar 1.11 to B<DirCache.db.gz> (and not architecture dependent).
603 sashby 1.6
604     =cut
605    
606 sashby 1.2 sub name()
607     {
608     my $self=shift;
609     # Set/return the name of the cache to use:
610     @_ ? $self->{CACHENAME} = shift
611     : $self->{CACHENAME}
612     }
613    
614 muzaffar 1.9 sub get_data()
615     {
616     my $self=shift;
617     my $type=shift;
618     @_ ? $self->{$type} = shift
619     : $self->{$type};
620     }
621    
622     sub extra_suffix()
623     {
624     my $self=shift;
625     my $path=shift;
626     @_ ? $self->{EXTRASUFFIX}{$path}=shift
627     : exists $self->{EXTRASUFFIX}{$path};
628     }
629    
630     sub get_nonxml()
631     {
632     my $self=shift;
633     return $self->{nonxml};
634     }
635    
636 sashby 1.2 1;
637 sashby 1.5
638     =back
639    
640     =head1 AUTHOR
641    
642     Shaun Ashby (with contribution from Lassi Tuura)
643    
644     =head1 MAINTAINER
645    
646     Shaun Ashby
647    
648     =cut
649