Home | History | Annotate | Download | only in build
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.build
     18 
     19 import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER
     20 import androidx.build.license.CheckExternalDependencyLicensesTask
     21 import com.android.build.gradle.LibraryExtension
     22 import com.android.build.gradle.internal.dsl.LintOptions
     23 import com.android.build.gradle.tasks.GenerateBuildConfig
     24 import net.ltgt.gradle.errorprone.ErrorProneBasePlugin
     25 import net.ltgt.gradle.errorprone.ErrorProneToolChain
     26 import org.gradle.api.JavaVersion
     27 import org.gradle.api.Plugin
     28 import org.gradle.api.Project
     29 import java.io.File
     30 
     31 /**
     32  * Support library specific com.android.library plugin that sets common configurations needed for
     33  * support library modules.
     34  */
     35 class SupportAndroidLibraryPlugin : Plugin<Project> {
     36 
     37     override fun apply(project: Project) {
     38         val supportLibraryExtension = project.extensions.create("supportLibrary",
     39                 SupportLibraryExtension::class.java, project)
     40         apply(project, supportLibraryExtension)
     41         CheckExternalDependencyLicensesTask.configure(project)
     42         val isCoreSupportLibrary = project.rootProject.name == "support"
     43 
     44         project.afterEvaluate {
     45             val library = project.extensions.findByType(LibraryExtension::class.java)
     46                     ?: return@afterEvaluate
     47 
     48             library.defaultConfig.minSdkVersion(supportLibraryExtension.minSdkVersion)
     49 
     50             // Java 8 is only fully supported on API 24+ and not all Java 8 features are binary
     51             // compatible with API < 24, so use Java 7 for both source AND target.
     52             val javaVersion: JavaVersion
     53             if (supportLibraryExtension.java8Library) {
     54                 if (library.defaultConfig.minSdkVersion.apiLevel < 24) {
     55                     throw IllegalArgumentException("Libraries can only support Java 8 if "
     56                             + "minSdkVersion is 24 or higher")
     57                 }
     58                 javaVersion = JavaVersion.VERSION_1_8
     59             } else {
     60                 javaVersion = JavaVersion.VERSION_1_7
     61             }
     62 
     63             library.compileOptions.setSourceCompatibility(javaVersion)
     64             library.compileOptions.setTargetCompatibility(javaVersion)
     65 
     66             VersionFileWriterTask.setUpAndroidLibrary(project, library)
     67             DiffAndDocs.registerAndroidProject(project, library, supportLibraryExtension)
     68 
     69             library.libraryVariants.all { libraryVariant ->
     70                 if (libraryVariant.getBuildType().getName().equals("debug")) {
     71                     @Suppress("DEPRECATION")
     72                     val javaCompile = libraryVariant.javaCompile
     73                     if (supportLibraryExtension.failOnUncheckedWarnings) {
     74                         javaCompile.options.compilerArgs.add("-Xlint:unchecked")
     75                     }
     76                     if (supportLibraryExtension.failOnDeprecationWarnings) {
     77                         javaCompile.options.compilerArgs.add("-Xlint:deprecation")
     78                     }
     79                     javaCompile.options.compilerArgs.add("-Werror")
     80                 }
     81             }
     82         }
     83 
     84         project.apply(mapOf("plugin" to "com.android.library"))
     85         project.apply(mapOf("plugin" to ErrorProneBasePlugin::class.java))
     86 
     87         project.afterEvaluate {
     88             project.tasks.all({
     89                 if (it is GenerateBuildConfig) {
     90                     // Disable generating BuildConfig.java
     91                     it.enabled = false
     92                 }
     93             })
     94         }
     95 
     96         project.configurations.all { configuration ->
     97             if (isCoreSupportLibrary && project.name != "annotations") {
     98                 // While this usually happens naturally due to normal project dependencies, force
     99                 // evaluation on the annotations project in case the below substitution is the only
    100                 // dependency to this project. See b/70650240 on what happens when this is missing.
    101                 project.evaluationDependsOn(":annotation")
    102 
    103                 // In projects which compile as part of the "core" support libraries (which include
    104                 // the annotations), replace any transitive pointer to the deployed Maven
    105                 // coordinate version of annotations with a reference to the local project. These
    106                 // usually originate from test dependencies and otherwise cause multiple copies on
    107                 // the classpath. We do not do this for non-"core" projects as they need to
    108                 // depend on the Maven coordinate variant.
    109                 configuration.resolutionStrategy.dependencySubstitution.apply {
    110                     substitute(module("androidx.annotation:annotation"))
    111                             .with(project(":annotation"))
    112                 }
    113             }
    114         }
    115 
    116         val library = project.extensions.findByType(LibraryExtension::class.java)
    117                 ?: throw Exception("Failed to find Android extension")
    118 
    119         library.compileSdkVersion(SupportConfig.CURRENT_SDK_VERSION)
    120 
    121         library.buildToolsVersion = SupportConfig.BUILD_TOOLS_VERSION
    122 
    123         // Update the version meta-data in each Manifest.
    124         library.defaultConfig.addManifestPlaceholders(
    125                 mapOf("target-sdk-version" to SupportConfig.CURRENT_SDK_VERSION))
    126 
    127         // Set test runner.
    128         library.defaultConfig.testInstrumentationRunner = INSTRUMENTATION_RUNNER
    129 
    130         library.testOptions.unitTests.isReturnDefaultValues = true
    131 
    132         // Use a local debug keystore to avoid build server issues.
    133         library.signingConfigs.findByName("debug")?.storeFile =
    134                 SupportConfig.getKeystore(project)
    135 
    136         project.afterEvaluate {
    137             setUpLint(library.lintOptions, SupportConfig.getLintBaseline(project),
    138                     (supportLibraryExtension.mavenVersion?.isFinalApi()) ?: false)
    139         }
    140 
    141         project.tasks.getByName("uploadArchives").dependsOn("lintRelease")
    142 
    143         setUpSoureJarTaskForAndroidProject(project, library)
    144 
    145         val toolChain = ErrorProneToolChain.create(project)
    146         project.dependencies.add("errorprone", ERROR_PRONE_VERSION)
    147         library.libraryVariants.all { libraryVariant ->
    148             if (libraryVariant.getBuildType().getName().equals("debug")) {
    149                 @Suppress("DEPRECATION")
    150                 libraryVariant.javaCompile.configureWithErrorProne(toolChain)
    151             }
    152         }
    153     }
    154 }
    155 
    156 private fun setUpLint(lintOptions: LintOptions, baseline: File, verifyTranslations: Boolean) {
    157     // Always lint check NewApi as fatal.
    158     lintOptions.isAbortOnError = true
    159     lintOptions.isIgnoreWarnings = true
    160 
    161     // Skip lintVital tasks on assemble. We explicitly run lintRelease for libraries.
    162     lintOptions.isCheckReleaseBuilds = false
    163 
    164     // Write output directly to the console (and nowhere else).
    165     lintOptions.textOutput("stderr")
    166     lintOptions.textReport = true
    167     lintOptions.htmlReport = false
    168 
    169     // Format output for convenience.
    170     lintOptions.isExplainIssues = true
    171     lintOptions.isNoLines = false
    172     lintOptions.isQuiet = true
    173 
    174     lintOptions.fatal("NewApi")
    175     lintOptions.fatal("ObsoleteSdkInt")
    176     lintOptions.fatal("VisibleForTests")
    177     lintOptions.fatal("NoHardKeywords")
    178 
    179     if (verifyTranslations) {
    180         lintOptions.fatal("MissingTranslation")
    181     } else {
    182         lintOptions.disable("MissingTranslation")
    183     }
    184 
    185     // Set baseline file for all legacy lint warnings.
    186     if (baseline.exists()) {
    187         lintOptions.baseline(baseline)
    188     }
    189 }
    190