123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- # ------------------------------------------------------------------------------
- # NAME
- # Fcm::SrcDirLayer
- #
- # DESCRIPTION
- # This class contains methods to manipulate the extract of a source
- # directory from a branch of a (Subversion) repository.
- #
- # COPYRIGHT
- # (C) Crown copyright Met Office. All rights reserved.
- # For further details please refer to the file COPYRIGHT.txt
- # which you should have received as part of this distribution.
- # ------------------------------------------------------------------------------
- use warnings;
- use strict;
- package Fcm::SrcDirLayer;
- use base qw{Fcm::Base};
- use Fcm::Util qw{run_command e_report w_report};
- use File::Basename qw{dirname};
- use File::Path qw{mkpath};
- use File::Spec;
- # List of property methods for this class
- my @scalar_properties = (
- 'cachedir', # cache directory for this directory branch
- 'commit', # revision at which the source directory was changed
- 'extracted', # is this branch already extracted?
- 'files', # list of source files in this directory branch
- 'location', # location of the source directory in the branch
- 'name', # sub-package name of the source directory
- 'package', # top level package name of which the current repository belongs
- 'reposroot', # repository root URL
- 'revision', # revision of the repository branch
- 'tag', # package/revision tag of the current repository branch
- 'type', # type of the repository branch ("svn" or "user")
- );
- my %ERR_MESS_OF = (
- CACHE_WRITE => '%s: cannot write to cache',
- SYMLINK => '%s/%s: ignore symbolic link',
- VC_TYPE => '%s: repository type not supported',
- );
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $obj = Fcm::SrcDirLayer->new (%args);
- #
- # DESCRIPTION
- # This method constructs a new instance of the Fcm::SrcDirLayer class. See
- # above for allowed list of properties. (KEYS should be in uppercase.)
- # ------------------------------------------------------------------------------
- sub new {
- my $this = shift;
- my %args = @_;
- my $class = ref $this || $this;
- my $self = Fcm::Base->new (%args);
- for (@scalar_properties) {
- $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
- }
- bless $self, $class;
- return $self;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $value = $obj->X;
- # $obj->X ($value);
- #
- # DESCRIPTION
- # Details of these properties are explained in @scalar_properties.
- # ------------------------------------------------------------------------------
- for my $name (@scalar_properties) {
- no strict 'refs';
- *$name = sub {
- my $self = shift;
- # Argument specified, set property to specified argument
- if (@_) {
- $self->{$name} = $_[0];
- }
- # Default value for property
- if (not defined $self->{$name}) {
- if ($name eq 'files') {
- # Reference to an array
- $self->{$name} = [];
- }
- }
- return $self->{$name};
- }
- }
- # Handles error/warning events.
- sub _err {
- my ($key, $args_ref, $warn_only) = @_;
- my $reporter = $warn_only ? \&w_report : \&e_report;
- $args_ref ||= [];
- $reporter->(sprintf($ERR_MESS_OF{$key} . ".\n", @{$args_ref}));
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $dir = $obj->localdir;
- #
- # DESCRIPTION
- # This method returns the user or cache directory for the current revision
- # of the repository branch.
- # ------------------------------------------------------------------------------
- sub localdir {
- my $self = shift;
- return $self->user ? $self->location : $self->cachedir;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $user = $obj->user;
- #
- # DESCRIPTION
- # This method returns the string "user" if the current source directory
- # branch is a local directory. Otherwise, it returns "undef".
- # ------------------------------------------------------------------------------
- sub user {
- my $self = shift;
- return $self->type eq 'user' ? 'user' : undef;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rev = $obj->get_commit;
- #
- # DESCRIPTION
- # If the current repository type is "svn", this method attempts to obtain
- # the revision in which the branch is last committed. On a successful
- # operation, it returns this revision number. Otherwise, it returns
- # "undef".
- # ------------------------------------------------------------------------------
- sub get_commit {
- my $self = shift;
- if ($self->type eq 'svn') {
- # Execute the "svn info" command
- my @lines = &run_command (
- [qw/svn info -r/, $self->revision, $self->location . '@' . $self->revision],
- METHOD => 'qx', TIME => $self->config->verbose > 2,
- );
- my $rev;
- for (@lines) {
- if (/^Last\s+Changed\s+Rev\s*:\s*(\d+)/i) {
- $rev = $1;
- last;
- }
- }
- # Commit revision of this source directory
- $self->commit ($rev);
- return $self->commit;
- } elsif ($self->type eq 'user') {
- return;
- } else {
- _err('VC_TYPE', [$self->type()]);
- }
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $rc = $obj->update_cache;
- #
- # DESCRIPTION
- # If the current repository type is "svn", this method attempts to extract
- # the current revision source directory from the current branch from the
- # repository, sending the output to the cache directory. It returns true on
- # a successful operation, or false if the repository is not of type "svn".
- # ------------------------------------------------------------------------------
- sub update_cache {
- my $self = shift;
- return unless $self->cachedir;
- # Create cache extract destination, if necessary
- my $dirname = dirname $self->cachedir;
- mkpath($dirname);
- if (!-w $dirname) {
- _err('CACHE_WRITE', [$dirname]);
- }
-
- if ($self->type eq 'svn') {
- # Set up the extract command, "svn export --force -q -N"
- my @command = (
- qw/svn export --force -q -N/,
- $self->location . '@' . $self->revision,
- $self->cachedir,
- );
- &run_command (\@command, TIME => $self->config->verbose > 2);
- } elsif ($self->type eq 'user') {
- return;
- } else {
- _err('VC_TYPE', [$self->type()]);
- }
- return 1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # @files = $obj->get_files();
- #
- # DESCRIPTION
- # This method returns a list of file base names in the (cache of) this source
- # directory in the current branch.
- # ------------------------------------------------------------------------------
- sub get_files {
- my ($self) = @_;
- opendir(my $dir, $self->localdir())
- || die($self->localdir(), ': cannot read directory');
- my @base_names = ();
- BASE_NAME:
- while (my $base_name = readdir($dir)) {
- if ($base_name =~ qr{\A\.}xms || $base_name =~ qr{~\z}xms) {
- next BASE_NAME;
- }
- my $path = File::Spec->catfile($self->localdir(), $base_name);
- if (-d $path) {
- next BASE_NAME;
- }
- if (-l $path) {
- _err('SYMLINK', [$self->location(), $base_name], 1);
- next BASE_NAME;
- }
- push(@base_names, $base_name);
- }
- closedir($dir);
- $self->files(\@base_names);
- return @base_names;
- }
- # ------------------------------------------------------------------------------
- 1;
- __END__
|