Patching VM Options Under Gradle Application Run Configuration
Answered
When running an "Application" configuration under a Maven project the com.intellij.execution.runners.JavaProgramPatcher class can be used to modify/extend the VM options which are used during execution. However, the same class is not used when running an "Application" configuration under a Gradle project.
I've done some searching and it seems the com.intellij.openapi.externalSystem.util.ExternalSystemUtil class should be used for this functionality. I can't seem to find an example of how this should work though. Is there an equivalent class for Gradle-based "Application" configurations or an alternative way of setting VM options via a plugin?
Please sign in to leave a comment.
That's the right class that you've found. Example usage:
Thanks for the input, Jakub. I was able to use your code to debug the issue further but I do not believe the above will work for me. I'm not trying to modify the VM options that Gradle itself is executing under but rather the VM options the application being run under Gradle is executing with.
For example, when I run the application under Gradle I was able to notice that at com.intellij.execution.impl.ExecutionManagerImpl.executeConfiguration() the variable environment.myRunProfile.myUserMap.INIT_SCRIPT_KEY has the VM options I actually want to modify. This script looks something like this:
To modify those VM options I have to input them into this screen:
I would like to do this programmatically (via plugin) as the user executes the application and not have them need to input these VM options every time they create a new application run configuration. I can do this via com.intellij.execution.runners.JavaProgramPatcher for Maven projects.
You can set INIT_SCRIPT_KEY programmatically with my above snippet as well by providing the userData:
Jakub, your snippet above relies on ExternalSystemUtil.runTask() though. I'm not trying to run a task for the user. I'm trying to patch the VM options of a task a user is trying to run. It also looks like using userData.putUserData() will re-write the entire script being used to execute the application. That seems like overkill if all I'm trying to do is patch the VM options.
I was able to follow where INIT_SCRIPT_KEY gets set back to org.jetbrains.plugins.gradle.execution.build.GradleApplicationEnvironmentProvider. Is there a way to patch the VM options that are used in there? Also, the functionality I'm trying to implement appears to be precisely the point of com.intellij.execution.runners.JavaProgramPatcher. Why is this class not used in this case?
I dug a bit around the Gradle plugin, and I think that you may be interested in creating an extension point for the taskManager:
Implementation should extend org.jetbrains.plugins.gradle.service.task.GradleTaskManagerExtension.
It is invoked by the GradleTaskManager with jvmParametersSetup string passed, which you may alter.
You should check as well the GradleExecutionSettings settings that hold List<String> myJvmArguments.
I quickly check how it reflects the UI and Gradle Run/Debug configuration's "VM options" is passed right to that myJvmArguments list.
Thanks for looking into this more, Jakub. Using org.jetbrains.plugins.gradle.service.task.GradleTaskManagerExtension I was finally able to successfully modify the VM options of the code which was being run under Gradle. I wasn't able to do it via myJvmArguments or via the vmOptions that are passed to GradleTaskManagerExtension.executeTasks() though. I think you are mistaken about what those are used for as I put some VM options in under "Gradle Run/Debug configurations" and they remained empty. When I put VM options in those variables they ended up being run on Gradle as opposed to the application under Gradle.
What I was able to get working was by doing a string replace on settings.getUserData(GradleTaskManager.INIT_SCRIPT_KEY) in the GradleTaskManagerExtension.executeTasks() method. This is not ideal but it is working so I won't bother you with looking for a more elegant solution.
Instead, I would like to submit what appears likely to be a bug with the VM options that are used under the Gradle application configurations.
Please follow these instructions:
Expected output: Listening for transport dt_socket at address: 5005
Actual output: ERROR: Cannot load this JVM TI agent twice, check your java command line for duplicate jdwp options.
Taking a look at the value of GradleTaskManager.INIT_SCRIPT_KEY, you can see it lists jvmArgs twice:
I'm able to fix this issue and get the expected output when I run the same instructions with a plugin with the following extension:
class SPPGradleTaskManager implements GradleTaskManagerExtension {
@Override
boolean executeTasks(@NotNull ExternalSystemTaskId id, @NotNull List<String> taskNames, @NotNull String projectPath, @Nullable GradleExecutionSettings settings, @Nullable String jvmParametersSetup, @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException {
settings.putUserData(GradleTaskManager.INIT_SCRIPT_KEY,
settings.getUserData(GradleTaskManager.INIT_SCRIPT_KEY).toString()
.replace(" jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'\n" +
"jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'",
" jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'")
)
List<String> vmOptions = settings != null ? settings.getJvmArguments() : Collections.emptyList()
List<String> arguments = settings != null ? settings.getArguments() : Collections.emptyList()
return executeTasks(id, taskNames, projectPath, settings, vmOptions, arguments, jvmParametersSetup, listener)
}
@Override
boolean cancelTask(@NotNull ExternalSystemTaskId id, @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException {
return false
}
}
IDEA:
Created an issue in case this is a bug: https://youtrack.jetbrains.com/issue/IDEA-234593
Hi Brandon,
Yeah, it looks like a good candidate for the YouTrack. Thank you!