Subversion Repositories DevTools

Rev

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