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

Add support for Bazel #292

Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
implementation(project(":mirrord-products-nodejs"))
implementation(project(":mirrord-products-rider"))
implementation(project(":mirrord-products-tomcat"))
implementation(project(":mirrord-products-bazel"))
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:ide-launcher:0.11.19.414")
Expand Down
1 change: 1 addition & 0 deletions changelog.d/207.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for Bazel run configurations.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pluginName = mirrord
# SemVer format -> https://semver.org
pluginVersion = 3.62.1

platformVersion = 2022.3.2
platformVersion = 2024.1
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.SystemInfo
import com.intellij.util.alsoIfNull

/**
* Functions to be called when one of our entry points to the program is called - when process is
Expand Down Expand Up @@ -169,16 +168,17 @@ class MirrordExecManager(private val service: MirrordProjectService) {
MirrordLogger.logger.debug("target not selected, showing dialog")

chooseTarget(cli, wslDistribution, configPath, mirrordApi)
.takeUnless { it == MirrordExecDialog.targetlessTargetName }
.alsoIfNull {
MirrordLogger.logger.info("No target specified - running targetless")
service.notifier.notification(
"No target specified, mirrord running targetless.",
NotificationType.INFORMATION
)
.withDontShowAgain(MirrordSettingsState.NotificationId.RUNNING_TARGETLESS)
.fire()
}
.takeUnless { it == MirrordExecDialog.targetlessTargetName } ?: run {
t4lz marked this conversation as resolved.
Show resolved Hide resolved
MirrordLogger.logger.info("No target specified - running targetless")
service.notifier.notification(
"No target specified, mirrord running targetless.",
NotificationType.INFORMATION
)
.withDontShowAgain(MirrordSettingsState.NotificationId.RUNNING_TARGETLESS)
.fire()

null
}
} else {
null
}
Expand Down
25 changes: 25 additions & 0 deletions modules/products/bazel/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
fun properties(key: String) = project.findProperty(key).toString()

plugins {
// Java support
id("java")
// Kotlin support
id("org.jetbrains.kotlin.jvm") version "1.8.22"
// Gradle IntelliJ Plugin
id("org.jetbrains.intellij") version "1.+"
}

tasks {
buildSearchableOptions {
enabled = false
}
}

intellij {
version.set(properties("platformVersion"))
plugins.set(listOf("com.google.idea.bazel.ijwb:2024.09.24.0.2-api-version-241"))
}

dependencies {
implementation(project(":mirrord-core"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.metalbear.mirrord.products.bazel
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved

import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState
import com.google.idea.blaze.base.scope.BlazeContext
import com.google.idea.blaze.base.settings.Blaze
import com.intellij.execution.ExecutionListener
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.target.createEnvironmentRequest
import com.intellij.execution.wsl.target.WslTargetEnvironmentRequest
import com.intellij.notification.NotificationType
import com.intellij.openapi.components.service
import com.intellij.openapi.util.SystemInfo
import com.metalbear.mirrord.MirrordLogger
import com.metalbear.mirrord.MirrordProjectService
import java.util.concurrent.ConcurrentHashMap

data class SavedConfigData(val envVars: Map<String, String>, val bazelPath: String?)

class BazelExecutionListener : ExecutionListener {
/**
* Preserves original configuration for active Bazel runs (user environment variables and Bazel binary path)
*/
private val savedEnvs: ConcurrentHashMap<String, SavedConfigData> = ConcurrentHashMap()

/**
* Tries to unwrap Bazel-specific state from generic execution environment.
*/
private fun getBlazeConfigurationState(env: ExecutionEnvironment): BlazeCommandRunConfigurationCommonState? {
val runProfile = env.runProfile as? BlazeCommandRunConfiguration ?: return null
val state = runProfile.handler.state as? BlazeCommandRunConfigurationCommonState
return state
}

@Suppress("UnstableApiUsage") // `createEnvironmentRequest
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
override fun processStartScheduled(executorId: String, env: ExecutionEnvironment) {
val service = env.project.service<MirrordProjectService>()
if (!service.enabled) {
return
}
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved

MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: $executorId $env")

val state = getBlazeConfigurationState(env) ?: run {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: Bazel not detected")
super.processStartScheduled(executorId, env)
return
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
}
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: got config state $state")

MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: wsl check")
val wsl = when (val request = createEnvironmentRequest(env.runProfile, env.project)) {
is WslTargetEnvironmentRequest -> request.configuration.distribution!!
else -> null
}

val originalEnv = state.userEnvVarsState.data.envs
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: found ${originalEnv.size} original env variables")
val originalBazelPath = if (SystemInfo.isMac) {
state.blazeBinaryState.blazeBinary?.let {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: found Bazel binary path in the config: $it")
it
} ?: run {
// Bazel binary path may be missing from the config.
// This is the logic that Bazel plugin uses to find global Bazel binary.
val global = Blaze.getBuildSystemProvider(env.project).buildSystem.getBuildInvoker(env.project, BlazeContext.create()).binaryPath
MirrordLogger.logger.debug("[${this.javaClass.name} processStartScheduled: found global Bazel binary path: $global")
global
}
} else {
null
}

try {
service.execManager.wrapper("bazel", originalEnv).apply {
this.wsl = wsl
this.executable = originalBazelPath
}.start()?.let { executionInfo ->
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: adding ${executionInfo.environment.size} environment variables")
var envVars = originalEnv + executionInfo.environment
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
executionInfo.envToUnset?.let { envToUnset ->
envVars = envVars.filter {
!envToUnset.contains(it.key)
}
}
state.userEnvVarsState.setEnvVars(envVars)

if (SystemInfo.isMac) {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: isMac, patching SIP.")
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
executionInfo.patchedPath?.let {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: patchedPath is not null: $it, meaning original was SIP")
state.blazeBinaryState.blazeBinary = it
}
}

savedEnvs[executorId] = SavedConfigData(originalEnv, originalBazelPath)
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (e: Throwable) {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: exception catched: ", e)
// Error notifications were already fired.
// We can't abort the execution here, so we let the app run without mirrord.
service.notifier.notifySimple(
"Cannot abort run due to platform limitations, running without mirrord",
NotificationType.WARNING
)
}

super.processStartScheduled(executorId, env)
}

/**
* Restores original configuration after Bazel run has ended.
*/
private fun restoreConfig(executorId: String, configState: BlazeCommandRunConfigurationCommonState) {
t4lz marked this conversation as resolved.
Show resolved Hide resolved
MirrordLogger.logger.debug("[${this.javaClass.name}] restoreEnv: $executorId $configState")

val saved = savedEnvs.remove(executorId) ?: run {
MirrordLogger.logger.debug("[${this.javaClass.name}] restoreConfig: no saved env found")
return
}

MirrordLogger.logger.debug("[${this.javaClass.name}] restoreConfig: found ${saved.envVars.size} saved original variables")
configState.userEnvVarsState.setEnvVars(saved.envVars)

if (SystemInfo.isMac) {
MirrordLogger.logger.debug("[${this.javaClass.name}] restoreConfig: found saved original Bazel path ${saved.bazelPath}")
configState.blazeBinaryState.blazeBinary = saved.bazelPath
}
}

override fun processTerminating(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {
MirrordLogger.logger.debug("[${this.javaClass.name}] processTerminating: $executorId $env $handler")

val state = getBlazeConfigurationState(env) ?: run {
MirrordLogger.logger.debug("[${this.javaClass.name}] processTerminating: Bazel not detected")
return
}

restoreConfig(executorId, state)

super.processTerminating(executorId, env, handler)
}

override fun processNotStarted(executorId: String, env: ExecutionEnvironment) {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStarted (noop): $executorId $env")
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
super.processNotStarted(executorId, env)
}

override fun processStarted(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {
MirrordLogger.logger.debug("[${this.javaClass.name}] processStarted (noop): $executorId $env $handler")
super.processStarted(executorId, env, handler)
}
t4lz marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 1 addition & 1 deletion modules/products/goland/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ tasks {

intellij {
version.set(properties("platformVersion"))
plugins.set(listOf("org.jetbrains.plugins.go:223.7571.182"))
plugins.set(listOf("org.jetbrains.plugins.go:241.14494.240"))
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion modules/products/idea/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ tasks {
intellij {

version.set(properties("platformVersion"))
plugins.set(listOf("java", "gradle", "maven", "org.intellij.scala:2022.3.8"))
plugins.set(listOf("java", "gradle", "maven", "org.intellij.scala:2024.1.25"))
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion modules/products/pycharm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ tasks {

intellij {
version.set(properties("platformVersion"))
plugins.set(listOf("PythonCore:223.8617.56"))
plugins.set(listOf("PythonCore:241.14494.240"))
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
@file:Suppress("UnstableApiUsage")

// ^^ `createEnvironmentRequest` used to get the env references unstable API

package com.metalbear.mirrord.products.rider

import com.intellij.execution.RunManager
Expand All @@ -21,6 +17,7 @@ import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.resolvedPromise

class RiderPatchCommandLineExtension : PatchCommandLineExtension {
@Suppress("UnstableApiUsage") // `createEnvironmentRequest`
private fun patchCommandLine(commandLine: GeneralCommandLine, project: Project) {
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
val service = project.service<MirrordProjectService>()

Expand All @@ -44,34 +41,13 @@ class RiderPatchCommandLineExtension : PatchCommandLineExtension {
}
}

override fun patchDebugCommandLine(
lifetime: Lifetime,
workerRunInfo: WorkerRunInfo,
project: Project
): Promise<WorkerRunInfo> {
override fun patchDebugCommandLine(lifetime: Lifetime, workerRunInfo: WorkerRunInfo, processInfo: ProcessInfo?, project: Project): Promise<WorkerRunInfo> {
patchCommandLine(workerRunInfo.commandLine, project)
workerRunInfo.commandLine.withEnvironment("MIRRORD_DETECT_DEBUGGER_PORT", "resharper")
return resolvedPromise(workerRunInfo)
}

/**
* This method is the one that overrides in newer Rider versions.
*/
@Suppress("unused", "unused_parameter")
fun patchDebugCommandLine(
lifetime: Lifetime,
workerRunInfo: WorkerRunInfo,
processInfo: ProcessInfo?,
project: Project
): Promise<WorkerRunInfo> {
return patchDebugCommandLine(lifetime, workerRunInfo, project)
}

override fun patchRunCommandLine(
commandLine: GeneralCommandLine,
dotNetRuntime: DotNetRuntime,
project: Project
): ProcessListener? {
override fun patchRunCommandLine(commandLine: GeneralCommandLine, dotNetRuntime: DotNetRuntime, project: Project): ProcessListener? {
patchCommandLine(commandLine, project)
return null
}
Expand Down
2 changes: 1 addition & 1 deletion modules/products/rubymine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ tasks {

intellij {
version.set(properties("platformVersion"))
plugins.set(listOf("org.jetbrains.plugins.ruby:223.8617.56"))
plugins.set(listOf("org.jetbrains.plugins.ruby:241.14494.240"))
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion modules/products/tomcat/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ tasks {
intellij {
version.set(properties("platformVersion"))
type.set("IU")
plugins.set(listOf("Tomcat:223.7571.182", "com.intellij.javaee.app.servers.integration"))
plugins.set(listOf("Tomcat:241.14494.158", "com.intellij.javaee.app.servers.integration"))
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ class TomcatExecutionListener : ExecutionListener {
}

override fun processStartScheduled(executorId: String, env: ExecutionEnvironment) {
val service = env.project.service<MirrordProjectService>()
if (!service.enabled) {
return
}
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved

MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: $executorId $env")

val config = getConfig(env) ?: run {
Expand All @@ -147,8 +152,6 @@ class TomcatExecutionListener : ExecutionListener {
var envVars = config.envVariables
val envVarsMap = envVars.map { it.NAME to it.VALUE }.toMap()

val service = env.project.service<MirrordProjectService>()

MirrordLogger.logger.debug("[${this.javaClass.name}] processStartScheduled: wsl check")
val wsl = when (val request = createEnvironmentRequest(env.runProfile, env.project)) {
is WslTargetEnvironmentRequest -> request.configuration.distribution!!
Expand All @@ -171,7 +174,7 @@ class TomcatExecutionListener : ExecutionListener {
}

try {
service.execManager.wrapper("idea", envVarsMap).apply {
service.execManager.wrapper("tomcat", envVarsMap).apply {
this.wsl = wsl
this.executable = scriptAndArgs?.command
}.start()?.let { executionInfo ->
Expand Down
3 changes: 2 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ include(
"modules/products/rubymine",
"modules/products/nodejs",
"modules/products/rider",
"modules/products/tomcat"
"modules/products/tomcat",
"modules/products/bazel"
)

// Rename modules to mirrord-<module>, I think this is required IntelliJ wise.
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/META-INF/mirrord-bazel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<idea-plugin>
<projectListeners>
<listener class="com.metalbear.mirrord.products.bazel.BazelExecutionListener"
topic="com.intellij.execution.ExecutionListener"/>
</projectListeners>
</idea-plugin>

3 changes: 2 additions & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Read more <a href="https://mirrord.dev/docs/overview/introduction/">here</a>.
]]></description>

<idea-version since-build="222"/>
<idea-version since-build="241"/>

<depends>com.intellij.modules.lang</depends>

Expand Down Expand Up @@ -74,5 +74,6 @@
<depends optional="true" config-file="mirrord-js.xml">NodeJS</depends>
<depends optional="true" config-file="mirrord-rider.xml">com.intellij.modules.rider</depends>
<depends optional="true" config-file="mirrord-tomcat.xml">Tomcat</depends>
<depends optional="true" config-file="mirrord-bazel.xml">com.google.idea.bazel.ijwb</depends>
<depends optional="true" config-file="mirrord-schema.xml">com.intellij.modules.json</depends>
</idea-plugin>
Loading