diff --git a/CHANGELOG.md b/CHANGELOG.md index 55f4db5d27..269c96d5ad 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [next] +### Added +- `targetProductPart` property is added to allow installing plugin on the frontend part when running in Split Mode [#1563](../../issues/1563) + ### Fixed - Fix for: `coroutinesJavaAgentPath` specifies file `.../build/tmp/initializeIntelliJPlugin/coroutines-javaagent.jar` which doesn't exist diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/argumentProviders/SandboxArgumentProvider.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/argumentProviders/SandboxArgumentProvider.kt index b85c932b3b..d917ce726d 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/argumentProviders/SandboxArgumentProvider.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/argumentProviders/SandboxArgumentProvider.kt @@ -3,12 +3,11 @@ package org.jetbrains.intellij.platform.gradle.argumentProviders import org.gradle.api.file.DirectoryProperty -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.PathSensitive +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.process.CommandLineArgumentProvider +import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware import org.jetbrains.intellij.platform.gradle.utils.asPath import java.io.File import java.nio.file.Path @@ -39,6 +38,14 @@ class SandboxArgumentProvider( @OutputDirectory val sandboxLogDirectory: DirectoryProperty, + + @Optional + @Input + val splitMode: Provider, + + @Optional + @Input + val targetProductPart: Provider, ) : CommandLineArgumentProvider { /** @@ -51,13 +58,24 @@ class SandboxArgumentProvider( it.listDirectoryEntries().joinToString("${File.pathSeparator},") } + private fun computePluginPathProperties(): List { + if (splitMode.get() && targetProductPart.get() == SplitModeAware.SplitModeTarget.FRONTEND) { + return listOfNotNull( + // Specifies an empty directory to ensure that the plugin won't be loaded by the backend process. + sandboxPluginsDirectory.ifExists { "-Didea.plugins.path=$it/backend" }, + ) + } + return listOfNotNull( + sandboxPluginsDirectory.ifExists { "-Didea.plugins.path=$it" }, + pluginPath?.let { "-Dplugin.path=$it" }, + ) + } + override fun asArguments() = listOfNotNull( sandboxConfigDirectory.ifExists { "-Didea.config.path=$it" }, sandboxSystemDirectory.ifExists { "-Didea.system.path=$it" }, - sandboxLogDirectory.ifExists { "-Didea.log.path=$it" }, - sandboxPluginsDirectory.ifExists { "-Didea.plugins.path=$it" }, - pluginPath?.let { "-Dplugin.path=$it" }, - ) + sandboxLogDirectory.ifExists { "-Didea.log.path=$it" } + ) + computePluginPathProperties() private fun DirectoryProperty.ifExists(block: (Path) -> T) = orNull?.asPath?.takeIf { it.exists() }?.run(block) } diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt index b09d259ac5..97911e8485 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/extensions/IntelliJPlatformExtension.kt @@ -30,13 +30,11 @@ import org.jetbrains.intellij.platform.gradle.models.productInfo import org.jetbrains.intellij.platform.gradle.models.toPublication import org.jetbrains.intellij.platform.gradle.models.validateSupportedVersion import org.jetbrains.intellij.platform.gradle.providers.ProductReleasesValueSource -import org.jetbrains.intellij.platform.gradle.tasks.BuildSearchableOptionsTask -import org.jetbrains.intellij.platform.gradle.tasks.PatchPluginXmlTask -import org.jetbrains.intellij.platform.gradle.tasks.PublishPluginTask -import org.jetbrains.intellij.platform.gradle.tasks.SignPluginTask +import org.jetbrains.intellij.platform.gradle.tasks.* import org.jetbrains.intellij.platform.gradle.tasks.VerifyPluginTask.* import org.jetbrains.intellij.platform.gradle.tasks.aware.PluginVerifierAware import org.jetbrains.intellij.platform.gradle.tasks.aware.SigningAware +import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware import org.jetbrains.intellij.platform.gradle.toIntelliJPlatformType import org.jetbrains.intellij.platform.gradle.utils.asLenient import org.jetbrains.intellij.platform.gradle.utils.asPath @@ -127,12 +125,20 @@ abstract class IntelliJPlatformExtension @Inject constructor( * is running a frontend part (JetBrains Client) which connects to the backend. * * This property allows running the IDE with backend and frontend parts running in separate processes. - * The developed plugin is installed in the backend part. + * The developed plugin is installed in the backend part by default, this can be changed via [splitModeTarget]. * * Default value: `false` */ abstract val splitMode: Property + /** + * Taken into account only if [splitMode] is set to `true` and specifies in which part of the IDE the plugin + * should be installed when `runIde` task is executed: the backend process, the frontend process, or both. + * + * Default value: [SplitModeAware.SplitModeTarget.BACKEND] + */ + abstract val splitModeTarget: Property + val pluginConfiguration get() = extensions.getByName(Extensions.PLUGIN_CONFIGURATION) diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/plugins/project/partials/IntelliJPlatformBasePlugin.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/plugins/project/partials/IntelliJPlatformBasePlugin.kt index 80796b3d7d..5be5dad2ac 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/plugins/project/partials/IntelliJPlatformBasePlugin.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/plugins/project/partials/IntelliJPlatformBasePlugin.kt @@ -308,6 +308,7 @@ abstract class IntelliJPlatformBasePlugin : Plugin { projectName.convention(project.name) sandboxContainer.convention(project.layout.buildDirectory.dir(Sandbox.CONTAINER)) splitMode.convention(false) + splitModeTarget.convention(SplitModeAware.SplitModeTarget.BACKEND) configureExtension(Extensions.PLUGIN_CONFIGURATION) { version.convention(project.provider { project.version.toString() }) diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/CustomTestIdeTask.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/CustomTestIdeTask.kt index 93d6b91f65..51a287603f 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/CustomTestIdeTask.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/CustomTestIdeTask.kt @@ -98,6 +98,8 @@ abstract class CustomTestIdeTask : Test(), TestableAware, CustomIntelliJPlatform sourceTask.sandboxPluginsDirectory, sourceTask.sandboxSystemDirectory, sourceTask.sandboxLogDirectory, + project.provider { false }, + project.provider { null }, ) ) diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTask.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTask.kt index f36faf06ff..0e098ffe39 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTask.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTask.kt @@ -26,9 +26,7 @@ import org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformDepende import org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformExtension import org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformPluginsExtension import org.jetbrains.intellij.platform.gradle.models.transformXml -import org.jetbrains.intellij.platform.gradle.tasks.aware.CustomIntelliJPlatformVersionAware -import org.jetbrains.intellij.platform.gradle.tasks.aware.SandboxAware -import org.jetbrains.intellij.platform.gradle.tasks.aware.SandboxProducerAware +import org.jetbrains.intellij.platform.gradle.tasks.aware.* import org.jetbrains.intellij.platform.gradle.utils.asPath import java.nio.file.Path import kotlin.io.path.* @@ -45,7 +43,7 @@ import kotlin.io.path.* * @see Constants.Sandbox */ @CacheableTask -abstract class PrepareSandboxTask : Sync(), SandboxProducerAware { +abstract class PrepareSandboxTask : Sync(), SandboxProducerAware, SplitModeAware { /** * Default sandbox destination directory to where the plugin files will be copied into. @@ -112,7 +110,7 @@ abstract class PrepareSandboxTask : Sync(), SandboxProducerAware { override fun copy() { disableIdeUpdate() disabledPlugins() - + createSplitModeFrontendPropertiesFile() super.copy() } @@ -175,6 +173,33 @@ abstract class PrepareSandboxTask : Sync(), SandboxProducerAware { .writeLines(disabledPlugins.get()) } + /** + * Creates a properties file which will be passed to the frontend process when the IDE is started in Split Mode. + */ + private fun createSplitModeFrontendPropertiesFile() { + if (!splitMode.get()) { + return + } + + val pluginsPath = when (splitModeTarget.get()) { + SplitModeAware.SplitModeTarget.FRONTEND, SplitModeAware.SplitModeTarget.BACKEND_AND_FRONTEND -> + sandboxPluginsDirectory.asPath + + SplitModeAware.SplitModeTarget.BACKEND -> + // Specifies an empty directory to ensure that the plugin won't be loaded. + sandboxPluginsDirectory.asPath.resolve("frontend") + } + + frontendPropertiesFile.asPath.writeText( + """ + idea.config.path=${sandboxConfigDirectory.asPath.resolve("frontend")} + idea.system.path=${sandboxSystemDirectory.asPath.resolve("frontend")} + idea.log.path=${sandboxLogDirectory.asPath.resolve("frontend")} + idea.plugins.path=$pluginsPath + """.trimIndent() + ) + } + fun ensureName(path: Path): String { var name = path.name var index = 1 @@ -215,6 +240,8 @@ abstract class PrepareSandboxTask : Sync(), SandboxProducerAware { from(pluginsClasspath) inputs.property("intellijPlatform.instrumentCode", extension.instrumentCode) + inputs.property("intellijPlatform.splitMode", extension.splitMode) + inputs.property("intellijPlatform.targetProductPart", extension.splitModeTarget) inputs.files(runtimeConfiguration) } } diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/RunIdeTask.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/RunIdeTask.kt index 8c75f2e46d..fcb9ff34b9 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/RunIdeTask.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/RunIdeTask.kt @@ -43,6 +43,7 @@ abstract class RunIdeTask : JavaExec(), RunnableIdeAware, IntelliJPlatformVersio if (splitMode.get()) { environment("JETBRAINS_CLIENT_JDK", runtimeDirectory.asPath.pathString) + environment("JETBRAINS_CLIENT_PROPERTIES", frontendPropertiesFile.asPath.pathString) if (args.orEmpty().isNotEmpty()) { throw InvalidUserDataException("Passing arguments directly is not supported in Split Mode. Use `argumentProviders` instead.") diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt index f909520c38..e55ca9e4e8 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt @@ -3,7 +3,9 @@ package org.jetbrains.intellij.platform.gradle.tasks.aware import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal import org.jetbrains.intellij.platform.gradle.Constants import org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformExtension @@ -63,4 +65,11 @@ interface SandboxAware : IntelliJPlatformVersionAware { */ @get:Internal val sandboxLogDirectory: DirectoryProperty + + /** + * Path to a properties file which will be used to configure the frontend process if the IDE is started in Split Mode. + */ + @get:Internal + val frontendPropertiesFile: Provider + get() = sandboxContainerDirectory.file("frontend.properties") } diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SplitModeAware.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SplitModeAware.kt index 8d35c822a5..123b360d86 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SplitModeAware.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SplitModeAware.kt @@ -28,6 +28,14 @@ interface SplitModeAware : IntelliJPlatformVersionAware { @get:Internal val splitMode: Property + /** + * Specifies in which part of the product the developed plugin should be installed. + * + * Default value: [SplitModeTarget.BACKEND] + */ + @get:Internal + val splitModeTarget: Property + /** * Validates that the resolved IntelliJ Platform supports Split Mode. * @@ -41,4 +49,15 @@ interface SplitModeAware : IntelliJPlatformVersionAware { throw IllegalArgumentException("Split Mode requires the IntelliJ Platform in version '${Constraints.MINIMAL_SPLIT_MODE_BUILD_NUMBER}' or later, but '$currentBuildNumber' was provided.") } } + + /** + * Describes a part of the product where the developed plugin can be installed when running in [splitMode]. + */ + enum class SplitModeTarget { + BACKEND, + FRONTEND, + BACKEND_AND_FRONTEND; + + override fun toString() = name.lowercase().replace('_', '-') + } } diff --git a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/tasks.kt b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/tasks.kt index d7bf1ca8d7..7bdf52506b 100644 --- a/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/tasks.kt +++ b/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/tasks.kt @@ -387,6 +387,7 @@ internal fun Project.preconfigureTask(task: T) { */ if (this is SplitModeAware) { splitMode.convention(extension.splitMode) + splitModeTarget.convention(extension.splitModeTarget) } /** @@ -418,6 +419,8 @@ internal fun Project.preconfigureTask(task: T) { sandboxPluginsDirectory, sandboxSystemDirectory, sandboxLogDirectory, + splitMode, + splitModeTarget, ) ) diff --git a/src/test/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTaskTest.kt b/src/test/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTaskTest.kt index 0de78a60ba..1e4485e205 100644 --- a/src/test/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTaskTest.kt +++ b/src/test/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTaskTest.kt @@ -5,6 +5,8 @@ package org.jetbrains.intellij.platform.gradle.tasks import org.jetbrains.intellij.platform.gradle.* import org.jetbrains.intellij.platform.gradle.Constants.Sandbox import org.jetbrains.intellij.platform.gradle.Constants.Tasks +import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware +import java.nio.file.Path import kotlin.io.path.* import kotlin.test.Ignore import kotlin.test.Test @@ -361,6 +363,61 @@ class PrepareSandboxTaskTest : IntelliJPluginTestBase() { ) } + @Test + fun `prepare sandbox for splitMode with plugin installed on frontend`() { + buildSandboxForSplitMode(SplitModeAware.SplitModeTarget.FRONTEND) + checkFrontendProperties(sandbox.resolve("plugins")) + } + + @Test + fun `prepare sandbox for splitMode with plugin installed on backend`() { + buildSandboxForSplitMode(SplitModeAware.SplitModeTarget.BACKEND) + checkFrontendProperties(sandbox.resolve("plugins").resolve("frontend")) + } + + @Test + fun `prepare sandbox for splitMode with plugin installed on backend and frontend`() { + buildSandboxForSplitMode(SplitModeAware.SplitModeTarget.BACKEND_AND_FRONTEND) + checkFrontendProperties(sandbox.resolve("plugins")) + } + + private fun checkFrontendProperties(pluginsPath: Path) { + assertFileContent( + sandbox.resolve("frontend.properties"), + """ + idea.config.path=${sandbox.resolve("config").resolve("frontend")} + idea.system.path=${sandbox.resolve("system").resolve("frontend")} + idea.log.path=${sandbox.resolve("log").resolve("frontend")} + idea.plugins.path=$pluginsPath + """.trimIndent() + ) + } + + private fun buildSandboxForSplitMode(splitModeTarget: SplitModeAware.SplitModeTarget) { + writeJavaFile() + + pluginXml write //language=xml + """ + + """.trimIndent() + + buildFile prepend // language=kotlin + """ + import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware + """.trimIndent() + + buildFile write //language=kotlin + """ + intellijPlatform { + sandboxContainer = file("${buildDirectory.resolve(Sandbox.CONTAINER)}") + splitMode = true + splitModeTarget = SplitModeAware.SplitModeTarget.${splitModeTarget.name} + } + """.trimIndent() + + build(Tasks.PREPARE_SANDBOX) + } + @Test fun `prepare sandbox with external jar-type plugin`() { writeJavaFile()