1 /*
<lambda>null2  * Copyright 2017 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 com.android.build.api.variant.AndroidComponentsExtension
20 import org.gradle.api.Project
21 import org.gradle.api.artifacts.Configuration
22 import org.gradle.api.logging.Logging
23 import org.gradle.api.plugins.JavaPlugin.COMPILE_JAVA_TASK_NAME
24 import org.gradle.api.provider.Provider
25 import org.gradle.api.tasks.Input
26 import org.gradle.api.tasks.SourceSetContainer
27 import org.gradle.api.tasks.TaskProvider
28 import org.gradle.api.tasks.compile.JavaCompile
29 import org.gradle.kotlin.dsl.exclude
30 import org.gradle.kotlin.dsl.get
31 import org.gradle.kotlin.dsl.getByName
32 import org.gradle.process.CommandLineArgumentProvider
33 import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
34 
35 const val ERROR_PRONE_TASK = "runErrorProne"
36 
37 private const val ERROR_PRONE_CONFIGURATION = "errorprone"
38 private val log = Logging.getLogger("ErrorProneConfiguration")
39 
40 fun Project.configureErrorProneForJava() {
41     val errorProneConfiguration = createErrorProneConfiguration()
42     project.extensions.getByName<SourceSetContainer>("sourceSets").configureEach {
43         project.configurations[it.annotationProcessorConfigurationName].extendsFrom(
44             errorProneConfiguration
45         )
46     }
47     val kmpExtension = project.multiplatformExtension
48     log.info("Configuring error-prone for ${project.path}")
49     if (kmpExtension != null) { // KMP project
50         val compileJavaTaskProvider =
51             kmpExtension
52                 .jvm()
53                 .compilations
54                 .getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
55                 .compileJavaTaskProvider
56         makeErrorProneTask(compileJavaTaskProvider)
57     } else { // non-KMP project
58         makeErrorProneTask(tasks.withType(JavaCompile::class.java).named(COMPILE_JAVA_TASK_NAME))
59     }
60 }
61 
Projectnull62 fun Project.configureErrorProneForAndroid() {
63     val androidComponents = extensions.findByType(AndroidComponentsExtension::class.java)
64     androidComponents?.onVariants { variant ->
65         if (variant.buildType == "release") {
66             val errorProneConfiguration = createErrorProneConfiguration()
67             configurations
68                 .getByName(variant.annotationProcessorConfiguration.name)
69                 .extendsFrom(errorProneConfiguration)
70 
71             log.info("Configuring error-prone for ${variant.name}'s java compile")
72             androidComponents.finalizeDsl {
73                 makeErrorProneTask(
74                     compileTaskProvider =
75                         tasks
76                             .withType(JavaCompile::class.java)
77                             .named("compile${variant.name.camelCase()}JavaWithJavac"),
78                     taskSuffix = variant.name.camelCase(),
79                 ) { javaCompile ->
80                     @Suppress("UnstableApiUsage") // JavaCompilation b/397707182
81                     val annotationArgs = variant.javaCompilation.annotationProcessor.arguments
82                     javaCompile.options.compilerArgumentProviders.add(
83                         CommandLineArgumentProviderAdapter(annotationArgs)
84                     )
85                 }
86             }
87         }
88     }
89 }
90 
91 class CommandLineArgumentProviderAdapter(@get:Input val arguments: Provider<Map<String, String>>) :
92     CommandLineArgumentProvider {
asArgumentsnull93     override fun asArguments(): MutableIterable<String> {
94         return mutableListOf<String>().also {
95             for ((key, value) in arguments.get()) {
96                 it.add("-A$key=$value")
97             }
98         }
99     }
100 }
101 
Projectnull102 private fun Project.createErrorProneConfiguration(): Configuration =
103     configurations.findByName(ERROR_PRONE_CONFIGURATION)
104         ?: configurations.create(ERROR_PRONE_CONFIGURATION).apply {
105             isVisible = false
106             isCanBeConsumed = false
107             isCanBeResolved = true
108             exclude(group = "com.google.errorprone", module = "javac")
109             project.dependencies.add(ERROR_PRONE_CONFIGURATION, getLibraryByName("errorProne"))
110         }
111 
112 // Given an existing JavaCompile task, reconfigures the task to use the ErrorProne compiler plugin
configureWithErrorPronenull113 private fun JavaCompile.configureWithErrorProne() {
114     options.isFork = true
115     options.forkOptions.jvmArgs!!.addAll(
116         listOf(
117             "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
118             "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
119             "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
120             "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
121             "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
122             "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
123             "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
124             "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
125             "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
126             "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
127         )
128     )
129     val compilerArgs = this.options.compilerArgs
130     compilerArgs +=
131         listOf(
132             // Tell error-prone that we are running it on android compatible libraries
133             "-XDandroidCompatible=true",
134             "-XDcompilePolicy=simple", // Workaround for b/36098770
135             listOf(
136                     "-Xplugin:ErrorProne",
137 
138                     // : Disables warnings in classes annotated with @Generated
139                     "-XepDisableWarningsInGeneratedCode",
140 
141                     // Ignore intermediate build output, generated files, and external sources. Also
142                     // sources
143                     // imported from Android Studio and IntelliJ which are used in the lint-checks
144                     // project.
145                     "-XepExcludedPaths:.*/(build/generated|build/errorProne|external|" +
146                         "compileTransaction/compile-output|" +
147                         "lint-checks/src/main/java/androidx/com)/.*",
148 
149                     // Consider re-enabling the following checks. Disabled as part of
150                     // error-prone upgrade
151                     "-Xep:InlineMeSuggester:OFF",
152                     "-Xep:NarrowCalculation:OFF",
153                     "-Xep:LongDoubleConversion:OFF",
154                     "-Xep:UnicodeEscape:OFF",
155                     "-Xep:JavaUtilDate:OFF",
156                     "-Xep:UnrecognisedJavadocTag:OFF",
157                     "-Xep:ObjectEqualsForPrimitives:OFF",
158                     "-Xep:DoNotCallSuggester:OFF",
159                     "-Xep:EqualsNull:OFF",
160                     "-Xep:MalformedInlineTag:OFF",
161                     "-Xep:MissingSuperCall:OFF",
162                     "-Xep:ToStringReturnsNull:OFF",
163                     "-Xep:ReturnValueIgnored:OFF",
164                     "-Xep:MissingImplementsComparable:OFF",
165                     "-Xep:EmptyTopLevelDeclaration:OFF",
166                     "-Xep:InvalidThrowsLink:OFF",
167                     "-Xep:StaticAssignmentOfThrowable:OFF",
168                     "-Xep:DoNotClaimAnnotations:OFF",
169                     "-Xep:AlreadyChecked:OFF",
170                     "-Xep:StringSplitter:OFF",
171                     "-Xep:NonApiType:OFF",
172                     "-Xep:StringCaseLocaleUsage:OFF",
173                     "-Xep:LabelledBreakTarget:OFF",
174                     "-Xep:Finalize:OFF",
175                     "-Xep:AddressSelection:OFF",
176                     "-Xep:StringCharset:OFF",
177                     "-Xep:EnumOrdinal:OFF",
178                     "-Xep:ClassInitializationDeadlock:OFF",
179                     "-Xep:VoidUsed:OFF",
180 
181                     // We allow inter library RestrictTo usage.
182                     "-Xep:RestrictTo:OFF",
183 
184                     // Disable the following checks.
185                     "-Xep:UnescapedEntity:OFF",
186                     "-Xep:MissingSummary:OFF",
187                     "-Xep:StaticAssignmentInConstructor:OFF",
188                     "-Xep:InvalidLink:OFF",
189                     "-Xep:InvalidInlineTag:OFF",
190                     "-Xep:EmptyBlockTag:OFF",
191                     "-Xep:EmptyCatch:OFF",
192                     "-Xep:JdkObsolete:OFF",
193                     "-Xep:PublicConstructorForAbstractClass:OFF",
194                     "-Xep:MutablePublicArray:OFF",
195                     "-Xep:NonCanonicalType:OFF",
196                     "-Xep:ModifyCollectionInEnhancedForLoop:OFF",
197                     "-Xep:InheritDoc:OFF",
198                     "-Xep:InvalidParam:OFF",
199                     "-Xep:InlineFormatString:OFF",
200                     "-Xep:InvalidBlockTag:OFF",
201                     "-Xep:ProtectedMembersInFinalClass:OFF",
202                     "-Xep:SameNameButDifferent:OFF",
203                     "-Xep:AnnotateFormatMethod:OFF",
204                     "-Xep:ReturnFromVoid:OFF",
205                     "-Xep:AlmostJavadoc:OFF",
206                     "-Xep:InjectScopeAnnotationOnInterfaceOrAbstractClass:OFF",
207                     "-Xep:InvalidThrows:OFF",
208 
209                     // Disable checks which are already enforced by lint.
210                     "-Xep:PrivateConstructorForUtilityClass:OFF",
211 
212                     // Enforce the following checks.
213                     "-Xep:JavaTimeDefaultTimeZone:ERROR",
214                     "-Xep:ParameterNotNullable:ERROR",
215                     "-Xep:MissingOverride:ERROR",
216                     "-Xep:EqualsHashCode:ERROR",
217                     "-Xep:NarrowingCompoundAssignment:ERROR",
218                     "-Xep:ClassNewInstance:ERROR",
219                     "-Xep:ClassCanBeStatic:ERROR",
220                     "-Xep:SynchronizeOnNonFinalField:ERROR",
221                     "-Xep:OperatorPrecedence:ERROR",
222                     "-Xep:IntLongMath:ERROR",
223                     "-Xep:MissingFail:ERROR",
224                     "-Xep:JavaLangClash:ERROR",
225                     "-Xep:TypeParameterUnusedInFormals:ERROR",
226                     // "-Xep:StringSplitter:ERROR", // disabled with upgrade to 2.14.0
227                     "-Xep:ReferenceEquality:ERROR",
228                     "-Xep:AssertionFailureIgnored:ERROR",
229                     "-Xep:UnnecessaryParentheses:ERROR",
230                     "-Xep:EqualsGetClass:ERROR",
231                     "-Xep:UnusedVariable:ERROR",
232                     "-Xep:UnusedMethod:ERROR",
233                     "-Xep:UndefinedEquals:ERROR",
234                     "-Xep:ThreadLocalUsage:ERROR",
235                     "-Xep:FutureReturnValueIgnored:ERROR",
236                     "-Xep:ArgumentSelectionDefectChecker:ERROR",
237                     "-Xep:HidingField:ERROR",
238                     "-Xep:UnsynchronizedOverridesSynchronized:ERROR",
239                     "-Xep:Finally:ERROR",
240                     "-Xep:ThreadPriorityCheck:ERROR",
241                     "-Xep:AutoValueFinalMethods:ERROR",
242                     "-Xep:ImmutableEnumChecker:ERROR",
243                     "-Xep:UnsafeReflectiveConstructionCast:ERROR",
244                     "-Xep:LockNotBeforeTry:ERROR",
245                     "-Xep:DoubleCheckedLocking:ERROR",
246                     "-Xep:InconsistentCapitalization:ERROR",
247                     "-Xep:ModifiedButNotUsed:ERROR",
248                     "-Xep:AmbiguousMethodReference:ERROR",
249                     "-Xep:EqualsIncompatibleType:ERROR",
250                     "-Xep:ParameterName:ERROR",
251                     "-Xep:RxReturnValueIgnored:ERROR",
252                     "-Xep:BadImport:ERROR",
253                     "-Xep:MissingCasesInEnumSwitch:ERROR",
254                     "-Xep:ObjectToString:ERROR",
255                     "-Xep:CatchAndPrintStackTrace:ERROR",
256                     "-Xep:MixedMutabilityReturnType:ERROR",
257 
258                     // Enforce checks related to nullness annotation usage
259                     "-Xep:NullablePrimitiveArray:ERROR",
260                     "-Xep:MultipleNullnessAnnotations:ERROR",
261                     "-Xep:NullablePrimitive:ERROR",
262                     "-Xep:NullableVoid:ERROR",
263                     "-Xep:NullableWildcard:ERROR",
264                     "-Xep:NullableTypeParameter:ERROR",
265                     "-Xep:NullableConstructor:ERROR",
266 
267                     // Nullaway
268                     "-XepIgnoreUnknownCheckNames", // https://github.com/uber/NullAway/issues/25
269                     "-Xep:NullAway:ERROR",
270                     "-XepOpt:NullAway:AnnotatedPackages=android.arch,android.support,androidx"
271                 )
272                 .joinToString(" ")
273         )
274 }
275 
276 /**
277  * Given a [JavaCompile] task, creates a task that runs the ErrorProne compiler with the same
278  * settings.
279  *
280  * @param onConfigure optional callback which lazily evaluates on task configuration. Use this to do
281  *   any additional configuration such as overriding default settings.
282  */
Projectnull283 private fun Project.makeErrorProneTask(
284     compileTaskProvider: TaskProvider<out JavaCompile>?,
285     taskSuffix: String = "",
286     onConfigure: (errorProneTask: JavaCompile) -> Unit = {}
<lambda>null287 ) = afterEvaluate {
288     val compileTaskProviderExists = provider { compileTaskProvider != null }
289     val errorProneTaskProvider =
290         tasks.register("$ERROR_PRONE_TASK$taskSuffix", JavaCompile::class.java) {
291             it.onlyIf { compileTaskProviderExists.get() }
292             val compileTask = compileTaskProvider?.get() ?: return@register
293             it.classpath = compileTask.classpath
294             it.source = compileTask.source
295             it.destinationDirectory.set(layout.buildDirectory.dir("errorProne/$taskSuffix"))
296             it.options.compilerArgs = compileTask.options.compilerArgs.toMutableList()
297             it.options.annotationProcessorPath = compileTask.options.annotationProcessorPath
298             it.options.bootstrapClasspath = compileTask.options.bootstrapClasspath
299             it.sourceCompatibility = compileTask.sourceCompatibility
300             it.targetCompatibility = compileTask.targetCompatibility
301             it.configureWithErrorProne()
302             it.dependsOn(compileTask.dependsOn)
303 
304             onConfigure(it)
305         }
306     addToCheckTask(errorProneTaskProvider)
307     addToBuildOnServer(errorProneTaskProvider)
308 }
309