Skip to content

Commit a759cec

Browse files
committed
Support for Kotlin Coroutines debugging
1 parent e3c2dfa commit a759cec

File tree

5 files changed

+63
-11
lines changed

5 files changed

+63
-11
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Added
66
- Configure all tasks that extend task classes instead of just those created by the plugin
77
- Make JbrResolver prefer Gradle javaToolchains by `JetBrains s.r.o`, if available. Only otherwise start fetching and running a new one.
8+
- Support for Kotlin Coroutines debugging
89

910
### Changed
1011
- Disabled caching for `BuildPluginTask`

src/main/kotlin/org/jetbrains/intellij/IntelliJPlugin.kt

+18-5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ import java.time.LocalDate
129129
import java.time.LocalDateTime
130130
import java.time.format.DateTimeFormatter
131131
import java.util.*
132+
import kotlin.io.path.exists
132133

133134
abstract class IntelliJPlugin : Plugin<Project> {
134135

@@ -907,6 +908,7 @@ abstract class IntelliJPlugin : Plugin<Project> {
907908
) {
908909
val taskContext = logCategory()
909910
val prepareSandboxTaskProvider = project.tasks.named<PrepareSandboxTask>(prepareSandBoxTaskName)
911+
val initializeIntelliJPluginTaskProvider = project.tasks.named<InitializeIntelliJPluginTask>(INITIALIZE_INTELLIJ_PLUGIN_TASK_NAME)
910912
val pluginIds = sourcePluginXmlFiles(project).mapNotNull { parsePluginXml(it, taskContext)?.id }
911913

912914
ideDir.convention(ideaDependencyProvider.map {
@@ -939,6 +941,9 @@ abstract class IntelliJPlugin : Plugin<Project> {
939941
ideDir = ideDir.orNull,
940942
).toString()
941943
})
944+
coroutinesJavaAgentPath.convention(initializeIntelliJPluginTaskProvider.flatMap {
945+
it.coroutinesJavaAgentPath
946+
})
942947
}
943948

944949
private fun configureJarSearchableOptionsTask(project: Project) {
@@ -1177,6 +1182,10 @@ abstract class IntelliJPlugin : Plugin<Project> {
11771182
val instrumentedTestCodeOutputsProvider = project.provider {
11781183
project.files(instrumentedTestCodeTaskProvider.map { it.outputDir.asFile })
11791184
}
1185+
val initializeIntellijPluginTaskProvider = project.tasks.named<InitializeIntelliJPluginTask>(INITIALIZE_INTELLIJ_PLUGIN_TASK_NAME)
1186+
val coroutinesJavaAgentPathProvider = initializeIntellijPluginTaskProvider.flatMap {
1187+
it.coroutinesJavaAgentPath
1188+
}
11801189

11811190
val testTasks = project.tasks.withType<Test>()
11821191
val pluginIds = sourcePluginXmlFiles(project).mapNotNull { parsePluginXml(it, context)?.id }
@@ -1263,7 +1272,7 @@ abstract class IntelliJPlugin : Plugin<Project> {
12631272

12641273
classpath = instrumentedCodeOutputsProvider.get() + instrumentedTestCodeOutputsProvider.get() + classpath
12651274
testClassesDirs = instrumentedTestCodeOutputsProvider.get() + testClassesDirs
1266-
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDirProvider.get(), this))
1275+
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDirProvider.get(), coroutinesJavaAgentPathProvider.get(), this))
12671276

12681277
doFirst {
12691278
classpath += ideaDependencyLibrariesProvider.get() +
@@ -1413,7 +1422,7 @@ abstract class IntelliJPlugin : Plugin<Project> {
14131422
)
14141423

14151424
onlyIf {
1416-
// Workaround for Gradle 7.x to don't fail on "An input file was expected to be present but it doesn't exist."
1425+
// Workaround for Gradle 7.x to don't fail on "An input file was expected to be present, but it doesn't exist."
14171426
inputArchiveFile.isSpecified
14181427
}
14191428
dependsOn(SIGN_PLUGIN_TASK_NAME)
@@ -1544,6 +1553,7 @@ abstract class IntelliJPlugin : Plugin<Project> {
15441553
}
15451554
}
15461555

1556+
@Suppress("UnstableApiUsage")
15471557
private fun configureProcessResources(project: Project) {
15481558
info(context, "Configuring resources task")
15491559
val patchPluginXmlTaskProvider = project.tasks.named<PatchPluginXmlTask>(PATCH_PLUGIN_XML_TASK_NAME)
@@ -1565,12 +1575,15 @@ abstract class IntelliJPlugin : Plugin<Project> {
15651575
selfUpdateCheck.convention(project.provider {
15661576
project.isBuildFeatureEnabled(SELF_UPDATE_CHECK)
15671577
})
1568-
lockFile.convention(project.provider {
1569-
temporaryDir.resolve(LocalDate.now().toString())
1578+
selfUpdateLockPath.convention(project.provider {
1579+
temporaryDir.toPath().resolve(LocalDate.now().toString())
1580+
})
1581+
coroutinesJavaAgentPath.convention(project.provider {
1582+
temporaryDir.toPath().resolve("coroutines-javaagent.jar")
15701583
})
15711584

15721585
onlyIf {
1573-
!lockFile.get().exists()
1586+
!selfUpdateLockPath.get().exists() || !coroutinesJavaAgentPath.get().exists()
15741587
}
15751588
}
15761589
}

src/main/kotlin/org/jetbrains/intellij/propertyProviders/IntelliJPlatformArgumentProvider.kt

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import java.nio.file.Path
1616

1717
class IntelliJPlatformArgumentProvider(
1818
@InputDirectory @PathSensitive(RELATIVE) val ideDirectory: Path,
19+
@InputDirectory @PathSensitive(RELATIVE) val coroutinesJavaAgentPath: Path,
1920
private val options: JavaForkOptions,
2021
) : CommandLineArgumentProvider {
2122

@@ -36,6 +37,8 @@ class IntelliJPlatformArgumentProvider(
3637
?.removePrefix("../")
3738
?.let { ideDirectory.resolve(it).readLines() }
3839
.orEmpty()
40+
.filter { !it.contains("kotlinx.coroutines.debug=off") }
41+
.let { it + "-javaagent:${coroutinesJavaAgentPath}" }
3942

4043
private val additionalJvmArguments
4144
get() = productInfo

src/main/kotlin/org/jetbrains/intellij/tasks/InitializeIntelliJPluginTask.kt

+34-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package org.jetbrains.intellij.tasks
44

55
import com.jetbrains.plugin.structure.base.utils.create
6+
import com.jetbrains.plugin.structure.base.utils.outputStream
67
import org.gradle.api.DefaultTask
78
import org.gradle.api.provider.Property
89
import org.gradle.api.tasks.Internal
@@ -14,7 +15,10 @@ import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_GROUP_NAME
1415
import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_ID
1516
import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_NAME
1617
import org.jetbrains.intellij.utils.LatestVersionResolver
17-
import java.io.File
18+
import java.nio.file.Path
19+
import java.util.jar.JarOutputStream
20+
import java.util.jar.Manifest
21+
import kotlin.io.path.exists
1822

1923
/**
2024
* Initializes the Gradle IntelliJ Plugin and performs various checks, like if the plugin is up to date.
@@ -29,7 +33,10 @@ abstract class InitializeIntelliJPluginTask : DefaultTask() {
2933
abstract val selfUpdateCheck: Property<Boolean>
3034

3135
@get:Internal
32-
abstract val lockFile: Property<File>
36+
abstract val selfUpdateLockPath: Property<Path>
37+
38+
@get:Internal
39+
abstract val coroutinesJavaAgentPath: Property<Path>
3340

3441
init {
3542
group = PLUGIN_GROUP_NAME
@@ -41,13 +48,14 @@ abstract class InitializeIntelliJPluginTask : DefaultTask() {
4148
@TaskAction
4249
fun initialize() {
4350
checkPluginVersion()
51+
createCoroutinesJavaAgentFile()
4452
}
4553

4654
/**
47-
* Checks if the plugin is up to date.
55+
* Checks if the plugin is up-to-date.
4856
*/
4957
private fun checkPluginVersion() {
50-
if (!selfUpdateCheck.get() || offline.get()) {
58+
if (!selfUpdateCheck.get() || selfUpdateLockPath.get().exists() || offline.get()) {
5159
return
5260
}
5361

@@ -60,9 +68,30 @@ abstract class InitializeIntelliJPluginTask : DefaultTask() {
6068
warn(context, "$PLUGIN_NAME is outdated: $version. Update `$PLUGIN_ID` to: $latestVersion")
6169
}
6270

63-
lockFile.get().toPath().create()
71+
selfUpdateLockPath.get().create()
6472
} catch (e: Exception) {
6573
error(context, e.message.orEmpty(), e)
6674
}
6775
}
76+
77+
/**
78+
* Creates a Java Agent file for the Coroutines library required to enable coroutines debugging.
79+
*/
80+
private fun createCoroutinesJavaAgentFile() {
81+
if (coroutinesJavaAgentPath.get().exists()) {
82+
return
83+
}
84+
85+
val manifest = Manifest(
86+
"""
87+
Manifest-Version: 1.0
88+
Premain-Class: kotlinx.coroutines.debug.AgentPremain
89+
Can-Retransform-Classes: true
90+
Multi-Release: true
91+
92+
""".trimIndent().byteInputStream()
93+
)
94+
95+
JarOutputStream(coroutinesJavaAgentPath.get().outputStream(), manifest).close()
96+
}
6897
}

src/main/kotlin/org/jetbrains/intellij/tasks/RunIdeBase.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ abstract class RunIdeBase : JavaExec() {
148148
@get:Internal
149149
abstract val projectExecutable: Property<String>
150150

151+
/**
152+
* Represents the path to the coroutines Java agent file.
153+
*/
154+
@get:Internal
155+
abstract val coroutinesJavaAgentPath: Property<Path>
156+
151157
private val ideDirPath by lazy {
152158
ideDir.get().toPath()
153159
}
@@ -182,7 +188,7 @@ abstract class RunIdeBase : JavaExec() {
182188
@TaskAction
183189
override fun exec() {
184190
workingDir = projectWorkingDir.get()
185-
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDir.get().toPath(), this))
191+
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDir.get().toPath(), coroutinesJavaAgentPath.get(), this))
186192
configureSystemProperties()
187193
configureClasspath()
188194

0 commit comments

Comments
 (0)