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 17package androidx.build 18 19import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER 20import androidx.build.license.CheckExternalDependencyLicensesTask 21import com.android.build.gradle.LibraryExtension 22import com.android.build.gradle.internal.dsl.LintOptions 23import com.android.build.gradle.tasks.GenerateBuildConfig 24import net.ltgt.gradle.errorprone.ErrorProneBasePlugin 25import net.ltgt.gradle.errorprone.ErrorProneToolChain 26import org.gradle.api.JavaVersion 27import org.gradle.api.Plugin 28import org.gradle.api.Project 29import java.io.File 30 31/** 32 * Support library specific com.android.library plugin that sets common configurations needed for 33 * support library modules. 34 */ 35class 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 156private 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