#! perl
########################################################################
# Copyright ( C ) 2005 ERG Limited, All rights reserved
#
# Module name   : jats.sh
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): jats
#
# Description   : Build an Install Shield project for deployment
#                 This script isolates the IS build from the rest of
#                 the deployment process.
#
#                 Assumes that:
#                       IS 11.5 Standalone builder has been installed
#                       Merge Modules have been installed / updated
#                       to subdirs with the SA installation of:
#                           Modules\i386
#                           Objects
#
#                 Note: IS SA builder can be copied and registered
#                       The installation is very simple.
#                       Registartion: regsvr32.exe
#
#                       Currently this script will register the required
#                       DLL anyway.
#
# Usage         : Refer to POD within this script
#
#......................................................................#

require 5.008_006;

use strict;
use warnings;
use JatsError;                              # Error reporting
use JatsSystem;                             # System interface
use Win32::OLE;                             # load the Win32::OLE module
use Pod::Usage;                             # required for help support
use Getopt::Long;                           # Parse input options
use Cwd;                                    # Where am I
use File::Copy;                             # Transfer files
use File::Path;
use File::Find;                             # Ony for kludge copy
use FileUtils;

my $VERSION = "1.0.0";                      # Update this

#
#   Globals
#
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;

my $opt_read_only = 1;
my $opt_use_sa = 1;
my $opt_build = 1;
my $opt_project;
my $opt_new_codes;
my $opt_name;
my $opt_version;
my $opt_suffix;
my $opt_outpath;
my @opt_mergemodule;
my $opt_workdir;
my $opt_name_version = 1;
my $opt_multi_prod = 0;
my $opt_multi_rel = 0;

#
#   Kludgy values
#       May need to be configured
#       These are current IS 11.5 install locations
#
my @AutoBuilderOLE = ('C:\Program Files\Macrovision\IS11.5',
                      'C:\Program Files (X86)\Macrovision\IS11.5' );


my @AutoBuilderSA = ('C:\Program Files\Macrovision\IS 11.5 StandaloneBuild',
                     'C:\Program Files (X86)\Macrovision\IS 11.5 StandaloneBuild');
my $AutoBuilder;
my $result_code = 0;

#-------------------------------------------------------------------------------
# Function        : Mainline Entry Point
#
# Description     :
#
# Inputs          :
#
my $result = GetOptions (
                "help+"         => \$opt_help,              # flag, multiple use allowed
                "manual"        => \$opt_manual,            # flag
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
                "readonly!"     => \$opt_read_only,         # [no]flag
                "standalone!"   => \$opt_use_sa,            # [no]flag
                "build!"        => \$opt_build,             # [no]flag
                "project=s"     => \$opt_project,           # string
                "codes!"        => \$opt_new_codes,         # [no]flag
                "version=s"     => \$opt_version,           # string
                "out=s"         => \$opt_outpath,           # string
                "mergemodule=s" => \@opt_mergemodule,       # string
                "workdir=s"     => \$opt_workdir,           # string
                "nameversion!"  => \$opt_name_version,      # [no]flag
                "multiprod!"    => \$opt_multi_prod,        # [no]flag no by default
                "multirel!"     => \$opt_multi_rel          # [no]flag no by default
                );

                #
                #   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));
#pod2usage(-verbose => 0, -message => "Version: $VERSION") if ( $#ARGV < 0 );

#
#   Configure the error reporting process now that we have the user options
#
ErrorConfig( 'name'    =>'ISBUILD',
             'verbose' => $opt_verbose,
            );

#
#   User must specify a project or we will attempt to locate one
#
Verbose ("Current directory: " . getcwd );
unless ( $opt_project )
{
    my @projects = glob ('*.ism' );
    Error ("No InstallShield projects found in current directory") unless ( $#projects >= 0 );
    Error ( "Multiple Install Shield projects located. Use -project option") if ( $#projects > 0 );
    $opt_project = $projects[0];
}
else
{
    Error ("Project file not found: $opt_project") unless ( -f $opt_project );
}
#
#   Determine project name from the project file name
#       - remove any path information
#       - remove .ism extension
#
$opt_name = $opt_project;
$opt_name =~ s~\\~/~g;
$opt_name =~ s~.*/~~;
$opt_name =~ s~\.ism$~~i;

#
#   User must specify a version
#   This will be processed and used within the build
#
Error ("Version must be specified") unless ( $opt_version );
Error ("Bad format for version: $opt_version") unless ( $opt_version =~ m~(\d+\.\d+\.\d+)\.(\w+)$~ );
$opt_version = $1;
$opt_suffix = $2;
Error ("Project suffix not found in version") unless ( $opt_suffix );
Message( "Package: $opt_name, Version: $opt_version, Project: $opt_suffix");

#
#   Output path must exist
#
if ( $opt_outpath )
{
    Error("Output path does not exist: $opt_outpath") unless ( -e $opt_outpath );
    Error("Output path is not a directory: $opt_outpath") unless ( -d $opt_outpath );
}

#
#   Workdir path must exist
#
$opt_workdir = getcwd unless( $opt_workdir );
Error("Workdir path does not exist: $opt_outpath") unless ( -e $opt_workdir );
Error("Workdir path is not a directory: $opt_outpath") unless ( -d $opt_workdir );

#
#   Process Merge Module paths
#
if ( @opt_mergemodule )
{
    #
    #   User may have entered a comma seperated list
    #   Convert into a full array
    #
    @opt_mergemodule = split(/,/,join(',',@opt_mergemodule));

    #
    #   Ensure that each path exists
    #   Clean up paths
    #
    foreach my $path ( @opt_mergemodule )
    {
        Warning ("MergeModule path not found: $path" ) unless ( -d $path );
        $path =~ s~/~\\~g;
    }
}

#
#   Instantiate the Developer Automation interface
#   Use the StandAlone interface if it can be found
#
my $dev;
if ( $opt_use_sa )
{
    Message "Using StandAlone OLE";
    $AutoBuilder = LocateInstallShield(\@AutoBuilderSA, 'SAAuto1150.dll');

    #
    #   Ensure the interface is registered
    #   This allows a simple 'copy' of the SA build environment to a build
    #   machine.
    #
    my $regdll = $AutoBuilder . '\SAAuto1150.dll' ;
    Error ("Cannot find SA OLE DLL: $regdll") unless ( -f $regdll );

    my $rv = System( 'regsvr32.exe', '/s', $regdll);
    Warning ("Failed to register $regdll: $rv" ) if ( $rv  );

    $dev = Win32::OLE->new("SAAuto1150.ISWiProject");

} else {

    Message "Using InstallShield OLE";
    $AutoBuilder = LocateInstallShield(\@AutoBuilderOLE, 'System\isdev.exe');
    $dev = Win32::OLE->new("IswiAuto1150.ISWiProject");

}
Error( "Cannot open Automation interface") unless ( $dev );

#
#   Open a project as read-write
#   The second argument ( if true ) will open as read only
#
Verbose ("Opening project: $opt_project, ReadOnly: $opt_read_only" );
$dev->OpenProject( $opt_project, $opt_read_only );
my $estring = Win32::OLE->LastError();
Error( $estring ) if ( $estring );
Verbose ("Project Open");

#DebugDumpData("Dev", $dev );

#
#   Massage the ProductName [ if required ]
#   Should contain a comma<space><version>
#
if ( $opt_name_version )
{
    Verbose( "Update ProductName" );
    Verbose( "Originial ProductName   :" . $dev->ProductName);
    my $product_name = $dev->ProductName;
    if ( $product_name =~ m/^(.*),/ )
    {
        $product_name = $1;
    }
    $product_name .= ', ' . $opt_version . '.' . $opt_suffix;
    $dev->SetProperty('ProductName', $product_name );
}
Message( "ProductName   :" . $dev->ProductName);

#
#   Massage the ProjectVersion
#   This will not have the project suffix on it
#
Verbose( "Update ProductVersion" );
Verbose( "Original ProductVersion:" . $dev->ProductVersion);
$dev->SetProperty('ProductVersion', $opt_version );
Message ("ProductVersion:" . $dev->ProductVersion);

#
#   If we need to generate new codes
#
if ( $opt_new_codes )
{
    Message ("Update GUIDs");
    $dev->SetProperty('PackageCode', $dev->GenerateGUID() );
    $dev->SetProperty('ProductCode', $dev->GenerateGUID() );
    $dev->SetProperty('UpgradeCode', $dev->GenerateGUID() );
}
Message ("PackageCode   :" . $dev->PackageCode );
Message ("ProductCode   :" . $dev->ProductCode );
Message ("UpgradeCode   :" . $dev->UpgradeCode );

#
#   Examine all components and display the files within each
#   The files are a collection and need to be processed in a special manner
#
if ( $opt_verbose )
{
    Verbose ("InstallShield Components");
    my $mycomps = $dev->ISWiComponents;
    foreach my $comp (in $mycomps)
    {
        Verbose ("Component: " . $comp->Name, ": ", $comp->Destination );

        my $files = $comp->ISWiFiles;
        my $fileCount = $files->Count;
        Verbose ("Number of Files: $fileCount");

        foreach my $index ( 1 .. $fileCount )
        {
            Verbose ("           Name: " . $files->Item($index)->Name );
            Verbose ("    DisplayName: " . $files->Item($index)->DisplayName );
            Verbose ("       FullPath: " .  $files->Item($index)->FullPath );
        }
    }
}

#
#   Examine all the Features
#
if ( $opt_verbose  )
{
    Verbose ("InstallShield Features");
    my $myfeature = $dev->ISWiFeatures;
    foreach my $feat (in $myfeature)
    {
        Verbose ("Feature: " . $feat->Name, ": ", $feat->Description );
    }
}

#
#   Display Releases
#   These are found within the product config
#   We need to locate the Release in order to build it
#
Verbose ("InstallShield Products");
my $products = $dev->ISWiProductConfigs;

# if we are building check to make sure -multiprod is enabled if more than 1 product configuration is configured
# and/or more than 1 release per product configuration
if ( $opt_build )
{
    Error("Multiple Product Configurations found, -multiprod must be specified to build them all") 
        if ( $products->Count > 1 && !$opt_multi_prod );
    
    foreach my $p ( 1 .. $products->Count )
    {
        Error("Multiple Releases found for a Product Configuration, -multirel must be specified to build them all") 
            if ( $products->Item($p)->ISWiReleases->Count > 1 && !$opt_multi_rel );
    }
}


foreach my $index ( 1 .. $products->Count )
{
    my $product = $products->Item($index);

    Verbose ("Product Index  : " . $index );
    Verbose ("  Product Name : " . $product->Name );
    Verbose ("  SetupFileName: " . $product->SetupFileName );

    my $releases = $product->ISWiReleases;
    foreach my $index ( 1 .. $releases->Count )
    {
        my $release = $releases->Item($index);

        Verbose ("Release Index      : " . $index );
        Verbose ("  Name             : " . $release->Name );
        Verbose ("  BuildLocation    : " . $release->BuildLocation );
        Verbose ("  SingleEXEFileName: " . ( $release->SingleEXEFileName || 'None Specified' ) );

        #
        #   Build the Project
        #   Not working at the moment !!!!
        #
        if ( $opt_build )
        {
            Message ("Building...");

            #
            #   Set build location
            #
            my $location = $opt_workdir . '/Media';
            $location =~ s~/~\\~g;
            $release->SetProperty('BuildLocation', $location );
            Message ("New BuildLocation: " . $release->BuildLocation );

            #
            #   Massage the ProductName [ if required ]
            #   Only update ProductName for this product Configuration if nameversion is supplied and the
            #   product Configuration has a product Name.  
            #   This overrides the default project product name for this product Configuration.
            #   Should contain a comma<space><version>
            #
            if ( $opt_name_version && $product->ProductName ne "" )
            {
                Verbose( "Update ProductName for this Product Configuration" );
                Verbose( "Originial ProductName   :" . $product->ProductName);
                my $product_name = $product->ProductName;
                if ( $product_name =~ m/^(.*),/ )
                {
                    $product_name = $1;
                }
                $product_name .= ', ' . $opt_version . '.' . $opt_suffix;
                $product->SetProperty('ProductName', $product_name );
                Message( "New ProductName   :" . $product->ProductName);
            }

            #
            #   Update the Product version [ if required ]
            #   Only update Product Version for this Product Configuration if the current Product 
            #   Version has a value.  
            #   This overrides the default project version for this product Configuration.
            #
            if ( $product->ProductVersion ne "" )
            {
                Verbose( "Update ProductVersion for this Product Configuration" );
                Verbose( "Original ProductVersion:" . $product->ProductVersion);
                $product->SetProperty('ProductVersion', $opt_version );
                Message ("New ProductVersion:" . $product->ProductVersion);
            }

            #
            #   If building with the Standalone interface will need to set
            #   up the merge module paths
            #
            if ( $opt_use_sa )
            {
                foreach my $dir ( @opt_mergemodule )
                {
                    Error ("MergeModule Directory not found: $dir") unless ( -d $dir );
                    Verbose( "Merge Module Search Path:", $dir );
                }
                $dev->SetProperty('MergeModuleSearchPath', join (',', @opt_mergemodule));
                Verbose2 ( "MergeModulePath: " . $dev->MergeModuleSearchPath );

                #
                #   See comments in this function
                #
                kludge_merge_module_stuff(@opt_mergemodule);
            }

            #
            #   Set thename of the setup file
            #   This contains the build name and number
            #
            Verbose ("Setting Setup Filename");
            Verbose2 ( "Initial SetupFileName: " . $product->SetupFileName );
            my $setup_name  = $opt_name . '-';
               $setup_name .= $product->Name . '-' if ($opt_multi_prod);
               $setup_name .= $release->Name . '-' if ($opt_multi_rel);
               $setup_name .= $dev->ProductVersion
                           .  '.'
                           .  $opt_suffix
                           .  '-WIN32';

            $product->SetProperty('SetupFileName', $setup_name );
            Message( "SetupFileName: " . $product->SetupFileName );

            #
            #   Determine the directory IS will use for logging
            #   Note: $logdir has spaces in the name and requires special care
            #
            my $logdir = $release->BuildLocation . "/ishield package/Release/LogFiles";
            $logdir =~ tr~\\/~/~s;

            #
            #   Delete old log files
            #
            if ( opendir (my $ld, $logdir) )
            {
                my @dirlist = readdir $ld;
                closedir $ld;

                foreach my $logfile ( @dirlist )
                {
                    next unless ( $logfile =~ m~\.txt$~i );
                    unlink  $logdir . '/' . $logfile;
                }
            }

            #
            #   Perform the build and report errors and warnings
            #
            $release->Build;
            $result_code = $release->BuildErrorCount;
            Message ("Build Error Count  : " . $release->BuildErrorCount );
            Message ("Build Warning Count: " . $release->BuildWarningCount );

            #
            #   Display log file ( if an error has been seen )
            #   There should be only ONE log file
            #   Reconfigure error reporting system to simplify logging
            #
            if ( $result_code || IsVerbose(1) )
            {
                if ( opendir (my $ld, $logdir) )
                {
                    my @dirlist = readdir $ld;
                    closedir $ld;

                    foreach my $logfile ( @dirlist )
                    {
                        next unless ( $logfile =~ m~\.txt$~i );
                        Message ("Dumping InstallShield Log File: $logfile");
                        my $estate = ErrorReConfig ('name' => 'ISLOG');
                        if ( open (my $lf, "<", $logdir . '/' . $logfile) )
                        {
                            while ( <$lf> )
                            {
                                $_ =~ s~\s+$~~;
                                Message ( $_ );
                            }
                            close $lf;
                        }
                        else
                        {
                            Warning ("Can't open file: $logfile",  "Reason: $!" );
                        }
                    }
                }
                else
                {
                    Warning( "InstallShield Log dir not found: $logdir")
                }
            }

            #
            #   Transfer the result file to the user
            #
            if ( $result_code == 0 && $opt_outpath )
            {
                Message("Transfer output file: $opt_outpath");
                my $ofile = $location . "/" . $product->Name . "/" . $release->Name . "/DiskImages/DISK1/" . $setup_name . '.exe';
                Error ("Build output file not found", "Expected: $ofile" ) unless ( -f $ofile );

                File::Copy::copy( $ofile, $opt_outpath) ||
                    Error ("Did not transfer InstallShield output file", $ofile, $opt_outpath);
            }
        }
    }
}

#
#   Save and Close the project
#   Release the OLE data
#
$dev->SaveProject( ) unless ($opt_read_only);
$dev->CloseProject( );
undef $dev;

#
#   Return build result to the user
#
exit $result_code;

#-------------------------------------------------------------------------------
# Function        : LocateInstallShield  
#
# Description     : Locate the InstallShield installation
#
# Inputs          : $dirList        - List of potential installation locations
#                   $tFile          - File to locate 
#
# Returns         : First installed path
#                   Will not return on error
#
sub LocateInstallShield {
    my ($dirList, $tFile) = @_;

    #
    #   Use EnvVar if it has been provided
    #
    if (exists $ENV{GBE_INSTALLSHIELD}) {
        $AutoBuilder = $ENV{GBE_INSTALLSHIELD};
        Error ("EnvVar GBE_INSTALLSHIELD does not address valid directory") unless -d $AutoBuilder;

        my $tPath = join ('\\', $AutoBuilder, $tFile );
        Error ("EnvVar GBE_INSTALLSHIELD does not reference a suitable directory") unless -f $tPath;

        return $AutoBuilder; 
    }

    #
    #   Locate the installed program in the list of user paths
    #
    foreach my $AutoBuilder (@{$dirList})
    {
        my $tPath = join ('\\', $AutoBuilder, $tFile );
        if (-f $tPath) {
            return $AutoBuilder; 
        }
    }

    Error('Cannot find InstallShield. You need to install either',
          'InstallShieldStandAloneBuilder',
          'InstallShield 11.5 Pro');
}


#-------------------------------------------------------------------------------
# Function        : kludge_merge_module_stuff
#
# Description     : Handle BUG in the SA builder
#
#   The current version of InstallShield StandAlone Builder has a bug associated
#   with the processing of Merge Modules
#
#   It would appear that the search path is not fully used
#   There is a interaction between the ObjectGallery directory and
#   the processing of Merge modules.
#
#   The problem (that I see) is solved by deleting the ObjectGallery
#   but InstallShield do not recomment this solution.
#
#   They recommend that the Object and Modules be copied into the
#   'C:\Program Files\Macrovision\IS 11.5 StandaloneBuild' directory
#   There are a few bug reports out for this one.
#   With luck this problem will be solved and this code can be removed
#
#   The solution that I have taken is to transfer the Object and
#   Modules directories as suggseted. This is UGLY:
#       Makes the build single user
#       Modifies the build environment
#       Time consuming
#
#
#
# Inputs          : List of Merge Module paths
#
# Returns         : Nothing
#
my $kludge_copy_dir_len;
my $kludge_copy_dir;

sub kludge_merge_module_stuff
{
    my (@mergemodules) = @_;
    foreach my $dir ( @mergemodules )
    {
        next if ( $dir =~ m/Merge Modules$/ );
        Message("KLUDGE copy: [$dir] to [$AutoBuilder]");
        $kludge_copy_dir = $dir;
        $kludge_copy_dir =~ s~\\+~/~g;
        $kludge_copy_dir =~ s~/$~~;
        $kludge_copy_dir =~ m~(.+/MergeModules)/~;
        $kludge_copy_dir_len = length($1);

        File::Find::find( \&kludge_copy, $dir);
    }
}

sub kludge_copy
{
    my $item = $File::Find::name;
    my $base = $_;
    my $tpath =  $AutoBuilder . substr( $item, $kludge_copy_dir_len );

    if ( -d $item )
    {
        unless ( -d $tpath )
        {
            Verbose( "Create Directory: $tpath");
            mkpath("$tpath", 1, 0775) || Error ("Cannot mkdir: $tpath");
        }
    }
    else
    {
        if ( FileIsNewer( $item, $tpath ) )
        {
            Verbose ("Copy File: tpath");
            File::Copy::copy($item, $tpath) || Error("Copying: $tpath");
        }
    }
}

#-------------------------------------------------------------------------------
#   Documentation
#

=pod

=head1 NAME

isbuild - Build an Install Shield Project

=head1 SYNOPSIS

jats eprog isbuild.pl [options]

 Options:
    -help              - brief help message
    -help -help        - Detailed help message
    -man               - Full documentation
    -version=version   - Specify build version
    -project=name      - Specifies the project to process
    -out=path          - Output directory (optional)
    -mergemodule=path  - Path to one or more merge modules
    -workdir=path      - Path to working directory base
    -[no]readonly      - Open project in readonly mode
    -[no]standalone    - Use SA or IS automation interface
    -[no]build         - Build project
    -[no]codes         - Modify GUID codes in release
    -[no]nameversion   - Add Version info to ProductName (default)
    -[no]multiprod     - Builds multiple product configurations (off by default)
    -[no]multirel      - Builds multiple releases per product configuration (off by default)

=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<-version=version>

This option is essential. It is used to specify the output package version.
The version information will be inserted in the InstallShield project before
it is compiled.

=item B<-project=name>

This option specifies the InstallShield project to be processed. If not project
name is specified then this script will locate an '.ism' file in the current
directory and use it. Multiple '.ism' files are not supported.

=item B<-out=path>

Specifies the output path. If specified the InstallShield project will by
moved to this directory, if it is build successfully.

The path must exist and it must be a directory.

=item B<-mergemodule=path>

This option specifies one or more merge module paths to be used by the Install
Shield compiler. Multiple paths may be specified with multiple directives or
as a comma seperated list.

=item B<-workdir=path>

This option specifies the path of a directory in which this program will create
its working directory. If not specified then the current directory will be
used.

=item B<-readony>

Open the project in readonly mode. This is the default
Changes to the project are not written back.

=item B<-standalone>

Invoke the StandAlone Install Shield builder. This is the default.

If B<-nostandalone> is specified then the InstallShield IDE Automation interface
is used. This may not be the same as that on the build machine.

=item B<-build>

Build the project. This is the default mode of operation.

If B<-nobuild> is specified then the project will not be built. All other
operations will be performed.


=item B<-codes>

If specified then the following GUID elements are rolled:

=over 8

=item * PackageCode

=item * ProductCode

=item * UpgradeCode

=back

The default operation is to NOT roll the specified GUID elements.

=item B<-nameversion>

This option Add Version information to the Product Name.

Additionally now if there are any Product Configurations in the Release view
of the project that have a value in the Product Name for that configuration then
they to will be modified in the same way.

The default operation will add Version Information. Use -nonameversion to disable
this operation.

=item B<-multiprod>

This option builds multiple product configurations from the single IS project.

The Releases view in Installshield allows multiple Product Configurations to 
be configured and built. This must be specified if the project contains more 
than 1 Product Configuration, but can also be used with a single configuration.
If there are multiple release for any Product Configuration then -multirel must
be specified as well.

This uses the Product Configuration Name, that is the name given to this Product
Configuration that appears in the Releases tree view of Installshield.
   
This name is used to modify the name to the final setup executable and is
appended to the Project Name.  For example each product configuration will
have a setup exe named 
   <ProjectName>-<ProductConfigName>-<Version>.<Suffix>-WIN32

Additionally if nameversion is active and a product configuration has a Product Name
then it to will be updated as per -nameversion option.

=item B<-multirel>

This option builds multiple releases per product configuration from the single 
IS project.

The Releases view in Installshield allows multiple releases per product 
configuration to be configured and built. This must be specified if the project 
contains more than 1 release per product configuration, but can also be used 
with a single release per product configuration.

This uses the Release Name, that is the name given to this Release for this Product
Configuration and appears under the Product Configuration in the Releases tree view 
of Installshield.
   
This name is used to modify the name to the final setup executable and is appended to 
the Project Name or Product Configuration name if -multiprod is enabled.  For example
If -multiprod is specified as well, each release of each product configuration
will have a setup exe named
   <ProjectName>-<ProductConfigName>-<ReleaseName>-<Version>.<Suffix>-WIN32
If -multiprod is Not specified, each release of the single product configuration
will have a setup exe named
   <ProjectName>-<ReleaseName>-<Version>.<Suffix>-WIN32

=back

=head1 DESCRIPTION

This program is used within the ERG deployment process to build up an Install
Shield project.

=head1 EXAMPLE


=cut

