Subversion Repositories DevTools

Rev

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