All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.javafxports.jfxmobile.plugin.JFXMobilePlugin.groovy Maven / Gradle / Ivy

There is a newer version: 2.0.30
Show newest version
package org.javafxports.jfxmobile.plugin

import com.android.build.gradle.internal.dsl.SigningConfig
import com.android.builder.core.BuilderConstants
import com.android.ide.common.res2.AssetSet
import com.android.ide.common.res2.ResourceSet
import com.android.sdklib.repository.FullRevision
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.plugins.ApplicationPlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.internal.reflect.Instantiator
import org.javafxports.jfxmobile.plugin.android.task.AndroidTask
import org.javafxports.jfxmobile.plugin.android.task.Apk
import org.javafxports.jfxmobile.plugin.android.task.CreateMainDexList
import org.javafxports.jfxmobile.plugin.android.task.CreateManifestKeepList
import org.javafxports.jfxmobile.plugin.android.task.Dex
import org.javafxports.jfxmobile.plugin.android.task.Install
import org.javafxports.jfxmobile.plugin.android.task.MergeAssets
import org.javafxports.jfxmobile.plugin.android.task.MergeResources
import org.javafxports.jfxmobile.plugin.android.task.ProcessResources
import org.javafxports.jfxmobile.plugin.android.task.Retrolambda
import org.javafxports.jfxmobile.plugin.android.task.ValidateManifest
import org.javafxports.jfxmobile.plugin.android.task.ValidateSigning
import org.javafxports.jfxmobile.plugin.android.task.WriteDexInputListFile
import org.javafxports.jfxmobile.plugin.android.task.ZipAlign
import org.javafxports.jfxmobile.plugin.embedded.RemotePlatformConfiguration
import org.javafxports.jfxmobile.plugin.embedded.task.CopyRemoteDir
import org.javafxports.jfxmobile.plugin.embedded.task.RunEmbedded
import org.javafxports.jfxmobile.plugin.ios.task.CreateDefaultLauncher
import org.javafxports.jfxmobile.plugin.ios.task.CreateIpa
import org.javafxports.jfxmobile.plugin.ios.task.IPadSimulator
import org.javafxports.jfxmobile.plugin.ios.task.IPhoneSimulator
import org.javafxports.jfxmobile.plugin.ios.task.IosDevice
import org.javafxports.jfxmobile.plugin.ios.task.IosTask
import proguard.gradle.ProGuardTask

import javax.inject.Inject

/**
 *
 * @author joeri
 */
class JFXMobilePlugin implements Plugin {

    private static RETROLAMBDA_COMPILE = 'net.orfjackal.retrolambda:retrolambda'

    private Instantiator instantiator
    private Project project

    private int gradleMajor
    private int gradleMinor

    List androidTasks = []
    List iosTasks = []
    List embeddedTasks = []

    private boolean hasAndroidMavenRepository = false

    @Inject
    JFXMobilePlugin(Instantiator instantiator) {
        this.instantiator = instantiator
    }

    void apply(Project project) {
        this.project = project

        def gradleVersion = project.gradle.gradleVersion.split("[\\.]")
        gradleMajor = Integer.parseInt(gradleVersion[0])
        gradleMinor = Integer.parseInt(gradleVersion[1])
        if (gradleMajor < 2 || gradleMajor == 2 && gradleMinor < 2) {
            throw new GradleException("You are using Gradle ${project.gradle.gradleVersion} but we require version 2.2 or higher")
        }

        project.plugins.apply JavaPlugin
        project.plugins.apply ApplicationPlugin

        project.sourceSets {
            desktop {
                java {
                    compileClasspath += main.output
                    runtimeClasspath += main.output
                }
            }
            android {
                java {
                    compileClasspath += main.output
                    runtimeClasspath += main.output
                }
            }
            ios {
                java {
                    compileClasspath += main.output
                    runtimeClasspath += main.output
                }
            }
            embedded {
                java {
                    compileClasspath += main.output
                    runtimeClasspath += main.output
                }
            }
        }

        project.extensions.create("jfxmobile", JFXMobileExtension, project, instantiator)

        JFXMobileConvention pluginConvention = new JFXMobileConvention(project)
        project.convention.plugins.'org.javafxports.jfxmobile' = pluginConvention

        project.configurations {
            retrolambdaConfig

            compileNoRetrolambda
            runtimeNoRetrolambda {
                extendsFrom compileNoRetrolambda
            }
            compile {
                extendsFrom compileNoRetrolambda
            }
            runtime {
                extendsFrom runtimeNoRetrolambda
            }

            androidCompileNoRetrolambda {
                extendsFrom compileNoRetrolambda
            }
            androidRuntimeNoRetrolambda {
                extendsFrom androidCompileNoRetrolambda, runtimeNoRetrolambda
            }

            androidBootclasspath
            iosBootclasspath

            androidSdk
            dalvikSdk
            iosSdk
            robovmSdk
            sshAntTask
        }

        project.configurations.desktopCompile.extendsFrom project.configurations.compile
        project.configurations.desktopRuntime.extendsFrom project.configurations.desktopCompile, project.configurations.runtime
        project.configurations.androidCompile.extendsFrom project.configurations.compile, project.configurations.androidCompileNoRetrolambda, project.configurations.androidBootclasspath, project.configurations.androidSdk
        project.configurations.androidRuntime.extendsFrom project.configurations.androidCompile, project.configurations.runtime, project.configurations.androidRuntimeNoRetrolambda
        project.configurations.iosCompile.extendsFrom project.configurations.compile
        project.configurations.iosRuntime.extendsFrom project.configurations.iosCompile, project.configurations.runtime
        project.configurations.embeddedCompile.extendsFrom project.configurations.compile
        project.configurations.embeddedRuntime.extendsFrom project.configurations.embeddedCompile, project.configurations.runtime

        createAndroidTasks()
        createIosTasks()
        createEmbeddedTasks()

        project.afterEvaluate {
            // include the maven repositories from the android sdk if they were downloaded
            String androidSdkLocation = locateAndroidSdk()
            if (androidSdkLocation != null) {
                File mavenRepository = project.file("$androidSdkLocation/extras/m2repository")
                if (mavenRepository.exists()) {
                    project.logger.info("Adding $mavenRepository.absolutePath to project repositories.")
                    project.repositories {
                        maven {
                            url mavenRepository.toURI().toString()
                        }
                    }
                }

                File androidMavenRepository = project.file("$androidSdkLocation/extras/android/m2repository")
                if (androidMavenRepository.exists()) {
                    project.logger.info("Adding $androidMavenRepository.absolutePath to project repositories.")
                    project.repositories {
                        maven {
                            url androidMavenRepository.toURI().toString()
                        }
                    }
                    hasAndroidMavenRepository = true
                }

                File googleMavenRepository = project.file("$androidSdkLocation/extras/google/m2repository")
                if (googleMavenRepository.exists()) {
                    project.logger.info("Adding $googleMavenRepository.absolutePath to project repositories.")
                    project.repositories {
                        maven {
                            url googleMavenRepository.toURI().toString()
                        }
                    }
                }
            }

            // apply downConfig to project configurations
            project.jfxmobile.downConfig.applyConfiguration(project.configurations.compile)
            project.jfxmobile.downConfig.applyConfiguration(project.configurations.desktopRuntime)
            project.jfxmobile.downConfig.applyConfiguration(project.configurations.iosRuntime)
            project.jfxmobile.downConfig.applyConfiguration(project.configurations.embeddedRuntime)

            // try to set android.jar dependency as early as possible, but only
            // when the jar can be found
            File platformAndroidJar = getPlatformAndroidJar()
            if (platformAndroidJar != null && platformAndroidJar.exists()) {
                project.logger.info("Adding $platformAndroidJar.absolutePath to androidSdk dependency configuration.")
                project.dependencies {
                    androidSdk project.files(platformAndroidJar.absolutePath)
                    androidRuntimeNoRetrolambda 'com.android.support:multidex:1.0.1'
                }
            }

            // configure android and ios dependencies
            project.dependencies {
                retrolambdaConfig "${RETROLAMBDA_COMPILE}:${project.jfxmobile.android.retrolambdaVersion}"

                androidCompile("org.javafxports:jfxdvk:${project.jfxmobile.javafxportsVersion}") {
                    force = true
                }

                iosCompile "com.gluonhq:robovm-cocoatouch:${project.jfxmobile.ios.robovmVersion}"

                iosBootclasspath "com.gluonhq:robovm-rt:${project.jfxmobile.ios.robovmVersion}"

                dalvikSdk "org.javafxports:dalvik-sdk:${project.jfxmobile.javafxportsVersion}@zip"
                iosSdk "org.javafxports:ios-sdk:${project.jfxmobile.javafxportsVersion}@zip"
                robovmSdk "com.gluonhq:robovm-dist:${project.jfxmobile.ios.robovmVersion}:[email protected]"

                sshAntTask 'org.apache.ant:ant-jsch:1.9.6'
            }

            // set the encoding option for the javac compile tasks
            project.tasks.withType(JavaCompile) {
                options.encoding = project.jfxmobile.javacEncoding
            }

            // add our own debug task, calling replace because the NetBeans gradle plugin already creates one
            project.tasks.replace('debug', JavaExec)
            project.tasks.debug {
                description = 'Runs this program as a JVM application for debugging'
                group = 'application'
                main = project.mainClassName
                classpath = project.sourceSets.desktop.runtimeClasspath
                debug = true
            }
            project.tasks.debug.dependsOn project.tasks.classes

            // include desktop when creating jar and running application
            project.tasks.run {
                classpath = project.sourceSets.desktop.runtimeClasspath
                if (project.preloaderClassName != null && !project.preloaderClassName.empty) {
                    systemProperties('javafx.preloader' : project.preloaderClassName)
                }
            }
            project.tasks.jar {
                from project.sourceSets.desktop.output
                manifest {
                    if (project.preloaderClassName != null && !project.preloaderClassName.empty) {
                        attributes(
                            'Main-Class' : project.mainClassName,
                            'JavaFX-Preloader-Class' : project.preloaderClassName
                        )
                    } else {
                        attributes(
                            'Main-Class' : project.mainClassName
                        )
                    }
                }
            }
        }

        project.gradle.taskGraph.whenReady {
            project.logger.info("Using javafxports version ${project.jfxmobile.javafxportsVersion}")
            configure()

            // only configure android when one of the android tasks will be run
            if (androidTasks.find { project.gradle.taskGraph.hasTask(it) } != null) {
                configureAndroid()

                project.jfxmobile.downConfig.applyConfiguration(project.configurations.androidRuntime)

                // explode aar dependencies, adding a dependency on the inner classes.jar
                pluginConvention.explodeAarDependencies(project.configurations.androidCompile,
                        project.configurations.androidRuntime, project.configurations.androidCompileNoRetrolambda,
                        project.configurations.androidRuntimeNoRetrolambda)

                project.dependencies {
                    androidRuntime project.fileTree("${project.jfxmobile.android.dalvikSdkLib}/ext") {
                        include 'compat-1.0.0.jar'
                    }
                    androidRuntimeNoRetrolambda project.fileTree("${project.jfxmobile.android.dalvikSdkLib}/ext") {
                        include 'jfxrt.jar'
                    }
                }

                // configure android boot classpath
                def androidBootclasspath = project.configurations.androidBootclasspath.asPath
                if (!androidBootclasspath.empty) {
                    project.tasks.compileAndroidJava {
                        options.bootClasspath = androidBootclasspath
                    }
                }

                // NOTE: from is set after all configuration for androidRuntime has completed
                project.tasks.copyClassesForRetrolambda.from {
                    (project.configurations.androidRuntime - project.configurations.androidRuntimeNoRetrolambda - project.configurations.androidSdk).filter {
                        !it.isDirectory()
                    }.collect {
                        project.logger.info("Apply Retrolambda to $it")
                        project.zipTree(it)
                    }
                }

                // NOTE: from is set after all configuration for androidRuntimeNoRetrolambda has completed
                project.tasks.mergeClassesIntoJar.from {
                    project.configurations.androidRuntimeNoRetrolambda.collect { project.zipTree(it) }
                }
            } else {
                // ignore tasks for android sourceSet
                project.tasks.androidClasses.enabled = false
                project.tasks.compileAndroidJava.enabled = false
                project.tasks.processAndroidResources.enabled = false
            }

            // only configure ios when one of the ios tasks will be run
            if (iosTasks.find { project.gradle.taskGraph.hasTask(it) } != null) {
                configureIos()

                project.dependencies {
                    iosRuntime project.fileTree("${project.jfxmobile.ios.iosSdkLib}/ext") {
                        include '*.jar'
                    }

                    iosBootclasspath project.fileTree("${project.jfxmobile.ios.iosSdkLib}/ext") {
                        include 'compat-1.0.0.jar'
                    }
                }

                // configure ios boot classpath
                project.tasks.compileIosJava {
                    options.bootClasspath = project.configurations.iosBootclasspath.asPath
                }

                // NOTE: from is set after all configuration for iosRuntime has completed
                project.tasks.iosExtractNativeLibs.from {
                    project.configurations.iosRuntime.collect { project.zipTree(it).matching { include 'native/*.a' }}
                }
            } else {
                // ignore tasks for ios sourceSet
                project.tasks.iosClasses.enabled = false
                project.tasks.compileIosJava.enabled = false
                project.tasks.processIosResources.enabled = false
            }
            // only configure embedded when one of the embedded tasks will be run
            if (embeddedTasks.find { project.gradle.taskGraph.hasTask(it) } != null) {
                configureEmbedded()
            }
        }
    }

    private void configure() {
        if (project.mainClassName == null || project.mainClassName.empty) {
            throw new GradleException("Missing or empty mainClassName property.")
        }
    }

    private void createAndroidTasks() {
        ValidateManifest validateManifestTask = project.tasks.create("validateManifest", ValidateManifest)
        validateManifestTask.conventionMapping.map("output") { project.file("${project.jfxmobile.android.temporaryDirectory}/AndroidManifest.xml") }
        androidTasks.add(validateManifestTask)

        CreateManifestKeepList manifestKeepListTask = project.tasks.create("collectMultiDexComponents", CreateManifestKeepList)
        manifestKeepListTask.conventionMapping.map("outputFile") { project.file("${project.jfxmobile.android.multidexOutputDirectory}/manifest_keep.txt") }
        manifestKeepListTask.conventionMapping.map("manifest") { validateManifestTask.output }
        manifestKeepListTask.conventionMapping.map("dexOptions") { project.jfxmobile.android.dexOptions }
        manifestKeepListTask.conventionMapping.map("proguardFile") { project.jfxmobile.android.proguardFile != null && !project.jfxmobile.android.proguardFile.trim().empty ? project.file(project.jfxmobile.android.proguardFile) : null }
        manifestKeepListTask.dependsOn validateManifestTask
        androidTasks.add(manifestKeepListTask)

        Copy copyClassesForRetrolambda = project.tasks.create("copyClassesForRetrolambda", Copy)
        if (gradleMajor < 4) {
            copyClassesForRetrolambda.from project.sourceSets.main.output.classesDir
            copyClassesForRetrolambda.from project.sourceSets.android.output.classesDir
        } else {
            copyClassesForRetrolambda.from project.sourceSets.main.output.classesDirs
            copyClassesForRetrolambda.from project.sourceSets.android.output.classesDirs
        }
        copyClassesForRetrolambda.include '**/*.class'
        copyClassesForRetrolambda.includeEmptyDirs = false
        copyClassesForRetrolambda.exclude 'META-INF/versions/**/*.class'
        copyClassesForRetrolambda.exclude 'module-info.class'
        copyClassesForRetrolambda.destinationDir = project.file("${project.jfxmobile.android.temporaryDirectory}/retrolambda/input")
        copyClassesForRetrolambda.dependsOn project.tasks.compileJava, project.tasks.compileAndroidJava
        androidTasks.add(copyClassesForRetrolambda)

        Retrolambda retrolambdaTask = project.tasks.create("applyRetrolambda", Retrolambda)
        retrolambdaTask.conventionMapping.map("classpath") { project.files(project.configurations.androidCompileNoRetrolambda, project.configurations.androidSdk) }
        retrolambdaTask.retrolambdaInput = copyClassesForRetrolambda.destinationDir
        retrolambdaTask.retrolambdaOutput = project.file("${project.jfxmobile.android.temporaryDirectory}/retrolambda/output")
        retrolambdaTask.dependsOn copyClassesForRetrolambda
        androidTasks.add(retrolambdaTask)

        Jar mergeClassesIntoJarTask = project.tasks.create("mergeClassesIntoJar", Jar)
        mergeClassesIntoJarTask.destinationDir = project.file("${project.jfxmobile.android.multidexOutputDirectory}")
        mergeClassesIntoJarTask.archiveName = 'allclasses.jar'
        mergeClassesIntoJarTask.from retrolambdaTask.retrolambdaOutput
        mergeClassesIntoJarTask.include '**/*.class'
        mergeClassesIntoJarTask.dependsOn retrolambdaTask
        androidTasks.add(mergeClassesIntoJarTask)

        ProGuardTask proguardComponentsTask = project.tasks.create("shrinkMultiDexComponents", ProGuardTask)
        proguardComponentsTask.dontobfuscate()
        proguardComponentsTask.dontoptimize()
        proguardComponentsTask.dontpreverify()
        proguardComponentsTask.dontnote()
        proguardComponentsTask.dontwarn()
        if (project.logger.debugEnabled) {
            proguardComponentsTask.verbose()
        }
        proguardComponentsTask.forceprocessing()
        proguardComponentsTask.configuration(manifestKeepListTask.outputFile)
        proguardComponentsTask.libraryjars({
            return project.file("${project.jfxmobile.android.buildToolsLib}/shrinkedAndroid.jar")
        })
        proguardComponentsTask.injars(mergeClassesIntoJarTask.archivePath)
        File componentsJarFile = project.file("${project.jfxmobile.android.multidexOutputDirectory}/componentClasses.jar")
        proguardComponentsTask.outjars(componentsJarFile)
        proguardComponentsTask.printconfiguration("${project.jfxmobile.android.multidexOutputDirectory}/components.flags")
        proguardComponentsTask.dependsOn manifestKeepListTask, mergeClassesIntoJarTask
        androidTasks.add(proguardComponentsTask)

        CreateMainDexList createMainDexListTask = project.tasks.create("createMainDexList", CreateMainDexList)
        createMainDexListTask.allClassesJarFile = mergeClassesIntoJarTask.archivePath
        createMainDexListTask.conventionMapping.map("componentsClassesJarFile") { componentsJarFile }
        createMainDexListTask.conventionMapping.map("dexOptions") { project.jfxmobile.android.dexOptions }
        createMainDexListTask.outputFile = project.file("${project.jfxmobile.android.multidexOutputDirectory}/maindexlist.txt")
        createMainDexListTask.dependsOn proguardComponentsTask
        androidTasks.add(createMainDexListTask)

        WriteDexInputListFile writeInputListFileTask = project.tasks.create("writeInputListFile", WriteDexInputListFile)
        writeInputListFileTask.inputListFile = project.file("${project.jfxmobile.android.dexOutputDirectory}/inputList.txt")
        writeInputListFileTask.jar = mergeClassesIntoJarTask.archivePath
        writeInputListFileTask.dependsOn mergeClassesIntoJarTask
        androidTasks.add(writeInputListFileTask)

        Dex dexTask = project.tasks.create("dex", Dex)
        dexTask.conventionMapping.map("mainDexListFile") { createMainDexListTask.outputFile }
        dexTask.conventionMapping.map("inputListFile") { writeInputListFileTask.inputListFile }
        dexTask.conventionMapping.map("dexOptions") { project.jfxmobile.android.dexOptions }
        dexTask.outputDirectory = project.file("${project.jfxmobile.android.dexOutputDirectory}")
        dexTask.dependsOn createMainDexListTask, writeInputListFileTask
        androidTasks.add(dexTask)

        MergeResources mergeResourcesTask = project.tasks.create("mergeAndroidResources", MergeResources)
        mergeResourcesTask.conventionMapping.map("aaptExe") { project.file("${project.jfxmobile.android.buildToolsDir}/aapt${platformExtension()}") }
        mergeResourcesTask.conventionMapping.map("inputResourceSets") {
            ResourceSet mainResourceSet = new ResourceSet(BuilderConstants.MAIN)
            mainResourceSet.addSource(project.file(project.jfxmobile.android.resDirectory))
            return [ mainResourceSet ]
        }
        mergeResourcesTask.conventionMapping.map("outputDir") { project.file("${project.jfxmobile.android.resourcesDirectory}/res") }
        androidTasks.add(mergeResourcesTask)

        MergeAssets mergeAssetsTask = project.tasks.create("mergeAndroidAssets", MergeAssets)
        mergeAssetsTask.conventionMapping.map("inputAssetSets") {
            AssetSet mainAssetSet = new AssetSet(BuilderConstants.MAIN)
            mainAssetSet.addSource(project.file(project.jfxmobile.android.assetsDirectory))
            return [ mainAssetSet ]
        }
        mergeAssetsTask.conventionMapping.map("outputDir") { project.file("${project.jfxmobile.android.resourcesDirectory}/assets") }
        androidTasks.add(mergeAssetsTask)

        SigningConfig releaseSigningConfig = project.jfxmobile.android.signingConfig
        ZipAlign zipAlignReleaseTask = createApkTasks('Release', releaseSigningConfig)

        AndroidTask androidReleaseTask = project.tasks.create("androidRelease", AndroidTask)
        androidReleaseTask.description("Generates a release Android apk containing the JavaFX application.")
        androidReleaseTask.dependsOn zipAlignReleaseTask
        androidTasks.add(androidReleaseTask)

        SigningConfig debugSigningConfig = new SigningConfig('debug')
        ZipAlign zipAlignDebugTask = createApkTasks('Debug', debugSigningConfig)

        Install installDebugTask = project.tasks.create("androidInstall", Install)
        installDebugTask.description("Launch the application on a connected android device.")
        installDebugTask.conventionMapping.map("adbExe") { project.file("${project.jfxmobile.android.androidSdk}/platform-tools/adb${platformExtension()}") }
        installDebugTask.conventionMapping.map("apk") { zipAlignDebugTask.outputFile }
        installDebugTask.dependsOn zipAlignDebugTask
        androidTasks.add(installDebugTask)

        AndroidTask androidDebugTask = project.tasks.create("android", AndroidTask)
        androidDebugTask.description("Generates a debug Android apk containing the JavaFX application.")
        androidDebugTask.dependsOn zipAlignDebugTask
        androidTasks.add(androidDebugTask)
    }

    private ZipAlign createApkTasks(String variant, SigningConfig signingConfig) {
        ProcessResources processResourcesTask = project.tasks.create("processAndroidResources${variant}", ProcessResources)
        if ("Debug" == variant) {
            processResourcesTask.setDebuggable(true)
        }
        processResourcesTask.conventionMapping.map("manifest") { project.tasks.collectMultiDexComponents.manifest }
        processResourcesTask.conventionMapping.map("resDir") { project.tasks.mergeAndroidResources.outputDir }
        processResourcesTask.conventionMapping.map("assetsDir") { project.tasks.mergeAndroidAssets.outputDir }
        processResourcesTask.conventionMapping.map("packageOutputFile") { project.file("${project.jfxmobile.android.resourcesDirectory}/resources.ap_") }
        processResourcesTask.conventionMapping.map("aaptExe") { project.file("${project.jfxmobile.android.buildToolsDir}/aapt${platformExtension()}") }
        processResourcesTask.dependsOn project.tasks.processAndroidResources, project.tasks.mergeAndroidResources, project.tasks.mergeAndroidAssets
        androidTasks.add(processResourcesTask)

        Apk apkTask = project.tasks.create("apk${variant}", Apk)
        apkTask.conventionMapping.map("resourceFile") { processResourcesTask.packageOutputFile }
        apkTask.conventionMapping.map("dexDirectory") { project.tasks.dex.outputDirectory }
        apkTask.conventionMapping.map("dexedLibraries") { Collections. emptyList() }
        apkTask.conventionMapping.map("jniFolders") {
            project.files(
                "${project.jfxmobile.android.dalvikSdkLib}",
                "${project.jfxmobile.android.nativeDirectory}"
            ).files
        }
        apkTask.conventionMapping.map("outputFile") { project.file("${project.jfxmobile.android.installDirectory}/${project.name}-unaligned.apk") }
        apkTask.conventionMapping.map("mainResourcesDirectory") {
            def mainResourcesOutputDir = project.tasks.processResources.destinationDir
            mainResourcesOutputDir != null && mainResourcesOutputDir.isDirectory() ? mainResourcesOutputDir : null
        }
        apkTask.conventionMapping.map("androidResourcesDirectory") {
            def androidResourcesOutputDir = project.tasks.processAndroidResources.destinationDir
            androidResourcesOutputDir != null && androidResourcesOutputDir.isDirectory() ? androidResourcesOutputDir : null
        }
        apkTask.conventionMapping.map("signingConfig") {
            if (signingConfig.getStoreFile() != null) {
                signingConfig
            }
        }
        apkTask.conventionMapping.map("packagingOptions") { project.jfxmobile.android.packagingOptions }
        apkTask.dependsOn processResourcesTask, project.tasks.dex
        androidTasks.add(apkTask)

        if (signingConfig != null) {
            ValidateSigning validateSigningTask = project.tasks.create("validateSigning${variant}", ValidateSigning)
            validateSigningTask.signingConfig = signingConfig
            androidTasks.add(validateSigningTask)
            apkTask.dependsOn validateSigningTask
        }

        ZipAlign zipAlignTask = project.tasks.create("zipalign${variant}", ZipAlign)
        zipAlignTask.conventionMapping.map("inputFile") { apkTask.outputFile }
        zipAlignTask.conventionMapping.map("outputFile") { project.file("${project.jfxmobile.android.installDirectory}/${project.name}.apk") }
        zipAlignTask.conventionMapping.map("zipAlignExe") { project.file("${project.jfxmobile.android.buildToolsDir}/zipalign${platformExtension()}") }
        zipAlignTask.dependsOn apkTask
        androidTasks.add(zipAlignTask)

        return zipAlignTask
    }

    private void createIosTasks() {
        if (project.jfxmobile.ios.launcherClassName == 'org.javafxports.jfxmobile.ios.BasicLauncher') {
            CreateDefaultLauncher createDefaultLauncherTask = project.tasks.create('createDefaultIOSLauncher', CreateDefaultLauncher)
            createDefaultLauncherTask.conventionMapping.map('mainClassName') { project.mainClassName }
            createDefaultLauncherTask.conventionMapping.map('preloaderClassName') { project.preloaderClassName }
            createDefaultLauncherTask.outputFile = project.file("${project.jfxmobile.ios.temporaryDirectory}/sources/org/javafxports/jfxmobile/ios/BasicLauncher.java")

            project.tasks.compileIosJava {
                dependsOn createDefaultLauncherTask
                source project.file("${project.jfxmobile.ios.temporaryDirectory}/sources")
            }
        }

        // NOTE: the from input is taken from the iosRuntime configuration, but can only be applied
        // when that configuration is completely configured. the from is applied above at a later
        // time after the project's taskGraph is ready
        Sync extractNativeLibsTask = project.tasks.create("iosExtractNativeLibs", Sync) {
            into project.file("${project.jfxmobile.ios.temporaryDirectory}")
            include 'native/*.a'
        }

        IosDevice iosDeviceTask = project.tasks.create("launchIOSDevice", IosDevice)
        iosDeviceTask.description("Launch the application on a connected ios device.")
        iosDeviceTask.dependsOn([project.tasks.iosClasses, extractNativeLibsTask])
        iosTasks.add(iosDeviceTask)

        IPadSimulator ipadSimulatorTask = project.tasks.create("launchIPadSimulator", IPadSimulator)
        ipadSimulatorTask.description("Launch the application on an iPad simulator.")
        ipadSimulatorTask.dependsOn([project.tasks.iosClasses, extractNativeLibsTask])
        iosTasks.add(ipadSimulatorTask)

        IPhoneSimulator iphoneSimulatorTask = project.tasks.create("launchIPhoneSimulator", IPhoneSimulator)
        iphoneSimulatorTask.description("Launch the application on an iPhone simulator.")
        iphoneSimulatorTask.dependsOn([project.tasks.iosClasses, extractNativeLibsTask])
        iosTasks.add(iphoneSimulatorTask)

        CreateIpa createIpaTask = project.tasks.create("createIpa", CreateIpa)
        createIpaTask.description("Generates an iOS ipa containing the JavaFX application.")
        createIpaTask.dependsOn([project.tasks.iosClasses, extractNativeLibsTask])
        iosTasks.add(createIpaTask)

        IosTask iosTask = project.tasks.create('ios', IosTask)
        iosTask.dependsOn createIpaTask
        iosTasks.add(iosTask)
    }

    private void createEmbeddedTasks() {
        project.task('copyEmbeddedDependencies', type: Copy) {
            into project.file("$project.buildDir/javafxports/embedded/libs")
            from project.configurations.embeddedRuntime
        }
        embeddedTasks.add(project.tasks.copyEmbeddedDependencies)

        project.task('embeddedJar', type: Jar) {
            from project.sourceSets.embedded.output
            from project.sourceSets.main.output
            destinationDir project.tasks.copyEmbeddedDependencies.destinationDir
        }
        project.tasks.embeddedJar.conventionMapping.map('manifest') {
            project.manifest {
                if (project.preloaderClassName != null && !project.preloaderClassName.empty) {
                    attributes(
                            'Main-Class': project.mainClassName,
                            'JavaFX-Preloader-Class': project.preloaderClassName
                    )
                } else {
                    attributes(
                            'Main-Class': project.mainClassName
                    )
                }
            }
        }
        embeddedTasks.add(project.tasks.embeddedJar)

        CopyRemoteDir copyJarTask = project.tasks.create("copyJarToEmbeddedPlatform", CopyRemoteDir)
        copyJarTask.conventionMapping.map('from') { project.tasks.copyEmbeddedDependencies.destinationDir }
        copyJarTask.conventionMapping.map('remotePlatform') { getRemotePlatformConfiguration() }
        copyJarTask.dependsOn project.tasks.embeddedJar, project.tasks.copyEmbeddedDependencies
        embeddedTasks.add(copyJarTask)

        RunEmbedded runEmbeddedTask = project.tasks.create("runEmbedded", RunEmbedded)
        runEmbeddedTask.description('Launch the application on a remote embedded platform.')
        runEmbeddedTask.conventionMapping.map('remotePlatform') { getRemotePlatformConfiguration() }
        runEmbeddedTask.dependsOn copyJarTask
        embeddedTasks.add(runEmbeddedTask)
    }

    /**
     * Locates the android sdk and returns the android.jar for the configured
     * android platform. The android platform can be configured by setting the
     * compileSdkVersion. If no android sdk could be located, this method will
     * return null.
     * Please note that the returned file does not necessarily need to exist.
     */
    private File getPlatformAndroidJar() {
        String androidSdkLocation = locateAndroidSdk()
        if (androidSdkLocation != null) {
            return project.file("${androidSdkLocation}/platforms/android-${project.jfxmobile.android.compileSdkVersion}/android.jar")
        }
        return null
    }

    private void configureAndroid() {
        project.logger.info("Configuring Build for Android")

        if (project.jfxmobile.android.dalvikSdk == null) {
            project.jfxmobile.android.dalvikSdk = resolveSdk(project.configurations.dalvikSdk, "dalvik-sdk")
        }
        project.jfxmobile.android.dalvikSdkLib = project.file("${project.jfxmobile.android.dalvikSdk}/rt/lib")
        if (!project.jfxmobile.android.dalvikSdkLib.exists()) {
            throw new GradleException("Configured dalvikSdk is invalid: ${project.jfxmobile.android.dalvikSdk}")
        }
        project.logger.info("Using javafxports dalvik sdk from location ${project.jfxmobile.android.dalvikSdk}")

        // try and set the androidSdk extension value if it is not set
        project.jfxmobile.android.androidSdk = locateAndroidSdk()
        if (project.jfxmobile.android.androidSdk == null) {
            throw new GradleException("ANDROID_HOME not specified. Either set it as a gradle property, a system environment variable or directly in your build.gradle by setting the extension jfxmobile.android.androidSdk.")
        }
        project.logger.info("Using Android SDK from location: ${project.jfxmobile.android.androidSdk}")

        // check if android sdk points to correct directory by checking if the
        // configured androidSdk directory contains a build-tools subdirectory
        def buildToolsDir = project.file("${project.jfxmobile.android.androidSdk}/build-tools")
        if (!buildToolsDir.exists()) {
            throw new GradleException("Configured Android SDK located at ${project.jfxmobile.android.androidSdk} is missing the build-tools directory. Please make sure that you install at least version ${project.jfxmobile.android.minimalBuildToolsVersion} of the Android Build Tools:\n     - from command line run: ${project.jfxmobile.android.androidSdk}/tools/bin/sdkmanager \"build-tools;27.0.3\"\n     - from Android Studio: see https://developer.android.com/studio/intro/update.html")
        }

        if (!hasAndroidMavenRepository)  {
            throw new GradleException("Configured Android SDK located at ${project.jfxmobile.android.androidSdk} is missing the Android Support Repository:\n     - from command line run: ${project.jfxmobile.android.androidSdk}/tools/bin/sdkmanager \"extras;android;m2repository\"\n     - from Android Studio: see https://developer.android.com/studio/intro/update.html")
        }

        // automatically figure out most recent build tools version if it is not
        // specified on extension
        if (project.jfxmobile.android.buildToolsVersion == null) {
            project.logger.info("There was no buildToolsVersion specified, looking for most recent installed version automatically")
            def maxRevisionDir = null
            def maxRevision = null
            buildToolsDir.eachDir { dir ->
                try {
                    def revision = FullRevision.parseRevision(dir.name)
                    if (revision.preview) {
                        project.logger.info("Ignoring directory ${dir.absolutePath} as it denotes a preview build tools version")
                    } else if (maxRevision == null || maxRevision.compareTo(revision) < 0) {
                        maxRevision = revision
                        maxRevisionDir = dir
                    }
                } catch (NumberFormatException ex) {
                    project.logger.info("Ignoring directory ${dir.absolutePath} as it does not denote a valid android build tools revision number")
                }
            }

            if (maxRevision == null) {
                throw new GradleException("No valid build tools version could be detected in ${project.jfxmobile.android.androidSdk}. Please check your androidSdk installation.")
            } else {
                project.jfxmobile.android.buildToolsVersion = maxRevisionDir.name
                project.logger.info("Using the following automatically detected buildToolsVersion: ${maxRevisionDir.name}")
            }
        }

        project.jfxmobile.android.buildToolsDir = project.file("${project.jfxmobile.android.androidSdk}/build-tools/${project.jfxmobile.android.buildToolsVersion}")
        project.jfxmobile.android.buildToolsLib = project.file("${project.jfxmobile.android.buildToolsDir}/lib")

        project.jfxmobile.android.validate()

        if (project.jfxmobile.android.applicationPackage == null) {
            def dotIndex = project.mainClassName.lastIndexOf('.')
            if (dotIndex != -1) {
                project.jfxmobile.android.applicationPackage = project.mainClassName[0.. files = configuration.resolve()
        if (!files.isEmpty()) {
            return unpackSdk(configuration.getAllDependencies().getAt(0), files.iterator().next(), unpackedSubDirectory).absolutePath
        }
        return null
    }

    private File unpackSdk(Dependency dependency, File archive, String unpackedSubDirectory) {
        final File unpackedDirectory = new File(archive.parent, "unpacked")
        final File unpackedDistDirectory = new File(unpackedDirectory, unpackedSubDirectory)

        if (unpackedDirectory.exists() && dependency.version.endsWith('-SNAPSHOT')) {
            unpackedDirectory.deleteDir()
        }

        if (!unpackedDirectory.exists()) {
            if (!unpackedDirectory.mkdirs()) {
                throw new GradleException("Unable to create base directory to unpack into: " + unpackedDirectory)
            }

            if (archive.name.endsWith(".zip")) {
                project.ant.unzip(src: archive, dest: unpackedDirectory)
            } else if (archive.name.endsWith(".tar.gz")) {
                project.ant.untar(src: archive, dest: unpackedDirectory, compression: 'gzip')
            }
            if (!unpackedDistDirectory.exists()) {
                throw new GradleException("Unable to unpack archive.")
            }

            File binDirectory = new File(unpackedDistDirectory, 'bin')
            if (binDirectory.exists()) {
                project.ant.chmod(dir: binDirectory, perm: '+x', includes: '*')
            }
        }

        return unpackedDistDirectory
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy