blob: 6413218420058d0f811dd8f8624ce1841e297b80 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.build
import androidx.build.gradle.getByType
import com.android.build.gradle.internal.dsl.LintOptions
import org.gradle.api.Project
import java.io.File
/**
* Setting this property means that lint will fail for UnknownNullness issues.
*/
private const val CHECK_UNKNOWN_NULLNESS = "checkUnknownNullness"
/**
* Setting this property means that lint will update lint-baseline.xml if it exists.
*/
private const val UPDATE_LINT_BASELINE = "updateLintBaseline"
/**
* Property used by Lint to continue creating baselines without failing lint, normally set by:
* -Dlint.baselines.continue=true from command line.
*/
private const val LINT_BASELINE_CONTINUE = "lint.baselines.continue"
fun Project.configureNonAndroidProjectForLint(extension: AndroidXExtension) {
apply(mapOf("plugin" to "com.android.lint"))
// Create fake variant tasks since that is what is invoked on CI and by developers.
val lintTask = tasks.named("lint")
tasks.register("lintDebug") {
it.dependsOn(lintTask)
}
tasks.register("lintRelease") {
it.dependsOn(lintTask)
}
val lintOptions = extensions.getByType<LintOptions>()
configureLint(lintOptions, extension)
}
fun Project.configureLint(lintOptions: LintOptions, extension: AndroidXExtension) {
// If -PcheckUnknownNullness was set we should fail on UnknownNullness warnings
val checkUnknownNullness = hasProperty(CHECK_UNKNOWN_NULLNESS)
// If -PupdateLintBaseline was set we should update the baseline if it exists
val updateLintBaseline = hasProperty(UPDATE_LINT_BASELINE)
// Lint is configured entirely in afterEvaluate so that individual projects cannot easily
// disable individual checks in the DSL for any reason. That being said, when rolling out a new
// check as fatal, it can be beneficial to set it to fatal above this comment. This allows you
// to override it in a build script rather than messing with the baseline files. This is
// especially relevant for checks which cause hundreds or more failures.
afterEvaluate {
lintOptions.apply {
isAbortOnError = true
isIgnoreWarnings = true
// Skip lintVital tasks on assemble. We explicitly run lintRelease for libraries.
isCheckReleaseBuilds = false
// Write output directly to the console (and nowhere else).
textOutput("stderr")
textReport = true
htmlReport = false
// Format output for convenience.
isExplainIssues = true
isNoLines = false
isQuiet = true
fatal("VisibleForTests")
if (extension.compilationTarget != CompilationTarget.HOST) {
// Ignore other errors since we are only interested in nullness here
if (checkUnknownNullness) {
fatal("UnknownNullness")
} else {
fatal("NewApi")
fatal("ObsoleteSdkInt")
fatal("NoHardKeywords")
fatal("UnusedResources")
fatal("KotlinPropertyAccess")
fatal("LambdaLast")
fatal("UnknownNullness")
// suppress warnings more specifically than issue-wide severity (regexes)
// Currently suppresses warnings from baseline files working as intended
lintConfig = project.rootProject.file("buildSrc/lint.xml")
// Only override if not set explicitly.
// Some Kotlin projects may wish to disable this.
if (lintOptions.severityOverrides["SyntheticAccessor"] == null) {
fatal("SyntheticAccessor")
}
if (extension.mavenVersion?.isFinalApi() == true) {
fatal("MissingTranslation")
} else {
disable("MissingTranslation")
}
}
}
val lintDebugTask = tasks.named("lintDebug")
if (checkUnknownNullness) {
lintDebugTask.configure {
it.doFirst {
logger.warn(
"-PcheckUnknownNullness set - checking UnknownNullness lint warnings."
)
}
}
} else if (lintBaseline.exists()) {
if (updateLintBaseline) {
// Continue generating baselines regardless of errors
isAbortOnError = false
// Avoid printing every single lint error to the terminal
textReport = false
lintBaseline.delete()
System.setProperty(LINT_BASELINE_CONTINUE, "true")
} else {
// Number of currently ignored UnknownNullness errors
val count = lintBaseline.readText().split("UnknownNullness").size - 1
if (count > 0) {
lintDebugTask.configure {
it.doLast {
logger.warn(getIgnoreNullnessError(count))
}
}
}
}
baseline(lintBaseline)
}
}
}
}
val Project.lintBaseline get() = File(projectDir, "/lint-baseline.xml")
private fun Project.getIgnoreNullnessError(count: Int): String = (
"\n${pluralizeMessage(count)} currently whitelisted in " +
"$projectDir/lint-baseline.xml - these warnings should ideally be fixed before " +
"this library moves to a stable release. Run " +
"'./gradlew $name:lintDebug -PcheckUnknownNullness' to fail lint on these warnings."
)
private fun pluralizeMessage(count: Int) = if (count > 1) {
"$count UnknownNullness issues are"
} else {
" UnknownNullness issue is"
}