mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-217708 Support plugins with use-idea-classloader by allowing platform modules to use them
GitOrigin-RevId: 96d07824221e2c4a6aea81eb7ce375eedc6676f2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
75fb9c760b
commit
4bcef8a3f2
+23
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.bootstrap
|
||||
|
||||
import com.intellij.ide.plugins.*
|
||||
@@ -6,6 +6,8 @@ import com.intellij.idea.AppMode
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.platform.plugins.parser.impl.PluginDescriptorFromXmlStreamConsumer
|
||||
import com.intellij.platform.plugins.parser.impl.consume
|
||||
import com.intellij.platform.runtime.product.IncludedRuntimeModule
|
||||
import com.intellij.platform.runtime.product.PluginModuleGroup
|
||||
import com.intellij.platform.runtime.product.ProductMode
|
||||
@@ -136,7 +138,7 @@ internal class ModuleBasedProductLoadingStrategy(internal val moduleRepository:
|
||||
}
|
||||
val bundled = productModules.bundledPluginModuleGroups.map { moduleGroup ->
|
||||
scope.async {
|
||||
if (moduleGroup.includedModules.none { it.moduleDescriptor.moduleId in mainGroupModulesSet }) {
|
||||
if (moduleGroup.includedModules.none { it.moduleDescriptor.moduleId in mainGroupModulesSet } || isPlatformPlugin(moduleGroup, context)) {
|
||||
val serviceModuleMapping = serviceModuleMappingDeferred.await()
|
||||
loadPluginDescriptorFromRuntimeModule(
|
||||
pluginModuleGroup = moduleGroup,
|
||||
@@ -161,6 +163,25 @@ internal class ModuleBasedProductLoadingStrategy(internal val moduleRepository:
|
||||
return scope.async { DiscoveredPluginsList(bundled.awaitAll().filterNotNull(), PluginsSourceContext.Bundled) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if [pluginModuleGroup] is a plugin with `use-idea-classloader` which should be loaded by platform.
|
||||
* Content modules of these plugins always should be loaded.
|
||||
*/
|
||||
private fun isPlatformPlugin(
|
||||
pluginModuleGroup: PluginModuleGroup,
|
||||
loadingContext: PluginDescriptorLoadingContext,
|
||||
): Boolean {
|
||||
val mainResourceRoot = pluginModuleGroup.mainModule.resourceRootPaths.singleOrNull() ?: return false
|
||||
val input = Files.readAllBytes(mainResourceRoot.resolve(PluginManagerCore.PLUGIN_XML_PATH))
|
||||
// TODO: do we need to support xIncludes in this case?
|
||||
@Suppress("TestOnlyProblems")
|
||||
val rawDescriptor = PluginDescriptorFromXmlStreamConsumer(loadingContext.readContext, xIncludeLoader = null).let {
|
||||
it.consume(input, mainResourceRoot.toString())
|
||||
it.build()
|
||||
}
|
||||
return rawDescriptor.isUseIdeaClassLoader
|
||||
}
|
||||
|
||||
private fun loadCustomPluginDescriptors(
|
||||
scope: CoroutineScope,
|
||||
customPluginDir: Path,
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.jetbrains.intellij.build.PLATFORM_LOADER_JAR
|
||||
import org.jetbrains.intellij.build.PLUGIN_XML_RELATIVE_PATH
|
||||
import org.jetbrains.intellij.build.UTIL_8_JAR
|
||||
import org.jetbrains.intellij.build.UTIL_JAR
|
||||
import org.jetbrains.intellij.build.getUnprocessedPluginXmlContent
|
||||
import org.jetbrains.intellij.build.impl.DescriptorCacheContainer
|
||||
import org.jetbrains.intellij.build.impl.ModuleIncludeReasons
|
||||
import org.jetbrains.intellij.build.impl.PRODUCT_DESCRIPTOR_META_PATH
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.intellij.build.impl.PlatformJarNames.PRODUCT_BACKEND_JAR
|
||||
import org.jetbrains.intellij.build.impl.PlatformLayout
|
||||
import org.jetbrains.intellij.build.impl.PluginLayout
|
||||
import org.jetbrains.intellij.build.impl.ScopedCachedDescriptorContainer
|
||||
import org.jetbrains.intellij.build.impl.filterAndProcessContentModules
|
||||
import org.jetbrains.intellij.build.impl.projectStructureMapping.CustomAssetEntry
|
||||
import org.jetbrains.intellij.build.impl.projectStructureMapping.DistributionFileEntry
|
||||
import org.jetbrains.intellij.build.impl.projectStructureMapping.ModuleOutputEntry
|
||||
@@ -91,6 +93,51 @@ internal fun generateClassPathByLayoutReport(libDir: Path, entries: List<Distrib
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a set of paths that should be included in the core classpath.
|
||||
* This generation is based on the plugins distribution,
|
||||
* so we would like to include all distribution entities of **embedded** modules (and their libraries) from plugins marked with `use-idea-classloader`.
|
||||
*/
|
||||
internal fun generateCoreClasspathFromPlugins(
|
||||
context: BuildContext,
|
||||
pluginEntities: List<PluginBuildDescriptor>,
|
||||
): Set<Path> {
|
||||
val classPathResult = mutableSetOf<Path>()
|
||||
for (pluginEntity in pluginEntities) {
|
||||
val pluginLayout = pluginEntity.layout
|
||||
val classPathModules = extractPlatformPluginsModules(context, pluginLayout.mainModule)
|
||||
for (distributionEntry in pluginEntity.distribution) {
|
||||
if (distributionEntry is ModuleOwnedFileEntry && distributionEntry.owner?.moduleName in classPathModules) {
|
||||
classPathResult.add(distributionEntry.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return classPathResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a set of content modules ("embedded" ones) and the module of the plugin itself, if it uses `use-idea-classloader`.
|
||||
* These modules should be included in the core classpath, also their libraries should be treated as platform libraries.
|
||||
*/
|
||||
internal fun extractPlatformPluginsModules(
|
||||
context: BuildContext,
|
||||
pluginMainModule: String,
|
||||
): Set<String> {
|
||||
val pluginModule = context.findRequiredModule(pluginMainModule)
|
||||
val pluginXmlContent = getUnprocessedPluginXmlContent(pluginModule, context).decodeToString()
|
||||
val rootElement = JDOMUtil.load(pluginXmlContent)
|
||||
if (rootElement.getAttribute("use-idea-classloader")?.value?.toBoolean() != true) {
|
||||
return emptySet()
|
||||
}
|
||||
val embeddedModules = mutableSetOf(pluginMainModule)
|
||||
filterAndProcessContentModules(rootElement, pluginMainModule, context) { _, moduleName, loadingRule ->
|
||||
if (loadingRule == "embedded") {
|
||||
embeddedModules.add(moduleName)
|
||||
}
|
||||
}
|
||||
return embeddedModules
|
||||
}
|
||||
|
||||
internal data class PluginBuildDescriptor(
|
||||
@JvmField val dir: Path,
|
||||
@JvmField val os: OsFamily?,
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.jetbrains.intellij.build.ScrambleTool
|
||||
import org.jetbrains.intellij.build.SearchableOptionSetDescriptor
|
||||
import org.jetbrains.intellij.build.WindowsDistributionCustomizer
|
||||
import org.jetbrains.intellij.build.classPath.generateClassPathByLayoutReport
|
||||
import org.jetbrains.intellij.build.classPath.generateCoreClasspathFromPlugins
|
||||
import org.jetbrains.intellij.build.classPath.generatePluginClassPath
|
||||
import org.jetbrains.intellij.build.classPath.generatePluginClassPathFromPrebuiltPluginFiles
|
||||
import org.jetbrains.intellij.build.classPath.writePluginClassPathHeader
|
||||
@@ -178,9 +179,8 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
|
||||
}
|
||||
|
||||
val searchableOptionSet = getSearchableOptionSet(context)
|
||||
|
||||
val platformLayoutResultDeferred = async(CoroutineName("platform distribution entries")) {
|
||||
val generateFilesInBinDirJob = launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
// PathManager.getBinPath() is used as a working dir for maven
|
||||
val binDir = Files.createDirectories(runDir.resolve("bin"))
|
||||
val oldFiles = Files.newDirectoryStream(binDir).use { it.toCollection(HashSet()) }
|
||||
@@ -207,7 +207,7 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
|
||||
}
|
||||
|
||||
val platformLayoutAwaited = platformLayout.await()
|
||||
val platformLayoutResult = spanBuilder("layout platform").use {
|
||||
spanBuilder("layout platform").use {
|
||||
layoutPlatform(
|
||||
runDir = runDir,
|
||||
platformLayout = platformLayoutAwaited,
|
||||
@@ -217,17 +217,6 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
|
||||
request = request,
|
||||
)
|
||||
}
|
||||
|
||||
if (request.writeCoreClasspath) {
|
||||
val classPathString = platformLayoutResult.coreClassPath.joinToString(separator = "\n")
|
||||
launch(Dispatchers.IO) {
|
||||
Files.writeString(runDir.resolve("core-classpath.txt"), classPathString)
|
||||
}
|
||||
}
|
||||
|
||||
generateFilesInBinDirJob.join()
|
||||
request.platformClassPathConsumer?.invoke(context.ideMainClassName, platformLayoutResult.coreClassPath, runDir)
|
||||
platformLayoutResult
|
||||
}
|
||||
|
||||
val pluginDistributionEntriesDeferred = async(CoroutineName("build plugins")) {
|
||||
@@ -242,6 +231,23 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
|
||||
)
|
||||
}
|
||||
|
||||
// write and update core classpath from platform and plugins distribution
|
||||
launch {
|
||||
val platformClasspath = platformLayoutResultDeferred.await().coreClassPath
|
||||
val pluginDistributionEntities = pluginDistributionEntriesDeferred.await().pluginEntries
|
||||
val coreClasspathFromPlugins = generateCoreClasspathFromPlugins(context, pluginDistributionEntities)
|
||||
val classPath = platformClasspath + coreClasspathFromPlugins
|
||||
|
||||
if (request.writeCoreClasspath) {
|
||||
val classPathString = classPath.joinToString(separator = "\n")
|
||||
launch(Dispatchers.IO) {
|
||||
Files.writeString(runDir.resolve("core-classpath.txt"), classPathString)
|
||||
}
|
||||
}
|
||||
|
||||
request.platformClassPathConsumer?.invoke(context.ideMainClassName, classPath, runDir)
|
||||
}
|
||||
|
||||
if (context.generateRuntimeModuleRepository) {
|
||||
launch {
|
||||
val allDistributionEntries = platformLayoutResultDeferred.await().distributionEntries.asSequence() +
|
||||
|
||||
+26
-12
@@ -36,6 +36,7 @@ import org.jetbrains.intellij.build.SearchableOptionSetDescriptor
|
||||
import org.jetbrains.intellij.build.buildSearchableOptions
|
||||
import org.jetbrains.intellij.build.classPath.PluginBuildDescriptor
|
||||
import org.jetbrains.intellij.build.classPath.generateClassPathByLayoutReport
|
||||
import org.jetbrains.intellij.build.classPath.generateCoreClasspathFromPlugins
|
||||
import org.jetbrains.intellij.build.classPath.generatePluginClassPath
|
||||
import org.jetbrains.intellij.build.classPath.generatePluginClassPathFromPrebuiltPluginFiles
|
||||
import org.jetbrains.intellij.build.classPath.writePluginClassPathHeader
|
||||
@@ -136,7 +137,10 @@ internal suspend fun buildDistribution(
|
||||
context = context,
|
||||
)
|
||||
|
||||
ContentReport(platform = buildPlatformJob.await(), bundledPlugins = bundledPluginItems, nonBundledPlugins = buildNonBundledPlugins.await())
|
||||
val platformItems = buildPlatformJob.await()
|
||||
context.bootClassPathJarNames = generateCoreClassPath(context, platformItems, bundledPluginItems)
|
||||
|
||||
ContentReport(platform = platformItems, bundledPlugins = bundledPluginItems, nonBundledPlugins = buildNonBundledPlugins.await())
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
@@ -162,6 +166,27 @@ internal suspend fun buildDistribution(
|
||||
contentReport
|
||||
}
|
||||
|
||||
private fun generateCoreClassPath(
|
||||
context: BuildContext,
|
||||
platformDistribution: List<DistributionFileEntry>,
|
||||
bundledPluginsDistribution: List<PluginBuildDescriptor>,
|
||||
): List<String> {
|
||||
val platformClassPath = if (context.useModularLoader) {
|
||||
listOf(PLATFORM_LOADER_JAR)
|
||||
}
|
||||
else {
|
||||
val libDir = context.paths.distAllDir.resolve("lib")
|
||||
generateClassPathByLayoutReport(
|
||||
libDir = libDir,
|
||||
entries = platformDistribution,
|
||||
skipNioFs = isMultiRoutingFileSystemEnabledForProduct(context.productProperties.platformPrefix)
|
||||
).map { libDir.relativize(it).toString() }
|
||||
}
|
||||
val pluginsDir = context.paths.distAllDir.resolve("plugins")
|
||||
val coreClassPathFromPlugins = generateCoreClasspathFromPlugins(context, bundledPluginsDistribution).map { pluginsDir.resolve(it).toString() }
|
||||
return platformClassPath + coreClassPathFromPlugins
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
suspend fun buildPlatform(
|
||||
moduleOutputPatcher: ModuleOutputPatcher,
|
||||
@@ -185,17 +210,6 @@ suspend fun buildPlatform(
|
||||
tool.scramble(platformLayout = state.platformLayout, platformFileEntries = distributionFileEntries, context = context)
|
||||
}
|
||||
}
|
||||
context.bootClassPathJarNames = if (context.useModularLoader) {
|
||||
listOf(PLATFORM_LOADER_JAR)
|
||||
}
|
||||
else {
|
||||
val libDir = context.paths.distAllDir.resolve("lib")
|
||||
generateClassPathByLayoutReport(
|
||||
libDir = libDir,
|
||||
entries = distributionFileEntries,
|
||||
skipNioFs = isMultiRoutingFileSystemEnabledForProduct(context.productProperties.platformPrefix)
|
||||
).map { libDir.relativize(it).toString() }
|
||||
}
|
||||
return distributionFileEntries
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.jetbrains.intellij.build.PLATFORM_LOADER_JAR
|
||||
import org.jetbrains.intellij.build.UTIL_8_JAR
|
||||
import org.jetbrains.intellij.build.UTIL_JAR
|
||||
import org.jetbrains.intellij.build.UTIL_RT_JAR
|
||||
import org.jetbrains.intellij.build.classPath.extractPlatformPluginsModules
|
||||
import org.jetbrains.intellij.build.impl.PlatformJarNames.TEST_FRAMEWORK_JAR
|
||||
import org.jetbrains.intellij.build.isModuleNameLikeFilename
|
||||
import org.jetbrains.intellij.build.productLayout.ProductModulesLayout
|
||||
@@ -378,12 +379,14 @@ fun collectExportedLibrariesFromLibraryModules(
|
||||
): Map<String, String> {
|
||||
val javaExtensionService = JpsJavaExtensionService.getInstance()
|
||||
val result = mutableMapOf<String, String>()
|
||||
val includedModuleNames = layout.includedModules.map { it.moduleName }
|
||||
val corePluginsContentModuleNames = computePlatformPluginsContentModules(context)
|
||||
|
||||
layout.includedModules
|
||||
(includedModuleNames + corePluginsContentModuleNames)
|
||||
.asSequence()
|
||||
.filter { it.moduleName.startsWith(LIB_MODULE_PREFIX) }
|
||||
.forEach { moduleItem ->
|
||||
val module = context.findRequiredModule(moduleItem.moduleName)
|
||||
.filter { it.startsWith(LIB_MODULE_PREFIX) }
|
||||
.forEach { moduleName ->
|
||||
val module = context.findRequiredModule(moduleName)
|
||||
// get all library dependencies from the module
|
||||
module.dependenciesList.dependencies
|
||||
.asSequence()
|
||||
@@ -394,7 +397,7 @@ fun collectExportedLibrariesFromLibraryModules(
|
||||
}
|
||||
.mapNotNull { it.library?.name }
|
||||
.forEach { libName ->
|
||||
result.put(libName, moduleItem.moduleName)
|
||||
result.put(libName, moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,12 +495,16 @@ private suspend fun computeImplicitRequiredModules(
|
||||
unique.add("intellij.pycharm.ds")
|
||||
unique.add("intellij.notebooks.visualization")
|
||||
|
||||
val result = mutableListOf<Pair<String, PersistentList<String>>>()
|
||||
computeTransitive(list = rootList, context = context, unique = unique, result = result)
|
||||
// we should filter out modules which are included in plugins with `use-idea-classloader`
|
||||
val corePluginContents = computePlatformPluginsContentModules(context)
|
||||
|
||||
val requiredDependencies = mutableListOf<Pair<String, PersistentList<String>>>()
|
||||
computeTransitive(list = rootList, context = context, unique = unique, result = requiredDependencies)
|
||||
val requiredModules = requiredDependencies.filter { it.first !in corePluginContents }
|
||||
|
||||
if (validateImplicitPlatformModule) {
|
||||
withContext(Dispatchers.IO) {
|
||||
for ((name, chain) in result) {
|
||||
for ((name, chain) in requiredModules) {
|
||||
launch(CoroutineName("validating the implicit platform module $name")) {
|
||||
val file = context.findFileInModuleSources(name, "META-INF/plugin.xml")
|
||||
check(file == null) {
|
||||
@@ -508,7 +515,13 @@ private suspend fun computeImplicitRequiredModules(
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return requiredModules
|
||||
}
|
||||
|
||||
private fun computePlatformPluginsContentModules(context: BuildContext): Set<String> {
|
||||
val bundledPlugins = getPluginLayoutsByJpsModuleNames(modules = context.getBundledPluginModules(), productLayout = context.productProperties.productLayout)
|
||||
val corePluginContents = bundledPlugins.flatMap { extractPlatformPluginsModules(context, it.mainModule) }.toSet()
|
||||
return corePluginContents
|
||||
}
|
||||
|
||||
private fun computeTransitive(
|
||||
|
||||
Reference in New Issue
Block a user