Skip to content

Commit

Permalink
Fix debug module publication with shadow plugin (Kotlin#3357)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwwdfsad authored and pablobaxter committed Sep 14, 2022
1 parent 836a4bb commit b707336
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 18 deletions.
6 changes: 1 addition & 5 deletions gradle/publish.gradle
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
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'])
}
@@ -0,0 +1,29 @@
/*
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
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
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

0 comments on commit b707336

Please sign in to comment.