Skip to content

Commit

Permalink
Android Gradle: add javac intermediates to classpath (#4723)
Browse files Browse the repository at this point in the history
* add javac intermediates to classpath

* use javac intermediates directly

* use java compile path

* fix registerAndroidDetektTask classpath

* revert unchanged code

* remove unused import

* implement review notes
  • Loading branch information
kvn-stgl committed Apr 25, 2022
1 parent 6462745 commit d74149f
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 7 deletions.
Expand Up @@ -420,6 +420,53 @@ class DetektAndroidSpec {
)
}
}

@Nested
inner class `configures android tasks android tasks have javac intermediates on classpath` {
val projectLayout = ProjectLayout(
numberOfSourceFilesInRootPerSourceDir = 0,
).apply {
addSubmodule(
name = "app",
numberOfSourceFilesPerSourceDir = 0,
numberOfCodeSmells = 0,
buildFileContent = """
$APP_PLUGIN_BLOCK
$ANDROID_BLOCK_WITH_VIEW_BINDING
""".trimIndent(),
srcDirs = listOf("src/main/java"),
)
}
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout, dryRun = false).also {
it.projectFile("app/src/main/java").mkdirs()
it.projectFile("app/src/main/res/layout").mkdirs()
it.writeProjectFile("app/src/main/AndroidManifest.xml", manifestContent())
it.writeProjectFile(
"app/src/main/res/layout/activity_sample.xml",
SAMPLE_ACTIVITY_LAYOUT
)
it.writeProjectFile(
"app/src/main/java/SampleActivity.kt",
SAMPLE_ACTIVITY_USING_VIEW_BINDING
)
}

@Test
@DisplayName("task :app:detektMain has javac intermediates on the classpath")
fun libDetektMain() {
gradleRunner.runTasksAndCheckResult(":app:detektMain") { buildResult ->
assertThat(buildResult.output).doesNotContain("UnreachableCode")
}
}

@Test
@DisplayName("task :app:detektTest has javac intermediates on the classpath")
fun libDetektTest() {
gradleRunner.runTasksAndCheckResult(":app:detektTest") { buildResult ->
assertThat(buildResult.output).doesNotContain("UnreachableCode")
}
}
}
}
}

Expand Down Expand Up @@ -475,6 +522,19 @@ private val ANDROID_BLOCK_WITH_FLAVOR = """
}
""".trimIndent()

private val ANDROID_BLOCK_WITH_VIEW_BINDING = """
android {
compileSdkVersion(30)
defaultConfig {
applicationId = "io.gitlab.arturbosch.detekt.app"
minSdkVersion(24)
}
buildFeatures {
viewBinding = true
}
}
""".trimIndent()

private val DETEKT_REPORTS_BLOCK = """
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
reports {
Expand All @@ -483,7 +543,40 @@ private val DETEKT_REPORTS_BLOCK = """
}
""".trimIndent()

private fun createGradleRunnerAndSetupProject(projectLayout: ProjectLayout) = DslGradleRunner(
private val SAMPLE_ACTIVITY_LAYOUT = """
<?xml version="1.0" encoding="utf-8"?>
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sample_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
""".trimIndent()

private val SAMPLE_ACTIVITY_USING_VIEW_BINDING = """
package io.gitlab.arturbosch.detekt.app
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import io.gitlab.arturbosch.detekt.app.databinding.ActivitySampleBinding
class SampleActivity : Activity() {
private lateinit var binding: ActivitySampleBinding
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySampleBinding.inflate(LayoutInflater.from(this))
binding.sampleView ?: return
setContentView(binding.root)
}
}
""".trimIndent() + "\n" // new line at end of file rule

private fun createGradleRunnerAndSetupProject(
projectLayout: ProjectLayout,
dryRun: Boolean = true,
) = DslGradleRunner(
projectLayout = projectLayout,
buildFileName = "build.gradle.kts",
mainBuildFileContent = """
Expand All @@ -495,5 +588,5 @@ private fun createGradleRunnerAndSetupProject(projectLayout: ProjectLayout) = Ds
}
}
""".trimIndent(),
dryRun = true
dryRun = dryRun,
).also { it.setupProject() }
Expand Up @@ -14,6 +14,7 @@ import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.DomainObjectSet
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskProvider

Expand Down Expand Up @@ -71,9 +72,10 @@ internal class DetektAndroid(private val project: Project) {
baseExtension.variants
?.matching { !extension.matchesIgnoredConfiguration(it) }
?.all { variant ->
project.registerAndroidDetektTask(bootClasspath, extension, variant).also { provider ->
mainTaskProvider.dependsOn(provider)
}
project.registerAndroidDetektTask(bootClasspath, extension, variant)
.also { provider ->
mainTaskProvider.dependsOn(provider)
}
project.registerAndroidCreateBaselineTask(bootClasspath, extension, variant)
.also { provider ->
mainBaselineTaskProvider.dependsOn(provider)
Expand Down Expand Up @@ -114,7 +116,11 @@ internal fun Project.registerAndroidDetektTask(
registerDetektTask(taskName, extension) {
setSource(variant.sourceSets.map { it.javaDirectories + it.kotlinDirectories })
extraInputSource?.let { source(it) }
classpath.setFrom(variant.getCompileClasspath(null).filter { it.exists() } + bootClasspath)
classpath.setFrom(
variant.getCompileClasspath(null).filter { it.exists() },
bootClasspath,
javaCompileDestination(variant),
)
// If a baseline file is configured as input file, it must exist to be configured, otherwise the task fails.
// We try to find the configured baseline or alternatively a specific variant matching this task.
extension.baseline?.existingVariantOrBaseFile(variant.name)?.let { baselineFile ->
Expand All @@ -134,8 +140,23 @@ internal fun Project.registerAndroidCreateBaselineTask(
registerCreateBaselineTask(taskName, extension) {
setSource(variant.sourceSets.map { it.javaDirectories + it.kotlinDirectories })
extraInputSource?.let { source(it) }
classpath.setFrom(variant.getCompileClasspath(null).filter { it.exists() } + bootClasspath)
classpath.setFrom(
variant.getCompileClasspath(null).filter { it.exists() },
bootClasspath,
javaCompileDestination(variant),
)
val variantBaselineFile = extension.baseline?.addVariantName(variant.name)
baseline.set(project.layout.file(project.provider { variantBaselineFile }))
description = "EXPERIMENTAL: Creates detekt baseline for ${variant.name} classes with type resolution"
}

private fun Project.javaCompileDestination(variant: BaseVariant): DirectoryProperty? {
val javaCompile = variant.javaCompileProvider.orNull
if (javaCompile == null) {
logger.warn(
"Unable to find Java compiler on variant '{}'. Detekt analysis can show false negatives.",
variant.name,
)
}
return javaCompile?.destinationDirectory
}

0 comments on commit d74149f

Please sign in to comment.