########################################################################
# 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 AndroidStudio based
#                 Android project. It does this by:
#                   Creating a build.xml file from the Eclispe files
#                   Injecting properties into the build
#                       Create gradle.properties (Must not be version controlled)
#                       Create local.properties (Must not be version controlled)
#                   Insert external dependencies
#                       Jar files
#                       Aar files
#                       JNI Libraries
#                   Invoking 'gradle' to perform the build 
#
#                 This process requires external tool - delivered in packages
#                 These are:
#                   gradle      - Provides the core of gradle
#                   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'
#                -autotest                  - Run Unit Tests
#                -hasTests                  - Build unit tests too    
#               Aguments that can be provided by the user
#                -Jar=name                  - Name of a Jar to include
#                -Aar=name                  - Name of an Aar to include
#                -lname                     - Name of a Jats library to include
#                -Lname                     - Name of a 3rd party library to include
#                -gradleProps=file          - Gradle properties that will be appended to the gradle.properties
#                -AllPackages               - Provide metadata for all dependent packages
#
# Note: This function may be provided by several packages, 
#       thus the interface must not change - this include the name of 
#       this file. See the AndroidBuilder package too.
#
#
#......................................................................#

require 5.008_002;
use strict;
use warnings;

use Getopt::Long qw(:config pass_through);
use File::Path;

use JatsError;
use JatsSystem;
use JatsEnv;
use FileUtils;
use JatsProperties;
use JatsVersionUtils;
use ReadBuildConfig;
use JatsCopy;
use ArrayHashUtils;

#
#   Globals
#   Command line arguments
#
my $opt_verbose = $ENV{GBE_VERBOSE};
my $opt_buildFile;
my $opt_interface;
my $opt_gbetype = 'P';
my @opt_gbetypes;
my $opt_clean;
my $opt_pkgname;
my $opt_pkgversion;
my $opt_platform;
my $opt_populate;
my $opt_populateDebug;
my $opt_populateProd;
my $opt_autotest;
my $opt_hastests;
my @opt_jlibs;                  # List of Jats Libraries
my @opt_elibs;                  # List of 3rd party libraries
my @opt_jars;                   # List of JARs
my @opt_aars;                   # List of AARs
my $opt_gprops;                 # Extra gradle props files
my $opt_allPackages;            # Metadata for all packages

#
#   Configuration
#   Map JATS platforms to Shared library targets
#       The key is a JATS name for the target
#       The value is the subdir that android will expect to find the files in
#
my %SharedLibMap = (
    'ANDROIDARM'    => 'armeabi',
    'ANDROIDARMV7'  => 'armeabi-v7a',
    'ANDROIDMIPS'   => 'mips',
    'ANDROIDX86'    => 'x86',

    'ANDROIDARM64'  => 'arm64-v8a',
    'ANDROIDMIPS64' => 'mips64',
    'ANDROIDX86_64' => 'x86_64',
    );

our $GBE_HOSTMACH;              # Sanity Test of machine type
our $GBE_MAKE_TARGET;           # Current build target

my $androidJars;                # Root of injected JARs and AARs
my $androidJniBase;             # Root of injected JNI files
my $androidJniProd;             # Root of injected JNI files - Prod
my $androidJniDebug;            # Root of injected JNI files - Debug
my $androidBuildSuffix = '';    # Prefix build commands

#-------------------------------------------------------------------------------
# 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);
$opt_verbose = $::ScmVerbose;               # Get the calculated verbosity level

#
#   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_buildFile,     # string
                "i=s"           => \$opt_interface,     # Interface directory
                "t=s"           => \@opt_gbetypes,      # string
                "pn=s"          => \$opt_pkgname,       # string
                "pv=s"          => \$opt_pkgversion,    # string
                "pf=s"          => \$opt_platform,      # string
                "clean"         => \$opt_clean,         # flag
                "populate"      => \$opt_populate,      # flag
                "autotest"      => \$opt_autotest,      # flag
                "hastests"      => \$opt_hastests,      # flag
                "Jar=s"         => \@opt_jars,
                "Aar=s"         => \@opt_aars,
                "GradleProps=s" => \$opt_gprops,
                "allPackages"   => \$opt_allPackages,
                );

#
#   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 ("Gradle build file not specified") unless ( defined $opt_buildFile); 
ReportError ("Gradle build file not found: $opt_buildFile") unless ( -f $opt_buildFile);

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); 

ReportError ("Platform not found") unless ( defined $opt_platform );

ErrorDoExit();

#
#   Now support Android builder under both Linux and Windows - for development
#   In the build system we only support Android builder under Windows
#       This is done for backward compatability
#       Also to ensure reproducability in escrow
#       Jats will prevent the use of ANDROID on a non-windows build machine
#   Generate a reminder - only once and only during the 'build' invocation.
#
if ($opt_populate) {
    EnvImport('GBE_HOSTMACH');
    unless ( $::GBE_HOSTMACH eq 'win32' ) {
        Message ("AndroidStudioBuilder is only supported under $::GBE_HOSTMACH for development only", "The build and escrow system will use a win32 machine" );
    }
}


#
#   Basic setup
#
#   Handle multiple opt_gbetypes for populate mode
#   If we are ony building for Prod, then only populate production artifacts 
#   If we are ony building for Debug, then only populate debug artifacts 
foreach ( @opt_gbetypes) {
    $opt_gbetype = $_;
    $opt_populateProd = 1 if ($opt_gbetype eq 'P');
    $opt_populateDebug = 1 if ($opt_gbetype eq 'D');
}

$androidBuildSuffix = ($opt_gbetype eq 'P') ? 'Release' : 'Debug';

#
#   The user provides the root build.gradle file
#   There MUST be a settings.gradle in the same directory
#       This is a JATS assumption - subject to change
#   There will be some other files there too
#   Calculate the root of the project
#
$opt_buildFile = RelPath(AbsPath($opt_buildFile));
my $project_root = StripFileExt($opt_buildFile);
   $project_root = '.' unless $project_root;
my $project_settingsFile = catfile($project_root, 'settings.gradle');

Message ("Project Base:" . Getcwd());
Message ("Project Root:" . $project_root);
Verbose ("Project Settings file:" . $project_settingsFile);

#
#   Directories to store Jar's, Aar's and JNI shared libarares
#   are created in the interface directory
#
$androidJars      = catdir($opt_interface, 'jatsLibs');
$androidJniBase   = catdir($opt_interface, 'jatsJni');
$androidJniProd   = catdir($androidJniBase, 'Release');
$androidJniDebug  = catdir($androidJniBase, 'Debug');

Verbose ("Interface:" . $opt_interface);
Verbose ("Android Jars: $androidJars");
Verbose ("Android JNI : $androidJniBase");

Error ("Gradle settings file not found: $project_settingsFile") unless ( -f $project_settingsFile);

#
#   Essential tool
#       gradle     - setup 
#                    GRADLE_USER_HOME
#                    JAVA_HOME
#                    
ReadBuildConfig( $opt_interface, $opt_platform, '--NoTest' );

my $gradleTool = getToolInfo('gradle', 'JAVA_VERSION', 'GRADLE_BIN');
my $gradleBinDir = catdir($gradleTool->{PKGBASE}, $gradleTool->{TOOLROOT}, $gradleTool->{GRADLE_BIN});

#
#   Setup a gradle home
#   Its used to cache stuff - create it within the interface directory
#
my $gradleHomeTarget = CleanPath(FullPath(catdir($opt_interface, 'gradleUserHome')));
mkpath($gradleHomeTarget, 0, 0775) 
    unless ( -d $gradleHomeTarget);
$ENV{GRADLE_USER_HOME} = $gradleHomeTarget;
Verbose("GRADLE_USER_HOME:", $ENV{GRADLE_USER_HOME});

#
#   Setup the required version of Java for the tool
#   Force JAVA_OPTS to set Min/Max Heap
#       Use JAVA_OPTS because
#           _JAVA_OPTIONS causes system to emit a lot of warnings that _JAVA_OPTIONS is being used
#           Use of org.gradle.jvmargs in gradle.properties causes warnins about forking speed
#           Fixing the max size will provide consistent builds
#           Perhaps one day it will be configured 
#
my $javaVersion = $gradleTool->{JAVA_VERSION};
ReportError ("$javaVersion not defined.", "Building ANDROID requires $javaVersion be installed and correctly configured.") 
    unless $ENV{$javaVersion};
$ENV{JAVA_HOME}=$ENV{$javaVersion};
$ENV{JAVA_OPTS} = '-Xms256m -Xmx1024m';

#
#   Essential tool
#       androidSdk  - setup path to the android executable
#
my $androidSdkTool = getToolInfo('androidSdk');
my $androidSdk = catdir($androidSdkTool->{PKGBASE}, $androidSdkTool->{TOOLROOT} );
Verbose ("Android SDK : $androidSdk");
ReportError("Tool Package 'androidSdk' - Invalid SDK Basedir", "Sdk Base: $androidSdk" )
    unless -d ($androidSdk);

#   Essential tool
#       androidGradleRepo   - A repo of gradle plugings for android
#
my $androidGradleRepo = getToolInfo('androidGradleRepo');
my $gradleMavenRepo = catdir($androidGradleRepo->{PKGBASE}, $androidGradleRepo->{TOOLROOT});
Verbose ("Maven Repo. Gradle support for android : $gradleMavenRepo");

ErrorDoExit();

#
#   Create a gradle file with JATS provided version information
#   and paths for use within the gradle build.
#
#   Always do this as the 'clean' will need them
#
createGradleFiles($opt_clean);

#
#   Clean out any build artifacts
#
if ($opt_clean)
{
    #
    #   Invoke GRADLE on the build script - if present
    #
    Message ("Clean the existing build");
    runGradle ('clean');
    deleteGeneratedFiles();
    exit 0;
}

#
#   If we are only populating the build then we 
#   need to inject dependencies.
#
if ($opt_populate)
{
    deleteInjectedFiles();
    injectDependencies();

    Verbose ("Populate complete");
    exit 0;
}

#
#   Build the Android project through gradle
#
my $rv = 0;
if ( ! $opt_autotest )
{
    #
    #   Build the project - does not run unit tests
    #       assemble all the code
    #       assemble code for unit tests -  does not run unit tests 
    #
    Message ("Build the android project: $androidBuildSuffix");
    my @tasks;
    push (@tasks, 'assemble'. $androidBuildSuffix); 
    push (@tasks, 'assemble' . $androidBuildSuffix . 'UnitTest') if $opt_hastests;
    $rv = runGradle('assemble'. $androidBuildSuffix , 'assemble' . $androidBuildSuffix . 'UnitTest');
    Error("Cannot build AndroidStudio project") if $rv;
}
else
{
    #
    #   Run unit tests - does not build the project, although it 
    #   will build the unit tests, but these have been buitl before
    #   
    #   If the gradle run fails, then its because one or more of the unit tests failed
    #   This is not a build failure as we want to process the test results and feed
    #   them up the chain
    #   
    #   Post processing MUST detect and report errors
    #
    Message ("Run Unit Tests within the android project: $androidBuildSuffix");
    $rv = runGradle('test'. $androidBuildSuffix);
    Message ("Unit Test reports: $rv");
}
exit(0);

#-------------------------------------------------------------------------------
# Function        : runGradle 
#
# Description     : Run gradle to build the project
#                   Generate a command like:
#                   PathToGradle/gradle --offline -I SomePath/init.gradle <task> 
#
#                   Use an init-script to inject a sanity test into the build
#                   Ensure that the user is using our version numbers.
#
# Inputs          : task        - Task to run
#
# Returns         : Returns the error code of the build
#
sub runGradle
{
    my (@tasks) = @_;
    Verbose ("runGradle: @tasks");

    #   The Windows batch file can run in debug mode
    #   Make sure that it doesn't by default
    $ENV{DEBUG} = "" unless $opt_verbose;

    my $gradleProg = catdir($gradleBinDir, 'gradle');
    Verbose ("GradleProg: $gradleProg");

    #
    #   Locate the 'init.gradle' script
    #   Its co-located with this script
    #
    my $initScript = catdir(StripFileExt(__FILE__), 'init.gradle');
    Verbose ("Gradle Init Script: $initScript");

    #
    #   Build up the arg list
    #
    my @gradleArgs;
    push (@gradleArgs, '--offline');
    push (@gradleArgs, '--no-daemon');
#    push (@gradleArgs, '--info');
#    push (@gradleArgs, '--debug');
#    push (@gradleArgs, '--stacktrace');
    push (@gradleArgs, '--warning-mode=none') unless ($opt_verbose);
    push (@gradleArgs, '--info') if $opt_verbose;
    push (@gradleArgs, '--debug') if ($opt_verbose > 2);
    push (@gradleArgs, '--stacktrace') if ($opt_verbose > 3);
    push (@gradleArgs, '-I', $initScript) unless ($tasks[0] =~ m/clean/); 
    push (@gradleArgs, '-p', $project_root);

    #
    #  Address a gradle issue, under LINUX, that appears to be solved by 
    #  having STDIN redirected.
    #  
    #  Downside is that Control-C can't be used to terminate the compile
    #
    my @gradlePrefix = qw( --NoExit );
    push (@gradlePrefix, '--Show') if $opt_verbose;
    if ($ENV{'GBE_UNIX'} ) {
        push (@gradlePrefix, '--Shell', '</dev/null');
    } else {
        push (@gradlePrefix, '--NoShell');
    }

    my $rv = System(@gradlePrefix, $gradleProg, @gradleArgs, @tasks);
    return $rv;
}

#-------------------------------------------------------------------------------
# Function        : createGradleFiles 
#
# Description     : Calculate Version information
#                       gradle.properies
#                       local.properties
#
# Inputs          : quiet           - No output 
#
# Returns         : Nothing
#
sub createGradleFiles
{
    my ($quiet) = @_;

    #
    #   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) unless $quiet;
    Message ("Project Version Num:" . $version_num) unless $quiet;

    #
    #   Create the gradle.properties file
    #       It needs to be in the projects root directory
    #
    my $gradleProperies = catfile($project_root,'gradle.properties');
    Message ("Create gradle.properties file: " . $gradleProperies) unless $quiet;

    my $data = JatsProperties::New();

    $data->setProperty('GBE_VERSION_NAME' , $version_text);
    $data->setProperty('GBE_VERSION_CODE' , $version_num);

    $data->setProperty('GBE_JARLIBS'        , NicePath($androidJars));
    $data->setProperty('GBE_JNI_RELEASE'    , NicePath($androidJniProd));
    $data->setProperty('GBE_JNI_DEBUG'      , NicePath($androidJniDebug));
    $data->setProperty('GBE_GRADLE_REPO'    , NicePath($gradleMavenRepo));

    #
    #   Create properties for JAVA Stores
    #   Name of variable is based on the package name and prject suffix
    #       Forced to uppercase
    #       '-' replaced with '_'
    #   Locate 'jar' dir and save path as GBE_REPO_<pkgName>
    #   Locate 'mavenRepository' and save path as GBE_MVN_<pkgName>
    #
    foreach my $pkg (getPackageList())
    {
        my $base = $pkg->getBase(3);
        if ($base)
        {
            if ($opt_allPackages) {
                $data->setProperty( $pkg->getUnifiedName('GBE_PKG_') , NicePath($base));
            }

            my $jarDir = catdir($base, 'jar');
            if (-d $jarDir )
            {
                $data->setProperty( $pkg->getUnifiedName('GBE_REPO_') , NicePath($jarDir));
            }

            my $mvnDir = catdir($base, 'mavenRepository');
            if (-d $mvnDir ) {
                $data->setProperty( $pkg->getUnifiedName('GBE_MVN_') , NicePath($mvnDir));
            }
        }
    }

    #
    #   Append user specified graadle properties
    #
    if ($opt_gprops) {
        Error("Gradle properties not found: $opt_gprops") unless -f $opt_gprops;
        $data->load($opt_gprops);
    }

    $data->store( $gradleProperies );

    #
    #   Create the local.properties file
    #       It needs to be in the projects root directory
    #
    #   May be able to do without this file - iff we set ANDROID_HOME
    #
    my $localProperies = catfile($project_root,'local.properties');
    Message ("Create local.properties file: " . $localProperies) unless $quiet;

    $data = JatsProperties::New();
    $data->setProperty('sdk.dir' , NicePath($androidSdk));
    $data->store( $localProperies );
}

#-------------------------------------------------------------------------------
# Function        : injectDependencies 
#
# Description     : Inject dependencies
#
#                   The android build can make use of files in a specific directory
#                   Place Jar and Aar files in a specific directory under the root of the project
#                   Use a directory called jatsLibs
#                   There are two types of files that can be placed in that directory
#                   These appear to be:
#                       1) .jar files
#                       2) .aar files
#
#                   NDK files will be process automatically by the builder, once the build is made
#                   aware of the location. We are not using the AndroidStudio default location
#                   Place them into jatsJni within the project root
#                       1) Shared libraries provided by NDK components
#
#                   Need to keep the production and debug JNI files seperate
#
#                   The gradle dependency processing needs both production and
#                   debug dependencies to be present at all times
#                   
#                   Create three areas:
#                       jatsLibs        - Prod and Debug Jars and Ars
#                       jatsJniDebug    - Debug JNI files
#                       jatsJniProd     - Production JNI files
#
# Inputs          : 
#
# Returns         : 
#
sub injectDependencies
{
    my @jlist;                  # List of JARs from default directories
    my @jpathlist;              # List of JARs from named directories
    my @alist;                  # List of AARs from default directories
    my @apathlist;              # List of AARs from named directories
    my @libListProd;            # List of production libraries
    my @libListDebug;           # List of debug libraries
    my @platformParts;          # Platforms Parts

    my @jarSearch;              # Search paths - diagnostic display
    my @aarSearch;
    my @libSearch;

    #
    #   Only if we need to do something
    #
    return unless (@opt_jars || @opt_aars || @opt_elibs || @opt_jlibs);

    #
    #   Determine the list of platformm parts
    #   This is where 'lib' files will be found
    #
    @platformParts = getPlatformParts();
    Verbose("Platform Parts", @platformParts);

    #
    #   Create search entries suitable for the CopyDir
    #   We will delete entries as the files are copied
    #   Allow for:
    #       jar/aar files to have a .jar /.aar suffix (optional)
    #       jar/aar files to have path specified with a package
    #
    #   Split into two lists ( per type ): 
    #       Those with a path and those without
    #
    foreach my $item ( @opt_jars) {
        $item =~ s~\.jar~~i;
        if ($item =~ m~/~) {
            UniquePush \@jpathlist, $item;
        } else {
            UniquePush \@jlist, $item;
        }
    }

    foreach my $item ( @opt_aars) {
        $item =~ s~\.aar~~i;
        if ($item =~ m~/~) {
            UniquePush \@apathlist, $item;
        } else {
            UniquePush \@alist, $item;
        }
    }

    #   Shared libraries
    #   Create full names
    foreach my $item ( @opt_elibs) {
        UniquePush (\@libListDebug, 'lib' . $item . '.so');
        UniquePush (\@libListProd,  'lib' . $item . '.so');
    }
    
    foreach my $item ( @opt_jlibs) {
        UniquePush (\@libListDebug, 'lib' . $item . 'D.so') if $opt_populateDebug;
        UniquePush (\@libListProd , 'lib' . $item . 'P.so') if $opt_populateProd;
    }

    #
    #   Where does it go
    #       JARs/AARs - ROOT/jatsLibs
    #       LIBS      - ROOT/jatsJni
    #
    #   Scan all external packages, and the interface directory
    #       Transfer in the required file types
    #
    my @pkg_paths = getPackagePaths("--Interface=$opt_interface");
    foreach my $pkg ( @pkg_paths)
    {
        #
        #   Copy in all JAR files found in dependent packages
        #   Need to allow for Jars that have a P/D suffix as well as those that don't
        #
        my $jarDir = catdir($pkg,'jar');
        push @jarSearch, $jarDir;
        if (-d $jarDir && @jlist)
        {
            Verbose("Jar Dir Found found", $jarDir);
            Message ("Copy in: $jarDir");

            #
            #   Create a matchlist from the JAR list
            #   Create a regular expresssion to find a suitable file
            #
            my @mlist;
            foreach  ( @jlist) {
                push @mlist, $_ . '.jar|' . $_ . 'P.jar|'. $_ . 'D.jar';
            }
            CopyDir ( $jarDir, $androidJars,
                        'MatchRE' => \@mlist,
                        'Log' => $opt_verbose + 1,
                        'SymlinkFiles' => 1,
                        'Examine' => sub 
                            {
                                my ($opt) = @_;
                                my $baseName = $opt->{file};
                                $baseName =~ s~\.jar~~;
                                $baseName =~ s~[PD]$~~;
                                ArrayDelete \@jlist, $baseName;
                                return 1;
                            },
                    );
        }

        #
        #   Copy in JARs specified by a full pathname
        #   Need to allow for Jars that have a P/D suffix as well as those that don't
        #
        my @jpathlistBase = @jpathlist;
        foreach my $file (@jpathlistBase) 
        {
            foreach my $suffix ( '', 'P' ,'D')
            {
                my $jarFile = catdir($pkg, $file . $suffix . '.jar');
                push @jarSearch, $jarFile;
                if (-f $jarFile)
                {
                    Verbose("Jar File Found found", $jarDir);
                    Message ("Copy in: $jarFile");
                    CopyFile ( $jarFile, $androidJars,
                                'Log' => $opt_verbose + 1,
                                'SymlinkFiles' => 1,
                             );
                    ArrayDelete \@jpathlist, $file;
                }
            }
        }

        #
        #   Copy in AAR files found in dependent packages
        #   Need to allow for both AAR files with a -debug/-release suffix 
        #   as well as those without
        #
        foreach my $part (@platformParts)
        {
            my $aarDir = catdir($pkg,'lib/' . $part);
            push @aarSearch, $aarDir;
            if (-d $aarDir && @alist)
            {
                Verbose("Library Dir Found found", $aarDir);
                Message ("Copy in: $aarDir");

                #
                #   Create a matchlist from the AAR list
                #   Create a regular expresssion to find a suitable file
                #
                my @mlist;
                foreach  ( @alist) {
                    push @mlist, $_ . '.aar|' . $_ . '-debug.aar|' . $_ . '-release.aar';
                }

                CopyDir ( $aarDir, $androidJars,
                            'MatchRE' => \@mlist,
                            'Log' => $opt_verbose + 1,
                            'SymlinkFiles' => 1,
                            'Examine' => sub 
                                {
                                    my ($opt) = @_;
                                    my $baseName = $opt->{file};
                                    $baseName =~ s~\.aar$~~;
                                    $baseName =~ s~-release$~~;
                                    $baseName =~ s~-debug$~~;
                                    ArrayDelete \@alist, $baseName;
                                    return 1;
                                },
                            );
            }
        }
        

        #
        #   Copy in AAR files specified by a full pathname
        #   Need to allow for both AAR files with a -debug/-release suffix 
        #   as well as those without
        #
        my @apathlistBase = @apathlist;
        foreach my $file (@apathlistBase) 
        {
            foreach my $suffix ( '', '-release', '-debug')
            {
                my $aarFile = catdir($pkg, $file . $suffix . '.aar');
                push @aarSearch, $aarFile;
                if (-f $aarFile)
                {
                    Verbose("Aar File Found found", $aarFile);
                    Message ("Copy in: $aarFile");
                    CopyFile ( $aarFile, $androidJars,
                                'Log' => $opt_verbose + 1,
                                'SymlinkFiles' => 1,
                             );
                    ArrayDelete \@apathlist, $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');
        push @libSearch, $libDir;
        if (-d $libDir && @libListProd)
        {
            Verbose("Lib Dir Found found", $libDir);
            Message ("Copy in: $libDir");
            CopyDir ( $libDir, $androidJniProd,
                        'Match' => \@libListProd,
                        '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 \@libListProd, $opt->{file};
                                        return 1;
                                    }
                                }
                                return 0;
                            },
                    );
        }

        if (-d $libDir && @libListDebug)
        {
            Verbose("Lib Dir Found found", $libDir);
            Message ("Copy in: $libDir");
            CopyDir ( $libDir, $androidJniDebug,
                        'Match' => \@libListDebug,
                        '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 \@libListDebug, $opt->{file};
                                        return 1;
                                    }
                                }
                                return 0;
                            },
                    );
        }

    }

    #
    #   Report files that could not be located. They were deleted from the lists
    #   as they were processed
    #
    if (@jlist || @jpathlist || @alist || @apathlist ||  @libListProd || @libListDebug )
    {
        ReportError("External dependencies not found:", @jlist , @jpathlist , @alist , @apathlist ,  @libListProd , @libListDebug);
        if (@jlist || @jpathlist)
        {
            ReportError("Jar Search Path", @jarSearch);
        }

        if (@alist || @apathlist)
        {
            ReportError("Aar Search Path", @aarSearch);
        }

        if ( @libListProd || @libListDebug)
        {
            ReportError("Lib Search Path", @libSearch);
        }
    ErrorDoExit();
    }
}

#-------------------------------------------------------------------------------
# Function        : deleteGeneratedFiles 
#
# Description     : Delete files that we generate
#
# Inputs          : 
#
# Returns         : 
#
sub deleteGeneratedFiles
{
    #
    #   Delete files that we will create
    #
    my @deleteList = qw(local.properties gradle.properties);
    foreach my $file (@deleteList)
    {
        Verbose ("Deleting $project_root/$file");
        unlink catfile($project_root, $file);
    }

}

#-------------------------------------------------------------------------------
# Function        : deleteInjectedFiles 
#
# Description     : Delete files that we inject
#
# Inputs          : 
#
# Returns         : 
#
sub deleteInjectedFiles
{
    #
    #   Remove the jatsJars and JatsJni directories
    #   These are created by this tool
    #
    Verbose("RmDirTree($androidJars)");
    RmDirTree($androidJars);

    Verbose("RmDirTree($androidJniBase)");
    RmDirTree($androidJniBase);
}


#-------------------------------------------------------------------------------
# Function        : NicePath 
#
# Description     : Process a path and return one that is
#                       An absolute Path
#                       Uses '/'
#
# Inputs          : path            - Path to process
#
# Returns         : A Nice version of path
#
sub NicePath
{
    my ($path) = @_;
    $path = FullPath($path);
    $path =~ s~\\~/~g;
    $path = CleanPath($path);
    return $path;
}

