Skip to content

Commit

Permalink
Support for Kotlin Coroutines debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
hsz committed Sep 6, 2023
1 parent e3c2dfa commit a759cec
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added
- Configure all tasks that extend task classes instead of just those created by the plugin
- Make JbrResolver prefer Gradle javaToolchains by `JetBrains s.r.o`, if available. Only otherwise start fetching and running a new one.
- Support for Kotlin Coroutines debugging

### Changed
- Disabled caching for `BuildPluginTask`
Expand Down
23 changes: 18 additions & 5 deletions src/main/kotlin/org/jetbrains/intellij/IntelliJPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.io.path.exists

abstract class IntelliJPlugin : Plugin<Project> {

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

ideDir.convention(ideaDependencyProvider.map {
Expand Down Expand Up @@ -939,6 +941,9 @@ abstract class IntelliJPlugin : Plugin<Project> {
ideDir = ideDir.orNull,
).toString()
})
coroutinesJavaAgentPath.convention(initializeIntelliJPluginTaskProvider.flatMap {
it.coroutinesJavaAgentPath
})
}

private fun configureJarSearchableOptionsTask(project: Project) {
Expand Down Expand Up @@ -1177,6 +1182,10 @@ abstract class IntelliJPlugin : Plugin<Project> {
val instrumentedTestCodeOutputsProvider = project.provider {
project.files(instrumentedTestCodeTaskProvider.map { it.outputDir.asFile })
}
val initializeIntellijPluginTaskProvider = project.tasks.named<InitializeIntelliJPluginTask>(INITIALIZE_INTELLIJ_PLUGIN_TASK_NAME)
val coroutinesJavaAgentPathProvider = initializeIntellijPluginTaskProvider.flatMap {
it.coroutinesJavaAgentPath
}

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

classpath = instrumentedCodeOutputsProvider.get() + instrumentedTestCodeOutputsProvider.get() + classpath
testClassesDirs = instrumentedTestCodeOutputsProvider.get() + testClassesDirs
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDirProvider.get(), this))
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDirProvider.get(), coroutinesJavaAgentPathProvider.get(), this))

doFirst {
classpath += ideaDependencyLibrariesProvider.get() +
Expand Down Expand Up @@ -1413,7 +1422,7 @@ abstract class IntelliJPlugin : Plugin<Project> {
)

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

@Suppress("UnstableApiUsage")
private fun configureProcessResources(project: Project) {
info(context, "Configuring resources task")
val patchPluginXmlTaskProvider = project.tasks.named<PatchPluginXmlTask>(PATCH_PLUGIN_XML_TASK_NAME)
Expand All @@ -1565,12 +1575,15 @@ abstract class IntelliJPlugin : Plugin<Project> {
selfUpdateCheck.convention(project.provider {
project.isBuildFeatureEnabled(SELF_UPDATE_CHECK)
})
lockFile.convention(project.provider {
temporaryDir.resolve(LocalDate.now().toString())
selfUpdateLockPath.convention(project.provider {
temporaryDir.toPath().resolve(LocalDate.now().toString())
})
coroutinesJavaAgentPath.convention(project.provider {
temporaryDir.toPath().resolve("coroutines-javaagent.jar")
})

onlyIf {
!lockFile.get().exists()
!selfUpdateLockPath.get().exists() || !coroutinesJavaAgentPath.get().exists()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import java.nio.file.Path

class IntelliJPlatformArgumentProvider(
@InputDirectory @PathSensitive(RELATIVE) val ideDirectory: Path,
@InputDirectory @PathSensitive(RELATIVE) val coroutinesJavaAgentPath: Path,
private val options: JavaForkOptions,
) : CommandLineArgumentProvider {

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

private val additionalJvmArguments
get() = productInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package org.jetbrains.intellij.tasks

import com.jetbrains.plugin.structure.base.utils.create
import com.jetbrains.plugin.structure.base.utils.outputStream
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
Expand All @@ -14,7 +15,10 @@ import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_GROUP_NAME
import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_ID
import org.jetbrains.intellij.IntelliJPluginConstants.PLUGIN_NAME
import org.jetbrains.intellij.utils.LatestVersionResolver
import java.io.File
import java.nio.file.Path
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import kotlin.io.path.exists

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

@get:Internal
abstract val lockFile: Property<File>
abstract val selfUpdateLockPath: Property<Path>

@get:Internal
abstract val coroutinesJavaAgentPath: Property<Path>

init {
group = PLUGIN_GROUP_NAME
Expand All @@ -41,13 +48,14 @@ abstract class InitializeIntelliJPluginTask : DefaultTask() {
@TaskAction
fun initialize() {
checkPluginVersion()
createCoroutinesJavaAgentFile()
}

/**
* Checks if the plugin is up to date.
* Checks if the plugin is up-to-date.
*/
private fun checkPluginVersion() {
if (!selfUpdateCheck.get() || offline.get()) {
if (!selfUpdateCheck.get() || selfUpdateLockPath.get().exists() || offline.get()) {
return
}

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

lockFile.get().toPath().create()
selfUpdateLockPath.get().create()
} catch (e: Exception) {
error(context, e.message.orEmpty(), e)
}
}

/**
* Creates a Java Agent file for the Coroutines library required to enable coroutines debugging.
*/
private fun createCoroutinesJavaAgentFile() {
if (coroutinesJavaAgentPath.get().exists()) {
return
}

val manifest = Manifest(
"""
Manifest-Version: 1.0
Premain-Class: kotlinx.coroutines.debug.AgentPremain
Can-Retransform-Classes: true
Multi-Release: true
""".trimIndent().byteInputStream()
)

JarOutputStream(coroutinesJavaAgentPath.get().outputStream(), manifest).close()
}
}
8 changes: 7 additions & 1 deletion src/main/kotlin/org/jetbrains/intellij/tasks/RunIdeBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ abstract class RunIdeBase : JavaExec() {
@get:Internal
abstract val projectExecutable: Property<String>

/**
* Represents the path to the coroutines Java agent file.
*/
@get:Internal
abstract val coroutinesJavaAgentPath: Property<Path>

private val ideDirPath by lazy {
ideDir.get().toPath()
}
Expand Down Expand Up @@ -182,7 +188,7 @@ abstract class RunIdeBase : JavaExec() {
@TaskAction
override fun exec() {
workingDir = projectWorkingDir.get()
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDir.get().toPath(), this))
jvmArgumentProviders.add(IntelliJPlatformArgumentProvider(ideDir.get().toPath(), coroutinesJavaAgentPath.get(), this))
configureSystemProperties()
configureClasspath()

Expand Down

0 comments on commit a759cec

Please sign in to comment.