Subversion Repositories DevTools

Rev

Rev 2930 | Blame | Compare with Previous | Last modification | View Log | RSS feed

########################################################################
# Copyright (C) 1998-2012 Vix Technology, All rights reserved
#
# Module name   : jats_vcsdiff.pl
# Module type   : Makefile system
# Compiler(s)   : Perl
# Environment(s): jats
#
# Description   : Create two views and DIFF them
#                 The views can be any type understood by JATS
#                 Can:
#                   - Compare with MD5SUms
#                   - Invoke BeyondCompare
#
#......................................................................#

require 5.008_002;
use strict;
use warnings;

use Pod::Usage;
use Getopt::Long;

use JatsError;
use JatsSystem;
use FileUtils;
use Cwd;

my $VERSION = "1.0.0";                      # Update this

#
#   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_new_label;
my $opt_old_label;
my $opt_md5check;
my $opt_mode;
my $opt_diff;

#
#   Globals - Provided by the JATS environment
#
my $USER            = $ENV{'USER'};
my $UNIX            = $ENV{'GBE_UNIX'};
my $TMP             = $UNIX ? "/tmp" : $ENV{'TMP'};
my $MACHINENAME     = $ENV{'GBE_HOSTNAME'};

#
#   Globals
#
my $Name            = 'BeyondCompare';
my $DiffProg;
my @DiffArgs;
my $DiffWait;
my @view_tags;
my @view_commands;
my @cleanFiles;


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

#
#   Parse the user options
#
my $result = GetOptions (
                "help+"         => \$opt_help,              # Help level
                "manual"        => \$opt_manual,            # Help level
                "verbose:+"     => \$opt_verbose,           # Verbosity
                "debug:+"       => \$opt_debug,             # Debug Verbosity
                "new=s"         => \$opt_new_label,         # Path1
                "old=s"         => \$opt_old_label,         # Path2
                'check'         => \$opt_md5check,          # Force MD5 Check
                'diff!'         => \$opt_diff,              # Force use of diff
                );

                #
                #   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_manual || ($opt_help > 2));

InitFileUtils();

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

Error ("Options -check and -diff cannot be combined")
    if ( $opt_md5check && $opt_diff );

#
#   Determine mode
#   Not all modes work on all machines
#
Verbose ("Machine Type: UNIX=$UNIX");
if ( $opt_md5check )
{
    $opt_mode = 'md5';
}
elsif ( $UNIX || $opt_diff )
{
    $opt_mode = 'diff';
    $Name = 'diff';
    push @DiffArgs, '-r';
    $Name = 'gdiff';
    $DiffProg = LocateProgInPath( $Name, '--All');
    unless ( $DiffProg =~ m~/~ )
    {
        $Name = 'diff';
        $DiffProg = LocateProgInPath( $Name, '--All');
    }

    Error ("Cannot locate a 'diff' utility in the users PATH")
        unless ( $DiffProg =~ m~/~ );
}
else
{
    $opt_mode = 'bc2';
    $DiffWait = 1;

    #
    #   Determine the path to Beyond Compare Exe
    #       It may not be installed in the default place, but the Windows
    #       registry will know where it is
    #
    GetBeyondCompareExePath();
}

#
#   Validate user options
#
#   Be nice to the user
#   If we have two options and no labels, then assign 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 ("Need two labels on the command line, or via options")
    unless ( $opt_old_label && $opt_new_label );

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

#
#   Extract parameters that will be used to create a view that is
#   unique. Will use hostname and user name
#
Error ("Machine Name not determined")
    unless ( $MACHINENAME );

Error ("USER name not determined" )
    unless ( $USER );

#
#   Need a TMP working directory
#   Used to create config files
#
Error ("TMP not found or not a directory")
    unless ( $TMP && -d $TMP );
$TMP = "$TMP/$$";

#
#   Create views for the two views
#   Verify that the view are present
#
Message ("Constructing views");
my $path1 = create_view( $opt_old_label, 1 );
my $path2 = create_view( $opt_new_label, 2 );

Error ("Cannot locate view directory: $path1" ) unless (-d $path1);
Error ("Cannot locate view directory: $path2" ) unless (-d $path2);

#
#   If one of the paths is a dynamic view and the other is a local path
#   then attempt to locate the common directories
#
if ( $#view_tags == 0 )
{
    massage_paths();
}

if ( $opt_md5check )
{
    Verbose ("Performing MD5 Check");
    my $checkfile = 'vcsdiff_md5.txt';
    my $rv;
    push @cleanFiles, $checkfile;
    $rv = JatsTool ('jats_manifest', '-quiet',
                '-manifest', $checkfile,
                '-rootdir', $path1 );

    $rv = JatsTool ('jats_manifest', '-quiet', '-check',
                '-manifest', $checkfile,
                '-rootdir', $path2 ) unless $rv;

    exit $rv;
}

#
#   Diffing the paths
#   Will use BeyondCompare under Windows
#   Will use diff under unix
#
Message ("Using '$Name' to compare two views",
         "Wait for utility to exit so that we can delete the views" ) if ($DiffWait);

Verbose ("Diff Utility: $DiffProg");
System ( $DiffProg, @DiffArgs, $path1, $path2 );
exit 0;

#-------------------------------------------------------------------------------
# Function        : create_view
#
# Description     : Create dynamic view, based on a Vcs Tag
#
# Inputs          : $vcstag
#
# Returns         : Path to the view
#
sub create_view
{
    my ($vcstag, $num) = @_;

    #
    #   Intercept and treat the special label 'current'
    #
    return create_path_view( $vcstag )
        if ( $vcstag eq 'current'  || $vcstag =~ m~^dir=.+~ || $vcstag =~ m~^current=.+~ );

    my $tag = "${USER}_${MACHINENAME}_vcsdiff_${num}";
    push @view_tags, $tag;
        
    #
    #   Use vcsrelease to do the hard work
    #
    my @command = ( 'jats_vcsrelease',
                        '-extractfiles',
                        '-root=.' ,
                        '-noprefix',
                        '-devmode=escrow',
                        '-view', $tag ,
                        '-label', $vcstag,
                   );
    push @view_commands, \@command;

    if ( $opt_debug && -d $tag )
    {
        Message ("Using existing view");
    }
    else
    {
        JatsTool( @command );
    }

    #
    #   Minor Kludge
    #       Should be in a library
    #   Iff CC::, then process path info too
    #
    if ( $vcstag =~ m~^CC::(.*?)(::(.+))?$~ )
    {
        my $path = $1;
        if ( $path )
        {
            my $try = $tag . '/' . $path;
            if ( -d  $try )
            {
                $tag = $try;
            }
        }
    }

    return $tag;
}

#-------------------------------------------------------------------------------
# Function        : create_path_view
#
# Description     : Not using a view, using a path
#                   Return the path as requested
#
# Inputs          : $label                  - with embedded path
#
# Returns         : Path to the (dummy) view
#
sub create_path_view
{
    my ($label) = @_;
    my $path  = '.';

    $path = $1
        if ( $label =~ m~.+=(.+)~ );

    Error ("Directory not found: $path" )
        unless ( -d $path );

    $path = FullPath( $path );
    return $path;
}

#-------------------------------------------------------------------------------
# Function        : massage_paths
#
# Description     : Used when one of the paths is a view and the the other
#                   is a local directory.
#
#                   Attempt to locate the common root
#
# Inputs          : None
#
# Returns         : Modifies $path1 and $path2
#
sub massage_paths
{
    my $view_path = $view_tags[0];
    my $user_path = $path1;
    $user_path = $path2 if ( $view_path eq $path1 );

    #
    #   Split the user path into its component directory entries
    #   Start at the top and look for one of these in the view
    #
    my @user_path = split ('/', $user_path );
    my $tpath = '';
    foreach my $dir ( @user_path )
    {
        if ( -d "$view_path/$dir" )
        {
            #
            #   Common directory found
            #   Set the user path to the previous directory
            #
            $user_path = $tpath;
            if ( $view_path eq $path1   )
            {
                $path2 = $user_path;
            }
            else
            {
                $path1 = $user_path;
            }

            #
            #   now add the common part
            #
            $path1 .= "/$dir";
            $path2 .= "/$dir";
            Message ("Setting common root path ($dir)", $path1, $path2);
            last;
        }
        $tpath .= '/' if ( $tpath );
        $tpath .= $dir;
    }
}

#-------------------------------------------------------------------------------
# Function        : GetBeyondCompareExePath
#
# Description     : Determine the path to the BeyondCompare executable
#                   by looking in the Windows Registry
#
# Inputs          : None
#
# Returns         : Path to an executable
#

sub GetBeyondCompareExePath
{
    eval "require Win32::TieRegistry"
        or Error ("Win32::TieRegistry not available");

    my $userKey= Win32::TieRegistry->new("CUser")
        or  Error( "Can't access HKEY_CURRENT_USER key: $^E" );

    my $bcKey;
    if ($bcKey = $userKey->Open( "Software/Scooter Software/Beyond Compare 3", {Delimiter=>"/"} ))
    {
        Verbose("Using BC3");
        push @DiffArgs, '/solo';
    }
    elsif ($bcKey = $userKey->Open( "Software/Scooter Software/Beyond Compare", {Delimiter=>"/"} ))
    {
        Verbose("Using BC2");
    }
    else
    {
        Error "Can't access BC2 or BC3 Keys: $^E";
    }

    $DiffProg = $bcKey->GetValue( 'ExePath' )
        or Error( "Cannot locate BeyondCompare ExePath in Windows Registry");

    Error ("BeyondCompare program not found", "Prog: $DiffProg")
        unless ( -x $DiffProg );
}

#-------------------------------------------------------------------------------
# Function        : END
#
# Description     : This function will be called as the program exits
#                   It will also be called under error conditions
#                   Close down stuff we created
#
# Inputs          : 
#
# Returns         : 
#

sub END
{
    my $exitCode = $?;

    if ( $opt_debug )
    {
        Message ("NOT Cleaning up views");
        return;
    }

    Message ("Cleaning up views") if @view_tags;
    foreach my $cmds ( @view_commands )
    {
        JatsTool( @{$cmds}, '-delete' );
    }

    foreach my $file ( @cleanFiles )
    {
        unlink $file;
    }

    #
    #   The exit code may get modified by the JatsTool
    #   Preserve any error indication
    #
    $? = $exitCode;
}

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

=pod

=head1 NAME

jats_vcsdiff - Difference two views

=head1 SYNOPSIS

  jats vcsdiff [options] [old_label new_label]

 Options:
    -help              - brief help message
    -help -help        - Detailed help message
    -man               - Full documentation
    -check             - Perform MD5SUM over both views
    -[no]diff          - Force the use of a 'diff' utility
    -old=tag           - Old VcsTag (or dir=path)
    -new=tag           - New VcsTag (or dir=path)

=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<-check>

This option controls the mode in which the program will operate.

If enabled the program will perform an MD5 Cheksum over the files in the first
view and compare that with files in the second view.

This option cannot be used in conjunction with the '-diff' option'.

=item B<-diff>

This option controls the mode in which the program will operate.

By default the program is Operating System dependent. It will:

=over 4

=item * Windows - Use Beyond Compare

=item * Unix - Use gdiff or diff

=back

This option will force the use of a 'diff' utility on both Windows and
Unix.

This option cannot be used in conjunction with the '-check' option'.

=item B<-old=tag>

This option specifies the old, or base, VcsTag for the difference report. This
tag is mandatory.

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

The tag may be of the form dir=path to force the utility to use a local
view or path.

=item B<-new=tag>

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

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

The tag may be of the form dir=path to force the utility to use a local
view or path.

=back

=head1 DESCRIPTION

This program has two modes of operation:

=over 4

=item 1 MD5Sum of the two views

=item 2 Invoke a differencing program.

The program that is invoked is, by default, Operating System dependent. It will:

=over 4

=item * Windows - Use Beyond Compare to perform a visual diff.

This mode simplifies the process of perform a code review between two
VCS Tags by:

=over 8

=item *

Creating a visual difference between two labels

=item *

Creating a visual difference between a label and a directory

=item *

Creating a visual difference between two directories.

=back

=item * Unix - Use gdiff or diff

=back

=back

The program will:

=over 8

=item *

Create two 'extract only' views based on the VCS Tags provided. The resultant
views are not connected to any version control system.

=item *

Perform the desired operation: MD5Sum or Difference.

=item *

Delete the created directories the comparison is complete.

=back

If one of the Vcs Tags is of the form:

=over 8

=item *

current

=item *

current=path

=item *

dir=path

=back

Then the tag will be treated as a directory and will be used for one side
of the comparison.

Two directories views will be created. These should be deleted by this program,
but may remain if the command line program is terminated.

=head1 EXAMPLE

The following command will compare a Subversion view with a ClearCase view.

    jats vcsdiff SVN::AUPERASVN02/COTS/crc/tags/crc_26.4.0007.cr@18587 CC::/MASS_Dev_Infra/crc::crc_26.4.0006.cr -check

The following command will compare a Subversion View with a local directory

    jats vcsdiff SVN::AUPERASVN02/COTS/crc/tags/crc_26.4.0000.cr dir=crc

=cut