Subversion Repositories DevTools

Rev

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