Rev 4595 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright (c) VIX TECHNOLOGY (AUST) LTD## Module name : androidBuilder.pl# Module type : Makefile system# Compiler(s) : Perl# Environment(s): jats## Description : This program is invoked by the JATS Makefile System# to 'build' an Android project from an Eclipse based# Android project. It does this by:# Creating a build.xml file from the Eclispe files# Injecting properties into the build# Invoking ANT to perform the build## This process requires external tool - delivered in packages# These are:# ant - Normally more recent than that installed# on the build machines# androidSdk - From package rather than installed# This provides flexability when a new Sdk# is required## Usage: The utility is invoked in a controlled manner from a Jats# makefile. The call is generated by the Android Toolset# Arguments:# -verbose - Increase debugging# -verbose=n - Increase debugging# -f=manifestFile.xml - project Manifest file# -i=path - Path to the interface directory# -t=[P|D]" - Build Type. Production or Debug# -pn=PackageName - Package Name# -pv=PackageVersion - Package Version# -clean - Will clean the build# -populate - Test Env and Populate 'libs'# Aguments that can be provided by the user# -projectname=ProjectName - Project Name [optional]# -target=number - Project Target# -Jar=name - Name of a Jar to include# -lname - Name of a Jats library to include# -Lname - Name of a 3rd party library to include##......................................................................#require 5.008_002;use strict;use warnings;use Getopt::Long qw(:config pass_through);use JatsError;use JatsSystem;use FileUtils;use JatsProperties;use JatsVersionUtils;use ReadBuildConfig;use JatsCopy;use ArrayHashUtils;## Globals# Command line arguments#my $opt_verbose = $ENV{GBE_VERBOSE};my $opt_manifestFile;my $opt_interface;my $opt_gbetype = 'P';my $opt_clean;my $opt_pkgname;my $opt_pkgversion;my $opt_projectname;my $opt_populate;my $opt_target = 1;my @opt_jlibs;my @opt_elibs;my @opt_jars;## Configuration# Map JATS platforms to Shared library targets#my %SharedLibMap = ('ANDROIDARM' => 'armeabi','ANDROIDMIPS' => 'mips','ANDROIDX86' => 'x86',);# Build type in ant format: release or debugmy $ant_target;#-------------------------------------------------------------------------------# Function : Main Entry Point## Description : Main entry to this program## Inputs : @ARGV - Array of command line arguments# See file header## Returns : 0 - No Error# 1 - Error encountered#InitFileUtils();ErrorConfig( 'name' => 'ANDROIDBUILDER','verbose' => $opt_verbose);## Install local signal handlers to process GetOptions messages#local $SIG{__WARN__} = sub { ReportError('AndroidBuilder.' . "@_"); };local $SIG{__DIE__} = sub { ReportError('AndroidBuilder.' . "@_"); };my $result = GetOptions ("verbose:+" => \$opt_verbose, # flag"f=s" => \$opt_manifestFile, # string"i=s" => \$opt_interface, # Interface directory"t=s" => \$opt_gbetype, # string"pn=s" => \$opt_pkgname, # string"pv=s" => \$opt_pkgversion, # string"projectname=s" => \$opt_projectname, # string"clean" => \$opt_clean, # flag"populate" => \$opt_populate, # flag"target:i" => \$opt_target, # Number"Jar=s" => \@opt_jars,);## Restore signal handlers and report parse errors#$SIG{__WARN__} = 'DEFAULT';$SIG{__DIE__} = 'DEFAULT';Error('AndroidBuilder. Invalid call options detected') if (!$result);## Process remaining arguments# Only --Lname and --lname are valid#foreach my $arg (@ARGV) {if ($arg =~ m~^[-]{1,2}l(.*)~) {push @opt_jlibs, $1;} elsif ($arg =~ m~^[-]{1,2}L(.*)~) {push @opt_elibs, $1;} else {ReportError("Invalid option: $arg");}}ErrorDoExit();## Sanity Test#ReportError ("Manifest file not specified") unless ( defined $opt_manifestFile);ReportError ("Manifest file not found: $opt_manifestFile") unless ( -f $opt_manifestFile);ReportError ("Interface directory not specified") unless ( defined $opt_interface);ReportError ("Interface directory not found: $opt_interface") unless ( -d $opt_interface);ReportError ("Package Name not specified") unless ( defined $opt_pkgname);ReportError ("Package Version not specified") unless ( defined $opt_pkgversion);ErrorDoExit();## Basic setup#$ant_target = $opt_gbetype eq 'P' ? 'release' : 'debug';## If multiple project are built in the same package it may be best to provide# a project name#$opt_projectname = $opt_pkgnameunless (defined $opt_projectname);## The AndroidManifest.xml file MUST be in the root of the project# There will be some other files there too# Calculate the root of the project#my $project_root = StripFileExt($opt_manifestFile);$project_root = '.' unless $project_root;my $project_buildfile = catfile($project_root, 'build.xml');Message ("Project Base:" . Getcwd());Message ("Project Root:" . $project_root);Message ("Project Name:" . $opt_projectname);Message ("Project build file:" . $project_buildfile);Verbose ("Interface:" . $opt_interface);## Essential tool# ant - setup ANT_HOME for other tools##ReadBuildConfig( $opt_interface, 'ANDROID', '--NoTest' );my $antTool = getToolInfo('ant', 'JAVA_VERSION');$ENV{ANT_HOME} = catdir($antTool->{PKGBASE}, $antTool->{TOOLROOT});## Setup the required version of Java for the tool#my $javaVersion = $antTool->{JAVA_VERSION};ReportError ("$javaVersion not defined.", "Building ANDROID requires $javaVersion be installed and correctly configured.")unless $ENV{$javaVersion};$ENV{JAVA_HOME}=$ENV{$javaVersion};## Essential tool# androidSdk - setup path to the android executable#my $androidSdk = getToolInfo('androidSdk');my $androidPkg = catdir($androidSdk->{PKGBASE}, $androidSdk->{TOOLROOT} );my $ANDROID = catdir($androidPkg,'sdk', 'tools', 'android' );ReportError("Tool Package 'androidSdk' does not provide program 'android'")unless -f ($ANDROID);ErrorDoExit();## Clean out any build artifacts#if ($opt_clean){## Invoke ANT on the build script - if present#Message ("Clean the existing build");JatsCmd('ant', '-buildfile', $project_buildfile, 'clean')if (-f $project_buildfile);deleteGeneratedFiles();exit 0;}## Delete files that we will create and inject# Not sure about project.properties# It appears that it can be written - and info will be lost#deleteGeneratedFiles();## Populate the Android Project 'libs' directory#injectDependencies();## Create the project files# The android tool does not appear to provide an non-zero exit code on error# Detect error via the non-creation of the expected output file#System($ANDROID, $opt_verbose ? '--verbose' : '--silent','update', 'project','--path', $project_root,'--subprojects','--target', $opt_target,'--name', $opt_projectname );unless ( -f catfile($project_root, 'build.xml')){Error("Cannot update android project: $opt_projectname");}## The provided AndroidManifest.xml file contains# android:versionCode and android:versionName# These prevent the ones in ant.properties from being reflected in the output# Rewrite the AndroidManifest.xml file with corrected versions#updateManifest();## If we are only populating the build then we have done all we need to do#if ($opt_populate){Verbose ("Populate complete");exit 0;}## Build the Android project through the ANT package# ANT_HOME - has been set up# JAVA_HOME - has been set up#Message ("Build the android project: $opt_projectname");my $rv = JatsCmd('ant', '-buildfile', $project_buildfile, $ant_target);Error("Cannot build android project: $opt_projectname") if $rv;exit(0);#-------------------------------------------------------------------------------# Function : updateManifest## Description : Calculate Version information# Rewrite the Projects Manifest file and local properties files## Inputs : None## Returns : Nothing#sub updateManifest{## Generate Package Versioning information# Need a text string and a number# Generate the 'number' from the version number#my $version_text;my $version_num;$version_text = $opt_pkgversion;my ($major, $minor, $patch, $build )= SplitVersion($opt_pkgversion);foreach my $item ($major, $minor, $patch, $build){Error("Package version has invalid form. It contains non-numeric parts", $item)unless ( $item =~ m~^\d+$~);}$version_num = ($major << 24) + ($minor << 16) + ($patch << 8) + $build;Message ("Project Version Txt:" . $version_text);Message ("Project Version Num:" . $version_num);## Rewrite the Manifest File# Delete the Versioning information# It will be picked up from the ant.properties file# Store it in the interface directory#my $jats_androidManifestFile = catfile($opt_interface, $opt_projectname . '_'.'AndroidManifest.xml');Message ("Rewrite Manifest: " . $jats_androidManifestFile);open (AM, '<', $opt_manifestFile) or Error("Cannot open $opt_manifestFile: $!");open (JAM, '>', $jats_androidManifestFile) or Error ("Cannot create $jats_androidManifestFile: $!");while (<AM>){s~(android:versionCode=").*(")~$1$version_num$2~;s~(android:versionName=").*(")~$1$version_text$2~;print JAM $_}close AM;close JAM;## Create the ant.properties file# It needs to be in the same directory as the build.xml#my $antProperies = catfile($project_root,'ant.properties');Message ("Create Properties file: " . $antProperies);my $data = JatsProperties::New();#$data->setProperty('source.dir' , 'src');# Appears to work best when left as default#$data->setProperty('out.dir' , 'bin');# Don't write these to the properties file as it will# create warning messages. The data is in the Manifest File##$data->setProperty('version.code' , $version_num);#$data->setProperty('version.name' , $version_text);$data->setProperty('manifest.file' , FullPath($jats_androidManifestFile));$data->setProperty('verbose' , $opt_verbose ? 'true' : 'false');# May be of interest#<property name="jar.libs.dir" value="libs" />#<property name="jar.libs.absolute.dir" location="${jar.libs.dir}" />#<property name="native.libs.absolute.dir" location="libs" />##<property name="out.dir" value="bin" />#<property name="out.absolute.dir" location="${out.dir}" />#<property name="out.classes.absolute.dir" location="${out.dir}/classes" />#<property name="out.res.absolute.dir" location="${out.dir}/res" />#<property name="out.rs.obj.absolute.dir" location="${out.dir}/rsObj" />#<property name="out.rs.libs.absolute.dir" location="${out.dir}/rsLibs" />#<property name="out.aidl.absolute.dir" location="${out.dir}/aidl" />#<property name="out.dexed.absolute.dir" location="${out.dir}/dexedLibs" />#<property name="out.manifest.abs.file" location="${out.dir}/AndroidManifest.xml" />## Insert key information# Only used in the 'release' build# At the moment the signing step MUST be done outside of the build system##$data->setProperty('key.store' , 'vix-pcc.keystore');#$data->setProperty('key.alias' , 'pcc');#$data->setProperty('key.store.password' , 'VixPassword');#$data->setProperty('key.alias.password' , 'VixPassword');$data->store( $antProperies );}#-------------------------------------------------------------------------------# Function : injectDependencies## Description : Populate the 'libs' directory## Inject dependencies# The android build will make use of files in the 'libs' directory# There are two types of files that can be placed in that directory# These appear to be:# 1) .jar files# 2) Shared libraries provided by NDK components## Assume that the user is doing the right thing and not manually placing# external dependencies in the 'libs' directory## Clean out all files and repopulate - depending on the build type# It may be different for debug and production builds## Inputs :## Returns :#sub injectDependencies{my @jlist;my @jpathlist;my @liblist;## Only if we need to do something#return unless (@opt_jars || @opt_elibs || @opt_jlibs);## Create search entries suitable for the CopyDir# We will delete entries as the files are copied# Allow for:# jar files to have a .jar suffix (optional)# jar files to have path specified with a package#foreach my $item ( @opt_jars) {$item =~ s~\.jar~~i;if ($item =~ m~/~){UniquePush \@jpathlist, $item . '.jar';} else {UniquePush \@jlist, $item . '.jar';}}foreach my $item ( @opt_elibs) {UniquePush \@liblist, 'lib' . $item . '.so';}foreach my $item ( @opt_jlibs) {UniquePush \@liblist, 'lib' . $item . $opt_gbetype . '.so';}## Where does it go#my $androidLibs = catdir($project_root, 'libs');Verbose ("Android libs: $androidLibs");my @pkg_paths = getPackagePaths("--Interface=$opt_interface");foreach my $pkg ( @pkg_paths){## Copy in all JAR files found in dependent packages#my $jarDir = catdir($pkg,'jar');if (-d $jarDir && @jlist){Verbose("Jar Dir Found found", $jarDir);Message ("Copy in: $jarDir");CopyDir ( $jarDir, $androidLibs,'Match' => \@jlist,'Log' => $opt_verbose + 1,'SymlinkFiles' => 1,'Examine' => sub{my ($opt) = @_;ArrayDelete \@jlist, $opt->{file};return 1;},);}## Copy in JARs specified by a full pathname#foreach my $file (@jpathlist){my $jarFile = catdir($pkg, $file);if (-f $jarFile){Verbose("Jar File Found found", $jarDir);Message ("Copy in: $jarFile");CopyFile ( $jarFile, $androidLibs,'Log' => $opt_verbose + 1,'SymlinkFiles' => 1,);ArrayDelete \@jpathlist, $file;}}## Build up the Shared Library structure as used by JNI# Note: Only support current JATS format# Copy in .so files and in to process massage the pathname so that# it confirms to that expected by the Android Project#my $libDir = catdir($pkg,'lib');if (-d $libDir && @liblist){Verbose("Lib Dir Found found", $libDir);Message ("Copy in: $libDir");CopyDir ( $libDir, $androidLibs,'Match' => \@liblist,'Log' => $opt_verbose + 1,'SymlinkFiles' => 1,'Examine' => sub{my ($opt) = @_;foreach my $platform ( keys %SharedLibMap ) {my $replace = $SharedLibMap{$platform};if ($opt->{'target'} =~ s~/$platform/~/$replace/~){ArrayDelete \@liblist, $opt->{file};return 1;}}return 0;},);}}## Report files that could not be located. They were deleted from the lists# as they were processed# These are Warnings in populate Mode# and errors at build time#if (@jlist || @liblist || @jpathlist){my $fn = $opt_populate ? \&Warning : \&Error;&$fn("External dependencies not found:", @jlist, @jpathlist, @liblist);}}#-------------------------------------------------------------------------------# Function : deleteGeneratedFiles## Description : Delete files that we generate## Inputs :## Returns :#sub deleteGeneratedFiles{## Delete files that we will create# Not sure about project.properties# It appears that it can be written - and info will be lost#my @deleteList = qw(local.properties build.xml proguard-project.txt);foreach my $file (@deleteList){Verbose ("Deleting $project_root/$file");unlink catfile($project_root, $file);}## Clean out the 'libs' directory# Assume that its populated with 'external' dependencies# Leave the directory - it may have been checked into version control#my $androidLibs = catdir($project_root, 'libs');foreach my $item (glob(catdir($androidLibs, '*'))) {RmDirTree($item);}}