Subversion Repositories DevTools

Rev

Rev 233 | 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 Striker.
#......................................................................#

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

use Getopt::Long;

#-------------------------------------------------------------------------------
#
#  Function Prototypes
#
sub prevElement($);
sub get_newFiles();
sub get_prev_viewname();
sub get_workingDirectory();

#-------------------------------------------------------------------------------
#
#  Global variables
#
my $VERSION = "1.0.1";                      # 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;
( my $startDirectory = getcwd() ) =~ s/[\n\r]+$//;

#
#   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
                "output=s"      => \$opt_outfile,           # String
                "new=s"         => \$opt_new_label,         # String
                "old=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 );

#
# $workingDirectory is only used if compareWithDirectory == 1, which is
# only true when user has specified that -new points to a directory
#
my ( $workingDirectory, $compareWithDirectory ) = get_workingDirectory ();

#
#   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 ("Output 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"
          );
}

#
# Under windows if user is comparing with a directory you need knowledge of under
# what view the command was invoked with
#
#my $prev_viewname = "";
#$prev_viewname = get_prev_viewname() if( $compareWithDirectory && ! $UNIX );

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
        #
        s~[\n\r]+$~~;
        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_view( $l1_vob, $opt_old_label, 1 ) if $opt_old_label;
files_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;  
}

#
#   Process files that are common, but have changed
#
my @no_text;
my $added = 0;
my $deleted = 0;
my $diffs = 0;
my $ifile = 0;

open (FO, ">", "$startDirectory/$opt_outfile") || Error ("Cannot open file: $startDirectory/$opt_outfile");

foreach my $file ( sort keys %diffs )
{
    $ifile ++;

    print "Running ClearDiff on file $ifile/" . scalar(keys %diffs) . "            \r";

    my ($hs, $id, $type);
    #
    #   Files are are common
    #
    if ( $diffs{$file}{1} && $diffs{$file}{2} )
    {
        $type = "different";
        ($hs, $id) = ClearDiff("-serial_format", "-blank_ignore", $diffs{$file}{1}, $diffs{$file}{2});
        $diffs++ if ( $hs );
    }
    elsif ($diffs{$file}{1} )
    {
        #
        #   File has been deleted
        #
        $type = "deleted";
        Verbose ("$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";
        if( $compareWithDirectory )
        {
            my $cmd = QuoteCommand( 'cleartool', 'dump', $diffs{$file}{2} );

            open( CMD, "$cmd 2>&1 |") or Error("can't run command: $!");
            <CMD>; # line 1 - blank
            <CMD>; # line 2 - clearcase filename and a code for it
            (my $basefilename = <CMD>) =~ s~[\n\r]+$~~; # line 3 is what we want
            while( <CMD> ){} # Get rid of broken pipe messages
            close( CMD );

            $basefilename =~ s{@@.*}{}; # strip off the branch/version number
#           $basefilename = "${opt_drive}/${prev_viewname}${basefilename}" if( ! $UNIX );

            ($hs, $id) = ClearDiff("-serial_format", $diffs{$file}{2}, $basefilename );
            $diffs++ if ( $hs );
        }
        else
        {
            ($hs, $id) = ClearDiff("-serial_format", element0($diffs{$file}{2}) , $diffs{$file}{2} );
            $added++ if ( $hs );
        }
    }

    $type = "identical"
        if ( $id  );
    $file = StripView($file);
    Verbose ("$type: $file" );
    
    push @no_text, $file unless ( $hs || $id );
}

#
# If comparing with a directory, do additional files that have been
# added since the label
#
if( $compareWithDirectory )
{
    my @newFiles = get_newFiles();
 
    my ($hs, $id);
    my $type = "added";

    Message ("Going to run cleardiff for new files") if( @newFiles );

    foreach my $newFile (@newFiles)
    {
        my $prev_element = prevElement($newFile);

        ($hs, $id) = ClearDiff("-serial_format", $prev_element, $newFile );
        $added++ if ( $hs );
        
        $type = "identical" if ( $id  );
        $newFile = StripView($newFile);
        Verbose ("$type: $newFile" );
        
        push @no_text, $newFile unless ( $hs || $id );
    }
}

#
#   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: " . scalar(@no_text),
             "Output file        : $startDirectory/$opt_outfile",
             );


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

close FO;
exit (0);

#-------------------------------------------------------------------------------
# Function        : get_workingDirectory
#
# Description     : Works out the working directory that should be used if the
#                   user has specified that the 'new' label is really a directory 
#
#
# Inputs          : 
#
# Returns         : $workingDirectory    - The directory to compare with
#                   $compareWithDirectory - 1 (true) if the user has specified that
#                                          a directory is to be compared
#
sub get_workingDirectory()
{
    return ("", 0) if( $opt_new_label !~ m/^dir=/ && $opt_new_label !~ m/^current/ );

    my $workingDirectory = "";

    $workingDirectory = $startDirectory 
        if ( $opt_new_label eq 'current' || $opt_new_label eq "dir=current" );
    
    if ( $opt_new_label =~ m~^dir=(.+)~ || $opt_new_label =~ m~^current=(.+)~ )
    {
        my $wdir = $1;

        if( ! $UNIX && $wdir =~ m~^.:~ ) # on windows, directory has been specified as a full drive path
        {
            $workingDirectory = $wdir;
        }
        elsif( $wdir =~ m~^/~ || ($wdir =~ m~^\\~ && ! $UNIX) ) # Full path on the current drive
        {
            $workingDirectory = $wdir;
        }
        else # Relative path
        {
            $workingDirectory = "$startDirectory/$wdir"; 
        }
    }

    Verbose ("working directory: '$workingDirectory'");

    Error("Could not find the directory '$workingDirectory'")
        unless( -d $workingDirectory );

    chdir $workingDirectory or Error ("Could not chdir into '$workingDirectory'");

    # We go through the same algorithm in many parts as the
    # no-minus-old algorithm, so to minimise code changes we just 
    # treat it the same with the addition of the '$compareWithDirectory' flag
    # to execute additional code.
    $opt_new_label = $opt_old_label;
    $opt_old_label = "";

    return( $workingDirectory, 1);
}


#-------------------------------------------------------------------------------
# Function        : prevElement
#
# Description     : Works out the element that should be compared with the 
#                   file in the working directory.
#
#
# Inputs          : $newFile        - File found by cleartool ls'ing the
#                                     working directory
#
# Returns         : $prevElement - the element to cleardiff on
#
sub prevElement($)
{
    my ( $newFile ) = @_;

    my $cmd = qq(cleartool find $newFile -directory -version "{lbtype($opt_new_label)}" -print);

    Verbose ("Cmd: '$cmd'");

    my $ret = "${newFile}@@/main/0";

    open( CMD, "$cmd 2>&1 |") or Error("can't run command: $!");
    while( <CMD> )
    {
        s~[\n\r]+$~~;
        next if( m/Error:/ || m/Warning:/ );
        ($ret = $_) =~ s{//}{/}g;
        $ret =~ s{vobs/vobs/}{vobs/}g;
        last;
    }
    while( <CMD> ){} # Get rid of broken pipe messages
    close( CMD );

    return $ret;
}

#-------------------------------------------------------------------------------
# Function        : get_newFiles
#
# Description     : Calls 'cleartool ls -recurse' to get a list of clearcase
#                   objects in the working directory that have been 
#                   checked in to clearcase only after the label was made
#
# Inputs          : 
#
# Returns         : @newFiles - an array of files
#
sub get_newFiles()
{
    Message ("Determine clearcase file versions for: $workingDirectory");

    # Step 1: get list of files that are in the directory and in clearcase
    my @candFiles = ();
    {
        my $cmd = QuoteCommand( 'cleartool', 'ls', '-recurse', $workingDirectory);
        open( CMD, "$cmd 2>& 1 |") or Error("can't run command: $!");

        while( <CMD> )
        {
            # Each line will be of the form (e.g.):
            # ./LIB/JatsMakeConfig.pm@@/main/4         Rule: core_devl_2.73.2000.cr

            s~[\n\r]+$~~;
            next if( ! m/Rule:/ ); # We only want clearcase files - these will show with what rule
                                   # they are in our view via some text like m/Rule: .*/.

            (my $firstWord = $_) =~ s/^(\S+).*/$1/;
            my ( $candFile, $candFileVersion) = split(/@@/, $firstWord);

            push @candFiles, $candFile;
        }
        close( CMD );
    }

    my @newFiles = ();

    # Step 2: See whether each candidate file is in the 'old' label
    # If not, then it is a new file that has been added since the label was created
    my $icandFile = 0;
    foreach my $candFile (@candFiles)
    {
        $icandFile ++;

        print "Running cleartool find on file $icandFile/" . scalar(@candFiles) . "           \r";

        my $isPartOfLabel = 0;

        # This command will return no output if file is not part of label, else
        # some output if it is part of the label.  This provides an simple way of
        # determining if the file is a new file that has been added since the label was created
        my $cmd = qq(cleartool find $candFile -directory -version "{lbtype($opt_new_label)}" -print);

        Verbose ("Cmd: '$cmd'");
        open( CMD, "$cmd 2>& 1 |") or Error("can't run command: $!");
        $isPartOfLabel = 1 if( <CMD> );
        while( <CMD> ){} # Get rid of broken pipe messages
        close( CMD );

        push @newFiles, $candFile if( ! $isPartOfLabel );
    }

    return @newFiles;
}

#-------------------------------------------------------------------------------
# Function        : get_prev_viewname
#
# Description     : Works out the view in use when CCdiff.pl is invoked.
#
# Inputs          : 
#
# Returns         : $prev_viewname
#
sub get_prev_viewname()
{
    my $cmd = "cleartool pwv";

    open( CMD, "$cmd 2>&1 |") or Error("can't run command: $!");
    (my $firstline = <CMD>) =~ s~[\n\r]+$~~;
    while( <CMD> ){} # Get rid of broken pipe messages
    close( CMD );

    if( $firstline !~ /^Working directory view: / || $firstline =~ / NONE / )
    {
        Error ("Cannot determine current view name",
               "Path may not be a clearcase view");  
    }

    (my $prev_viewname = $firstline) =~ s/^Working directory view: (\S+).*/$1/;

    Verbose ("Previous viewname: '$prev_viewname'");
    return $prev_viewname;
}


#-------------------------------------------------------------------------------
# Function        : files_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_view
{
    my ($vpath, $label, $tag) = @_;
    my $cutlen = length ($vpath );
    Message ("Determine file versions for label: $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 = qq(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>)
    {
        Verbose( "Label$tag: $_");
        my $file = $_;
        $file =~ s~[\n\r]+$~~;
        $file =~ tr{\\/}{/}s;
        $files{$file}{$tag} = 1;
    }
    close(CMD);
    Verbose2 ("ClearTool Exit Status: $?");

    Message ("There are " . scalar(keys %files) . " files in label $label");
}

#-------------------------------------------------------------------------------
# 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 = QuoteCommand("cleardiff", @_);

    Verbose ("ClearDiff: $cmd");    

    open(CMD, "$cmd 2>&1 |") || Error "can't run command: $!";

    while (<CMD>)
    {
        $header_seen = 1
            if ( m~^[*]{32}~ );
        unless ( $header_seen )
        {
            $identical = 1 if ( m~^Files are identical~ );
            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 = QuoteCommand( @_ );
    
    Verbose ("cleartool $cmd");

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

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

#-------------------------------------------------------------------------------
# Function        : LocateLabel
#
# Description     : Determine the VOBs that contains the specified label
#
# Inputs          : $label  - Label to locate
#
# Returns         : First VOB that contains 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 && $vob !~ m~^${UNIX_VOB_PREFIX}~ );
        (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
            #
            s~[\n\r]+$~~;
            Verbose2 ("lstype: $_");
            next if ( m~Error~ );
            next unless ( m~label type~ );
            $found = $vob;
            last;
        }
        while( <CMD> ){} # Get rid of broken pipe messages
        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 (or dir=path) (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 label may be of the form dir=path to force the utility to use a
local view or path.  You may also use '-new=current', or -new=current=path,
as in jats CCbc2.

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