Skip to content

Commit

Permalink
Conditional Go Delve patch (#288)
Browse files Browse the repository at this point in the history
* Checking DLV version in Goland config extension

* Changelog entry

* Style + docs

* Lint

* Throwing exception when bundled delve is not found.
  • Loading branch information
Razz4780 authored Oct 9, 2024
1 parent b920a3b commit 543b409
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 23 deletions.
1 change: 1 addition & 0 deletions changelog.d/284.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Go Delve is now patched only if its version is older than the one bundled with our plugin.
1 change: 1 addition & 0 deletions modules/products/goland/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ intellij {

dependencies {
implementation(project(":mirrord-core"))
implementation("com.github.zafarkhaja:java-semver:0.9.0")
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.metalbear.mirrord.products.goland

import com.github.zafarkhaja.semver.Version
import com.goide.execution.GoRunConfigurationBase
import com.goide.execution.GoRunningState
import com.goide.execution.extension.GoRunConfigurationExtension
Expand All @@ -10,6 +11,8 @@ import com.intellij.execution.target.TargetedCommandLineBuilder
import com.intellij.execution.wsl.target.WslTargetEnvironmentRequest
import com.intellij.openapi.components.service
import com.intellij.openapi.util.SystemInfo
import com.metalbear.mirrord.MirrordError
import com.metalbear.mirrord.MirrordLogger
import com.metalbear.mirrord.MirrordPathManager
import com.metalbear.mirrord.MirrordProjectService
import java.nio.file.Paths
Expand Down Expand Up @@ -65,6 +68,10 @@ class GolandRunConfigurationExtension : GoRunConfigurationExtension() {
super.patchCommandLine(configuration, runnerSettings, cmdLine, runnerId, state, commandLineType)
}

/**
* Patch delve used in the commandline, if necessary
* (debugging on Mac and commandline uses delve version older than ours).
*/
override fun patchExecutor(
configuration: GoRunConfigurationBase<*>,
runnerSettings: RunnerSettings?,
Expand All @@ -75,35 +82,92 @@ class GolandRunConfigurationExtension : GoRunConfigurationExtension() {
) {
val service = configuration.getProject().service<MirrordProjectService>()

if (commandLineType == GoRunningState.CommandLineType.RUN &&
service.enabled &&
SystemInfo.isMac &&
state.isDebug
) {
val delvePath = getCustomDelvePath()
// convert the delve file to an executable
val delveExecutable = Paths.get(delvePath).toFile()
if (delveExecutable.exists()) {
if (!delveExecutable.canExecute()) {
delveExecutable.setExecutable(true)
}
// parameters returns a copy, and we can't modify it so need to reset it.
val patchedParameters = executor.parameters
for (i in 0 until patchedParameters.size) {
// no way to reset the whole commandline, so we just remove each entry.
executor.withoutParameter(0)
val value = patchedParameters[i]
if (value.toPresentableString().endsWith("/dlv", true)) {
patchedParameters[i] = PathParameter(delveExecutable.toString())
}
}
executor.withParameters(*patchedParameters.toTypedArray())
if (commandLineType != GoRunningState.CommandLineType.RUN || !service.enabled || !SystemInfo.isMac || !state.isDebug) {
super.patchExecutor(configuration, runnerSettings, executor, runnerId, state, commandLineType)
return
}

val ourDelvePath = getCustomDelvePath()
val delveExecutable = Paths.get(ourDelvePath).toFile()
if (!delveExecutable.exists()) {
val error = MirrordError(
"Failed to find delve bundled with mirrord plugin ($ourDelvePath",
"This is a bug, please contact us."
)
error.showHelp(configuration.getProject())
throw error
}

if (!delveExecutable.canExecute()) {
delveExecutable.setExecutable(true)
}

val ourDelveVersion = try {
getDelveVersion(ourDelvePath)
} catch (e: Throwable) {
MirrordLogger.logger.error("Failed to get version delve bundled with mirrord plugin ($ourDelvePath)", e)
super.patchExecutor(configuration, runnerSettings, executor, runnerId, state, commandLineType)
return
}

// parameters returns a copy, and we can't modify it so need to reset it.
val patchedParameters = executor.parameters
for (i in 0 until patchedParameters.size) {
// no way to reset the whole commandline, so we just remove each entry.
executor.withoutParameter(0)

val foundDelvePath = patchedParameters[i].toPresentableString()

if (!foundDelvePath.endsWith("/dlv", true)) {
continue
}

val delveVersion = try {
getDelveVersion(foundDelvePath)
} catch (e: Throwable) {
MirrordLogger
.logger
.error("Failed to get version of delve found in executor parameters ($foundDelvePath)", e)
continue
}

if (delveVersion.lessThan(ourDelveVersion)) {
patchedParameters[i] = PathParameter(delveExecutable.toString())
}
}

executor.withParameters(*patchedParameters.toTypedArray())

super.patchExecutor(configuration, runnerSettings, executor, runnerId, state, commandLineType)
}

/**
* Return path to custom delve bundled with mirrord plugin.
*/
private fun getCustomDelvePath(): String {
return MirrordPathManager.getBinary("dlv", false)!!
}

/**
* Get version of the given delve.
*/
private fun getDelveVersion(path: String): Version {
val process = ProcessBuilder(path, "version")
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.DISCARD)
.start()

val exitCode = process.waitFor()
if (exitCode != 0) {
throw Exception("dlv version command exited with code $exitCode")
}

val versionLine = process
.inputStream
.bufferedReader()
.readLines()
.find { it.startsWith("Version: ") } ?: throw Exception("no output line starts with 'Version: '")

return Version.valueOf(versionLine.removePrefix("Version: "))
}
}

0 comments on commit 543b409

Please sign in to comment.