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