Dest.pm 26 KB

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