SrcDirLayer.pm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. # ------------------------------------------------------------------------------
  2. # NAME
  3. # Fcm::SrcDirLayer
  4. #
  5. # DESCRIPTION
  6. # This class contains methods to manipulate the extract of a source
  7. # directory from a branch of a (Subversion) repository.
  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::SrcDirLayer;
  17. use base qw{Fcm::Base};
  18. use Fcm::Util qw{run_command e_report w_report};
  19. use File::Basename qw{dirname};
  20. use File::Path qw{mkpath};
  21. use File::Spec;
  22. # List of property methods for this class
  23. my @scalar_properties = (
  24. 'cachedir', # cache directory for this directory branch
  25. 'commit', # revision at which the source directory was changed
  26. 'extracted', # is this branch already extracted?
  27. 'files', # list of source files in this directory branch
  28. 'location', # location of the source directory in the branch
  29. 'name', # sub-package name of the source directory
  30. 'package', # top level package name of which the current repository belongs
  31. 'reposroot', # repository root URL
  32. 'revision', # revision of the repository branch
  33. 'tag', # package/revision tag of the current repository branch
  34. 'type', # type of the repository branch ("svn" or "user")
  35. );
  36. my %ERR_MESS_OF = (
  37. CACHE_WRITE => '%s: cannot write to cache',
  38. SYMLINK => '%s/%s: ignore symbolic link',
  39. VC_TYPE => '%s: repository type not supported',
  40. );
  41. # ------------------------------------------------------------------------------
  42. # SYNOPSIS
  43. # $obj = Fcm::SrcDirLayer->new (%args);
  44. #
  45. # DESCRIPTION
  46. # This method constructs a new instance of the Fcm::SrcDirLayer class. See
  47. # above for allowed list of properties. (KEYS should be in uppercase.)
  48. # ------------------------------------------------------------------------------
  49. sub new {
  50. my $this = shift;
  51. my %args = @_;
  52. my $class = ref $this || $this;
  53. my $self = Fcm::Base->new (%args);
  54. for (@scalar_properties) {
  55. $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
  56. }
  57. bless $self, $class;
  58. return $self;
  59. }
  60. # ------------------------------------------------------------------------------
  61. # SYNOPSIS
  62. # $value = $obj->X;
  63. # $obj->X ($value);
  64. #
  65. # DESCRIPTION
  66. # Details of these properties are explained in @scalar_properties.
  67. # ------------------------------------------------------------------------------
  68. for my $name (@scalar_properties) {
  69. no strict 'refs';
  70. *$name = sub {
  71. my $self = shift;
  72. # Argument specified, set property to specified argument
  73. if (@_) {
  74. $self->{$name} = $_[0];
  75. }
  76. # Default value for property
  77. if (not defined $self->{$name}) {
  78. if ($name eq 'files') {
  79. # Reference to an array
  80. $self->{$name} = [];
  81. }
  82. }
  83. return $self->{$name};
  84. }
  85. }
  86. # Handles error/warning events.
  87. sub _err {
  88. my ($key, $args_ref, $warn_only) = @_;
  89. my $reporter = $warn_only ? \&w_report : \&e_report;
  90. $args_ref ||= [];
  91. $reporter->(sprintf($ERR_MESS_OF{$key} . ".\n", @{$args_ref}));
  92. }
  93. # ------------------------------------------------------------------------------
  94. # SYNOPSIS
  95. # $dir = $obj->localdir;
  96. #
  97. # DESCRIPTION
  98. # This method returns the user or cache directory for the current revision
  99. # of the repository branch.
  100. # ------------------------------------------------------------------------------
  101. sub localdir {
  102. my $self = shift;
  103. return $self->user ? $self->location : $self->cachedir;
  104. }
  105. # ------------------------------------------------------------------------------
  106. # SYNOPSIS
  107. # $user = $obj->user;
  108. #
  109. # DESCRIPTION
  110. # This method returns the string "user" if the current source directory
  111. # branch is a local directory. Otherwise, it returns "undef".
  112. # ------------------------------------------------------------------------------
  113. sub user {
  114. my $self = shift;
  115. return $self->type eq 'user' ? 'user' : undef;
  116. }
  117. # ------------------------------------------------------------------------------
  118. # SYNOPSIS
  119. # $rev = $obj->get_commit;
  120. #
  121. # DESCRIPTION
  122. # If the current repository type is "svn", this method attempts to obtain
  123. # the revision in which the branch is last committed. On a successful
  124. # operation, it returns this revision number. Otherwise, it returns
  125. # "undef".
  126. # ------------------------------------------------------------------------------
  127. sub get_commit {
  128. my $self = shift;
  129. if ($self->type eq 'svn') {
  130. # Execute the "svn info" command
  131. my @lines = &run_command (
  132. [qw/svn info -r/, $self->revision, $self->location . '@' . $self->revision],
  133. METHOD => 'qx', TIME => $self->config->verbose > 2,
  134. );
  135. my $rev;
  136. for (@lines) {
  137. if (/^Last\s+Changed\s+Rev\s*:\s*(\d+)/i) {
  138. $rev = $1;
  139. last;
  140. }
  141. }
  142. # Commit revision of this source directory
  143. $self->commit ($rev);
  144. return $self->commit;
  145. } elsif ($self->type eq 'user') {
  146. return;
  147. } else {
  148. _err('VC_TYPE', [$self->type()]);
  149. }
  150. }
  151. # ------------------------------------------------------------------------------
  152. # SYNOPSIS
  153. # $rc = $obj->update_cache;
  154. #
  155. # DESCRIPTION
  156. # If the current repository type is "svn", this method attempts to extract
  157. # the current revision source directory from the current branch from the
  158. # repository, sending the output to the cache directory. It returns true on
  159. # a successful operation, or false if the repository is not of type "svn".
  160. # ------------------------------------------------------------------------------
  161. sub update_cache {
  162. my $self = shift;
  163. return unless $self->cachedir;
  164. # Create cache extract destination, if necessary
  165. my $dirname = dirname $self->cachedir;
  166. mkpath($dirname);
  167. if (!-w $dirname) {
  168. _err('CACHE_WRITE', [$dirname]);
  169. }
  170. if ($self->type eq 'svn') {
  171. # Set up the extract command, "svn export --force -q -N"
  172. my @command = (
  173. qw/svn export --force -q -N/,
  174. $self->location . '@' . $self->revision,
  175. $self->cachedir,
  176. );
  177. &run_command (\@command, TIME => $self->config->verbose > 2);
  178. } elsif ($self->type eq 'user') {
  179. return;
  180. } else {
  181. _err('VC_TYPE', [$self->type()]);
  182. }
  183. return 1;
  184. }
  185. # ------------------------------------------------------------------------------
  186. # SYNOPSIS
  187. # @files = $obj->get_files();
  188. #
  189. # DESCRIPTION
  190. # This method returns a list of file base names in the (cache of) this source
  191. # directory in the current branch.
  192. # ------------------------------------------------------------------------------
  193. sub get_files {
  194. my ($self) = @_;
  195. opendir(my $dir, $self->localdir())
  196. || die($self->localdir(), ': cannot read directory');
  197. my @base_names = ();
  198. BASE_NAME:
  199. while (my $base_name = readdir($dir)) {
  200. if ($base_name =~ qr{\A\.}xms || $base_name =~ qr{~\z}xms) {
  201. next BASE_NAME;
  202. }
  203. my $path = File::Spec->catfile($self->localdir(), $base_name);
  204. if (-d $path) {
  205. next BASE_NAME;
  206. }
  207. if (-l $path) {
  208. _err('SYMLINK', [$self->location(), $base_name], 1);
  209. next BASE_NAME;
  210. }
  211. push(@base_names, $base_name);
  212. }
  213. closedir($dir);
  214. $self->files(\@base_names);
  215. return @base_names;
  216. }
  217. # ------------------------------------------------------------------------------
  218. 1;
  219. __END__