Skip to content

Commit

Permalink
Fixed usage Kover Gradle Plugin in buildSrc directory
Browse files Browse the repository at this point in the history
`kotlin-dsl` itself specifies the language version to ensure compatibility of the Kotlin DSL API.
Since we ourselves guarantee and test compatibility with previous Gradle versions, we can override language version
The easiest way to do this now is to specify the version in the `afterEvaluate` block

Fixes #415

PR #432

Co-authored-by: Leonid Startsev <sandwwraith@users.noreply.github.com>
  • Loading branch information
shanshin and sandwwraith committed Jul 14, 2023
1 parent 867d8bd commit 3e2aeca
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 30 deletions.
27 changes: 17 additions & 10 deletions kover-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
Expand Down Expand Up @@ -70,6 +72,7 @@ val functionalTest by tasks.registering(Test::class) {
setSystemPropertyFromProject("kover.test.kotlin.version")

systemProperties["kotlinVersion"] = embeddedKotlinVersion
systemProperties["gradleVersion"] = gradle.gradleVersion
systemProperties["koverVersion"] = version
systemProperties["localRepositoryPath"] = localRepositoryUri.path

Expand Down Expand Up @@ -125,19 +128,23 @@ fun Test.setBooleanSystemPropertyFromProject(

tasks.check { dependsOn(functionalTest) }

tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
allWarningsAsErrors = true
jvmTarget = "1.8"

// Kover works with the stdlib of at least version `1.4.x`
languageVersion = "1.4"
apiVersion = "1.4"
// Kotlin compiler 1.7 issues a warning if `languageVersion` or `apiVersion` 1.4 is used - suppress it
freeCompilerArgs = freeCompilerArgs + "-Xsuppress-version-warnings"
afterEvaluate {
// Workaround:
// `kotlin-dsl` itself specifies the language version to ensure compatibility of the Kotlin DSL API
// Since we ourselves guarantee and test compatibility with previous Gradle versions, we can override language version
// The easiest way to do this now is to specify the version in the `afterEvaluate` block
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
allWarningsAsErrors.set(true)
jvmTarget.set(JvmTarget.JVM_1_8)
languageVersion.set(KotlinVersion.KOTLIN_1_5)
apiVersion.set(KotlinVersion.KOTLIN_1_5)
freeCompilerArgs.add("-Xsuppress-version-warnings")
}
}
}


tasks.dokkaHtml {
moduleName.set("Kover Gradle Plugin")
outputDirectory.set(rootProject.layout.projectDirectory.dir("docs/gradle-plugin/dokka").asFile)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kotlinx.kover.gradle.plugin.test.functional.cases

import kotlinx.kover.gradle.plugin.test.functional.framework.checker.CheckerContext
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.TemplateTest

internal class MetadataCompatibilityTests {

@TemplateTest("buildsrc-usage", [":koverXmlReport"])
fun CheckerContext.test() {
// no-op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@ internal val overriddenKotlinVersion = System.getProperty("kover.test.kotlin.ver
/**
* Custom version of Gradle runner for functional tests.
*/
internal val overriddenGradleWrapperVersion: String? = System.getProperty("kover.test.gradle.version")
internal val overriddenGradleVersion: String? = System.getProperty("kover.test.gradle.version")

/**
* Result path to the Android SDK. `null` if not defined.
*/
internal val androidSdkDir: String? = System.getProperty("kover.test.android.sdk")


/**
* Version of gradle which functional tests are run by.
*/
internal val defaultGradleVersion: String = System.getProperty("gradleVersion")
?: throw Exception("System property 'gradleVersion' not defined for functional tests")

/**
* Flag to run functional tests within debug agent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ package kotlinx.kover.gradle.plugin.test.functional.framework.runner
import kotlinx.kover.gradle.plugin.test.functional.framework.common.isDebugEnabled
import kotlinx.kover.gradle.plugin.test.functional.framework.common.logInfo
import kotlinx.kover.gradle.plugin.test.functional.framework.common.uri
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.*
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.buildSrc
import kotlinx.kover.gradle.plugin.test.functional.framework.starter.patchSettingsFile
import kotlinx.kover.gradle.plugin.util.SemVer
import org.opentest4j.TestAbortedException
import java.io.File
import java.nio.file.Files

Expand Down Expand Up @@ -39,6 +43,7 @@ internal interface GradleBuild {
}

internal data class BuildEnv(
val gradleVersion: SemVer,
val wrapperDir: File,
val androidSdkDir: String? = null,
val disableBuildCacheByDefault: Boolean = true
Expand Down Expand Up @@ -75,11 +80,15 @@ private class BuildSourceImpl(val localMavenDir: String, val koverVersion: Strin
actualDir
}

targetDir.patchSettingsFile(
targetDir.settings.patchSettingsFile(
"$buildType '$buildName', project dir: ${targetDir.uri}",
koverVersion, localMavenDir, overriddenKotlinVersion
)

val buildSrcScript = targetDir.buildSrc?.build
buildSrcScript?.patchKoverDependency(koverVersion)
buildSrcScript?.addLocalRepository(localMavenDir)

return GradleBuildImpl(targetDir, copy, buildName, buildType)
}
}
Expand All @@ -94,6 +103,18 @@ private class GradleBuildImpl(
private var runCount = 0

override fun run(args: List<String>, env: BuildEnv): BuildResult {
val requirements = targetDir.requirements
requirements.minGradle?.let { minVersion ->
if (env.gradleVersion < minVersion) {
throw TestAbortedException("Used Gradle version '${env.gradleVersion}' lower than minimal required '$minVersion'")
}
}
requirements.maxGradle?.let { maxVersion ->
if (env.gradleVersion >= maxVersion) {
throw TestAbortedException("Used Gradle version '${env.gradleVersion}' higher or equals than maximal '$maxVersion'")
}
}

logInfo("Starting build $buildType '$buildName' with commands '${args.joinToString(" ")}'")

val gradleArgs: MutableList<String> = mutableListOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import kotlinx.kover.gradle.plugin.test.functional.framework.common.androidSdkDi
import kotlinx.kover.gradle.plugin.test.functional.framework.common.koverVersionCurrent
import kotlinx.kover.gradle.plugin.test.functional.framework.common.defaultGradleWrapperDir
import kotlinx.kover.gradle.plugin.test.functional.framework.common.examplesDir
import kotlinx.kover.gradle.plugin.test.functional.framework.common.overriddenGradleWrapperVersion
import kotlinx.kover.gradle.plugin.test.functional.framework.common.overriddenGradleVersion
import kotlinx.kover.gradle.plugin.test.functional.framework.common.localRepositoryPath
import kotlinx.kover.gradle.plugin.test.functional.framework.common.overriddenKotlinVersion
import kotlinx.kover.gradle.plugin.test.functional.framework.common.templateBuildsDir
import kotlinx.kover.gradle.plugin.util.SemVer
import org.gradle.kotlin.dsl.provideDelegate
import java.io.File
import java.nio.file.Files

Expand Down Expand Up @@ -57,20 +59,27 @@ internal fun generateBuild(generator: (File) -> Unit): BuildSource {
}


internal fun GradleBuild.runWithParams(args: List<String>): BuildResult {
val wrapperDir =
if (overriddenGradleWrapperVersion == null) defaultGradleWrapperDir else getWrapper(overriddenGradleWrapperVersion)
internal fun GradleBuild.runWithParams(vararg args: String): BuildResult {
return runWithParams(args.toList())
}

val buildEnv = BuildEnv(wrapperDir, androidSdkDir)
internal fun GradleBuild.runWithParams(args: List<String>): BuildResult {
val buildEnv = BuildEnv(gradleVersion, wrapperDir, androidSdkDir)

return run(args, buildEnv)
}
internal fun GradleBuild.runWithParams(vararg args: String): BuildResult {
return runWithParams(args.toList())

private val gradleVersion: SemVer by lazy {
val version = overriddenGradleVersion ?: defaultGradleVersion
SemVer.ofVariableOrNull(version) ?: throw IllegalArgumentException("Can not parse Gradle version '$version'")
}

private fun getWrapper(version: String): File {
val wrapperDir = gradleWrappersRoot.resolve(version)
if (!wrapperDir.exists()) throw Exception("Wrapper for Gradle version '$version' is not supported by functional tests")

private val wrapperDir =
if (overriddenGradleVersion == null) defaultGradleWrapperDir else getWrapper(overriddenGradleVersion)

private fun getWrapper(gradleVersion: String): File {
val wrapperDir = gradleWrappersRoot.resolve(gradleVersion)
if (!wrapperDir.exists()) throw Exception("Wrapper for Gradle version '$gradleVersion' is not supported by functional tests")
return wrapperDir
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlinx.kover.gradle.plugin.test.functional.framework.runner.BuildSource
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.GradleBuild
import kotlinx.kover.gradle.plugin.test.functional.framework.runner.runWithParams
import kotlinx.kover.gradle.plugin.test.functional.framework.writer.*
import kotlinx.kover.gradle.plugin.util.SemVer
import org.junit.jupiter.api.extension.*
import java.io.*
import java.lang.reflect.AnnotatedElement
Expand Down Expand Up @@ -73,6 +74,40 @@ internal abstract class DirectoryBasedGradleTest : BeforeTestExecutionCallback,
}
}

internal val File.requirements: BuildRequirements
get() {
val file = resolve("requires")
if (!(file.exists() && file.isFile)) return BuildRequirements()

var minGradle: SemVer? = null
var maxGradle: SemVer? = null

file.readLines().forEach { line ->
when {
line.startsWith("MIN_GRADLE=") -> minGradle =
SemVer.ofVariableOrNull(line.substringAfter("MIN_GRADLE="))
line.startsWith("MAX_GRADLE=") -> maxGradle =
SemVer.ofVariableOrNull(line.substringAfter("MAX_GRADLE="))

}
}

return BuildRequirements(minGradle, maxGradle)
}

internal data class BuildRequirements(val minGradle: SemVer? = null, val maxGradle: SemVer? = null)

internal val File.buildSrc: File?
get() = listFiles()?.firstOrNull { it.isDirectory && it.name == "buildSrc" }

internal val File.settings: File
get() = listFiles()?.firstOrNull { it.name == "settings.gradle" || it.name == "settings.gradle.kts" }
?: throw Exception("No Gradle settings file in project ${this.uri}")
internal val File.build: File
get() = listFiles()?.firstOrNull { it.name == "build.gradle" || it.name == "build.gradle.kts" }
?: throw Exception("No Gradle build file in project ${this.uri}")



/**
* Override Kover version and add local repository to find artifact for current build.
Expand All @@ -84,13 +119,11 @@ internal fun File.patchSettingsFile(
localRepositoryPath: String,
overrideKotlinVersion: String?
) {
val settingsFile = (listFiles()?.firstOrNull { it.name == "settings.gradle" || it.name == "settings.gradle.kts" }
?: throw Exception("No Gradle settings file in project ${this.uri}"))
val language = if (settingsFile.name.endsWith(".kts")) ScriptLanguage.KOTLIN else ScriptLanguage.GROOVY
val language = if (name.endsWith(".kts")) ScriptLanguage.KOTLIN else ScriptLanguage.GROOVY

val originLines = settingsFile.readLines()
val originLines = readLines()

settingsFile.bufferedWriter().use { writer ->
bufferedWriter().use { writer ->
var firstStatement = true
originLines.forEach { line ->

Expand Down Expand Up @@ -132,4 +165,33 @@ internal fun File.patchSettingsFile(
}
}

/**
* Override Kover version
*/
internal fun File.patchKoverDependency(koverVersion: String) {
val originLines = readLines()
bufferedWriter().use { writer ->
originLines.forEach { line ->
val lineToWrite = if (line.contains("org.jetbrains.kotlinx:kover-gradle-plugin:TEST")) {
line.replace("org.jetbrains.kotlinx:kover-gradle-plugin:TEST", "org.jetbrains.kotlinx:kover-gradle-plugin:$koverVersion")
} else {
line
}
writer.appendLine(lineToWrite)
}
}
}

/**
* Override Kover version
*/
internal fun File.addLocalRepository(path: String) {
this.appendText("""
repositories {
maven("$path")
}
""".trimIndent())
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
kotlin("jvm") version "1.4.20"
}

apply(plugin = "org.jetbrains.kotlinx.kover")

repositories {
mavenCentral()
}

dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.8.0")
}

tasks.test {
useJUnitPlatform()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
kotlin("jvm") version "1.4.20"
}

repositories {
mavenCentral()
}

dependencies {
implementation("org.jetbrains.kotlinx:kover-gradle-plugin:TEST")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Class {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MAX_GRADLE=8.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "buildsrc-usage"

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class ExampleClass {
fun example() {
println("example")
}

fun unused() {
println("unused")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import org.junit.jupiter.api.Test

class TestClass {
@Test
fun testBranches() {
ExampleClass().example()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import org.gradle.language.base.plugins.LifecycleBasePlugin

internal sealed class ReportsVariantApplier(
internal abstract class ReportsVariantApplier(
private val project: Project,
private val variantName: String,
private val type: ReportsVariantType,
Expand All @@ -58,14 +58,16 @@ internal sealed class ReportsVariantApplier(

init {
artifactGenTask = project.tasks.register<KoverArtifactGenerationTask>(artifactGenerationTaskName(variantName))

val artifactProperty = project.layout.buildDirectory.file(artifactFilePath(variantName))
artifactGenTask.configure {
artifactFile.set(project.layout.buildDirectory.file(artifactFilePath(variantName)))
artifactFile.set(artifactProperty)
}

config = project.configurations.register(artifactConfigurationName(variantName)) {
// disable generation of Kover artifacts on `assemble`, fix of https://github.com/Kotlin/kotlinx-kover/issues/353
isVisible = false
outgoing.artifact(artifactGenTask.map { task -> task.artifactFile }) {
outgoing.artifact(artifactProperty) {
asProducer()
attributes {
// common Kover artifact attributes
Expand Down

0 comments on commit 3e2aeca

Please sign in to comment.