ConfigSystem.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. # ------------------------------------------------------------------------------
  2. # NAME
  3. # Fcm::ConfigSystem
  4. #
  5. # DESCRIPTION
  6. # This is the base class for FCM systems that are based on inherited
  7. # configuration files, e.g. the extract and the build systems.
  8. #
  9. # COPYRIGHT
  10. # (C) Crown copyright Met Office. All rights reserved.
  11. # For further details please refer to the file COPYRIGHT.txt
  12. # which you should have received as part of this distribution.
  13. # ------------------------------------------------------------------------------
  14. package Fcm::ConfigSystem;
  15. use base qw{Fcm::Base};
  16. use strict;
  17. use warnings;
  18. use Fcm::CfgFile;
  19. use Fcm::CfgLine;
  20. use Fcm::Dest;
  21. use Fcm::Util qw{expand_tilde e_report w_report};
  22. use Sys::Hostname qw{hostname};
  23. # List of property methods for this class
  24. my @scalar_properties = (
  25. 'cfg', # configuration file
  26. 'cfg_methods', # list of sub-methods for parse_cfg
  27. 'cfg_prefix', # optional prefix in configuration declaration
  28. 'dest', # destination for output
  29. 'inherit', # list of inherited configurations
  30. 'inherited', # list of inheritance hierarchy
  31. 'type', # system type
  32. );
  33. # ------------------------------------------------------------------------------
  34. # SYNOPSIS
  35. # $obj = Fcm::ConfigSystem->new;
  36. #
  37. # DESCRIPTION
  38. # This method constructs a new instance of the Fcm::ConfigSystem class.
  39. # ------------------------------------------------------------------------------
  40. sub new {
  41. my $this = shift;
  42. my %args = @_;
  43. my $class = ref $this || $this;
  44. my $self = Fcm::Base->new (%args);
  45. $self->{$_} = undef for (@scalar_properties);
  46. bless $self, $class;
  47. # List of sub-methods for parse_cfg
  48. $self->cfg_methods ([qw/header inherit dest/]);
  49. return $self;
  50. }
  51. # ------------------------------------------------------------------------------
  52. # SYNOPSIS
  53. # $value = $obj->X;
  54. # $obj->X ($value);
  55. #
  56. # DESCRIPTION
  57. # Details of these properties are explained in @scalar_properties.
  58. # ------------------------------------------------------------------------------
  59. for my $name (@scalar_properties) {
  60. no strict 'refs';
  61. *$name = sub {
  62. my $self = shift;
  63. # Argument specified, set property to specified argument
  64. if (@_) {
  65. $self->{$name} = $_[0];
  66. }
  67. # Default value for property
  68. if (not defined $self->{$name}) {
  69. if ($name eq 'cfg') {
  70. # New configuration file
  71. $self->{$name} = Fcm::CfgFile->new (TYPE => $self->type);
  72. } elsif ($name =~ /^(?:cfg_methods|inherit|inherited)$/) {
  73. # Reference to an array
  74. $self->{$name} = [];
  75. } elsif ($name eq 'cfg_prefix' or $name eq 'type') {
  76. # Reference to an array
  77. $self->{$name} = '';
  78. } elsif ($name eq 'dest') {
  79. # New destination
  80. $self->{$name} = Fcm::Dest->new (TYPE => $self->type);
  81. }
  82. }
  83. return $self->{$name};
  84. }
  85. }
  86. # ------------------------------------------------------------------------------
  87. # SYNOPSIS
  88. # ($rc, $out_of_date) = $obj->check_cache ();
  89. #
  90. # DESCRIPTION
  91. # This method returns $rc = 1 on success or undef on failure. It returns
  92. # $out_of_date = 1 if current cache file is out of date relative to those in
  93. # inherited runs or 0 otherwise.
  94. # ------------------------------------------------------------------------------
  95. sub check_cache {
  96. my $self = shift;
  97. my $rc = 1;
  98. my $out_of_date = 0;
  99. if (@{ $self->inherit } and -f $self->dest->cache) {
  100. # Get modification time of current cache file
  101. my $cur_mtime = (stat ($self->dest->cache))[9];
  102. # Compare with modification times of inherited cache files
  103. for my $use (@{ $self->inherit }) {
  104. next unless -f $use->dest->cache;
  105. my $use_mtime = (stat ($use->dest->cache))[9];
  106. $out_of_date = 1 if $use_mtime > $cur_mtime;
  107. }
  108. }
  109. return ($rc, $out_of_date);
  110. }
  111. # ------------------------------------------------------------------------------
  112. # SYNOPSIS
  113. # $rc = $obj->check_lock ();
  114. #
  115. # DESCRIPTION
  116. # This method returns true if no lock is found in the destination or if the
  117. # locks found are allowed.
  118. # ------------------------------------------------------------------------------
  119. sub check_lock {
  120. my $self = shift;
  121. # Check all types of locks
  122. for my $method (@Fcm::Dest::lockfiles) {
  123. my $lock = $self->dest->$method;
  124. # Check whether lock exists
  125. next unless -e $lock;
  126. # Check whether this lock is allowed
  127. next if $self->check_lock_is_allowed ($lock);
  128. # Throw error if a lock exists
  129. w_report 'ERROR: ', $lock, ': lock file exists,';
  130. w_report ' ', $self->dest->rootdir, ': destination is busy.';
  131. return;
  132. }
  133. return 1;
  134. }
  135. # ------------------------------------------------------------------------------
  136. # SYNOPSIS
  137. # $rc = $self->check_lock_is_allowed ($lock);
  138. #
  139. # DESCRIPTION
  140. # This method returns true if it is OK for $lock to exist in the destination.
  141. # ------------------------------------------------------------------------------
  142. sub check_lock_is_allowed {
  143. my ($self, $lock) = @_;
  144. # Disallow all types of locks by default
  145. return 0;
  146. }
  147. # ------------------------------------------------------------------------------
  148. # SYNOPSIS
  149. # $rc = $self->compare_setting (
  150. # METHOD_LIST => \@method_list,
  151. # [METHOD_ARGS => \@method_args,]
  152. # [CACHEBASE => $cachebase,]
  153. # );
  154. #
  155. # DESCRIPTION
  156. # This method gets settings from the previous cache and updates the current.
  157. #
  158. # METHOD
  159. # The method returns true on success. @method_list must be a list of method
  160. # names for processing the cached lines in the previous run. If an existing
  161. # cache exists, its content is read into $old_lines, which is a list of
  162. # Fcm::CfgLine objects. Otherwise, $old_lines is set to undef. If $cachebase
  163. # is set, it is used for as the cache basename. Otherwise, the default for
  164. # the current system is used. It calls each method in the @method_list using
  165. # $self->$method ($old_lines, @method_args), which should return a
  166. # two-element list. The first element should be a return code (1 for out of
  167. # date, 0 for up to date and undef for failure). The second element should be
  168. # a reference to a list of Fcm::CfgLine objects for the output.
  169. # ------------------------------------------------------------------------------
  170. sub compare_setting {
  171. my ($self, %args) = @_;
  172. my @method_list = exists ($args{METHOD_LIST}) ? @{ $args{METHOD_LIST} } : ();
  173. my @method_args = exists ($args{METHOD_ARGS}) ? @{ $args{METHOD_ARGS} } : ();
  174. my $cachebase = exists ($args{CACHEBASE}) ? $args{CACHEBASE} : undef;
  175. my $rc = 1;
  176. # Read cache if the file exists
  177. # ----------------------------------------------------------------------------
  178. my $cache = $cachebase
  179. ? File::Spec->catfile ($self->dest->cachedir, $cachebase)
  180. : $self->dest->cache;
  181. my @in_caches = ();
  182. if (-r $cache) {
  183. push @in_caches, $cache;
  184. } else {
  185. for my $use (@{ $self->inherit }) {
  186. my $use_cache = $cachebase
  187. ? File::Spec->catfile ($use->dest->cachedir, $cachebase)
  188. : $use->dest->cache;
  189. push @in_caches, $use_cache if -r $use_cache;
  190. }
  191. }
  192. my $old_lines = undef;
  193. for my $in_cache (@in_caches) {
  194. next unless -r $in_cache;
  195. my $cfg = Fcm::CfgFile->new (SRC => $in_cache);
  196. if ($cfg->read_cfg) {
  197. $old_lines = [] if not defined $old_lines;
  198. push @$old_lines, @{ $cfg->lines };
  199. }
  200. }
  201. # Call methods in @method_list to see if cache is out of date
  202. # ----------------------------------------------------------------------------
  203. my @new_lines = ();
  204. my $out_of_date = 0;
  205. for my $method (@method_list) {
  206. my ($return, $lines);
  207. ($return, $lines) = $self->$method ($old_lines, @method_args) if $rc;
  208. if (defined $return) {
  209. # Method succeeded
  210. push @new_lines, @$lines;
  211. $out_of_date = 1 if $return;
  212. } else {
  213. # Method failed
  214. $rc = $return;
  215. last;
  216. }
  217. }
  218. # Update the cache in the current run
  219. # ----------------------------------------------------------------------------
  220. if ($rc) {
  221. if (@{ $self->inherited } and $out_of_date) {
  222. # If this is an inherited configuration, the cache must not be changed
  223. w_report 'ERROR: ', $self->cfg->src,
  224. ': inherited configuration does not match with its cache.';
  225. $rc = undef;
  226. } elsif ((not -f $cache) or $out_of_date) {
  227. my $cfg = Fcm::CfgFile->new;
  228. $cfg->lines ([sort {$a->label cmp $b->label} @new_lines]);
  229. $rc = $cfg->print_cfg ($cache, 1);
  230. }
  231. }
  232. return $rc;
  233. }
  234. # ------------------------------------------------------------------------------
  235. # SYNOPSIS
  236. # ($changed_hash_ref, $new_lines_array_ref) =
  237. # $self->compare_setting_in_config($prefix, \@old_lines);
  238. #
  239. # DESCRIPTION
  240. # This method compares old and current settings for a specified item.
  241. #
  242. # METHOD
  243. # This method does two things.
  244. #
  245. # It uses the current configuration for the $prefix item to generate a list of
  246. # new Fcm::CfgLine objects (which is returned as a reference in the second
  247. # element of the returned list).
  248. #
  249. # The values of the old lines are then compared with those of the new lines.
  250. # Any settings that are changed are stored in a hash, which is returned as a
  251. # reference in the first element of the returned list. The key of the hash is
  252. # the name of the changed setting, and the value is the value of the new
  253. # setting or undef if the setting no longer exists.
  254. #
  255. # ARGUMENTS
  256. # $prefix - the name of an item in Fcm::Config to be compared
  257. # @old_lines - a list of Fcm::CfgLine objects containing the old settings
  258. # ------------------------------------------------------------------------------
  259. sub compare_setting_in_config {
  260. my ($self, $prefix, $old_lines_ref) = @_;
  261. my %changed = %{$self->setting($prefix)};
  262. my (@new_lines, %new_val_of);
  263. while (my ($key, $val) = each(%changed)) {
  264. $new_val_of{$key} = (ref($val) eq 'ARRAY' ? join(q{ }, sort(@{$val})) : $val);
  265. push(@new_lines, Fcm::CfgLine->new(
  266. LABEL => $prefix . $Fcm::Config::DELIMITER . $key,
  267. VALUE => $new_val_of{$key},
  268. ));
  269. }
  270. if (defined($old_lines_ref)) {
  271. my %old_val_of
  272. = map {($_->label_from_field(1), $_->value())} # converts into a hash
  273. grep {$_->label_starts_with($prefix)} # gets relevant lines
  274. @{$old_lines_ref};
  275. while (my ($key, $val) = each(%old_val_of)) {
  276. if (exists($changed{$key})) {
  277. if ($val eq $new_val_of{$key}) { # no change from old to new
  278. delete($changed{$key});
  279. }
  280. }
  281. else { # exists in old but not in new
  282. $changed{$key} = undef;
  283. }
  284. }
  285. }
  286. return (\%changed, \@new_lines);
  287. }
  288. # ------------------------------------------------------------------------------
  289. # SYNOPSIS
  290. # $rc = $obj->invoke ([CLEAN => 1, ]%args);
  291. #
  292. # DESCRIPTION
  293. # This method invokes the system. If CLEAN is set to true, it will only parse
  294. # the configuration and set up the destination, but will not invoke the
  295. # system. See the invoke_setup_dest and the invoke_system methods for list of
  296. # other arguments in %args.
  297. # ------------------------------------------------------------------------------
  298. sub invoke {
  299. my $self = shift;
  300. my %args = @_;
  301. # Print diagnostic at beginning of run
  302. # ----------------------------------------------------------------------------
  303. # Name of the system
  304. (my $name = ref ($self)) =~ s/^Fcm:://;
  305. # Print start time on system run, if verbose is true
  306. my $date = localtime;
  307. print $name, ' command started on ', $date, '.', "\n"
  308. if $self->verbose;
  309. # Start time (seconds since epoch)
  310. my $otime = time;
  311. # Parse the configuration file
  312. my $rc = $self->invoke_stage ('Parse configuration', 'parse_cfg');
  313. # Set up the destination
  314. $rc = $self->invoke_stage ('Setup destination', 'invoke_setup_dest', %args)
  315. if $rc;
  316. # Invoke the system
  317. # ----------------------------------------------------------------------------
  318. $rc = $self->invoke_system (%args) if $rc and not $args{CLEAN};
  319. # Remove empty directories
  320. $rc = $self->dest->clean (MODE => 'EMPTY') if $rc;
  321. # Print diagnostic at end of run
  322. # ----------------------------------------------------------------------------
  323. # Print lapse time at the end, if verbose is true
  324. if ($self->verbose) {
  325. my $total = time - $otime;
  326. my $s_str = $total > 1 ? 'seconds' : 'second';
  327. print '->TOTAL: ', $total, ' ', $s_str, "\n";
  328. }
  329. # Report end of system run
  330. $date = localtime;
  331. if ($rc) {
  332. # Success
  333. print $name, ' command finished on ', $date, '.', "\n"
  334. if $self->verbose;
  335. } else {
  336. # Failure
  337. e_report $name, ' failed on ', $date, '.';
  338. }
  339. return $rc;
  340. }
  341. # ------------------------------------------------------------------------------
  342. # SYNOPSIS
  343. # $rc = $obj->invoke_setup_dest ([CLEAN|FULL => 1], [IGNORE_LOCK => 1]);
  344. #
  345. # DESCRIPTION
  346. # This method sets up the destination and returns true on success.
  347. #
  348. # ARGUMENTS
  349. # CLEAN|FULL - If set to "true", set up the system in "clean|full" mode.
  350. # Sub-directories and files in the root directory created by
  351. # the previous invocation of the system will be removed. If
  352. # not set, the default is to run in "incremental" mode.
  353. # IGNORE_LOCK - If set to "true", it ignores any lock files that may exist in
  354. # the destination root directory.
  355. # ------------------------------------------------------------------------------
  356. sub invoke_setup_dest {
  357. my $self = shift;
  358. my %args = @_;
  359. # Set up destination
  360. # ----------------------------------------------------------------------------
  361. # Print destination in verbose mode
  362. if ($self->verbose()) {
  363. printf(
  364. "Destination: %s@%s:%s\n",
  365. scalar(getpwuid($<)),
  366. hostname(),
  367. $self->dest()->rootdir(),
  368. );
  369. }
  370. my $rc = 1;
  371. my $out_of_date = 0;
  372. # Check whether lock exists in the destination root
  373. $rc = $self->check_lock if $rc and not $args{IGNORE_LOCK};
  374. # Check whether current cache is out of date relative to the inherited ones
  375. ($rc, $out_of_date) = $self->check_cache if $rc;
  376. # Remove sub-directories and files in destination in "full" mode
  377. $rc = $self->dest->clean (MODE => 'ALL')
  378. if $rc and ($args{FULL} or $args{CLEAN} or $out_of_date);
  379. # Create build root directory if necessary
  380. $rc = $self->dest->create if $rc;
  381. # Set a lock in the destination root
  382. $rc = $self->dest->set_lock if $rc;
  383. # Generate an as-parsed configuration file
  384. $self->cfg->print_cfg ($self->dest->parsedcfg);
  385. return $rc;
  386. }
  387. # ------------------------------------------------------------------------------
  388. # SYNOPSIS
  389. # $rc = $self->invoke_stage ($name, $method, @args);
  390. #
  391. # DESCRIPTION
  392. # This method invokes a named stage of the system, where $name is the name of
  393. # the stage, $method is the name of the method for invoking the stage and
  394. # @args are the arguments to the &method.
  395. # ------------------------------------------------------------------------------
  396. sub invoke_stage {
  397. my ($self, $name, $method, @args) = @_;
  398. # Print diagnostic at beginning of a stage
  399. print '->', $name, ': start', "\n" if $self->verbose;
  400. my $stime = time;
  401. # Invoke the stage
  402. my $rc = $self->$method (@args);
  403. # Print diagnostic at end of a stage
  404. my $total = time - $stime;
  405. my $s_str = $total > 1 ? 'seconds' : 'second';
  406. print '->', $name, ': ', $total, ' ', $s_str, "\n";
  407. return $rc;
  408. }
  409. # ------------------------------------------------------------------------------
  410. # SYNOPSIS
  411. # $rc = $self->invoke_system (%args);
  412. #
  413. # DESCRIPTION
  414. # This is a prototype method for invoking the system.
  415. # ------------------------------------------------------------------------------
  416. sub invoke_system {
  417. my $self = shift;
  418. my %args = @_;
  419. print "Dummy code.\n";
  420. return 0;
  421. }
  422. # ------------------------------------------------------------------------------
  423. # SYNOPSIS
  424. # $rc = $obj->parse_cfg ();
  425. #
  426. # DESCRIPTION
  427. # This method calls other methods to parse the configuration file.
  428. # ------------------------------------------------------------------------------
  429. sub parse_cfg {
  430. my $self = shift;
  431. return unless $self->cfg->src;
  432. # Read config file
  433. # ----------------------------------------------------------------------------
  434. return unless $self->cfg->read_cfg;
  435. if ($self->cfg->type ne $self->type) {
  436. w_report 'ERROR: ', $self->cfg->src, ': not a ', $self->type,
  437. ' config file.';
  438. return;
  439. }
  440. # Strip out optional prefix from all labels
  441. # ----------------------------------------------------------------------------
  442. if ($self->cfg_prefix) {
  443. for my $line (@{ $self->cfg->lines }) {
  444. $line->prefix ($self->cfg_prefix);
  445. }
  446. }
  447. # Filter lines from the configuration file
  448. # ----------------------------------------------------------------------------
  449. my @cfg_lines = grep {
  450. $_->slabel and # ignore empty/comment lines
  451. index ($_->slabel, '%') != 0 and # ignore user variable
  452. not $_->slabel_starts_with_cfg ('INC') # ignore INC line
  453. } @{ $self->cfg->lines };
  454. # Parse the lines to read in the various settings, by calling the methods:
  455. # $self->parse_cfg_XXX, where XXX is: header, inherit, dest, and the values
  456. # in the list @{ $self->cfg_methods }.
  457. # ----------------------------------------------------------------------------
  458. my $rc = 1;
  459. for my $name (@{ $self->cfg_methods }) {
  460. my $method = 'parse_cfg_' . $name;
  461. $self->$method (\@cfg_lines) or $rc = 0;
  462. }
  463. # Report warnings/errors
  464. # ----------------------------------------------------------------------------
  465. for my $line (@cfg_lines) {
  466. $rc = 0 if not $line->parsed;
  467. my $mesg = $line->format_error;
  468. w_report $mesg if $mesg;
  469. }
  470. return ($rc);
  471. }
  472. # ------------------------------------------------------------------------------
  473. # SYNOPSIS
  474. # $rc = $self->parse_cfg_dest (\@cfg_lines);
  475. #
  476. # DESCRIPTION
  477. # This method parses the destination settings in the @cfg_lines.
  478. # ------------------------------------------------------------------------------
  479. sub parse_cfg_dest {
  480. my ($self, $cfg_lines) = @_;
  481. my $rc = 1;
  482. # DEST/DIR declarations
  483. # ----------------------------------------------------------------------------
  484. my @lines = grep {
  485. $_->slabel_starts_with_cfg ('DEST') or $_->slabel_starts_with_cfg ('DIR')
  486. } @$cfg_lines;
  487. # Only ROOTDIR declarations are accepted
  488. for my $line (@lines) {
  489. my ($d, $method) = $line->slabel_fields;
  490. $d = lc $d;
  491. $method = lc $method;
  492. # Backward compatibility
  493. $d = 'dest' if $d eq 'dir';
  494. # Default to "rootdir"
  495. $method = 'rootdir' if (not $method) or $method eq 'root';
  496. # Only "rootdir" can be set
  497. next unless $method eq 'rootdir';
  498. $self->$d->$method (&expand_tilde ($line->value));
  499. $line->parsed (1);
  500. }
  501. # Make sure root directory is set
  502. # ----------------------------------------------------------------------------
  503. if (not $self->dest->rootdir) {
  504. w_report 'ERROR: ', $self->cfg->actual_src,
  505. ': destination root directory not set.';
  506. $rc = 0;
  507. }
  508. # Inherit destinations
  509. # ----------------------------------------------------------------------------
  510. for my $use (@{ $self->inherit }) {
  511. push @{ $self->dest->inherit }, (@{ $use->dest->inherit }, $use->dest);
  512. }
  513. return $rc;
  514. }
  515. # ------------------------------------------------------------------------------
  516. # SYNOPSIS
  517. # $rc = $self->parse_cfg_header (\@cfg_lines);
  518. #
  519. # DESCRIPTION
  520. # This method parses the header setting in the @cfg_lines.
  521. # ------------------------------------------------------------------------------
  522. sub parse_cfg_header {
  523. my ($self, $cfg_lines) = @_;
  524. # Set header lines as "parsed"
  525. map {$_->parsed (1)} grep {$_->slabel_starts_with_cfg ('CFGFILE')} @$cfg_lines;
  526. return 1;
  527. }
  528. # ------------------------------------------------------------------------------
  529. # SYNOPSIS
  530. # $rc = $self->parse_cfg_inherit (\@cfg_lines);
  531. #
  532. # DESCRIPTION
  533. # This method parses the inherit setting in the @cfg_lines.
  534. # ------------------------------------------------------------------------------
  535. sub parse_cfg_inherit {
  536. my ($self, $cfg_lines) = @_;
  537. # USE declaration
  538. # ----------------------------------------------------------------------------
  539. my @lines = grep {$_->slabel_starts_with_cfg ('USE')} @$cfg_lines;
  540. # Check for cyclic dependency
  541. if (@lines and grep {$_ eq $self->cfg->actual_src} @{ $self->inherited }) {
  542. # Error if current configuration file is in its own inheritance hierarchy
  543. w_report 'ERROR: ', $self->cfg->actual_src, ': attempt to inherit itself.';
  544. $_->error ($_->label . ': ignored due to cyclic dependency.') for (@lines);
  545. return 0;
  546. }
  547. my $rc = 1;
  548. for my $line (@lines) {
  549. # Invoke new instance of the current class
  550. my $use = ref ($self)->new;
  551. # Set configuration file, inheritance hierarchy
  552. # and attempt to parse the configuration
  553. $use->cfg->src (&expand_tilde ($line->value));
  554. $use->inherited ([$self->cfg->actual_src, @{ $self->inherited }]);
  555. $use->parse_cfg;
  556. # Add to list of inherit configurations
  557. push @{ $self->inherit }, $use;
  558. $line->parsed (1);
  559. }
  560. # Check locks in inherited destination
  561. # ----------------------------------------------------------------------------
  562. for my $use (@{ $self->inherit }) {
  563. $rc = 0 unless $use->check_lock;
  564. }
  565. return $rc;
  566. }
  567. # ------------------------------------------------------------------------------
  568. # SYNOPSIS
  569. # @cfglines = $obj->to_cfglines ();
  570. #
  571. # DESCRIPTION
  572. # This method returns the configuration lines of this object.
  573. # ------------------------------------------------------------------------------
  574. sub to_cfglines {
  575. my ($self) = @_;
  576. my @inherited_dests = map {
  577. Fcm::CfgLine->new (
  578. label => $self->cfglabel ('USE'), value => $_->dest->rootdir
  579. );
  580. } @{ $self->inherit };
  581. return (
  582. Fcm::CfgLine::comment_block ('File header'),
  583. Fcm::CfgLine->new (
  584. label => $self->cfglabel ('CFGFILE') . $Fcm::Config::DELIMITER . 'TYPE',
  585. value => $self->type,
  586. ),
  587. Fcm::CfgLine->new (
  588. label => $self->cfglabel ('CFGFILE') . $Fcm::Config::DELIMITER . 'VERSION',
  589. value => '1.0',
  590. ),
  591. Fcm::CfgLine->new (),
  592. @inherited_dests,
  593. Fcm::CfgLine::comment_block ('Destination'),
  594. ($self->dest->to_cfglines()),
  595. );
  596. }
  597. # ------------------------------------------------------------------------------
  598. 1;
  599. __END__