Blob Blame History Raw
/* -*- Mode: Groovy; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// The omnijar inputs are listed as resource directory inputs to a dummy JAR.
// That arrangement labels them nicely in IntelliJ.  See the comment in the
// :omnijar project for more context.
evaluationDependsOn(':omnijar')

task buildOmnijars(type:Exec) {
    dependsOn rootProject.generateCodeAndResources

    // See comment in :omnijar project regarding interface mismatches here.
    inputs.file(project(':omnijar').sourceSets.main.resources.srcDirs).skipWhenEmpty() 

    // Produce both the Fennec and the GeckoView omnijars.
    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
    outputs.file "${topobjdir}/dist/geckoview/assets/omni.ja"

    workingDir "${topobjdir}"

    commandLine mozconfig.substs.GMAKE
    args '-C'
    args "${topobjdir}/mobile/android/base"
    args 'gradle-omnijar'

    // Only show the output if something went wrong.
    ignoreExitValue = true
    standardOutput = new ByteArrayOutputStream()
    errorOutput = standardOutput
    doLast {
        if (execResult.exitValue != 0) {
            throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
        }
    }
}

ext.configureVariantWithGeckoBinaries = { variant ->
    // :app needs the full Fennec omni.ja, whereas other projects need the
    // GeckoView-specific omni.ja.
    def omnijar_dir = "app".equals(project.name) ? "fennec" : "geckoview"
    def distDir = "${topobjdir}/dist/${omnijar_dir}"

    def syncOmnijarFromDistDir = task("syncOmnijarFromDistDirFor${variant.name.capitalize()}", type: Sync) {
        onlyIf {
            if (source.empty) {
                throw new StopExecutionException("Required omnijar not found in ${distDir}/{omni.ja,assets/omni.ja}.  Have you built and packaged?")
            }
            return true
        }

        into("${project.buildDir}/moz.build/src/${variant.name}/omnijar")
        from("${distDir}/omni.ja",
             "${distDir}/assets/omni.ja") {
            // Throw an exception if we find multiple, potentially conflicting omni.ja files.
            duplicatesStrategy 'fail'
        }
    }

    def syncLibsFromDistDir = task("syncLibsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
        onlyIf {
            if (source.empty) {
                throw new StopExecutionException("Required JNI libraries not found in ${distDir}/lib.  Have you built and packaged?")
            }
            return true
        }

        into("${project.buildDir}/moz.build/src/${variant.name}/jniLibs")
        from("${distDir}/lib")
    }

    def syncAssetsFromDistDir = task("syncAssetsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
        onlyIf {
            if (source.empty) {
                throw new StopExecutionException("Required assets not found in ${distDir}/assets.  Have you built and packaged?")
            }
            return true
        }

        into("${project.buildDir}/moz.build/src/${variant.name}/assets")
        from("${distDir}/assets") {
            exclude 'omni.ja'
        }
    }

    // Local (read, not 'official') builds want to reflect developer changes to
    // the Omnijar sources.  To do this, the Gradle build calls out to the
    // moz.build system, which can be re-entrant.  Official builds are driven by
    // the moz.build system and should never be re-entrant in this way.
    if (!((variant.productFlavors*.name).contains('official'))) {
        syncOmnijarFromDistDir.dependsOn buildOmnijars
    }

    def assetGenTask = tasks.findByName("generate${variant.name.capitalize()}Assets")
    if ((variant.productFlavors*.name).contains('withGeckoBinaries')) {
        assetGenTask.dependsOn syncOmnijarFromDistDir
        assetGenTask.dependsOn syncLibsFromDistDir
        assetGenTask.dependsOn syncAssetsFromDistDir

        android.sourceSets."${variant.name}".assets.srcDir syncOmnijarFromDistDir.destinationDir
        android.sourceSets."${variant.name}".assets.srcDir syncAssetsFromDistDir.destinationDir
        android.sourceSets."${variant.name}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
    }
}

ext.configureLibraryVariantWithJNIWrappers = { variant, module ->
    // Library variants have two essentially independent transform* tasks:
    //
    // - ...WithSyncLibJars... is used by assemble* and bundle*
    // - ...WithPrepareIntermediateJars... is used by consuming applications.
    //
    // It's not really possible to insert something immediately _after_
    // ...WithPrepareIntermediateJars..., so we make the consuming moz.build
    // system invoke this target directly, and force the
    // ...WithPrepareIntermediateJars... dependency.  The real consumer of the
    // JNI wrappers is the moz.build system, which always builds geckoview to
    // consume from Fennec, so that dependency likely adds less to the build time.
    def jarTask = tasks["transformClassesAndResourcesWithPrepareIntermediateJarsFor${variant.name.capitalize()}"]
    def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })

    def wrapperTask
    if (System.env.IS_LANGUAGE_REPACK == '1') {
        // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
        // really have a build environment.
        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
    } else {
        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
            classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
        
            // Configure the classpath at evaluation-time, not at
            // configuration-time: see above comment.
            doFirst {
                classpath variant.javaCompile.classpath
                // Include android.jar.
                classpath variant.javaCompile.options.bootClasspath
            }
    
            main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
            args module
            args output
            
            workingDir "${topobjdir}/mobile/android/base"
            
            dependsOn jarTask
        }
    }

    if (module == 'Generated') {
        tasks["bundle${variant.name.capitalize()}"].dependsOn wrapperTask
    } else {
        tasks["assemble${variant.name.capitalize()}"].dependsOn wrapperTask
    }
}

ext.configureApplicationVariantWithJNIWrappers = { variant, module ->
    def jarTask = tasks["bundleAppClasses${variant.name.capitalize()}"]
    def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })

    def wrapperTask
    if (System.env.IS_LANGUAGE_REPACK == '1') {
        // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
        // really have a build environment.
        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
    } else {
        wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
            classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
        
            // Configure the classpath at evaluation-time, not at
            // configuration-time: see above comment.
            doFirst {
                classpath variant.javaCompile.classpath
                // Include android.jar.
                classpath variant.javaCompile.options.bootClasspath
            }
        
            main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
            args module
            args output
        
            workingDir "${topobjdir}/mobile/android/base"
    
            // This forces bundling, which isn't usually part of the assemble* process.
            dependsOn jarTask
        }
    }
}