1 | #!/usr/bin/perl |
---|
2 | # ------------------------------------------------------------------------------ |
---|
3 | # NAME |
---|
4 | # Fcm::Dest |
---|
5 | # |
---|
6 | # DESCRIPTION |
---|
7 | # This class contains methods to set up a destination location of an FCM |
---|
8 | # extract/build. |
---|
9 | # |
---|
10 | # COPYRIGHT |
---|
11 | # (C) Crown copyright Met Office. All rights reserved. |
---|
12 | # For further details please refer to the file COPYRIGHT.txt |
---|
13 | # which you should have received as part of this distribution. |
---|
14 | # ------------------------------------------------------------------------------ |
---|
15 | |
---|
16 | package Fcm::Dest; |
---|
17 | @ISA = qw(Fcm::Base); |
---|
18 | |
---|
19 | # Standard pragma |
---|
20 | use warnings; |
---|
21 | use strict; |
---|
22 | |
---|
23 | # Standard modules |
---|
24 | use Carp; |
---|
25 | use Cwd; |
---|
26 | use File::Basename; |
---|
27 | use File::Find; |
---|
28 | use File::Path; |
---|
29 | use File::Spec; |
---|
30 | use Sys::Hostname; |
---|
31 | |
---|
32 | # In-house modules |
---|
33 | use Fcm::Base; |
---|
34 | use Fcm::CfgLine; |
---|
35 | use Fcm::Util; |
---|
36 | |
---|
37 | # Useful variables |
---|
38 | # ------------------------------------------------------------------------------ |
---|
39 | # List of configuration files |
---|
40 | our @cfgfiles = ( |
---|
41 | 'bldcfg', # default location of the build configuration file |
---|
42 | 'extcfg', # default location of the extract configuration file |
---|
43 | ); |
---|
44 | |
---|
45 | # List of cache and configuration files, according to the dest type |
---|
46 | our @cfgfiles_type = ( |
---|
47 | 'cache', # default location of the cache file |
---|
48 | 'cfg', # default location of the configuration file |
---|
49 | 'parsedcfg', # default location of the as-parsed configuration file |
---|
50 | ); |
---|
51 | |
---|
52 | # List of lock files |
---|
53 | our @lockfiles = ( |
---|
54 | 'bldlock', # the build lock file |
---|
55 | 'extlock', # the extract lock file |
---|
56 | ); |
---|
57 | |
---|
58 | # List of misc files |
---|
59 | our @miscfiles_bld = ( |
---|
60 | 'bldrunenvsh', # the build run environment shell script |
---|
61 | 'bldmakefile', # the build Makefile |
---|
62 | ); |
---|
63 | |
---|
64 | # List of sub-directories created by extract |
---|
65 | our @subdirs_ext = ( |
---|
66 | 'cfgdir', # sub-directory for configuration files |
---|
67 | 'srcdir', # sub-directory for source tree |
---|
68 | ); |
---|
69 | |
---|
70 | # List of sub-directories that can be archived by "tar" at end of build |
---|
71 | our @subdirs_tar = ( |
---|
72 | 'donedir', # sub-directory for "done" files |
---|
73 | 'flagsdir', # sub-directory for "flags" files |
---|
74 | 'incdir', # sub-directory for include files |
---|
75 | 'ppsrcdir', # sub-directory for pre-process source tree |
---|
76 | 'objdir', # sub-directory for object files |
---|
77 | ); |
---|
78 | |
---|
79 | # List of sub-directories created by build |
---|
80 | our @subdirs_bld = ( |
---|
81 | 'bindir', # sub-directory for executables |
---|
82 | 'etcdir', # sub-directory for miscellaneous files |
---|
83 | 'libdir', # sub-directory for object libraries |
---|
84 | 'tmpdir', # sub-directory for temporary build files |
---|
85 | @subdirs_tar, # -see above- |
---|
86 | ); |
---|
87 | |
---|
88 | # List of sub-directories under rootdir |
---|
89 | our @subdirs = ( |
---|
90 | 'cachedir', # sub-directory for caches |
---|
91 | @subdirs_ext, # -see above- |
---|
92 | @subdirs_bld, # -see above- |
---|
93 | ); |
---|
94 | |
---|
95 | # List of inherited search paths |
---|
96 | # "rootdir" + all @subdirs, with "XXXdir" replaced with "XXXpath" |
---|
97 | our @paths = ('rootpath'); |
---|
98 | for (@subdirs) { |
---|
99 | (my $name = $_) =~ s/dir$/path/; |
---|
100 | push @paths, $name; |
---|
101 | } |
---|
102 | |
---|
103 | # List of normal properties |
---|
104 | my @scalar_properties = ( |
---|
105 | 'dest0', # the original destination (if current destination is a mirror) |
---|
106 | 'inherit', # list of inherited Fcm::Dest objects |
---|
107 | 'logname', # remote login name |
---|
108 | 'lockfile', # lock file |
---|
109 | 'machine', # remote machine |
---|
110 | 'mirror_cmd', # mirror command to use |
---|
111 | 'rootdir', # destination root directory |
---|
112 | 'type', # destination type, "bld" (default) or "ext" |
---|
113 | ); |
---|
114 | |
---|
115 | # ------------------------------------------------------------------------------ |
---|
116 | # SYNOPSIS |
---|
117 | # $obj = Fcm::Dest->new (%args); |
---|
118 | # |
---|
119 | # DESCRIPTION |
---|
120 | # This method constructs a new instance of the Fcm::Dest class. See above for |
---|
121 | # allowed list of properties. (KEYS should be in uppercase.) |
---|
122 | # ------------------------------------------------------------------------------ |
---|
123 | |
---|
124 | sub new { |
---|
125 | my $this = shift; |
---|
126 | my %args = @_; |
---|
127 | my $class = ref $this || $this; |
---|
128 | |
---|
129 | my $self = Fcm::Base->new (%args); |
---|
130 | |
---|
131 | bless $self, $class; |
---|
132 | |
---|
133 | for (@scalar_properties) { |
---|
134 | $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef; |
---|
135 | } |
---|
136 | |
---|
137 | for (@subdirs, @paths, @lockfiles, @cfgfiles) { |
---|
138 | $self->{$_} = undef; |
---|
139 | } |
---|
140 | |
---|
141 | return $self; |
---|
142 | } |
---|
143 | |
---|
144 | # ------------------------------------------------------------------------------ |
---|
145 | # SYNOPSIS |
---|
146 | # $self->DESTROY; |
---|
147 | # |
---|
148 | # DESCRIPTION |
---|
149 | # This method is called automatically when the Fcm::Dest object is |
---|
150 | # destroyed. |
---|
151 | # ------------------------------------------------------------------------------ |
---|
152 | |
---|
153 | sub DESTROY { |
---|
154 | my $self = shift; |
---|
155 | |
---|
156 | # Remove the lockfile if it is set |
---|
157 | unlink $self->lockfile if $self->lockfile and -w $self->lockfile; |
---|
158 | |
---|
159 | return; |
---|
160 | } |
---|
161 | |
---|
162 | # ------------------------------------------------------------------------------ |
---|
163 | # SYNOPSIS |
---|
164 | # $value = $obj->X; |
---|
165 | # $obj->X ($value); |
---|
166 | # |
---|
167 | # DESCRIPTION |
---|
168 | # Details of these properties are explained in @scalar_properties. |
---|
169 | # ------------------------------------------------------------------------------ |
---|
170 | |
---|
171 | for my $name (@scalar_properties) { |
---|
172 | no strict 'refs'; |
---|
173 | |
---|
174 | *$name = sub { |
---|
175 | my $self = shift; |
---|
176 | |
---|
177 | # Set property to specified value |
---|
178 | if (@_) { |
---|
179 | $self->{$name} = $_[0]; |
---|
180 | |
---|
181 | if ($name eq 'rootdir') { |
---|
182 | # If root is changed, reset locations derived from it |
---|
183 | for (@cfgfiles, @lockfiles, @miscfiles_bld, @subdirs) { |
---|
184 | $self->{$_} = undef; |
---|
185 | } |
---|
186 | |
---|
187 | } elsif ($name eq 'inherit') { |
---|
188 | # If list of inherited destinations has changed, reset search paths |
---|
189 | for (@paths) { |
---|
190 | $self->{$_} = undef; |
---|
191 | } |
---|
192 | } |
---|
193 | } |
---|
194 | |
---|
195 | # Default value for property |
---|
196 | if (not defined $self->{$name}) { |
---|
197 | if ($name eq 'inherit') { |
---|
198 | # Set to reference to empty array |
---|
199 | $self->{$name} = []; |
---|
200 | |
---|
201 | } elsif ($name eq 'logname') { |
---|
202 | # Attempt to get logname from the current logname |
---|
203 | $self->{$name} = $self->config->user_id; |
---|
204 | |
---|
205 | } elsif ($name eq 'machine') { |
---|
206 | # Use current hostname |
---|
207 | $self->{$name} = &hostname(); |
---|
208 | |
---|
209 | } elsif ($name eq 'mirror_cmd') { |
---|
210 | # Use default mirror command from config setting |
---|
211 | $self->{$name} = $self->setting (qw/TOOL MIRROR/); |
---|
212 | |
---|
213 | } elsif ($name eq 'type') { |
---|
214 | # Default to build destination |
---|
215 | $self->{$name} = 'bld'; |
---|
216 | } |
---|
217 | } |
---|
218 | |
---|
219 | return $self->{$name}; |
---|
220 | } |
---|
221 | } |
---|
222 | |
---|
223 | # ------------------------------------------------------------------------------ |
---|
224 | # SYNOPSIS |
---|
225 | # $value = $obj->X; |
---|
226 | # |
---|
227 | # DESCRIPTION |
---|
228 | # This method returns X, where X is a location derived from rootdir, and can |
---|
229 | # be one of: |
---|
230 | # bindir, bldcfg, blddir, bldlock, bldrunenv, cache, cachedir, cfg, cfgdir, |
---|
231 | # donedir, etcdir, extcfg, extlock, flagsdir, incdir, libdir, parsedcfg, |
---|
232 | # ppsrcdir, objdir, or tmpdir. |
---|
233 | # |
---|
234 | # Details of these properties are explained earlier. |
---|
235 | # ------------------------------------------------------------------------------ |
---|
236 | |
---|
237 | for my $name (@cfgfiles, @cfgfiles_type, @lockfiles, @miscfiles_bld, @subdirs) { |
---|
238 | no strict 'refs'; |
---|
239 | |
---|
240 | *$name = sub { |
---|
241 | my $self = shift; |
---|
242 | |
---|
243 | # If variable not set, derive it from rootdir |
---|
244 | if ($self->rootdir and not defined $self->{$name}) { |
---|
245 | if ($name eq 'cache') { |
---|
246 | # Cache file under root/.cache |
---|
247 | $self->{$name} = File::Spec->catfile ( |
---|
248 | $self->cachedir, $self->setting ('CACHE'), |
---|
249 | ); |
---|
250 | |
---|
251 | } elsif ($name eq 'cfg') { |
---|
252 | # Configuration file of current type |
---|
253 | my $method = $self->type . 'cfg'; |
---|
254 | $self->{$name} = $self->$method; |
---|
255 | |
---|
256 | } elsif (grep {$name eq $_} @cfgfiles) { |
---|
257 | # Configuration files under the root/cfg |
---|
258 | (my $label = uc ($name)) =~ s/CFG//; |
---|
259 | $self->{$name} = File::Spec->catfile ( |
---|
260 | $self->cfgdir, $self->setting ('CFG_NAME', $label), |
---|
261 | ); |
---|
262 | |
---|
263 | } elsif (grep {$name eq $_} @lockfiles) { |
---|
264 | # Lock file |
---|
265 | $self->{$name} = File::Spec->catfile ( |
---|
266 | $self->rootdir, $self->setting ('LOCK', uc ($name)), |
---|
267 | ); |
---|
268 | |
---|
269 | } elsif (grep {$name eq $_} @miscfiles_bld) { |
---|
270 | # Misc file |
---|
271 | $self->{$name} = File::Spec->catfile ( |
---|
272 | $self->rootdir, $self->setting ('BLD_MISC', uc ($name)), |
---|
273 | ); |
---|
274 | |
---|
275 | } elsif ($name eq 'parsedcfg') { |
---|
276 | # As-parsed configuration file of current type |
---|
277 | $self->{$name} = File::Spec->catfile ( |
---|
278 | dirname ($self->cfg), |
---|
279 | $self->setting (qw/CFG_NAME PARSED/) . basename ($self->cfg), |
---|
280 | ) |
---|
281 | |
---|
282 | } elsif (grep {$name eq $_} @subdirs) { |
---|
283 | # Sub-directories under the root |
---|
284 | (my $label = uc ($name)) =~ s/DIR//; |
---|
285 | $self->{$name} = File::Spec->catfile ( |
---|
286 | $self->rootdir, |
---|
287 | $self->setting ('DIR', $label), |
---|
288 | ($name eq 'cachedir' ? '.' . $self->type : ()), |
---|
289 | ); |
---|
290 | } |
---|
291 | } |
---|
292 | |
---|
293 | return $self->{$name}; |
---|
294 | } |
---|
295 | } |
---|
296 | |
---|
297 | # ------------------------------------------------------------------------------ |
---|
298 | # SYNOPSIS |
---|
299 | # $value = $obj->X; |
---|
300 | # |
---|
301 | # DESCRIPTION |
---|
302 | # This method returns X, an array containing the search path of a destination |
---|
303 | # directory, which can be one of: |
---|
304 | # binpath, bldpath, cachepath, cfgpath, donepath, etcpath, flagspath, |
---|
305 | # incpath, libpath, ppsrcpath, objpath, rootpath, srcpath, or tmppath, |
---|
306 | # |
---|
307 | # Details of these properties are explained earlier. |
---|
308 | # ------------------------------------------------------------------------------ |
---|
309 | |
---|
310 | for my $name (@paths) { |
---|
311 | no strict 'refs'; |
---|
312 | |
---|
313 | *$name = sub { |
---|
314 | my $self = shift; |
---|
315 | |
---|
316 | (my $dir = $name) =~ s/path/dir/; |
---|
317 | |
---|
318 | if ($self->$dir and not defined $self->{$name}) { |
---|
319 | my @path = (); |
---|
320 | |
---|
321 | # Recursively inherit the search path |
---|
322 | for my $d (@{ $self->inherit }) { |
---|
323 | unshift @path, $d->$dir; |
---|
324 | } |
---|
325 | |
---|
326 | # Place the path of the current build in the front |
---|
327 | unshift @path, $self->$dir; |
---|
328 | |
---|
329 | $self->{$name} = \@path; |
---|
330 | } |
---|
331 | |
---|
332 | return $self->{$name}; |
---|
333 | } |
---|
334 | } |
---|
335 | |
---|
336 | # ------------------------------------------------------------------------------ |
---|
337 | # SYNOPSIS |
---|
338 | # $rc = $obj->archive (); |
---|
339 | # |
---|
340 | # DESCRIPTION |
---|
341 | # This method creates TAR archives for selected sub-directories. |
---|
342 | # ------------------------------------------------------------------------------ |
---|
343 | |
---|
344 | sub archive { |
---|
345 | my $self = shift; |
---|
346 | |
---|
347 | # Save current directory |
---|
348 | my $cwd = cwd (); |
---|
349 | |
---|
350 | my $tar = $self->setting (qw/OUTFILE_EXT TAR/); |
---|
351 | my $verbose = $self->verbose; |
---|
352 | |
---|
353 | for my $name (@subdirs_tar) { |
---|
354 | my $dir = $self->$name; |
---|
355 | |
---|
356 | # Ignore unless sub-directory exists |
---|
357 | next unless -d $dir; |
---|
358 | |
---|
359 | # Change to container directory |
---|
360 | my $base = basename ($dir); |
---|
361 | print 'cd ', dirname ($dir), "\n" if $verbose > 2; |
---|
362 | chdir dirname ($dir); |
---|
363 | |
---|
364 | # Run "tar" command |
---|
365 | my $rc = &run_command ( |
---|
366 | [qw/tar -czf/, $base . $tar, $base], |
---|
367 | PRINT => $verbose > 1, ERROR => 'warn', |
---|
368 | ); |
---|
369 | |
---|
370 | # Remove sub-directory |
---|
371 | &run_command ([qw/rm -rf/, $base], PRINT => $verbose > 1) if not $rc; |
---|
372 | } |
---|
373 | |
---|
374 | # Change back to "current" directory |
---|
375 | print 'cd ', $cwd, "\n" if $verbose > 2; |
---|
376 | chdir $cwd; |
---|
377 | |
---|
378 | return 1; |
---|
379 | } |
---|
380 | |
---|
381 | # ------------------------------------------------------------------------------ |
---|
382 | # SYNOPSIS |
---|
383 | # $authority = $obj->authority(); |
---|
384 | # |
---|
385 | # DESCRIPTION |
---|
386 | # Returns LOGNAME@MACHINE for this destination if LOGNAME is defined and not |
---|
387 | # the same as the user ID of the current process. Returns MACHINE if LOGNAME |
---|
388 | # is the same as the user ID of the current process, but MACHINE is not the |
---|
389 | # same as the current hostname. Returns an empty string if LOGNAME and |
---|
390 | # MACHINE are not defined or are the same as in the current process. |
---|
391 | # ------------------------------------------------------------------------------ |
---|
392 | |
---|
393 | sub authority { |
---|
394 | my $self = shift; |
---|
395 | my $return = ''; |
---|
396 | |
---|
397 | if ($self->logname ne $self->config->user_id) { |
---|
398 | $return = $self->logname . '@' . $self->machine; |
---|
399 | |
---|
400 | } elsif ($self->machine ne &hostname()) { |
---|
401 | $return = $self->machine; |
---|
402 | } |
---|
403 | |
---|
404 | return $return; |
---|
405 | } |
---|
406 | |
---|
407 | # ------------------------------------------------------------------------------ |
---|
408 | # SYNOPSIS |
---|
409 | # $rc = $obj->clean ([ITEM => <list>,] [MODE => 'ALL|CONTENT|EMPTY',]); |
---|
410 | # |
---|
411 | # DESCRIPTION |
---|
412 | # This method removes files/directories from the destination. If ITEM is set, |
---|
413 | # it must be a reference to a list of method names for files/directories to |
---|
414 | # be removed. Otherwise, the list is determined by the destination type. If |
---|
415 | # MODE is ALL, all directories/files created by the extract/build are |
---|
416 | # removed. If MODE is CONTENT, only contents within sub-directories are |
---|
417 | # removed. If MODE is EMPTY (default), only empty sub-directories are |
---|
418 | # removed. |
---|
419 | # ------------------------------------------------------------------------------ |
---|
420 | |
---|
421 | sub clean { |
---|
422 | my ($self, %args) = @_; |
---|
423 | |
---|
424 | my $mode = exists $args{MODE} ? $args{MODE} : 'EMPTY'; |
---|
425 | my $rc = 1; |
---|
426 | |
---|
427 | my @files; |
---|
428 | if (exists $args{ITEM} and $args{ITEM}) { |
---|
429 | # Remove only selected directories |
---|
430 | @files = @{ $args{ITEM} }; |
---|
431 | |
---|
432 | } else { |
---|
433 | # Remove cachedir and read-write sub-directories for extract or build |
---|
434 | @files = ( |
---|
435 | 'cachedir', |
---|
436 | ($self->type eq 'ext' ? @subdirs_ext : (@subdirs_bld, @miscfiles_bld)), |
---|
437 | ); |
---|
438 | } |
---|
439 | |
---|
440 | for my $name (@files) { |
---|
441 | my $item = $self->$name; |
---|
442 | my $clean_ok = 1; |
---|
443 | |
---|
444 | my @files; |
---|
445 | if (-d $item and $mode ne 'ALL') { |
---|
446 | # Read contents in directory |
---|
447 | opendir DIR, $item; |
---|
448 | @files = grep !/^\.\.?$/, readdir DIR; |
---|
449 | closedir DIR; |
---|
450 | |
---|
451 | # OK to remove if it is empty |
---|
452 | $clean_ok = not @files; |
---|
453 | |
---|
454 | } elsif (-f $item and $mode ne 'ALL') { |
---|
455 | $clean_ok = 0; |
---|
456 | } |
---|
457 | |
---|
458 | # Remove only if it is OK to do so |
---|
459 | next unless $clean_ok; |
---|
460 | |
---|
461 | # If $item exists and writable by the user, remove it |
---|
462 | if (-w $item) { |
---|
463 | if (-d $item and $mode eq 'CONTENT') { |
---|
464 | print 'Remove contents in : ', $item, "\n" |
---|
465 | if $self->verbose > 1; |
---|
466 | |
---|
467 | for my $file (@files) { |
---|
468 | rmtree (File::Spec->catfile ($item, $file)); |
---|
469 | } |
---|
470 | |
---|
471 | } else { |
---|
472 | print 'Remove: ', $item, "\n" if $self->verbose > 1; |
---|
473 | rmtree $item; |
---|
474 | } |
---|
475 | } |
---|
476 | |
---|
477 | # If $item still exists, issue warning |
---|
478 | if (-e $item and not $mode eq 'CONTENT') { |
---|
479 | w_report 'ERROR: ', $item, ': cannot remove.'; |
---|
480 | $rc = 0; |
---|
481 | } |
---|
482 | } |
---|
483 | |
---|
484 | return $rc; |
---|
485 | } |
---|
486 | |
---|
487 | # ------------------------------------------------------------------------------ |
---|
488 | # SYNOPSIS |
---|
489 | # $rc = $obj->create ([DIR => <dir-list>,]); |
---|
490 | # |
---|
491 | # DESCRIPTION |
---|
492 | # This method creates the directories of a destination. If DIR is set, it |
---|
493 | # must be a reference to a list of sub-directories to be created. Otherwise, |
---|
494 | # the sub-directory list is determined by the destination type. It returns |
---|
495 | # true if the destination is created or if it exists and is writable. |
---|
496 | # ------------------------------------------------------------------------------ |
---|
497 | |
---|
498 | sub create { |
---|
499 | my ($self, %args) = @_; |
---|
500 | |
---|
501 | my $rc = 1; |
---|
502 | |
---|
503 | my @dirs; |
---|
504 | if (exists $args{DIR} and $args{DIR}) { |
---|
505 | # Create only selected sub-directories |
---|
506 | @dirs = @{ $args{DIR} }; |
---|
507 | |
---|
508 | } else { |
---|
509 | # Create rootdir, cachedir and read-write sub-directories for extract/build |
---|
510 | @dirs = ( |
---|
511 | qw/rootdir cachedir/, |
---|
512 | ($self->type eq 'ext' ? @subdirs_ext : @subdirs_bld), |
---|
513 | ); |
---|
514 | } |
---|
515 | |
---|
516 | for my $name (@dirs) { |
---|
517 | my $dir = $self->$name; |
---|
518 | |
---|
519 | # Create directory if it does not already exist |
---|
520 | if (not -d $dir) { |
---|
521 | print 'Make directory: ', $dir, "\n" if $self->verbose > 1; |
---|
522 | mkpath $dir; |
---|
523 | } |
---|
524 | |
---|
525 | # Check whether directory exists and is writable |
---|
526 | unless (-d $dir and -w $dir) { |
---|
527 | w_report 'ERROR: ', $dir, ': cannot write to destination.'; |
---|
528 | $rc = 0; |
---|
529 | } |
---|
530 | } |
---|
531 | |
---|
532 | return $rc; |
---|
533 | } |
---|
534 | |
---|
535 | # ------------------------------------------------------------------------------ |
---|
536 | # SYNOPSIS |
---|
537 | # $rc = $obj->create_bldrunenvsh (); |
---|
538 | # |
---|
539 | # DESCRIPTION |
---|
540 | # This method creates the runtime environment script for the build. |
---|
541 | # ------------------------------------------------------------------------------ |
---|
542 | |
---|
543 | sub create_bldrunenvsh { |
---|
544 | my $self = shift; |
---|
545 | |
---|
546 | # Path to executable files and directory for misc files |
---|
547 | my @binpath = grep {-d} @{ $self->binpath }; |
---|
548 | my $etc_dir = -d $self->etcdir ? $self->etcdir : undef; |
---|
549 | |
---|
550 | # Create a runtime environment script if necessary |
---|
551 | if (@binpath or $etc_dir) { |
---|
552 | my $file = $self->bldrunenvsh; |
---|
553 | |
---|
554 | open FILE, '>', $file or croak $file, ': cannot open (', $!, '), abort'; |
---|
555 | print FILE '#!', $self->setting(qw/TOOL SHELL/), "\n"; |
---|
556 | print FILE 'PATH=', join (':', (@binpath, '$PATH')), "\n" if @binpath; |
---|
557 | print FILE 'FCM_ETCDIR=', $self->etcdir, "\n" if $etc_dir; |
---|
558 | print FILE 'export PATH FCM_ETCDIR'; |
---|
559 | close FILE or croak $file, ': cannot close (', $!, '), abort'; |
---|
560 | |
---|
561 | # Create symbolic links fcm_env.ksh and bin/fcm_env.ksh for backward |
---|
562 | # compatibility |
---|
563 | my $old_runenv = 'fcm_env.ksh'; |
---|
564 | my @symlinks = ( |
---|
565 | File::Spec->catfile ($self->rootdir, $old_runenv), |
---|
566 | (-d $self->bindir ? File::Spec->catfile ($self->bindir, $old_runenv) : ()), |
---|
567 | ); |
---|
568 | |
---|
569 | for my $link (@symlinks) { |
---|
570 | # Remove old link if necessary |
---|
571 | if (-l $link) { |
---|
572 | unlink $link if readlink ($link) ne $file; |
---|
573 | |
---|
574 | } elsif (-e $link) { |
---|
575 | unlink $link; |
---|
576 | } |
---|
577 | |
---|
578 | # Create the new link |
---|
579 | symlink $file, $link if not -l $link; |
---|
580 | } |
---|
581 | |
---|
582 | # Information on the location/usage of the runtime environment script |
---|
583 | if ($self->verbose > 1 and $file) { |
---|
584 | print '# ', '-' x 78, "\n"; |
---|
585 | print '# To use this build, source the following shell script:', "\n"; |
---|
586 | print '. ', $file, "\n"; |
---|
587 | print '# ', '-' x 78, "\n"; |
---|
588 | } |
---|
589 | } |
---|
590 | |
---|
591 | return 1; |
---|
592 | } |
---|
593 | |
---|
594 | # ------------------------------------------------------------------------------ |
---|
595 | # SYNOPSIS |
---|
596 | # $rc = $obj->dearchive (); |
---|
597 | # |
---|
598 | # DESCRIPTION |
---|
599 | # This method extracts from TAR archives for selected sub-directories. |
---|
600 | # ------------------------------------------------------------------------------ |
---|
601 | |
---|
602 | sub dearchive { |
---|
603 | my $self = shift; |
---|
604 | |
---|
605 | my $tar = $self->setting (qw/OUTFILE_EXT TAR/); |
---|
606 | my $verbose = $self->verbose; |
---|
607 | |
---|
608 | # Extract archives if necessary |
---|
609 | for my $name (@subdirs_tar) { |
---|
610 | my $tar_file = $self->$name . $tar; |
---|
611 | |
---|
612 | # Check whether tar archive exists for the named sub-directory |
---|
613 | next unless -f $tar_file; |
---|
614 | |
---|
615 | # If so, extract the archive and remove it afterwards |
---|
616 | &run_command ([qw/tar -xzf/, $tar_file], PRINT => $verbose > 1); |
---|
617 | &run_command ([qw/rm -f/, $tar_file], PRINT => $verbose > 1); |
---|
618 | } |
---|
619 | |
---|
620 | return 1; |
---|
621 | } |
---|
622 | |
---|
623 | # ------------------------------------------------------------------------------ |
---|
624 | # SYNOPSIS |
---|
625 | # $name = $obj->get_pkgname_of_path ($path); |
---|
626 | # |
---|
627 | # DESCRIPTION |
---|
628 | # This method returns the package name of $path if $path is in (a relative |
---|
629 | # path of) $self->srcdir, or undef otherwise. |
---|
630 | # ------------------------------------------------------------------------------ |
---|
631 | |
---|
632 | sub get_pkgname_of_path { |
---|
633 | my ($self, $path) = @_; |
---|
634 | |
---|
635 | my $relpath = File::Spec->abs2rel ($path, $self->srcdir); |
---|
636 | my $name = $relpath ? [File::Spec->splitdir ($relpath)] : undef; |
---|
637 | |
---|
638 | return $name; |
---|
639 | } |
---|
640 | |
---|
641 | # ------------------------------------------------------------------------------ |
---|
642 | # SYNOPSIS |
---|
643 | # %src = $obj->get_source_files (); |
---|
644 | # |
---|
645 | # DESCRIPTION |
---|
646 | # This method returns a hash (keys = package names, values = file names) |
---|
647 | # under $self->srcdir. |
---|
648 | # ------------------------------------------------------------------------------ |
---|
649 | |
---|
650 | sub get_source_files { |
---|
651 | my $self = shift; |
---|
652 | |
---|
653 | my %src; |
---|
654 | if ($self->srcdir and -d $self->srcdir) { |
---|
655 | &find (sub { |
---|
656 | return if /^\./; # ignore system/hidden file |
---|
657 | return if -d $File::Find::name; # ignore directory |
---|
658 | return if not -r $File::Find::name; # ignore unreadable files |
---|
659 | |
---|
660 | my $name = join ( |
---|
661 | '__', @{ $self->get_pkgname_of_path ($File::Find::name) }, |
---|
662 | ); |
---|
663 | $src{$name} = $File::Find::name; |
---|
664 | }, $self->srcdir); |
---|
665 | } |
---|
666 | |
---|
667 | return \%src; |
---|
668 | } |
---|
669 | |
---|
670 | # ------------------------------------------------------------------------------ |
---|
671 | # SYNOPSIS |
---|
672 | # $rc = $obj->mirror (\@items); |
---|
673 | # |
---|
674 | # DESCRIPTION |
---|
675 | # This method mirrors @items (list of method names for directories or files) |
---|
676 | # from $dest0 (which must be an instance of Fcm::Dest for a local |
---|
677 | # destination) to this destination. |
---|
678 | # ------------------------------------------------------------------------------ |
---|
679 | |
---|
680 | sub mirror { |
---|
681 | my ($self, $items) = @_; |
---|
682 | |
---|
683 | my $rc = 1; |
---|
684 | |
---|
685 | if ($self->authority or $self->dest0->rootdir ne $self->rootdir) { |
---|
686 | # Diagnostic |
---|
687 | if ($self->verbose) { |
---|
688 | print 'Destination: ' . ($self->authority ? $self->authority . ':' : '') . |
---|
689 | $self->rootdir . "\n"; |
---|
690 | } |
---|
691 | |
---|
692 | # Check whether mirror command is implemented |
---|
693 | my $method = '_mirror_with_' . $self->mirror_cmd; |
---|
694 | if ($self->can ($method)) { |
---|
695 | $rc = $self->$method ($self->dest0, $items); |
---|
696 | |
---|
697 | } else { |
---|
698 | # Unknown mirroring tool |
---|
699 | w_report $self->mirror_cmd, ': unknown mirroring tool, abort.'; |
---|
700 | $rc = 0; |
---|
701 | } |
---|
702 | } |
---|
703 | |
---|
704 | return $rc; |
---|
705 | } |
---|
706 | |
---|
707 | # ------------------------------------------------------------------------------ |
---|
708 | # SYNOPSIS |
---|
709 | # $rc = $self->_mirror_with_rdist ($dest0, \@items); |
---|
710 | # |
---|
711 | # DESCRIPTION |
---|
712 | # This internal method implements $self->mirror with "rdist". |
---|
713 | # ------------------------------------------------------------------------------ |
---|
714 | |
---|
715 | sub _mirror_with_rdist { |
---|
716 | my ($self, $dest0, $items) = @_; |
---|
717 | |
---|
718 | my $rhost = $self->authority ? $self->authority : &hostname(); |
---|
719 | |
---|
720 | # Print distfile content to temporary file |
---|
721 | my @distfile = (); |
---|
722 | for my $label (@$items) { |
---|
723 | push @distfile, '( ' . $dest0->$label . ' ) -> ' . $rhost . "\n"; |
---|
724 | push @distfile, ' install ' . $self->$label . ';' . "\n"; |
---|
725 | } |
---|
726 | |
---|
727 | # Set up mirroring command (use "rdist" at the moment) |
---|
728 | my $command = 'rdist -R'; |
---|
729 | $command .= ' -q' unless $self->verbose > 1; |
---|
730 | $command .= ' -f - 1>/dev/null'; |
---|
731 | |
---|
732 | # Diagnostic |
---|
733 | my $croak = 'Cannot execute "' . $command . '"'; |
---|
734 | if ($self->verbose > 2) { |
---|
735 | print timestamp_command ($command, 'Start'); |
---|
736 | print ' ', $_ for (@distfile); |
---|
737 | } |
---|
738 | |
---|
739 | # Execute the mirroring command |
---|
740 | open COMMAND, '|-', $command or croak $croak, ' (', $!, '), abort'; |
---|
741 | for my $line (@distfile) { |
---|
742 | print COMMAND $line; |
---|
743 | } |
---|
744 | close COMMAND or croak $croak, ' (', $?, '), abort'; |
---|
745 | |
---|
746 | # Diagnostic |
---|
747 | print timestamp_command ($command, 'End ') if $self->verbose > 2; |
---|
748 | |
---|
749 | return 1; |
---|
750 | } |
---|
751 | |
---|
752 | # ------------------------------------------------------------------------------ |
---|
753 | # SYNOPSIS |
---|
754 | # $rc = $self->_mirror_with_rsync ($dest0, \@items); |
---|
755 | # |
---|
756 | # DESCRIPTION |
---|
757 | # This internal method implements $self->mirror with "rsync". |
---|
758 | # ------------------------------------------------------------------------------ |
---|
759 | |
---|
760 | sub _mirror_with_rsync { |
---|
761 | my ($self, $dest0, $items) = @_; |
---|
762 | |
---|
763 | my $rsh = $self->setting (qw/TOOL REMOTE_SHELL/); |
---|
764 | my $rhost = $self->authority ? $self->authority . ':' : ''; |
---|
765 | |
---|
766 | for my $label (@$items) { |
---|
767 | # Create container directory, as rsync does not do it automatically |
---|
768 | my $dir = dirname $self->$label; |
---|
769 | |
---|
770 | if ($self->authority) { |
---|
771 | # Create container directory using remote shell |
---|
772 | &run_command ( |
---|
773 | [$rsh, $self->machine, '-n', '-l', $self->logname, qw/mkdir -p/, $dir], |
---|
774 | TIME => $self->verbose > 2 |
---|
775 | ); |
---|
776 | |
---|
777 | } else { |
---|
778 | mkpath $dir; |
---|
779 | } |
---|
780 | |
---|
781 | # Build the rsync command |
---|
782 | my @command = ( |
---|
783 | qw/rsync -a --exclude='.*' --delete-excluded --timeout=900/, |
---|
784 | '--rsh=' . $rsh, |
---|
785 | ($self->verbose > 2 ? '-v' : ()), |
---|
786 | $dest0->$label, $rhost . $dir, |
---|
787 | ); |
---|
788 | |
---|
789 | # Execute command |
---|
790 | &run_command (\@command, TIME => $self->verbose > 2); |
---|
791 | } |
---|
792 | |
---|
793 | return 1; |
---|
794 | } |
---|
795 | |
---|
796 | # ------------------------------------------------------------------------------ |
---|
797 | # SYNOPSIS |
---|
798 | # $rc = $obj->set_lock (); |
---|
799 | # |
---|
800 | # DESCRIPTION |
---|
801 | # This method sets a lock in the current destination. |
---|
802 | # ------------------------------------------------------------------------------ |
---|
803 | |
---|
804 | sub set_lock { |
---|
805 | my $self = shift; |
---|
806 | |
---|
807 | $self->lockfile (); |
---|
808 | |
---|
809 | if ($self->type eq 'ext' and not $self->dest0) { |
---|
810 | # Only set an extract lock for the local destination |
---|
811 | $self->lockfile ($self->extlock); |
---|
812 | |
---|
813 | } elsif ($self->type eq 'bld') { |
---|
814 | # Set a build lock |
---|
815 | $self->lockfile ($self->bldlock); |
---|
816 | } |
---|
817 | |
---|
818 | return &touch_file ($self->lockfile) if $self->lockfile; |
---|
819 | } |
---|
820 | |
---|
821 | # ------------------------------------------------------------------------------ |
---|
822 | # SYNOPSIS |
---|
823 | # @cfglines = $obj->to_cfglines ([$index]); |
---|
824 | # |
---|
825 | # DESCRIPTION |
---|
826 | # This method returns a list of configuration lines for the current |
---|
827 | # destination. If it is set, $index is the index number of the current |
---|
828 | # destination. |
---|
829 | # ------------------------------------------------------------------------------ |
---|
830 | |
---|
831 | sub to_cfglines { |
---|
832 | my ($self, $index) = @_; |
---|
833 | |
---|
834 | my @return = (); |
---|
835 | |
---|
836 | my $dest_label = $self->cfglabel ($self->dest0 ? 'RDEST' : 'DEST'); |
---|
837 | |
---|
838 | push @return, Fcm::CfgLine->new ( |
---|
839 | label => $dest_label . ($index ? $Fcm::Config::DELIMITER . $index : ''), |
---|
840 | value => $self->rootdir, |
---|
841 | ); |
---|
842 | |
---|
843 | if ($self->dest0) { |
---|
844 | my @names = (); |
---|
845 | push @names, 'logname' |
---|
846 | if $self->logname ne $self->config->user_id; |
---|
847 | push @names, 'mirror_cmd' |
---|
848 | if $self->mirror_cmd ne $self->setting (qw/TOOL MIRROR/); |
---|
849 | push @names, 'machine' |
---|
850 | if $self->machine ne &hostname (); |
---|
851 | |
---|
852 | for my $name (@names) { |
---|
853 | my $sub_label = $Fcm::Config::DELIMITER . uc ($name) |
---|
854 | . ($index ? $Fcm::Config::DELIMITER . $index : ''); |
---|
855 | push @return, Fcm::CfgLine->new ( |
---|
856 | label => $dest_label . $sub_label, value => $self->$name, |
---|
857 | ); |
---|
858 | } |
---|
859 | } |
---|
860 | |
---|
861 | return @return; |
---|
862 | } |
---|
863 | |
---|
864 | # ------------------------------------------------------------------------------ |
---|
865 | # SYNOPSIS |
---|
866 | # $string = $obj->write_rules (); |
---|
867 | # |
---|
868 | # DESCRIPTION |
---|
869 | # This method returns a string containing Makefile variable declarations for |
---|
870 | # directories and search paths in this destination. |
---|
871 | # ------------------------------------------------------------------------------ |
---|
872 | |
---|
873 | sub write_rules { |
---|
874 | my $self = shift; |
---|
875 | my $return = ''; |
---|
876 | |
---|
877 | # FCM_*DIR* |
---|
878 | for my $i (0 .. @{ $self->inherit }) { |
---|
879 | for my $name (@paths) { |
---|
880 | (my $label = $name) =~ s/path$/dir/; |
---|
881 | my $dir = $name eq 'rootpath' ? $self->$name->[$i] : File::Spec->catfile ( |
---|
882 | '$(FCM_ROOTDIR' . ($i ? $i : '') . ')', |
---|
883 | File::Spec->abs2rel ($self->$name->[$i], $self->rootpath->[$i]), |
---|
884 | ); |
---|
885 | |
---|
886 | $return .= ($i ? '' : 'export ') . 'FCM_' . uc ($label) . ($i ? $i : '') . |
---|
887 | ' := ' . $dir . "\n"; |
---|
888 | } |
---|
889 | } |
---|
890 | |
---|
891 | # FCM_*PATH |
---|
892 | for my $name (@paths) { |
---|
893 | (my $label = $name) =~ s/path$/dir/; |
---|
894 | |
---|
895 | $return .= 'export FCM_' . uc ($name) . ' := '; |
---|
896 | for my $i (0 .. @{ $self->$name } - 1) { |
---|
897 | $return .= ($i ? ':' : '') . '$(FCM_' . uc ($label) . ($i ? $i : '') . ')'; |
---|
898 | } |
---|
899 | $return .= "\n"; |
---|
900 | } |
---|
901 | |
---|
902 | $return .= "\n"; |
---|
903 | |
---|
904 | return $return; |
---|
905 | } |
---|
906 | |
---|
907 | # ------------------------------------------------------------------------------ |
---|
908 | |
---|
909 | 1; |
---|
910 | |
---|
911 | __END__ |
---|