ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/Cache/Cache.pm
Revision: 1.10.2.2.2.2
Committed: Mon Jun 2 16:20:26 2008 UTC (16 years, 11 months ago) by muzaffar
Content type: text/plain
Branch: SCRAM_V2_0
CVS Tags: V2_0_5, V2_0_4, V2_0_4_relcand2, V2_0_4_relcand1, V2_0_3, V2_0_3_relcand3, V2_0_3_relcand2, V2_0_3_relcand1, V2_0_2, V2_0_2_relcand1, V2_0_1, V2_0_1_relcand4
Changes since 1.10.2.2.2.1: +2 -2 lines
Log Message:
no more non-xml based BuildFile read warning

File Contents

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