Skip to content

Commit

Permalink
Allow custom jacoco version (#16)
Browse files Browse the repository at this point in the history
The version of the jacoco used to pre-instrument classes must match the
version of jacoco used at runtime.

Instead of having the coverage plugin depend on the jacoco runtime, we
inject the jacoco dependency (with the right version) when we publish a
plugin under test into the integration repository
  • Loading branch information
ogolberg authored Nov 1, 2024
1 parent 04afa16 commit bd90132
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 30 deletions.
6 changes: 0 additions & 6 deletions coverage-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ gradlePlugin {

dependencies {
implementation(gradleApi())
implementation(libs.jacoco.agent) {
artifact {
classifier = "runtime"
extension = "jar"
}
}
implementation(projects.jacocoReflect)

testImplementation(projects.junit5)
Expand Down
10 changes: 10 additions & 0 deletions integration-tests/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.gradle.publish.PublishPlugin
import com.gradle.publish.PublishTask
import com.toasttab.gradle.testkit.shared.RepositoryDescriptor
import com.toasttab.gradle.testkit.shared.configureIntegrationPublishing
import com.toasttab.gradle.testkit.shared.publishOnlyIf
Expand All @@ -22,6 +24,10 @@ gradlePlugin {
}
}

jacoco {
toolVersion = "0.8.12"
}

tasks {
test {
systemProperty("version", "$version")
Expand All @@ -32,6 +38,10 @@ tasks {
configureIntegrationPublishing("testRuntimeClasspath")
publishOnlyIf { _, repo -> repo == RepositoryDescriptor.INTEGRATION }

tasks.withType<PublishTask> {
enabled = false
}

dependencies {
implementation(gradleApi())
testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,12 @@

package com.toasttab.gradle.testkit.shared

import org.gradle.api.Project
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ArtifactResult
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.plugins.JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME

private val ARTIFACT_TYPE_ATTRIBUTE = Attribute.of("artifactType", String::class.java)

fun Project.runtimeArtifacts() = configurations.getAt(RUNTIME_CLASSPATH_CONFIGURATION_NAME).incoming.artifactView {
fun Configuration.artifacts() = incoming.artifactView {
lenient(true)
attributes.attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar")
}.artifacts

fun ArtifactResult.isProject() = id.componentIdentifier is ProjectComponentIdentifier

fun ArtifactResult.isExternalPluginDependency(): Boolean {
val identifier = id.componentIdentifier

return identifier is ModuleComponentIdentifier && identifier.group != "org.jetbrains.kotlin"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
/*
* Copyright (c) 2024 Toast Inc.
*
* 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 com.toasttab.gradle.testkit.shared

import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Attribute
import org.gradle.api.publish.PublishingExtension
Expand All @@ -16,7 +30,6 @@ import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType
import org.gradle.plugin.devel.GradlePluginDevelopmentExtension
import org.gradle.testing.jacoco.plugins.JacocoPlugin

sealed interface RepositoryDescriptor {
object MavenLocal : RepositoryDescriptor
Expand Down Expand Up @@ -59,33 +72,33 @@ fun Project.configureIntegrationPublishing(
val repo = integrationRepo

afterEvaluate {
val jacocoAnt = project.configurations.findByName(JacocoPlugin.ANT_CONFIGURATION_NAME)
val coverage = project.coverage()

configurations.getAt(configuration).incoming.artifactView {
lenient(true)
attributes.attribute(Attribute.of("artifactType", String::class.java), "jar")
}.artifacts.map {
it.id.componentIdentifier
}.filterIsInstance<ProjectComponentIdentifier>().forEach {
configureIntegrationPublishingForDependency(project(":${it.projectPath}"), repo, jacocoAnt)
configureIntegrationPublishingForDependency(project(":${it.projectPath}"), repo, coverage)
}

configureIntegrationPublishingForDependency(this, repo, jacocoAnt)
configureIntegrationPublishingForDependency(this, repo, coverage)
}

tasks.named("test") {
dependsOn("publishIntegrationPublicationToIntegrationRepository")
}
}

private fun Project.configureIntegrationPublishingForDependency(project: Project, repo: Any, jacocoAnt: Configuration?) {
private fun Project.configureIntegrationPublishingForDependency(project: Project, repo: Any, coverage: CoverageConfiguration) {
project.pluginManager.apply("maven-publish")

if (jacocoAnt != null) {
if (coverage is CoverageConfiguration.Jacoco) {
project.tasks.register<InstrumentWithJacocoOfflineTask>("instrument") {
dependsOn("jar")

classpath = jacocoAnt
classpath = coverage.configuration

jar = project.tasks.named<Jar>("jar").flatMap { it.archiveFile }

Expand All @@ -105,7 +118,16 @@ private fun Project.configureIntegrationPublishingForDependency(project: Project
create<MavenPublication>(PublicationDescriptor.INTEGRATION_PUBLICATION_NAME) {
from(project.components["java"])

if (jacocoAnt != null) {
if (coverage is CoverageConfiguration.Jacoco) {
pom {
injectDependency(
groupId = "org.jacoco",
artifactId = "org.jacoco.agent",
version = coverage.version,
classifier = "runtime"
)
}

artifacts.clear()

artifact(project.layout.buildDirectory.file("instrumented/${project.name}-${project.version}.jar")) {
Expand All @@ -130,7 +152,7 @@ private fun Project.configureIntegrationPublishingForDependency(project: Project
}
}

if (jacocoAnt != null) {
if (coverage is CoverageConfiguration.Jacoco) {
project.tasks.named<GenerateModuleMetadata>("generateMetadataFileForIntegrationPublication") {
enabled = false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2024 Toast Inc.
*
* 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 com.toasttab.gradle.testkit.shared

import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.testing.jacoco.plugins.JacocoPlugin

sealed interface CoverageConfiguration {
object None: CoverageConfiguration

class Jacoco(
val configuration: Configuration
): CoverageConfiguration {
val version by lazy {
configuration.artifacts().map { it.id.componentIdentifier }
.filterIsInstance<ModuleComponentIdentifier>()
.first { it.group == "org.jacoco" && it.module == "org.jacoco.ant" }
.version
}
}
}

fun Project.coverage() = configurations.findByName(JacocoPlugin.ANT_CONFIGURATION_NAME)?.let {
CoverageConfiguration.Jacoco(it)
} ?: CoverageConfiguration.None
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Toast Inc.
*
* 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 com.toasttab.gradle.testkit.shared

import groovy.namespace.QName
import groovy.util.Node
import org.gradle.api.publish.maven.MavenPom

fun MavenPom.injectDependency(groupId: String, artifactId: String, version: String, classifier: String) {
withXml {
asNode().findOrCreateChild("dependencies").appendNode("dependency").apply {
appendNode("groupId", groupId)
appendNode("artifactId", artifactId)
appendNode("version", version)
appendNode("classifier", classifier)
}
}
}

private fun Node.findChild(localName: String) = children().filterIsInstance<Node>().firstOrNull { (it.name() as QName).localPart == localName }
private fun Node.findOrCreateChild(localName: String) = findChild(localName) ?: appendNode(localName)

0 comments on commit bd90132

Please sign in to comment.