Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix debug module publication with shadow plugin #3357

Merged
merged 7 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions gradle/publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ publishing {
// Configure java publications for regular non-MPP modules
publications {
maven(MavenPublication) {
if (project.name == "kotlinx-coroutines-debug") {
project.shadow.component(it)
} else {
from components.java
}
from components.java
artifact sourcesJar
}
}
Expand Down
25 changes: 24 additions & 1 deletion integration-testing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
}

sourceSets {
// Test that relies on Guava to reflectively check all Throwable subclasses in coroutines
withGuavaTest {
kotlin
compileClasspath += sourceSets.test.runtimeClasspath
Expand All @@ -33,6 +34,7 @@ sourceSets {
implementation 'com.google.guava:guava:31.1-jre'
}
}
// Checks correctness of Maven publication (JAR resources) and absence of atomicfu symbols
mavenTest {
kotlin
compileClasspath += sourceSets.test.runtimeClasspath
Expand All @@ -43,6 +45,7 @@ sourceSets {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
}
}
// Checks that kotlinx-coroutines-debug can be used as -javaagent parameter
debugAgentTest {
kotlin
compileClasspath += sourceSets.test.runtimeClasspath
Expand All @@ -53,6 +56,20 @@ sourceSets {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutines_version"
}
}

// Checks that kotlinx-coroutines-debug agent can self-attach dynamically to JVM as standalone dependency
debugDynamicAgentTest {
kotlin
compileClasspath += sourceSets.test.runtimeClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath

dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutines_version"
}
}

// Checks that kotlinx-coroutines-core can be used as -javaagent parameter
coreAgentTest {
kotlin
compileClasspath += sourceSets.test.runtimeClasspath
Expand Down Expand Up @@ -93,6 +110,12 @@ task debugAgentTest(type: Test) {
systemProperties project.properties.subMap(["overwrite.probes"])
}

task debugDynamicAgentTest(type: Test) {
def sourceSet = sourceSets.debugDynamicAgentTest
testClassesDirs = sourceSet.output.classesDirs
classpath = sourceSet.runtimeClasspath
}

task coreAgentTest(type: Test) {
def sourceSet = sourceSets.coreAgentTest
def coroutinesCoreJar = sourceSet.runtimeClasspath.filter {it.name == "kotlinx-coroutines-core-jvm-${coroutines_version}.jar" }.singleFile
Expand All @@ -106,5 +129,5 @@ compileTestKotlin {
}

check {
dependsOn([withGuavaTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build'])
dependsOn([withGuavaTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build'])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
qwwdfsad marked this conversation as resolved.
Show resolved Hide resolved
*/
import org.junit.*
import kotlinx.coroutines.*
import kotlinx.coroutines.debug.*
import org.junit.Test
import java.io.*
import java.lang.IllegalStateException

class DynamicAttachDebugTest {

@Test
fun testAgentDumpsCoroutines() =
DebugProbes.withDebugProbes {
runBlocking {
val baos = ByteArrayOutputStream()
DebugProbes.dumpCoroutines(PrintStream(baos))
// if the agent works, then dumps should contain something,
// at least the fact that this test is running.
Assert.assertTrue(baos.toString().contains("testAgentDumpsCoroutines"))
}
}

@Test(expected = IllegalStateException::class)
fun testAgentIsNotInstalled() {
DebugProbes.dumpCoroutines(PrintStream(ByteArrayOutputStream()))
}
}
37 changes: 25 additions & 12 deletions kotlinx-coroutines-debug/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ configurations {
shadowDeps // shaded dependencies, not included into the resulting .pom file
compileOnly.extendsFrom(shadowDeps)
runtimeOnly.extendsFrom(shadowDeps)

/*
* It is possible to extend a particular configuration with shadow,
* but in that case it changes dependency type to "runtime" and resolves it
* (so it cannot be further modified). Otherwise, shadow just ignores all dependencies.
*/
shadow.extendsFrom(api) // shadow - resulting configuration with shaded jar file
configureKotlinJvmPlatform(shadow)
}

dependencies {
Expand All @@ -39,17 +31,38 @@ java {
}

jar {
manifest {
attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain"
attributes "Can-Redefine-Classes": "true"
setEnabled(false)
}

// This is a rough estimation of what shadow plugin has been doing with our default configuration prior to
// 1.6.2: https://github.com/johnrengelman/shadow/blob/1ff12fc816629ae5bc331fa3889c8ecfcaee7b27/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy#L72-L82
// We just emulate it here for backwards compatibility
shadowJar.configure {
def classpath = project.objects.fileCollection().from { ->
project.configurations.findByName('runtimeClasspath')
}
doFirst {
manifest.attributes 'Class-Path': classpath.collect { "${it.name}" }.findAll { it }.join(' ')
}
}

shadowJar {
def shadowJarTask = shadowJar {
classifier null
// Shadow only byte buddy, do not package kotlin stdlib
configurations = [project.configurations.shadowDeps]
relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy')

manifest {
attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain"
attributes "Can-Redefine-Classes": "true"
}
}

configurations {
artifacts {
add("apiElements", shadowJarTask)
add("runtimeElements", shadowJarTask)
}
}

def commonKoverExcludes =
Expand Down