Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1532 dpurdie 1
#! perl
2
########################################################################
3
# Copyright ( C ) 2005 ERG Limited, All rights reserved
4
#
5
# Module name   : jats.sh
6
# Module type   : Makefile system
7
# Compiler(s)   : n/a
8
# Environment(s): jats
9
#
10
# Description   : Build an Install Shield project for deployment
11
#                 This script isolates the IS build from the rest of
12
#                 the deployment process.
13
#
14
#                 Assumes that:
15
#                       IS 11.5 Standalone builder has been installed
16
#                       Merge Modules have been installed / updated
17
#                       to subdirs with the SA installation of:
18
#                           Modules\i386
19
#                           Objects
20
#
21
#                 Note: IS SA builder can be copied and registered
22
#                       The installation is very simple.
23
#                       Registartion: regsvr32.exe
24
#
25
#                       Currently this script will register the required
26
#                       DLL anyway.
27
#
28
# Usage         : Refer to POD within this script
29
#
30
#......................................................................#
31
 
32
require 5.8.6;
33
use strict;
34
use warnings;
35
use JatsError;                              # Error reporting
36
use Win32::OLE;                             # load the Win32::OLE module
37
use Pod::Usage;                             # required for help support
38
use Getopt::Long;                           # Parse input options
39
use Cwd;                                    # Where am I
40
use File::Copy;                             # Transfer files
41
use File::Path;
42
use File::Find;                             # Ony for kludge copy
43
use FileUtils;
44
 
45
my $VERSION = "1.0.0";                      # Update this
46
 
47
#
48
#   Globals
49
#
50
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
51
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
52
my $opt_help = 0;
53
my $opt_manual;
54
 
55
my $opt_read_only = 1;
56
my $opt_use_sa = 1;
57
my $opt_build = 1;
58
my $opt_project;
59
my $opt_new_codes;
60
my $opt_name;
61
my $opt_version;
62
my $opt_suffix;
63
my $opt_outpath;
64
my @opt_mergemodule;
65
my $opt_workdir;
66
 
67
#
68
#   Kludgy values
69
#       May need to be configured
70
#       These are current IS 11.5 install locations
71
#
72
my $AutoBuilder = 'C:\Program Files\Macrovision\IS 11.5 StandaloneBuild';
73
my $result_code = 0;
74
 
75
#-------------------------------------------------------------------------------
76
# Function        : Mainline Entry Point
77
#
78
# Description     :
79
#
80
# Inputs          :
81
#
82
my $result = GetOptions (
83
                "help+"         => \$opt_help,              # flag, multiple use allowed
84
                "manual"        => \$opt_manual,            # flag
85
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
86
                "readonly!"     => \$opt_read_only,         # [no]flag
87
                "standalone!"   => \$opt_use_sa,            # [no]flag
88
                "build!"        => \$opt_build,             # [no]flag
89
                "project=s"     => \$opt_project,           # string
90
                "codes!"        => \$opt_new_codes,         # [no]flag
91
                "version=s"     => \$opt_version,           # string
92
                "out=s"         => \$opt_outpath,           # string
93
                "mergemodule=s" => \@opt_mergemodule,       # string
94
                "workdir=s"     => \$opt_workdir,           # string
95
                );
96
 
97
                #
98
                #   UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!
99
                #
100
 
101
#
102
#   Process help and manual options
103
#
104
pod2usage(-verbose => 0, -message => "Version: $VERSION") if ($opt_help == 1 || ! $result);
105
pod2usage(-verbose => 1) if ($opt_help == 2 );
106
pod2usage(-verbose => 2) if ($opt_manual || ($opt_help > 2));
107
#pod2usage(-verbose => 0, -message => "Version: $VERSION") if ( $#ARGV < 0 );
108
 
109
#
110
#   Configure the error reporting process now that we have the user options
111
#
112
ErrorConfig( 'name'    =>'ISBUILD',
113
             'verbose' => $opt_verbose,
114
            );
115
 
116
#
117
#   User must specify a project or we will attempt to locate one
118
#
119
Verbose ("Current directory: " . getcwd );
120
unless ( $opt_project )
121
{
122
    my @projects = glob ('*.ism' );
123
    Error ("No InstallShield projects found in current directory") unless ( $#projects >= 0 );
124
    Error ( "Multiple Install Shield projects located. Use -project option") if ( $#projects > 0 );
125
    $opt_project = $projects[0];
126
}
127
else
128
{
129
    Error ("Project file not found: $opt_project") unless ( -f $opt_project );
130
}
131
#
132
#   Determine project name from the project file name
133
#       - remove any path information
134
#       - remove .ism extension
135
#
136
$opt_name = $opt_project;
137
$opt_name =~ s~\\~/~g;
138
$opt_name =~ s~.*/~~;
139
$opt_name =~ s~\.ism$~~i;
140
 
141
#
142
#   User must specify a version
143
#   This will be processed and used within the build
144
#
145
Error ("Version must be specified") unless ( $opt_version );
146
Error ("Bad format for version: $opt_version") unless ( $opt_version =~ m~(\d+\.\d+\.\d+)\.(\w+)$~ );
147
$opt_version = $1;
148
$opt_suffix = $2;
149
Error ("Project suffix not found in version") unless ( $opt_suffix );
150
Message( "Package: $opt_name, Version: $opt_version, Project: $opt_suffix");
151
 
152
#
153
#   Output path must exist
154
#
155
if ( $opt_outpath )
156
{
157
    Error("Output path does not exist: $opt_outpath") unless ( -e $opt_outpath );
158
    Error("Output path is not a directory: $opt_outpath") unless ( -d $opt_outpath );
159
}
160
 
161
#
162
#   Workdir path must exist
163
#
164
$opt_workdir = getcwd unless( $opt_workdir );
165
Error("Workdir path does not exist: $opt_outpath") unless ( -e $opt_workdir );
166
Error("Workdir path is not a directory: $opt_outpath") unless ( -d $opt_workdir );
167
 
168
#
169
#   Process Merge Module paths
170
#
171
if ( @opt_mergemodule )
172
{
173
    #
174
    #   User may have entered a comma seperated list
175
    #   Convert into a full array
176
    #
177
    @opt_mergemodule = split(/,/,join(',',@opt_mergemodule));
178
 
179
    #
180
    #   Ensure that each path exists
181
    #   Clean up paths
182
    #
183
    foreach my $path ( @opt_mergemodule )
184
    {
185
        Warning ("MergeModule path not found: $path" ) unless ( -d $path );
186
        $path =~ s~/~\\~g;
187
    }
188
}
189
 
190
 
191
#
192
#   Instantiate the Developer Automation interface
193
#   Use the StandAlone interface if it can be found
194
#
195
my $dev;
196
if ( $opt_use_sa )
197
{
198
    Message "Using StandAlone OLE";
199
 
200
    #
201
    #   Ensure the interface is registered
202
    #   This allows a simple 'copy' of the SA build environment to a build
203
    #   machine.
204
    #
205
    my $regdll = $AutoBuilder . '\SAAuto1150.dll' ;
206
    Error ("Cannot find SA OLE DLL: $regdll") unless ( -f $regdll );
207
 
208
    my $rv = System( 'regsvr32.exe', '/s', $regdll);
209
    Error ("Failed to register $regdll: $rv" ) if ( $rv  );
210
 
211
    $dev = Win32::OLE->new("SAAuto1150.ISWiProject");
212
 
213
} else {
214
    Message "Using InstallShield OLE";
215
    $dev = Win32::OLE->new("IswiAuto1150.ISWiProject");
216
 
217
}
218
Error( "Cannot open Automation interface") unless ( $dev );
219
 
220
#
221
#   Open a project as read-write
222
#   The second argument ( if true ) will open as read only
223
#
224
Verbose ("Opening project: $opt_project, ReadOnly: $opt_read_only" );
225
$dev->OpenProject( $opt_project, $opt_read_only );
226
my $estring = Win32::OLE->LastError();
227
Error( $estring ) if ( $estring );
228
Verbose ("Project Open");
229
 
230
#DebugDumpData("Dev", $dev );
231
 
232
#
233
#   Massage the ProductName
234
#   Should contain a comma<space><version>
235
#
236
Verbose( "Update ProductName" );
237
Verbose( "Originial ProductName   :" . $dev->ProductName);
238
my $product_name = $dev->ProductName;
239
if ( $product_name =~ m/^(.*),/ )
240
{
241
    $product_name = $1;
242
}
243
$product_name .= ', ' . $opt_version . '.' . $opt_suffix;
244
$dev->SetProperty('ProductName', $product_name );
245
Message( "ProductName   :" . $dev->ProductName);
246
 
247
#
248
#   Massage the ProjectVersion
249
#   This will not have the project suffix on it
250
#
251
Verbose( "Update ProductVersion" );
252
Verbose( "Original ProductVersion:" . $dev->ProductVersion);
253
$dev->SetProperty('ProductVersion', $opt_version );
254
Message ("ProductVersion:" . $dev->ProductVersion);
255
 
256
#
257
#   If we need to generate new codes
258
#
259
if ( $opt_new_codes )
260
{
261
    Message ("Update GUIDs");
262
    $dev->SetProperty('PackageCode', $dev->GenerateGUID() );
263
    $dev->SetProperty('ProductCode', $dev->GenerateGUID() );
264
    $dev->SetProperty('UpgradeCode', $dev->GenerateGUID() );
265
}
266
Message ("PackageCode   :" . $dev->PackageCode );
267
Message ("ProductCode   :" . $dev->ProductCode );
268
Message ("UpgradeCode   :" . $dev->UpgradeCode );
269
 
270
#
271
#   Examine all components and display the files within each
272
#   The files are a collection and need to be processed in a special manner
273
#
274
if ( $opt_verbose )
275
{
276
    Verbose ("InstallShield Components");
277
    my $mycomps = $dev->ISWiComponents;
278
    foreach my $comp (in $mycomps)
279
    {
280
        Verbose ("Component: " . $comp->Name, ": ", $comp->Destination );
281
 
282
        my $files = $comp->ISWiFiles;
283
        my $fileCount = $files->Count;
284
        Verbose ("Number of Files: $fileCount");
285
 
286
        foreach my $index ( 1 .. $fileCount )
287
        {
288
            Verbose ("           Name: " . $files->Item($index)->Name );
289
            Verbose ("    DisplayName: " . $files->Item($index)->DisplayName );
290
            Verbose ("       FullPath: " .  $files->Item($index)->FullPath );
291
        }
292
    }
293
}
294
 
295
#
296
#   Examine all the Features
297
#
298
if ( $opt_verbose  )
299
{
300
    Verbose ("InstallShield Features");
301
    my $myfeature = $dev->ISWiFeatures;
302
    foreach my $feat (in $myfeature)
303
    {
304
        Verbose ("Feature: " . $feat->Name, ": ", $feat->Description );
305
    }
306
}
307
 
308
#
309
#   Display Releases
310
#   These are found within the product config
311
#   We need to locate the Release in order to build it
312
#
313
Verbose ("InstallShield Products");
314
my $products = $dev->ISWiProductConfigs;
315
foreach my $index ( 1 .. $products->Count )
316
{
317
    my $product = $products->Item($index);
318
 
319
    Verbose ("Product Index  : " . $index );
320
    Verbose ("  Product Name : " . $product->Name );
321
    Verbose ("  SetupFileName: " . $product->SetupFileName );
322
 
323
    my $releases = $product->ISWiReleases;
324
    foreach my $index ( 1 .. $releases->Count )
325
    {
326
        my $release = $releases->Item($index);
327
 
328
        Verbose ("Release Index      : " . $index );
329
        Verbose ("  Name             : " . $release->Name );
330
        Verbose ("  BuildLocation    : " . $release->BuildLocation );
331
        Verbose ("  SingleEXEFileName: " . ( $release->SingleEXEFileName || 'None Specified' ) );
332
 
333
 
334
        #
335
        #   Build the Project
336
        #   Not working at the moment !!!!
337
        #
338
        if ( $opt_build )
339
        {
340
            Message ("Building...");
341
 
342
            #
343
            #   Set build location
344
            #
345
            my $location = $opt_workdir . '/Media';
346
            $location =~ s~/~\\~g;
347
            $release->SetProperty('BuildLocation', $location );
348
            Message ("BuildLocation: " . $release->BuildLocation );
349
 
350
 
351
            #
352
            #   If building with the Standalone interface will need to set
353
            #   up the merge module paths
354
            #
355
            if ( $opt_use_sa )
356
            {
357
                foreach my $dir ( @opt_mergemodule )
358
                {
359
                    Error ("MergeModule Directory not found: $dir") unless ( -d $dir );
360
                    Verbose( "Merge Module Search Path:", $dir );
361
                }
362
                $dev->SetProperty('MergeModuleSearchPath', join (',', @opt_mergemodule));
363
                Verbose2 ( "MergeModulePath: " . $dev->MergeModuleSearchPath );
364
 
365
                #
366
                #   See comments in this function
367
                #
368
                kludge_merge_module_stuff(@opt_mergemodule);
369
            }
370
 
371
            #
372
            #   Set thename of the setup file
373
            #   This contains the build name and number
374
            #
375
            Verbose ("Setting Setup Filename");
376
            Verbose2 ( "Initial SetupFileName: " . $product->SetupFileName );
377
            my $setup_name = $opt_name
378
                           . '-'
379
                           . $dev->ProductVersion
380
                           . '.'
381
                           . $opt_suffix
382
                           . '-WIN32';
383
 
384
            $product->SetProperty('SetupFileName', $setup_name );
385
            Message( "SetupFileName: " . $product->SetupFileName );
386
 
387
            #
388
            #   Perform the build and report errors and warnings
389
            #
390
            $release->Build;
391
            $result_code = $release->BuildErrorCount;
392
            Message ("Build Error Count  : " . $release->BuildErrorCount );
393
            Message ("Build Warning Count: " . $release->BuildWarningCount );
394
 
395
            #
396
            #   Transfer the result file to the user
397
            #
398
            if ( $result_code == 0 && $opt_outpath )
399
            {
400
                Message("Transfer output file: $opt_outpath");
401
                my$ofile = $location . "/ishield package/Release/DiskImages/DISK1/" . $setup_name . '.exe';
402
                Error ("Build output file not found", "Expected: $ofile" ) unless ( -f $ofile );
403
 
404
                File::Copy::copy( $ofile, $opt_outpath) ||
405
                    Error ("Did not transfer InstallShield output file", $ofile, $opt_outpath);
406
            }
407
        }
408
    }
409
}
410
 
411
#
412
#   Save and Close the project
413
#   Release the OLE data
414
#
415
$dev->SaveProject( ) unless ($opt_read_only);
416
$dev->CloseProject( );
417
undef $dev;
418
 
419
#
420
#   Return build result to the user
421
#
422
exit $result_code;
423
 
424
#-------------------------------------------------------------------------------
425
# Function        : kludge_merge_module_stuff
426
#
427
# Description     : Handle BUG in the SA builder
428
#
429
#   The current version of InstallShield StandAlone Builder has a bug associated
430
#   with the processing of Merge Modules
431
#
432
#   It would appear that the search path is not fully used
433
#   There is a interaction between the ObjectGallery directory and
434
#   the processing of Merge modules.
435
#
436
#   The problem (that I see) is solved by deleting the ObjectGallery
437
#   but InstallShield do not recomment this solution.
438
#
439
#   They recommend that the Object and Modules be copied into the
440
#   'C:\Program Files\Macrovision\IS 11.5 StandaloneBuild' directory
441
#   There are a few bug reports out for this one.
442
#   With luck this problem will be solved and this code can be removed
443
#
444
#   The solution that I have taken is to transfer the Object and
445
#   Modules directories as suggseted. This is UGLY:
446
#       Makes the build single user
447
#       Modifies the build environment
448
#       Time consuming
449
#
450
#
451
#
452
# Inputs          : List of Merge Module paths
453
#
454
# Returns         : Nothing
455
#
456
my $kludge_copy_dir_len;
457
my $kludge_copy_dir;
458
 
459
sub kludge_merge_module_stuff
460
{
461
    my (@mergemodules) = @_;
462
    foreach my $dir ( @mergemodules )
463
    {
464
        next if ( $dir =~ m/Merge Modules$/ );
465
        Message("KLUDGE copy: [$dir] to [$AutoBuilder]");
466
        $kludge_copy_dir = $dir;
467
        $kludge_copy_dir =~ s~\\+~/~g;
468
        $kludge_copy_dir =~ s~/$~~;
469
        $kludge_copy_dir =~ s~/[^/]*$~~;
470
        $kludge_copy_dir_len = length($kludge_copy_dir);
471
 
472
        File::Find::find( \&kludge_copy, $dir);
473
    }
474
}
475
 
476
sub kludge_copy
477
{
478
    my $item = $File::Find::name;
479
    my $base = $_;
480
    my $tpath =  $AutoBuilder . substr( $item, $kludge_copy_dir_len );
481
 
482
    if ( -d $item )
483
    {
484
        unless ( -d $tpath )
485
        {
486
            Verbose( "Create Directory: $tpath");
487
            mkpath("$tpath", 1, 0775) || Error ("Cannot mkdir: $tpath");
488
        }
489
    }
490
    else
491
    {
492
        if ( FileIsNewer( $item, $tpath ) )
493
        {
494
            Verbose ("Copy File: tpath");
495
            File::Copy::copy($item, $tpath) || Error("Copying: $tpath");
496
        }
497
    }
498
}
499
 
500
 
501
########################################################################
502
# System( command ) ---
503
#       Exec the specified command ...
504
#       Note: Command list is preserved, helps with spaces in arguments
505
#
506
sub System
507
{
508
    my( @cmd ) = @_;
509
    my( $rv );
510
 
511
    Verbose2 "System:" , join ',', map ( "\"$_\"" , @cmd) if ($::ScmVerbose >= 2);
512
    $rv = system( @cmd );
513
    Verbose2 "Result Code: $rv";
514
    $rv = $rv / 256;
515
    return $rv;
516
}
517
 
518
 
519
#-------------------------------------------------------------------------------
520
#   Documentation
521
#
522
 
523
=pod
524
 
525
=head1 NAME
526
 
527
isbuild - Build an Install Shield Project
528
 
529
=head1 SYNOPSIS
530
 
531
jats eprog isbuild.pl [options]
532
 
533
 Options:
534
    -help              - brief help message
535
    -help -help        - Detailed help message
536
    -man               - Full documentation
537
    -version=version   - Specify build version
538
    -project=name      - Specifies the project to process
539
    -out=path          - Output directory (optional)
540
    -mergemodule=path  - Path to one or more merge modules
541
    -workdir=path      - Path to working directory base
542
    -[no]readonly      - Open project in readonly mode
543
    -[no]standalone    - Use SA or IS automation interface
544
    -[no]build         - Build project
545
    -[no]codes         - Modify GUID codes in release
546
 
547
=head1 OPTIONS
548
 
549
=over 8
550
 
551
=item B<-help>
552
 
553
Print a brief help message and exits.
554
 
555
=item B<-help -help>
556
 
557
Print a detailed help message with an explanation for each option.
558
 
559
=item B<-man>
560
 
561
Prints the manual page and exits.
562
 
563
=item B<-version=version>
564
 
565
This option is essential. It is used to specify the output package version.
566
The version information will be inserted in the InstallShield project before
567
it is compiled.
568
 
569
=item B<-project=name>
570
 
571
This option specifies the InstallShield project to be processed. If not project
572
name is specified then this script will locate an '.ism' file in the current
573
directory and use it. Multiple '.ism' files are not supported.
574
 
575
=item B<-out=path>
576
 
577
Specifies the output path. If specified the InstallShield project will by
578
moved to this directory, if it is build successfully.
579
 
580
The path must exist and it must be a directory.
581
 
582
=item B<-mergemodule=path>
583
 
584
This option specifies one or more merge module paths to be used by the Install
585
Shield compiler. Multiple paths may be specified with multiple directives or
586
as a comma seperated list.
587
 
588
=item B<-workdir=path>
589
 
590
This option specifies the path of a directory in which this program will create
591
its working directory. If not specified then the current directory will be
592
used.
593
 
594
=item B<-readony>
595
 
596
Open the project in readonly mode. This is the default
597
Changes to the project are not written back.
598
 
599
=item B<-standalone>
600
 
601
Invoke the StandAlone Install Shield builder. This is the default.
602
 
603
If B<-nostandalone> is specified then the InstallShield IDE Automation interface
604
is used. This may not be the same as that on the build machine.
605
 
606
=item B<-build>
607
 
608
Build the project. This is the default mode of operation.
609
 
610
If B<-nobuild> is specified then the project will not be built. All other
611
operations will be performed.
612
 
613
 
614
=item B<-codes>
615
 
616
If specified then the following GUID elements are rolled:
617
 
618
=over 8
619
 
620
=item * PackageCode
621
 
622
=item * ProductCode
623
 
624
=item * UpgradeCode
625
 
626
=back
627
 
628
The default operation is to NOT roll the specified GUID elements.
629
 
630
=back
631
 
632
=head1 DESCRIPTION
633
 
634
This program is used within the ERG deployment process to build up an Install
635
Shield project.
636
 
637
=head1 EXAMPLE
638
 
639
 
640
=cut
641