Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

########################################################################
# Copyright (C) 1998-2007 ERG Limited, All rights reserved
#
# Module name   : CCdiff.pl
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): JATS. This script is designed to be run under JATS
#
# Description   : Make ClearCase difference report suitable for uploading
#                 to Code Stricker.
#......................................................................#

require 5.6.1;
use strict;
use warnings;
use JatsError;
use JatsSystem;
use Pod::Usage;                             # required for help support
use Getopt::Long;

my $VERSION = "1.0.0";                      # Update this


#
#   Globals - Provided by the JATS environment
#
my $UNIX            = $ENV{'GBE_UNIX'};

#
#   Options
#
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
my $opt_help = 0;
my $opt_manual = 0;
my $opt_drive = $UNIX ? '/view' : 'o:';
my $opt_viewname = 'administration_view';
my $opt_outfile;
my @opt_vobs;
my $opt_new_label;
my $opt_old_label;

#
#   Globals
#
my @error_list;                             # ClearCmd detected errors
my $UNIX_VOB_PREFIX = '/vobs';
my $VOB_SEP         = $UNIX ? '/' : '\\';
my $view_path;
my @view_tags;
my %files;
my %diffs;

#
#   ROOT_VOBS is a list of VOBS too look in first
#   If a label is not found in these vobs, then the program will
#   look in all vobs. This list is a hint to speed up searching
#
my @ROOT_VOBS = qw( /LMOS /DPG_SWBase /DPG_SWCode /ProjectCD /MASS_Dev_Bus
                    /MASS_Dev_Infra /MOS /MASS_Dataman /MASS_Dev /MASS_Dev_Dataman
                    /COTS /GMPTE2005 /GMPTE2005_obe /MPR /MOS );



#-------------------------------------------------------------------------------
# Function        : Mainline Entry Point
#
# Description     :
#
# Inputs          :
#

#
#   Parse the user options
#
my $result = GetOptions (
                "help+"         => \$opt_help,              # flag, multiple use allowed
                "manual"        => sub{ $opt_help = 3},     # flag, multiple use allowed
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
                "out=s"         => \$opt_outfile,           # String
                "new=s"         => \$opt_new_label,         # String
                "oldput=s"      => \$opt_old_label,         # String
                "drive=s"       => \$opt_drive,             # String
                "vob=s"         => \@opt_vobs,              # String
                );

                #
                #   UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!
                #

#
#   Process help and manual options
#
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result );
pod2usage(-verbose => 1)  if ($opt_help == 2);
pod2usage(-verbose => 2)  if ($opt_help > 2);

#
#   Configure the error reporting process now that we have the user options
#
ErrorConfig( 'name'    => 'CCDIFF',
             'verbose' => $opt_verbose );

#
#   Be nice to the user
#   If we have two options and no labels, then assigne them
#
if ( ! $opt_new_label && ! $opt_old_label )
{
    Error ("Must provide two labels on command line unless they are provided via -old and -new options")
         if ( $#ARGV < 1 );

    $opt_old_label = shift @ARGV;
    $opt_new_label = shift @ARGV;
}

#
#   Error check the user arguments
#

Error ("Need to provide the 'new' label")
    unless ( $opt_new_label );

Error ("Too many command line arguments" )
    unless ( $#ARGV < 0 );

#
#   Generate a default ouput put file name
#
unless ( $opt_outfile )
{
    if ( $opt_old_label )
    {
        $opt_outfile = "${opt_old_label}-${opt_new_label}-diff.txt";
    }
    else
    {
        $opt_outfile = "${opt_new_label}-diff.txt";
    }
}
Verbose ("Outout file: $opt_outfile");

#
#   Determine the machine type
#
Verbose ("Machine Type: UNIX=$UNIX");

#
#   Ensure that the 'cleartool' program can be located
#
Verbose ("Locate clearcase utility in users path");
Error ("Cannot locate the 'cleartool' utility in the users PATH")
    unless ( LocateProgInPath('cleartool', '--All') );

#
#   Ensure that the 'administration_view' is availalable
#   Then start the view, before checking its availability
#
if ( ClearCmd ( "lsview $opt_viewname" ) )
{
    Error ("Required view not found: $opt_viewname",
           "This is a dynamic view that should exist as it is used by the build system"
          );
}

if ( ClearCmd ( "startview $opt_viewname" ) )
{
    Error ("Cannot start the required view: $opt_viewname");
}

$view_path = "$opt_drive/$opt_viewname";
$view_path .= $UNIX_VOB_PREFIX if ( $UNIX );
if ( ! -d $view_path  )
{
    Error ("Cannot locate the required dynamic view: $view_path",
            "The view exits and has been started. It cannot be found"
          );
}

#
#
#   Extend the list of ROOT_VOBS with all the known vobs
#   The initial ROOT_VOBS are treated as a "hint" to assist searching
#
if ( @opt_vobs )
{
    @ROOT_VOBS = ();
    foreach my $vob ( @opt_vobs )
    {
        $vob = '/' . $vob;
        $vob =~ s~^$UNIX_VOB_PREFIX~~ if ($UNIX);
        $vob =~ tr~\\/~/~s;
        push @ROOT_VOBS, $vob;
    }
}
else
{
    my $cmd = "cleartool lsvob -short";
    open(CMD, "$cmd 2>&1 |") || Error( "can't run command: $!");
    while (<CMD>)
    {
        #
        #   Filter output from the user
        #
        chomp;
        s~^$UNIX_VOB_PREFIX~~ if ($UNIX);
        Verbose2("lsvob: $_");
        tr~\\/~/~s;
        push @ROOT_VOBS, $_;
    }
    close(CMD);
}

#
#   Ensure the two labels are present - determine the VOB root
#
my $l1_vob = LocateLabel( $opt_old_label) if $opt_old_label;
my $l2_vob = LocateLabel( $opt_new_label);

#
#   Locate all files for the two labels
#
files_from_from_view( $l1_vob, ,$opt_old_label, 1 ) if $opt_old_label;;
files_from_from_view( $l2_vob, ,$opt_new_label, 2 );

#DebugDumpData ("Files", \%files );

#
#   Create a hash of files that are different
#   The hash key will be the file name
#   The tag will be the branch that the file is on
#
foreach my $file ( sort keys %files )
{
    #
    #   These files are the same
    #
    next if ( $files{$file}{1} && $files{$file}{2} );

    my $tag = 1;
    $tag = 2 if $files{$file}{2};

    #
    #   Break path into file and version
    #
    $file =~ m~(.*)(@@[^@]*$)~;
    my ($f,$b) = ($1,$2);
    $diffs{$f}{$tag} = $file;

#    print "$file\n";
}

#
#   Process files that are common, but have changed
#
my @no_text;
my $added = 0;
my $deleted = 0;
my $diffs = 0;
my $funny = 0;
open (FO, ">", $opt_outfile) || Error ("Cannot open file: $opt_outfile");
foreach my $file ( sort keys %diffs )
{
    my ($hs, $id, $type);
    #
    #   Files are are common
    #
    if ( $diffs{$file}{1} && $diffs{$file}{2} )
    {
        $type = "different";
        ($hs, $id) = ClearDiff("-serial_format", $diffs{$file}{1}, $diffs{$file}{2});
        $diffs++ if ( $hs );
    }
    elsif ($diffs{$file}{1} )
    {
        #
        #   File has been deleted
        #
        $type = "deleted";
        Message ("$file has been deleted");
        ($hs, $id) = ClearDiff("-serial_format", $diffs{$file}{1}, element0($diffs{$file}{1}) );
        $deleted++ if ( $hs );
    }
    else
    {
        #
        #   File has been added
        #
        $type = "added";
        ($hs, $id) = ClearDiff("-serial_format", element0($diffs{$file}{2}) , $diffs{$file}{2} );
        $added++ if ( $hs );
    }

    $type = "identical"
        if ( $id  );
    $file = StripView($file);
    Message ("$type: $file" );

    unless ( $hs || $id )
    {
        push @no_text, $file;
        $funny ++;
    }
}
close FO;

#
#   Warn about problem files
#
if ( @no_text )
{
    Warning ("The following files did not generate any difference report, although",
             "they are different. They may be binary files:", @no_text);
}

#
#   Summary information
#
Information ("Summary Information",
             "Old Label:         : " . ( $opt_old_label ? $opt_old_label : '-None-') ,
             "New Label:         : $opt_new_label",
             "Files different    : $diffs",
             "Files added        : $added",
             "Files deleted      : $deleted",
             "Files not in report: $funny",
             "Output file        : $opt_outfile",
             );


#DebugDumpData ("Files", \%diffs);

exit (0);

#-------------------------------------------------------------------------------
# Function        : files_from_from_view
#
# Description     : Determine the list of files/versions in a given view
#
# Inputs          : $vpath          - Path to the view
#                   $$label         - Label
#                   $tag            - File tag
#
# Returns         : Nothing
#                   Populates the %files array
#

sub files_from_from_view
{
    my ($vpath, $label, $tag) = @_;
    my $cutlen = length ($vpath );
    Message("Determine file versions for: $label");

    #
    #   Ensure that the VOB is mounted
    #   The mount command MUST have the correct vob format
    #
    my $vob = $vpath;
    $vob =~ s~^/+~~;
    $vob = $VOB_SEP . $vob;
    ClearCmd ("mount $vob" );


    my $cmd = "cleartool find $opt_drive/$opt_viewname/$vpath -all -follow -type f -element \"lbtype_sub($label)\" -version \"lbtype_sub($label)\" -print";
    Verbose ("ClearTool: $cmd");
    open(CMD, "$cmd 2>&1 |") || Error "Can't run command: $!";
    while (<CMD>)
    {
        print "Label$tag: $_" if $opt_verbose;
        my $file = $_;
        $file =~ s~[\r\n]+$~~;
        $file =~ tr~\\/~/~s;
        $files{$file}{$tag} = 1;
    }
    close(CMD);
    Verbose2 "ClearTool Exit Status: $?";
}

#-------------------------------------------------------------------------------
# Function        : ClearDiff
#
# Description     : Issue a cleartool command
#                   Filter out many of the stupid messages
#
# Inputs          : Options and Command line
#                   Options:
#
# Returns         : Error code
#
sub ClearDiff
{
    my $header_seen = 0;
    my $identical = 0;
    my $cmd = "cleardiff " . QuoteCommand (@_);

    Verbose ("ClearDiff: $cmd");
    open(CMD, "$cmd 2>&1 |") || Error "can't run command: $!";
    while (<CMD>)
    {
        $header_seen = 1
            if ( m~^[*]{32}~ );
        unless ( $header_seen )
        {
            if ( m~^Files are identical~ )
            {
                $identical = 1;
                next;
            }
            next;
        }

        #
        #   Filter output from the user
        #
        s~(file [12]: )$view_path/~$1/~i;
        print FO $_;
    }
    close(CMD);

    #
    #   Ensure the section ends with a complete line
    #   An extra line doesn't affect CS parsing, but without it any file
    #   without a trailing \n will kill the header parsing
    #
    print FO "\n" if($header_seen);
    
    Verbose "ClearDiff Exit Status: $?";

    return $header_seen, $identical;
}

#-------------------------------------------------------------------------------
# Function        : ClearCmd
#
# Description     : Execute a cleartool command
#                   Capture error messages only
#
# Inputs          : Command to execute
#
# Returns         : Exit code
#                   Also the global @error_list
#
sub ClearCmd
{
    my( $cmd ) = @_;
    Verbose "cleartool $cmd";

        @error_list = ();
        open(CMD, "cleartool $cmd  2>&1 |")    || Error "can't run command: $!";
        while (<CMD>)
        {
            chomp;
            Verbose2 ($_);
            push @error_list, $_ if ( m~Error:~ );
        }
        close(CMD);

    Verbose2 "Exit Status: $?";
    return $? / 256;
}

#-------------------------------------------------------------------------------
# Function        : LocateLabel
#
# Description     : Determine the VOBs that conatins the specified label
#
# Inputs          : $label  - Label to locate
#
# Returns         : First VOB that conatins the label
#
sub LocateLabel
{
    my ($label) = @_;

    Message ("Locate label in VOB: $label" );
    Verbose("Ensure Label is found in a VOB");
    my $found = 0;
    foreach my $vob ( @ROOT_VOBS )
    {
        $vob = $UNIX_VOB_PREFIX . $vob if ( $UNIX );
        (my $vob_name = $vob) =~ s~/~$VOB_SEP~g;

        Verbose2 ("Examine label $label in vob: $vob" );

        my $cmd = "cleartool lstype \"lbtype:$label\@$vob_name\"";
        open(CMD, "$cmd 2>&1 |") || Error( "can't run command: $!");
        while (<CMD>)
        {
            #
            #   Filter output from the user
            #
            chomp;
            Verbose2("lstype: $_");
            next if ( m~Error~ );
            next unless ( m~label type~ );
            $found = $vob;
            last;
        }
        close(CMD);
        last if ( $found );
    }

    Error ("Label $label not found in @ROOT_VOBS")
        unless ( $found );

    Verbose ("Label $label found in $found");
    return $found;
}


#-------------------------------------------------------------------------------
# Function        : element0
#
# Description     : Given a branch version, this function will return the
#                   zero-th element on the branch
#
#                   ie: /DPG_SWBase/file@@some_branch/12
#                   ->  /DPG_SWBase/file@@some_branch/0
#
# Inputs          : $element
#
# Returns         : as described
#
sub element0
{
    my ($element) = @_;
    $element =~ s~/\d+$~/0~;
    return $element;
}

#-------------------------------------------------------------------------------
# Function        : StripView
#
# Description     : Strips the view nae from a file
#
# Inputs          : $name       - A pathname with view name prefix
#
# Returns         : The name without the view name
#
my $StripView_len;
sub StripView
{
    my ($name) = @_;

    #
    #   Determine the length to strip off - once
    #
    unless ( $StripView_len )
    {
        $StripView_len = length($view_path);
    }

    return substr ($name, $StripView_len );
}


#-------------------------------------------------------------------------------
#   Documentation
#

=pod

=head1 NAME

CCdiff - ClearCase Difference Report

=head1 SYNOPSIS

  jats CCdiff [options] [[old_label] new-label]

 Options:
    -help              - brief help message
    -help -help        - Detailed help message
    -man               - Full documentation
    -old=label         - Old label (optional)
    -new=label         - New label (mandatory)
    -output=file       - Output filename
    -vob=name          - Vob for labels
    -drive=path        - Alternate vob location

=head1 OPTIONS

=over 8

=item B<-help>

Print a brief help message and exits.

=item B<-help -help>

Print a detailed help message with an explanation for each option.

=item B<-man>

Prints the manual page and exits.

=item B<-old=label>

This option specifies the old, or base, label for the difference report. This
option is not required when a new package is being processed.

=item B<-new=label>

This option specifies the new, or current, label for the difference report. This
label is mandatory for the difference report.

The old and new labels may be provided on the command line, or via named
options, but not both.

=item B<-vob=name>

This option limits the label search to the specified VOB. This option may be
needed if the labels are to be found in multiple VOBs.

This option may be used multiple times. All specified vobs will be searched and
the first one containing the label will be used.

=item B<-output=file>

This option specifies the output filename. The program will generate an output
file based on the two source labels.

=item B<-drive=path>

This option allows the user to provide an alternate location for the
administration vob used by the program. The default location is:

=over 8

=item * Windows o:

=item * Unix /view

=back

=head1 DESCRIPTION

This program is the primary tool for creating 'diff' reports to be uploaded to
Code Striker.

The program will determine the files that are different between the two specified
labels. It will determine full pathnames for the files and create a difference
report that is suitable for Code Striker.

The program uses a global administration view for the purposes of determining
file versions. The path names that are generated are full vob-extended pathnames.
These may be very long and may not be directly usable under windows.

=cut