Test-Smoke

 view release on metacpan or  search on metacpan

lib/Test/Smoke/SourceTree.pm  view on Meta::CPAN

Get the directory.

=cut

sub tree_dir { return $_[0]->{tree_dir} }

=head2 $tree->verbose

Get verbosity.

=cut

sub verbose { return $_[0]->{verbose} }

=head2 $tree->canonpath( )

C<canonpath()> returns the canonical name for the path,
see L<File::Spec>.

=cut

sub canonpath {
    my $self = shift;
    return File::Spec->canonpath( $self->tree_dir );
}

=head2 $tree->rel2abs( [$base_dir] )

C<rel2abs()> returns the absolute path, see L<File::Spec>.

=cut

sub rel2abs {
    my $self = shift;
    return File::Spec->rel2abs( $self->tree_dir, @_ );
}

=head2 $tree->abs2rel( [$base_dir] )

C<abs2rel()> returns  a relative path,
see L<File::Spec>.

=cut

sub abs2rel {
    my $self = shift;
    return File::Spec->abs2rel( $self->tree_dir, @_ );
}

=head2 $tree->mani2abs( $file[, $base_path] )

C<mani2abs()> returns the absolute filename of C<$file>, which should
be in "MANIFEST" format (i.e. using '/' as directory separator).

=cut

sub mani2abs {
    my $self = shift;

    my $path = shift;
    my @dirs = split m{/+}, $path;
    my $file = pop @dirs;
    if ( $^O eq 'VMS' ) {
        my @parts = split m/\./, $file;
        my $last = pop @parts;
        @parts and
            $file = join( "_", map { s/[^\w-]/_/g; $_ } @parts ) . ".$last";
    }
    @dirs and $file = join '/', @dirs, $file;
    my @split_path = split m|/|, $file;
    my $base_path = File::Spec->rel2abs( $self->tree_dir, @_ );
    return File::Spec->catfile( $base_path, @split_path );
}

=head2 $tree->mani2absdir( $dir[, $base_path] )

C<mani2abs()> returns the absolute dirname of C<$dir>, which should
be in "MANIFEST" format (i.e. using '/' as directory separator).

=cut

sub mani2absdir {
    my $self = shift;

    my @split_path = split m|/|, shift;
    my $base_path = File::Spec->rel2abs( $self->tree_dir, @_ );
    return File::Spec->catdir( $base_path, @split_path );
}

=head2 $tree->abs2mani( $file )

C<abs2mani()> returns the MANIFEST style filename.

=cut

sub abs2mani {
    my $self = shift;
    my ($source_file) = @_;

    my $relfile = File::Spec->abs2rel(
        File::Spec->canonpath( $source_file ), $self->tree_dir
    );
    $self->log_debug("[abs2mani($source_file)] $relfile");

    my( undef, $directories, $file ) = File::Spec->splitpath( $relfile );
    my @dirs = grep $_ && length $_ => File::Spec->splitdir( $directories );
    push @dirs, $file;
    return join '/', @dirs;
}

=head2 $tree->check_MANIFEST( @ignore )

C<check_MANIFEST()> reads the B<MANIFEST> file from C<< $self->tree_dir >> and
compares it with the actual contents of C<< $self->tree_dir >>.

Returns a hashref with suspicious entries (if any) as keys that have a
value of either B<ST_MISSING> (not in directory) or B<ST_UNDECLARED>
(not in MANIFEST).

=cut

sub check_MANIFEST {
    my $self = shift;

    my %manifest = %{ $self->_read_mani_file( 'MANIFEST' ) };
    $self->log_debug("Found %d entries in MANIFEST", scalar(keys %manifest));

    my %ignore = map {
        my $entry = $NOCASE ? uc $_ : $_;
        $entry => undef
    } ( ".patch", "MANIFEST.SKIP", '.git', '.gitignore', '.mailmap', @_ ),
      keys %{ $self->_read_mani_file( 'MANIFEST.SKIP', 1 ) };
    $self->log_debug("Found %d entries in MANIFEST.SKIP", scalar(keys %ignore));

    # Walk the tree, remove all found files from %manifest
    # and add other files to %manifest
    # unless they are in the ignore list
    my $cwd = abs_path(File::Spec->curdir);
    chdir $self->tree_dir or die "Cannot chdir($self->tree_dir): $!";
    require File::Find;
    File::Find::find(
        sub {
            -f or return;
            my $cpath = File::Spec->canonpath($File::Find::name);
            my (undef, $dirs, $file) = File::Spec->splitpath($cpath);
            my @dirs = grep $_ && length $_ => File::Spec->splitdir($dirs);
            $^O eq 'VMS' and $file =~ s/\.$//;
            my $mani_name = join '/', @dirs, $file;
            $NOCASE and $mani_name = uc $mani_name;
            if (exists $manifest{$mani_name}) {
                $self->log_debug("[manicheck] Matched $mani_name");
                delete $manifest{$mani_name};
            }
            else {
                if (!grep $mani_name =~ /$_/, keys %ignore) {
                    $self->log_debug("[manicheck] Undeclared $mani_name");
                    $manifest{$mani_name} = ST_UNDECLARED;
                }
                else {
                    $self->log_debug("[manicheck] Skipped $mani_name");
                }
            }
        },
        '.'
    );
    chdir $cwd;
    $self->log_debug("[manicheck] %d entries missing", scalar(keys %manifest));

    return \%manifest;
}

=head2 $self->_read_mani_file( $path[, $no_croak] )

C<_read_mani_file()> reads the contents of C<$path> like it is a
MANIFEST typeof file and returns a ref to hash with all values set
C<ST_MISSING>.

=cut

sub _read_mani_file {
    my $self = shift;
    my( $path, $no_croak ) = @_;

    my $manifile = $self->mani2abs( $path );
    local *MANIFEST;
    open MANIFEST, "< $manifile" or do {
        $no_croak and return { };
        croak( "Can't open '$manifile': $!" );
    };

    my %manifest = map {
        m|(\S+)|;
        my $entry = $NOCASE ? uc $1 : $1;
        if ( $^O eq 'VMS' ) {
            my @dirs = split m|/|, $entry;
            my $file = pop @dirs;
            my @parts = split /[.@#]/, $file;
            if ( @parts > 1 ) {
                my $ext = ( pop @parts ) || '';
                $file = join( "_", @parts ) . ".$ext";
            }
            $entry = @dirs ? join( "/", @dirs, $file ) : $file;
        }
        ( $entry => ST_MISSING );
    } <MANIFEST>;
    close MANIFEST;

    return \%manifest;
}

=head2 $tree->clean_from_MANIFEST( )

C<clean_from_MANIFEST()> removes all files from the source-tree that are
not declared in the B<MANIFEST> file.

=cut

sub clean_from_MANIFEST {
    my $self = shift;

    my $mani_check = $self->check_MANIFEST( @_ );
    my @to_remove = grep {
        $mani_check->{ $_ } == ST_UNDECLARED
    } keys %$mani_check;

    foreach my $entry ( @to_remove ) {
        my $file = $self->mani2abs( $entry );
        1 while unlink $file;
    }
}

=head2 copy_from_MANIFEST( $dest_dir )

C<_copy_from_MANIFEST()> uses the B<MANIFEST> file from C<$self->tree_dir>
to copy a source-tree to C<< $dest_dir >>.

=cut

sub copy_from_MANIFEST {
    my ($self, $dest_dir) = @_;

    my $manifest = $self->mani2abs( 'MANIFEST' );

    local *MANIFEST;
    open MANIFEST, "< $manifest" or do {
        carp "Can't open '$manifest': $!\n";
        return undef;
    };

    $self->log_info("Reading from '%s'", $manifest);
    my @manifest_files = map {
        /^([^\s]+)/ ? $1 : $_
    } <MANIFEST>;
    close MANIFEST;
    my $dot_patch = $self->mani2abs( '.patch' );



( run in 0.667 second using v1.01-cache-2.11-cpan-71847e10f99 )