Subversion Repositories DevTools

Rev

Rev 4752 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4350 dpurdie 1
########################################################################
2
# Copyright (c) VIX TECHNOLOGY (AUST) LTD
3
#
4
# Module name   : androidBuilder.pl
5
# Module type   : Makefile system
6
# Compiler(s)   : Perl
7
# Environment(s): jats
8
#
9
# Description   : This program is invoked by the JATS Makefile System
10
#                 to 'build' an Android project from an Eclipse based
11
#                 Android project. It does this by:
12
#                   Creating a build.xml file from the Eclispe files
13
#                   Injecting properties into the build
14
#                   Invoking ANT to perform the build 
15
#
16
#                 This process requires external tool - delivered in packages
17
#                 These are:
18
#                   ant         - Normally more recent than that installed 
19
#                                 on the build machines
20
#                   androidSdk  - From package rather than installed
21
#                                 This provides flexability when a new Sdk
22
#                                 is required
23
#
24
# Usage:        The utility is invoked in a controlled manner from a Jats
25
#               makefile. The call is generated by the Android Toolset
26
#               Arguments:
27
#                -verbose                   - Increase debugging
28
#                -verbose=n                 - Increase debugging
29
#                -f=manifestFile.xml        - project Manifest file
30
#                -i=path                    - Path to the interface directory
31
#                -t=[P|D]"                  - Build Type. Production or Debug
7574 dpurdie 32
#                -pf=platform               - Current platform [Not used]
4350 dpurdie 33
#                -pn=PackageName            - Package Name
34
#                -pv=PackageVersion         - Package Version
35
#                -clean                     - Will clean the build
36
#                -populate                  - Test Env and Populate 'libs'
37
#               Aguments that can be provided by the user
38
#                -projectname=ProjectName   - Project Name [optional]
39
#                -target=number             - Project Target
4370 dpurdie 40
#                -Jar=name                  - Name of a Jar to include
41
#                -lname                     - Name of a Jats library to include
42
#                -Lname                     - Name of a 3rd party library to include
4350 dpurdie 43
#
44
#......................................................................#
45
 
46
require 5.008_002;
47
use strict;
48
use warnings;
49
 
4364 dpurdie 50
use Getopt::Long qw(:config pass_through);
4350 dpurdie 51
 
52
use JatsError;
53
use JatsSystem;
54
use FileUtils;
55
use JatsProperties;
56
use JatsVersionUtils;
57
use ReadBuildConfig;
58
use JatsCopy;
4364 dpurdie 59
use ArrayHashUtils;
4350 dpurdie 60
 
61
#
62
#   Globals
63
#   Command line arguments
64
#
65
my $opt_verbose = $ENV{GBE_VERBOSE};
66
my $opt_manifestFile;
67
my $opt_interface;
68
my $opt_gbetype = 'P';
69
my $opt_clean;
70
my $opt_pkgname;
71
my $opt_pkgversion;
7574 dpurdie 72
my $opt_platform;
4350 dpurdie 73
my $opt_projectname;
74
my $opt_populate;
75
my $opt_target = 1;
4364 dpurdie 76
my @opt_jlibs;
77
my @opt_elibs;
78
my @opt_jars;
4350 dpurdie 79
 
80
#
81
#   Configuration
82
#   Map JATS platforms to Shared library targets
83
#
84
my %SharedLibMap = (
85
    'ANDROIDARM'    => 'armeabi',
86
    'ANDROIDMIPS'   => 'mips',
87
    'ANDROIDX86'    => 'x86',
88
    );
89
 
90
#   Build type in ant format: release or debug
91
my $ant_target;
92
 
93
#-------------------------------------------------------------------------------
94
# Function        : Main Entry Point 
95
#
96
# Description     : Main entry to this program
97
#
98
# Inputs          : @ARGV           - Array of command line arguments
99
#                                     See file header
100
#
101
# Returns         : 0               - No Error
102
#                   1               - Error encountered    
103
#
4354 dpurdie 104
InitFileUtils();
4350 dpurdie 105
ErrorConfig( 'name'    => 'ANDROIDBUILDER',
106
             'verbose' => $opt_verbose);
107
 
108
#
109
#   Install local signal handlers to process GetOptions messages
110
#
111
local $SIG{__WARN__} = sub { ReportError('AndroidBuilder.' . "@_"); };
112
local $SIG{__DIE__} = sub { ReportError('AndroidBuilder.' . "@_"); };
113
my $result = GetOptions (
114
                "verbose:+"     => \$opt_verbose,       # flag
115
                "f=s"           => \$opt_manifestFile,  # string
116
                "i=s"           => \$opt_interface,     # Interface directory
117
                "t=s"           => \$opt_gbetype,       # string
118
                "pn=s"          => \$opt_pkgname,       # string
119
                "pv=s"          => \$opt_pkgversion,    # string
7574 dpurdie 120
                "pf=s"          => \$opt_platform,      # string
4350 dpurdie 121
                "projectname=s" => \$opt_projectname,   # string
122
                "clean"         => \$opt_clean,         # flag
123
                "populate"      => \$opt_populate,      # flag
124
                "target:i"      => \$opt_target,        # Number
4364 dpurdie 125
                "Jar=s"         => \@opt_jars,
4350 dpurdie 126
                );
127
 
128
#
129
#   Restore signal handlers and report parse errors
130
#
4364 dpurdie 131
$SIG{__WARN__} = 'DEFAULT';
132
$SIG{__DIE__} = 'DEFAULT';
4350 dpurdie 133
Error('AndroidBuilder. Invalid call options detected') if (!$result);
4364 dpurdie 134
 
135
#
136
#   Process remaining arguments
137
#   Only --Lname and --lname are valid
138
#
139
foreach my $arg (@ARGV) {
140
    if ($arg =~ m~^[-]{1,2}l(.*)~) {
141
        push @opt_jlibs, $1;
142
    } elsif ($arg =~ m~^[-]{1,2}L(.*)~) {
143
        push @opt_elibs, $1;
144
    } else {
145
        ReportError("Invalid option: $arg");
146
    }
147
}
4350 dpurdie 148
ErrorDoExit();
149
 
150
#
151
#   Sanity Test
152
#
153
ReportError ("Manifest file not specified") unless ( defined $opt_manifestFile); 
154
ReportError ("Manifest file not found: $opt_manifestFile") unless ( -f $opt_manifestFile);
155
 
156
ReportError ("Interface directory not specified") unless ( defined $opt_interface);
157
ReportError ("Interface directory not found: $opt_interface") unless ( -d $opt_interface);
158
 
159
ReportError ("Package Name not specified") unless ( defined $opt_pkgname); 
160
ReportError ("Package Version not specified") unless ( defined $opt_pkgversion); 
161
ErrorDoExit();
162
 
163
#
164
#   Basic setup
165
#
166
$ant_target = $opt_gbetype eq 'P' ? 'release' : 'debug';
167
 
168
#
169
#   If multiple project are built in the same package it may be best to provide
170
#   a project name
171
#
172
$opt_projectname = $opt_pkgname
173
    unless (defined $opt_projectname);
174
 
175
#
176
#   The AndroidManifest.xml file MUST be in the root of the project
177
#   There will be some other files there too
178
#   Calculate the root of the project
179
#
180
my $project_root = StripFileExt($opt_manifestFile);
4371 dpurdie 181
   $project_root = '.' unless $project_root;
4350 dpurdie 182
my $project_buildfile = catfile($project_root, 'build.xml');
183
 
4371 dpurdie 184
Message ("Project Base:" . Getcwd());
4350 dpurdie 185
Message ("Project Root:" . $project_root);
186
Message ("Project Name:" . $opt_projectname);
187
Message ("Project build file:" . $project_buildfile);
4354 dpurdie 188
Verbose ("Interface:" . $opt_interface);
4350 dpurdie 189
 
190
#
191
#   Essential tool
192
#       ant     - setup ANT_HOME for other tools
193
#
194
#
195
ReadBuildConfig( $opt_interface, 'ANDROID', '--NoTest' );
196
my $antTool = getToolInfo('ant', 'JAVA_VERSION');
197
$ENV{ANT_HOME} = catdir($antTool->{PKGBASE}, $antTool->{TOOLROOT});
198
 
199
#
200
#   Setup the required version of Java for the tool
201
#
202
my $javaVersion = $antTool->{JAVA_VERSION};
203
ReportError ("$javaVersion not defined.", "Building ANDROID requires $javaVersion be installed and correctly configured.") 
204
    unless $ENV{$javaVersion};
205
$ENV{JAVA_HOME}=$ENV{$javaVersion};
206
 
207
#
208
#   Essential tool
209
#       androidSdk  - setup path to the android executable
210
#
211
my $androidSdk = getToolInfo('androidSdk');
212
my $androidPkg = catdir($androidSdk->{PKGBASE}, $androidSdk->{TOOLROOT} );
213
my $ANDROID = catdir($androidPkg,'sdk', 'tools', 'android' );
214
ReportError("Tool Package 'androidSdk' does not provide program 'android'")
215
    unless -f ($ANDROID);
216
ErrorDoExit();
217
 
218
#
219
#   Clean out any build artifacts
220
#
221
if ($opt_clean)
222
{
223
    #
224
    #   Invoke ANT on the build script - if present
225
    #
226
    Message ("Clean the existing build");
227
    JatsCmd('ant', '-buildfile', $project_buildfile, 'clean')
228
        if (-f $project_buildfile);
229
    deleteGeneratedFiles();
230
    exit 0;
231
}
232
 
233
#
234
#   Delete files that we will create and inject
235
#       Not sure about project.properties
236
#       It appears that it can be written - and info will be lost
237
#
238
deleteGeneratedFiles();
239
 
240
#
241
#   Populate the Android Project 'libs' directory
242
#
243
injectDependencies();
244
 
245
#
246
#   Create the project files
247
#       The android tool does not appear to provide an non-zero exit code on error
248
#       Detect error via the non-creation of the expected output file
249
#
250
System($ANDROID, $opt_verbose ? '--verbose' : '--silent',
251
                 'update', 'project', 
252
                 '--path', $project_root, 
253
                 '--subprojects', 
254
                 '--target', $opt_target, 
255
                 '--name', $opt_projectname );
256
unless ( -f catfile($project_root, 'build.xml'))
257
{
258
    Error("Cannot update android project: $opt_projectname");
259
}
260
 
261
#
262
#   The provided AndroidManifest.xml file contains 
263
#       android:versionCode and android:versionName
264
#   These prevent the ones in ant.properties from being reflected in the output
265
#   Rewrite the AndroidManifest.xml file with corrected versions
266
#
267
updateManifest();
268
 
269
#
270
#   If we are only populating the build then we have done all we need to do
271
#
272
if ($opt_populate)
273
{
274
    Verbose ("Populate complete");
275
    exit 0;
276
}
277
 
278
#
279
#   Build the Android project through the ANT package
280
#       ANT_HOME    - has been set up
281
#       JAVA_HOME   - has been set up
282
#
283
Message ("Build the android project: $opt_projectname");
284
my $rv = JatsCmd('ant', '-buildfile', $project_buildfile, $ant_target);
285
Error("Cannot build android project: $opt_projectname") if $rv;
286
exit(0);
287
 
288
#-------------------------------------------------------------------------------
289
# Function        : updateManifest 
290
#
291
# Description     : Calculate Version information
292
#                   Rewrite the Projects Manifest file and local properties files
293
#
294
# Inputs          : None 
295
#
296
# Returns         : Nothing
297
#
298
sub updateManifest
299
{
300
    #
301
    #   Generate Package Versioning information   
302
    #       Need a text string and a number
303
    #       Generate the 'number' from the version number
304
    #
305
    my $version_text;
306
    my $version_num;
307
 
308
    $version_text = $opt_pkgversion;
309
    my ($major, $minor, $patch, $build )= SplitVersion($opt_pkgversion);
310
    foreach my $item ($major, $minor, $patch, $build)
311
    {
312
        Error("Package version has invalid form. It contains non-numeric parts", $item)
313
            unless ( $item =~ m~^\d+$~);
314
    }
315
    $version_num = ($major << 24) + ($minor << 16) + ($patch << 8) + $build;
316
 
317
    Message ("Project Version Txt:" . $version_text);
318
    Message ("Project Version Num:" . $version_num);
319
 
320
    #
321
    #   Rewrite the Manifest File 
322
    #       Delete the Versioning information
323
    #       It will be picked up from the ant.properties file
324
    #       Store it in the interface directory
325
    #
326
    my $jats_androidManifestFile = catfile($opt_interface, $opt_projectname . '_'.'AndroidManifest.xml');
327
    Message ("Rewrite Manifest: " . $jats_androidManifestFile);
328
 
329
    open (AM, '<', $opt_manifestFile) or Error("Cannot open $opt_manifestFile: $!");
330
    open (JAM, '>', $jats_androidManifestFile) or Error ("Cannot create $jats_androidManifestFile: $!");
331
    while (<AM>)
332
    {
333
        s~(android:versionCode=").*(")~$1$version_num$2~;
334
        s~(android:versionName=").*(")~$1$version_text$2~;
335
        print JAM $_
336
    }
337
    close AM;
338
    close JAM;
339
 
340
    #
341
    #   Create the ant.properties file
342
    #   It needs to be in the same directory as the build.xml
343
    #
344
    my $antProperies = catfile($project_root,'ant.properties');
345
    Message ("Create Properties file: " . $antProperies);
346
 
347
    my $data = JatsProperties::New();
348
 
349
    #$data->setProperty('source.dir'        , 'src');
350
 
351
    #   Appears to work best when left as default
352
    #$data->setProperty('out.dir'           , 'bin');
353
 
354
    #   Don't write these to the properties file as it will
355
    #   create warning messages. The data is in the Manifest File
356
    #
357
    #$data->setProperty('version.code'      , $version_num);
358
    #$data->setProperty('version.name'      , $version_text);
359
 
4354 dpurdie 360
    $data->setProperty('manifest.file'      , FullPath($jats_androidManifestFile));
4350 dpurdie 361
    $data->setProperty('verbose'            , $opt_verbose ? 'true' : 'false');
362
 
363
    #   May be of interest
364
    #<property name="jar.libs.dir" value="libs" />
365
    #<property name="jar.libs.absolute.dir" location="${jar.libs.dir}" />
366
    #<property name="native.libs.absolute.dir" location="libs" />
367
    #
368
    #<property name="out.dir" value="bin" />
369
    #<property name="out.absolute.dir" location="${out.dir}" />
370
    #<property name="out.classes.absolute.dir" location="${out.dir}/classes" />
371
    #<property name="out.res.absolute.dir" location="${out.dir}/res" />
372
    #<property name="out.rs.obj.absolute.dir" location="${out.dir}/rsObj" />
373
    #<property name="out.rs.libs.absolute.dir" location="${out.dir}/rsLibs" />
374
    #<property name="out.aidl.absolute.dir" location="${out.dir}/aidl" />
375
    #<property name="out.dexed.absolute.dir" location="${out.dir}/dexedLibs" />
376
    #<property name="out.manifest.abs.file" location="${out.dir}/AndroidManifest.xml" />
377
 
378
    #
379
    #   Insert key information
380
    #   Only used in the 'release' build
381
    #   At the moment the signing step MUST be done outside of the build system
382
    #
383
    #$data->setProperty('key.store'               , 'vix-pcc.keystore');
384
    #$data->setProperty('key.alias'               , 'pcc');
385
    #$data->setProperty('key.store.password'      , 'VixPassword');
386
    #$data->setProperty('key.alias.password'      , 'VixPassword');
387
 
388
    $data->store( $antProperies );
389
}
390
 
391
#-------------------------------------------------------------------------------
392
# Function        : injectDependencies 
393
#
394
# Description     : Populate the 'libs' directory
395
#                       
396
#                   Inject dependencies
397
#                   The android build will make use of files in the 'libs' directory
398
#                   There are two types of files that can be placed in that directory
399
#                   These appear to be:
400
#                       1) .jar files
401
#                       2) Shared libraries provided by NDK components
402
#                   
403
#                   Assume that the user is doing the right thing and not manually placing
404
#                   external dependencies in the 'libs' directory
405
#                   
406
#                   Clean out all files and repopulate - depending on the build type
407
#                   It may be different for debug and production builds
408
#
409
# Inputs          : 
410
#
411
# Returns         : 
412
#
413
sub injectDependencies
414
{
4364 dpurdie 415
    my @jlist;
4595 dpurdie 416
    my @jpathlist;
4364 dpurdie 417
    my @liblist;
418
 
419
    #
420
    #   Only if we need to do something
421
    #
422
    return unless (@opt_jars || @opt_elibs || @opt_jlibs);
423
 
424
    #
425
    #   Create search entries suitable for the CopyDir
426
    #   We will delete entries as the files are copied
4595 dpurdie 427
    #   Allow for:
428
    #       jar files to have a .jar suffix (optional)
4752 dpurdie 429
    #       jar files to have path specified with a package
4364 dpurdie 430
    #
431
    foreach my $item ( @opt_jars) {
4595 dpurdie 432
        $item =~ s~\.jar~~i;
433
        if ($item =~ m~/~)
434
        {
435
            UniquePush \@jpathlist, $item . '.jar';
436
        } else {
437
            UniquePush \@jlist, $item . '.jar';
438
        }
4364 dpurdie 439
    }
440
 
441
    foreach my $item ( @opt_elibs) {
442
        UniquePush \@liblist, 'lib' . $item . '.so';
443
    }
444
 
445
    foreach my $item ( @opt_jlibs) {
446
        UniquePush \@liblist, 'lib' . $item . $opt_gbetype . '.so';
447
    }
448
 
449
 
450
    #
451
    #   Where does it go
452
    #
4350 dpurdie 453
    my $androidLibs = catdir($project_root, 'libs');
454
    Verbose ("Android libs: $androidLibs");
455
 
456
    my @pkg_paths = getPackagePaths("--Interface=$opt_interface");
457
    foreach my $pkg ( @pkg_paths)
458
    {
459
        #
460
        #   Copy in all JAR files found in dependent packages
461
        #
4595 dpurdie 462
        my $jarDir = catdir($pkg,'jar');
4364 dpurdie 463
        if (-d $jarDir && @jlist)
4350 dpurdie 464
        {
465
            Verbose("Jar Dir Found found", $jarDir);
466
            Message ("Copy in: $jarDir");
467
            CopyDir ( $jarDir, $androidLibs,
4364 dpurdie 468
                        'Match' => \@jlist,
4350 dpurdie 469
                        'Log' => $opt_verbose + 1,
470
                        'SymlinkFiles' => 1,
4364 dpurdie 471
                        'Examine' => sub 
472
                            {
473
                                my ($opt) = @_;
474
                                ArrayDelete \@jlist, $opt->{file};
4752 dpurdie 475
                                return 1;
4364 dpurdie 476
                            },
4350 dpurdie 477
                        );
478
        }
479
 
480
        #
4595 dpurdie 481
        #   Copy in JARs specified by a full pathname
482
        #
483
        foreach my $file (@jpathlist) 
484
        {
485
            my $jarFile = catdir($pkg, $file);
486
            if (-f $jarFile)
487
            {
488
                Verbose("Jar File Found found", $jarDir);
489
                Message ("Copy in: $jarFile");
490
                CopyFile ( $jarFile, $androidLibs,
491
                            'Log' => $opt_verbose + 1,
492
                            'SymlinkFiles' => 1,
493
                         );
494
                ArrayDelete \@jpathlist, $file;
495
            }
496
        }
497
 
498
        #
4350 dpurdie 499
        #   Build up the Shared Library structure as used by JNI
500
        #   Note: Only support current JATS format
501
        #   Copy in .so files and in to process massage the pathname so that
502
        #   it confirms to that expected by the Android Project
503
        #
504
        my $libDir = catdir($pkg,'lib');
4364 dpurdie 505
        if (-d $libDir && @liblist)
4350 dpurdie 506
        {
507
            Verbose("Lib Dir Found found", $libDir);
508
            Message ("Copy in: $libDir");
509
            CopyDir ( $libDir, $androidLibs,
4364 dpurdie 510
                        'Match' => \@liblist,
4350 dpurdie 511
                        'Log' => $opt_verbose + 1,
512
                        'SymlinkFiles' => 1,
513
                        'Examine' => sub 
514
                            { 
515
                                my ($opt) = @_;
516
                                foreach my $platform ( keys %SharedLibMap ) {
517
                                    my $replace = $SharedLibMap{$platform};
518
                                    if ($opt->{'target'} =~ s~/$platform/~/$replace/~)
519
                                    {
4364 dpurdie 520
                                        ArrayDelete \@liblist, $opt->{file};
4350 dpurdie 521
                                        return 1;
522
                                    }
523
                                }
524
                                return 0;
525
                            },
526
                        );
527
        }
528
    }
4364 dpurdie 529
 
530
    #
531
    #   Report files that could not be located. They were deleted from the lists
532
    #   as they were processed
533
    #       These are Warnings in populate Mode
534
    #       and errors at build time
535
    #
4595 dpurdie 536
    if (@jlist || @liblist || @jpathlist)
4364 dpurdie 537
    {
538
        my $fn = $opt_populate ? \&Warning : \&Error; 
4595 dpurdie 539
        &$fn("External dependencies not found:", @jlist, @jpathlist, @liblist);
4364 dpurdie 540
    }
4350 dpurdie 541
}
542
 
543
#-------------------------------------------------------------------------------
544
# Function        : deleteGeneratedFiles 
545
#
546
# Description     : Delete files that we generate
547
#
548
# Inputs          : 
549
#
550
# Returns         : 
551
#
552
sub deleteGeneratedFiles
553
{
554
    #
555
    #   Delete files that we will create
556
    #       Not sure about project.properties
557
    #       It appears that it can be written - and info will be lost
558
    #
559
    my @deleteList = qw(local.properties build.xml proguard-project.txt);
560
    foreach my $file (@deleteList)
561
    {
562
        Verbose ("Deleting $project_root/$file");
563
        unlink catfile($project_root, $file);
564
    }
565
 
566
    #
567
    #   Clean out the 'libs' directory
568
    #       Assume that its populated with 'external' dependencies
569
    #       Leave the directory - it may have been checked into version control
570
    #
571
    my $androidLibs = catdir($project_root, 'libs');
4364 dpurdie 572
    foreach my $item (glob(catdir($androidLibs, '*'))) {
573
        RmDirTree($item);
574
    }
4350 dpurdie 575
}
576
 
577