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