[plugins] IJPL-251952 init context: add provideCompatibilityDependenciesForRemainingCandidates method, prepare stubs

(cherry picked from commit 7114f46e90ec94ed70bd236b50138dd3448dbd91)

GitOrigin-RevId: 99ba70b8fa9cefdf2b0552dc6aa01df74cbf2fb6
This commit is contained in:
Vadim Salavatov
2026-08-20 13:50:40 +00:00
committed by intellij-monorepo-bot
parent 3100a9016a
commit abdbf534bf
5 changed files with 70 additions and 4 deletions
@@ -58,13 +58,43 @@ interface PluginInitializationContext {
}
/**
* Processed for all possible modules and "depends" sub-descriptors independently.
* @return a sequence of modules that should be deemed as additional dependencies of a given [descriptor].
* Produces a sequence of modules that should be deemed as additional dependencies of a given [descriptor].
* Note that the generated dependency is "strict", meaning that if the target gets excluded (e.g., if the target is a plugin that is marked disabled),
* then [descriptor] will also be excluded.
*
* Called for all possible modules and "depends" sub-descriptors independently.
*
* TODO Ideally, [pluginSet] should not be used, but it's required in the current [ProductPluginInitContext] implementation.
*
* @see [provideCompatibilityDependenciesForRemainingCandidates]
*/
fun provideCompatibilityDependencies(descriptor: IdeaPluginDescriptorImpl, pluginSet: UnambiguousPluginSet): Sequence<DependencyRef>
/**
* This method is different from [provideCompatibilityDependencies] in that it allows generating "soft" compatibility dependencies:
* imagine that several modules were extracted from the IDE's core and now form a separate plugin that can be disabled.
* Previously, these modules were available to external plugins via the Core classloader, i.e. without any explicit dependency,
* but now they are not available without an explicit dependency, which breaks compatibility.
* To remedy this, we want to supply additional dependencies on extracted modules. Producing a "strict" dependency
* (as in [provideCompatibilityDependencies]) may sometimes be too strict, e.g., if that new extracted plugin is disabled, external plugins
* that receive such a compatibility dependency (even those that don't actually need it) will be excluded since the dependency is "strict".
* However, this method is called when the preliminary set of remaining candidates is already constructed, i.e. when all regular module
* exclusion rules are processed, and it allows skipping generation of compatibility dependencies if the dependency target is already excluded.
*
* This method is called for every remaining candidate descriptor.
*
* Note that producing additional dependencies here still may cause exclusions (e.g., if a dependency cycle appears).
*
* Note that eventually every implicit dependency that is added through this method should become explicit in the affected plugins.
* This method should only work as a temporary compatibility mechanism, it should not grow indefinitely.
*/
fun provideCompatibilityDependenciesForRemainingCandidates(descriptor: IdeaPluginDescriptorImpl, remainingCandidates: RemainingCandidatesView): Sequence<DependencyRef>
interface RemainingCandidatesView {
fun resolvePluginId(id: PluginId): PluginModuleDescriptor?
fun resolveContentModuleId(id: PluginModuleId): ContentModuleDescriptor?
}
fun provideModuleExclusionsImposedByProductRules(pluginSet: UnambiguousPluginSet): Sequence<Pair<PluginModuleDescriptor, ProductRulesImposedExclusionReason>>
/**
@@ -109,4 +139,12 @@ fun PluginInitializationContext.validatePluginIsCompatible(plugin: PluginMainDes
}
@ApiStatus.Internal
data class PluginsPerProjectConfig(val isMainProcess: Boolean)
data class PluginsPerProjectConfig(val isMainProcess: Boolean)
@ApiStatus.Internal
fun PluginInitializationContext.RemainingCandidatesView.resolveReference(ref: DependencyRef): PluginModuleDescriptor? {
return when (ref) {
is DependencyRef.Plugin -> resolvePluginId(ref.pluginId)
is DependencyRef.ContentModule -> resolveContentModuleId(ref.moduleId)
}
}
@@ -4,6 +4,7 @@ package com.intellij.ide.plugins
import com.intellij.core.CoreBundle
import com.intellij.ide.plugins.PluginDependencyAnalysis.DependencyRef
import com.intellij.ide.plugins.PluginInitializationContext.EnvironmentConfiguredModuleData
import com.intellij.ide.plugins.PluginInitializationContext.RemainingCandidatesView
import com.intellij.ide.plugins.PluginManagerCore.CORE_ID
import com.intellij.ide.plugins.PluginManagerCore.JAVA_PLUGIN_ALIAS_ID
import com.intellij.ide.plugins.PluginManagerCore.getPluginNameAndVendor
@@ -104,6 +105,9 @@ class ProductPluginInitContext(
override fun provideCompatibilityDependencies(descriptor: IdeaPluginDescriptorImpl, pluginSet: UnambiguousPluginSet): Sequence<DependencyRef> =
defaultProductCompatibilityDependenciesProvider(descriptor, pluginSet)
override fun provideCompatibilityDependenciesForRemainingCandidates(descriptor: IdeaPluginDescriptorImpl, remainingCandidates: RemainingCandidatesView): Sequence<DependencyRef> =
defaultProductCompatibilityDependenciesForRemainingCandidatesProvider(descriptor, remainingCandidates)
override fun provideModuleExclusionsImposedByProductRules(pluginSet: UnambiguousPluginSet): Sequence<Pair<PluginModuleDescriptor, ProductRulesImposedExclusionReason>> =
defaultProductRulesImposedExclusions(pluginSet, expiredPlugins, thirdPartyPluginsWithoutConsentCheckResult)
@@ -335,6 +339,16 @@ class ProductPluginInitContext(
return null
}
@VisibleForTesting
fun defaultProductCompatibilityDependenciesForRemainingCandidatesProvider(
descriptor: IdeaPluginDescriptorImpl,
remainingCandidates: RemainingCandidatesView,
): Sequence<DependencyRef> {
return sequence {
}
}
@VisibleForTesting
fun defaultProductRulesImposedExclusions(
pluginSet: UnambiguousPluginSet,
@@ -179,7 +179,8 @@ private class PluginSetConstraintsResolver(
}
private fun sequenceAllDependenciesOfCandidateIncludingCompatibility(candidate: IdeaPluginDescriptorImpl): Sequence<DependencyRef> {
return PluginDependencyAnalysis.sequenceStrictDependencies(candidate) + initContext.provideCompatibilityDependencies(candidate, pluginSet)
return PluginDependencyAnalysis.sequenceStrictDependencies(candidate) +
initContext.provideCompatibilityDependencies(candidate, pluginSet)
}
/**
@@ -5,6 +5,7 @@ import com.intellij.ide.plugins.AmbiguousPluginSet
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
import com.intellij.ide.plugins.PluginDependencyAnalysis.DependencyRef
import com.intellij.ide.plugins.PluginInitializationContext
import com.intellij.ide.plugins.PluginInitializationContext.RemainingCandidatesView
import com.intellij.ide.plugins.PluginMainDescriptor
import com.intellij.ide.plugins.PluginModuleDescriptor
import com.intellij.ide.plugins.PluginModuleId
@@ -30,6 +31,11 @@ abstract class EmptyTestPluginInitContext : PluginInitializationContext {
pluginSet: UnambiguousPluginSet,
): Sequence<DependencyRef> = emptySequence()
override fun provideCompatibilityDependenciesForRemainingCandidates(
descriptor: IdeaPluginDescriptorImpl,
remainingCandidates: RemainingCandidatesView,
): Sequence<DependencyRef> = emptySequence()
override fun provideModuleExclusionsImposedByProductRules(pluginSet: UnambiguousPluginSet): Sequence<Pair<PluginModuleDescriptor, ProductRulesImposedExclusionReason>> =
emptySequence()
@@ -8,6 +8,7 @@ import com.intellij.ide.plugins.PluginMainDescriptor
import com.intellij.ide.plugins.PluginModuleDescriptor
import com.intellij.ide.plugins.PluginModuleId
import com.intellij.ide.plugins.ProductPluginInitContext.Companion.configureProductModeModules
import com.intellij.ide.plugins.ProductPluginInitContext.Companion.defaultProductCompatibilityDependenciesForRemainingCandidatesProvider
import com.intellij.ide.plugins.ProductPluginInitContext.Companion.defaultProductCompatibilityDependenciesProvider
import com.intellij.ide.plugins.ProductPluginInitContext.Companion.defaultProductRulesImposedExclusions
import com.intellij.ide.plugins.ProductPluginInitContext.Companion.defaultRuntimeModuleGroupAffiliation
@@ -42,6 +43,12 @@ abstract class PseudoProductTestPluginInitContext : EmptyTestPluginInitContext()
): PluginModuleDescriptor? =
defaultRuntimeModuleGroupAffiliation(module, pluginSet)
override fun provideCompatibilityDependenciesForRemainingCandidates(
descriptor: IdeaPluginDescriptorImpl,
remainingCandidates: PluginInitializationContext.RemainingCandidatesView
): Sequence<DependencyRef> =
defaultProductCompatibilityDependenciesForRemainingCandidatesProvider(descriptor, remainingCandidates)
override fun shouldIncludeContentModulesForDependsEdgeTarget(resolvedTarget: PluginMainDescriptor): Boolean =
defaultShouldIncludeContentModulesForDependsEdgeTarget(resolvedTarget)
}