IJPL-149476 load product core plugin using pre-built descriptor file

GitOrigin-RevId: 433d5809bf45910e95c0e704d69a6e627ad586c9
This commit is contained in:
Vladimir Krivosheev
2024-05-10 15:53:50 +02:00
committed by intellij-monorepo-bot
parent 155813717e
commit 9e54d54915
14 changed files with 211 additions and 137 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.intellij.build
import kotlinx.coroutines.Dispatchers
@@ -101,6 +101,7 @@ suspend fun buildCommunityStandaloneJpsBuilder(targetDir: Path,
platformLayout = null,
isRootDir = false,
isCodesignEnabled = false,
moduleOutputPatcher = ModuleOutputPatcher(),
dryRun = dryRun)
val targetFile = targetDir.resolve("standalone-jps-$buildNumber.zip")

View File

@@ -57,9 +57,6 @@ abstract class ProductProperties {
*/
lateinit var applicationInfoModule: String
@ApiStatus.Internal
var productPluginSourceModuleName: String? = null
/**
* Enables fast activation of a running IDE instance from the launcher
* (at the moment, it is only implemented in the native Windows one).

View File

@@ -59,7 +59,7 @@ fun reorderJar(relativePath: String, file: Path) {
internal val excludedLibJars: Set<String> = java.util.Set.of(PlatformJarNames.TEST_FRAMEWORK_JAR, "junit.jar")
fun generateClasspath(homeDir: Path, libDir: Path): List<String> {
internal fun generateClasspath(homeDir: Path, libDir: Path): List<String> {
spanBuilder("generate classpath")
.setAttribute("dir", homeDir.toString())
.useWithoutActiveScope { span ->
@@ -165,11 +165,23 @@ internal data class PluginBuildDescriptor(
@JvmField val moduleNames: List<String>,
)
fun writePluginClassPathHeader(out: DataOutputStream, isJarOnly: Boolean, pluginCount: Int) {
internal fun writePluginClassPathHeader(out: DataOutputStream, isJarOnly: Boolean, pluginCount: Int, moduleOutputPatcher: ModuleOutputPatcher, context: BuildContext) {
// format version
out.write(1)
out.write(2)
// jarOnly
out.write(if (isJarOnly) 1 else 0)
// main plugin
val mainDescriptor = moduleOutputPatcher.getPatchedContent(context.productProperties.applicationInfoModule)
.let { it.get("META-INF/plugin.xml") ?: it.get("META-INF/${context.productProperties.platformPrefix}Plugin.xml") }
val mainPluginDescriptorContent = requireNotNull(mainDescriptor) {
"Cannot find core plugin descriptor (module=${context.productProperties.applicationInfoModule})"
}
out.writeInt(mainPluginDescriptorContent.size)
out.write(mainPluginDescriptorContent)
// bundled plugin metadata
out.writeShort(pluginCount)
}

View File

@@ -116,6 +116,8 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
compileIfNeeded(context)
coroutineScope {
val moduleOutputPatcher = ModuleOutputPatcher()
val platformLayout = async {
createPlatformLayout(pluginsToPublish = emptySet(), context = context)
}
@@ -136,7 +138,13 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
}
val (platformDistributionEntries, classPath) = spanBuilder("layout platform").useWithScope {
layoutPlatform(runDir = runDir, platformLayout = platformLayout.await(), searchableOptionSet = searchableOptionSet, context = context)
layoutPlatform(
runDir = runDir,
platformLayout = platformLayout.await(),
searchableOptionSet = searchableOptionSet,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
}
launch(Dispatchers.IO) {
@@ -158,7 +166,6 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
}
}
val moduleOutputPatcher = ModuleOutputPatcher()
val pluginDistributionEntriesDeferred = async {
buildPlugins(
request = request,
@@ -175,13 +182,14 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
launch {
val (pluginEntries, additionalEntries) = pluginDistributionEntriesDeferred.await()
spanBuilder("generate plugin classpath").useWithScope(Dispatchers.IO) {
val mainData = generatePluginClassPath(pluginEntries, moduleOutputPatcher)
val mainData = generatePluginClassPath(pluginEntries = pluginEntries, moduleOutputPatcher = moduleOutputPatcher)
val additionalData = additionalEntries?.let { generatePluginClassPathFromPrebuiltPluginFiles(it) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = pluginEntries.size + (additionalEntries?.size ?: 0)
writePluginClassPathHeader(out = out, isJarOnly = !request.isUnpackedDist, pluginCount = pluginCount)
platformDistributionEntriesDeferred.join()
writePluginClassPathHeader(out = out, isJarOnly = !request.isUnpackedDist, pluginCount = pluginCount, moduleOutputPatcher = moduleOutputPatcher, context = context)
out.write(mainData)
additionalData?.let { out.write(it) }
out.close()
@@ -512,9 +520,10 @@ private suspend fun layoutPlatform(
platformLayout: PlatformLayout,
searchableOptionSet: SearchableOptionSetDescriptor?,
context: BuildContext,
moduleOutputPatcher: ModuleOutputPatcher,
): Pair<List<DistributionFileEntry>, Set<Path>> {
val entries = layoutPlatformDistribution(
moduleOutputPatcher = ModuleOutputPatcher(),
moduleOutputPatcher = moduleOutputPatcher,
targetDirectory = runDir,
platform = platformLayout,
searchableOptionSet = searchableOptionSet,

View File

@@ -48,7 +48,7 @@ internal fun buildHelpPlugin(pluginVersion: String, context: BuildContext): Plug
patcher.patchModuleOutput(moduleName = BUILT_IN_HELP_MODULE_NAME,
path = "META-INF/plugin.xml",
content = pluginXml(buildContext, pluginVersion),
overwrite = true)
overwrite = PatchOverwriteMode.TRUE)
}
LUCENE_LIBRARIES.forEach { spec.withProjectLibrary(it) }
}

View File

@@ -74,7 +74,7 @@ internal suspend fun buildDistribution(
val traceContext = Context.current().asContextElement()
val entries = coroutineScope {
// must be completed before plugin building
val searchableOptionSetDescriptor = context.executeStep(spanBuilder("build searchable options index"), BuildOptions.SEARCHABLE_OPTIONS_INDEX_STEP) {
val searchableOptionSet = context.executeStep(spanBuilder("build searchable options index"), BuildOptions.SEARCHABLE_OPTIONS_INDEX_STEP) {
buildSearchableOptions(productRunner = productRunner, context = context)
}
@@ -85,24 +85,14 @@ internal suspend fun buildDistribution(
val moduleOutputPatcher = ModuleOutputPatcher()
val buildPlatformJob: Deferred<List<DistributionFileEntry>> = async(traceContext) {
spanBuilder("build platform lib").useWithScope {
val result = buildLib(
moduleOutputPatcher = moduleOutputPatcher,
platform = state.platform,
searchableOptionSetDescriptor = searchableOptionSetDescriptor,
context = context
)
val result = buildLib(moduleOutputPatcher = moduleOutputPatcher, platform = state.platform, searchableOptionSetDescriptor = searchableOptionSet, context = context)
if (!isUpdateFromSources && context.productProperties.scrambleMainJar) {
scramble(state.platform, context)
}
val distAllDir = context.paths.distAllDir
val libDir = distAllDir.resolve("lib")
context.bootClassPathJarNames = if (context.useModularLoader) {
listOf(PLATFORM_LOADER_JAR)
}
else {
generateClasspath(homeDir = distAllDir, libDir = libDir)
}
context.bootClassPathJarNames = if (context.useModularLoader) listOf(PLATFORM_LOADER_JAR) else generateClasspath(homeDir = distAllDir, libDir = libDir)
result
}
}
@@ -113,7 +103,7 @@ internal suspend fun buildDistribution(
compressPluginArchive = !isUpdateFromSources && context.options.compressZipFiles,
buildPlatformLibJob = buildPlatformJob,
state = state,
searchableOptionSet = searchableOptionSetDescriptor,
searchableOptionSet = searchableOptionSet,
context = context,
)
}
@@ -123,7 +113,8 @@ internal suspend fun buildDistribution(
pluginLayouts = pluginLayouts,
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
searchableOptionSetDescriptor = searchableOptionSetDescriptor,
searchableOptionSetDescriptor = searchableOptionSet,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
@@ -162,9 +153,9 @@ private suspend fun buildBundledPluginsForAllPlatforms(
isUpdateFromSources: Boolean,
buildPlatformJob: Deferred<List<DistributionFileEntry>>,
searchableOptionSetDescriptor: SearchableOptionSetDescriptor?,
moduleOutputPatcher: ModuleOutputPatcher,
context: BuildContext,
): List<DistributionFileEntry> {
val moduleOutputPatcher = ModuleOutputPatcher()
return coroutineScope {
val commonDeferred = async {
doBuildBundledPlugins(
@@ -182,7 +173,7 @@ private suspend fun buildBundledPluginsForAllPlatforms(
copyAdditionalPlugins(context = context, pluginDir = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
}
val pluginDirs = getPluginDirs(context, isUpdateFromSources)
val pluginDirs = getPluginDirs(context = context, isUpdateFromSources = isUpdateFromSources)
val specificDeferred = async {
buildOsSpecificBundledPlugins(
state = state,
@@ -190,38 +181,55 @@ private suspend fun buildBundledPluginsForAllPlatforms(
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
context = context,
searchableOptionSetDescriptor = searchableOptionSetDescriptor,
searchableOptionSet = searchableOptionSetDescriptor,
pluginDirs = pluginDirs,
moduleOutputPatcher = moduleOutputPatcher,
)
}
val common = commonDeferred.await()
val commonClassPath = generatePluginClassPath(common, moduleOutputPatcher = moduleOutputPatcher)
val additional = additionalDeferred.await()
val additionalClassPath = additional?.let { generatePluginClassPathFromPrebuiltPluginFiles(it) }
val specific = specificDeferred.await()
for ((supportedDist) in pluginDirs) {
val specificList = specific[supportedDist]
val specificClasspath = specificList?.let { generatePluginClassPath(pluginEntries = it, moduleOutputPatcher = moduleOutputPatcher) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = common.size + (additional?.size ?: 0) + (specificList?.size ?: 0)
writePluginClassPathHeader(out, isJarOnly = true, pluginCount)
out.write(commonClassPath)
additionalClassPath?.let { out.write(it) }
specificClasspath?.let { out.write(it) }
out.close()
context.addDistFile(DistFile(relativePath = PLUGIN_CLASSPATH, content = InMemoryDistFileContent(byteOut.toByteArray()), os = supportedDist.os, arch = supportedDist.arch))
}
buildPlatformJob.join()
writePluginInfo(
moduleOutputPatcher = moduleOutputPatcher,
pluginDirs = pluginDirs,
common = common,
specific = specific,
additional = additionalDeferred.await(),
context = context,
)
listOf(common, specific.values.flatten())
}.flatMap { list -> list.flatMap { it.second } }
}
private fun writePluginInfo(
moduleOutputPatcher: ModuleOutputPatcher,
pluginDirs: List<Pair<SupportedDistribution, Path>>,
common: List<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>>,
specific: Map<SupportedDistribution, List<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>>>,
additional: List<Pair<Path, List<Path>>>?,
context: BuildContext,
) {
val commonClassPath = generatePluginClassPath(pluginEntries = common, moduleOutputPatcher = moduleOutputPatcher)
val additionalClassPath = additional?.let { generatePluginClassPathFromPrebuiltPluginFiles(it) }
for ((supportedDist) in pluginDirs) {
val specificList = specific.get(supportedDist)
val specificClasspath = specificList?.let { generatePluginClassPath(pluginEntries = it, moduleOutputPatcher = moduleOutputPatcher) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = common.size + (additional?.size ?: 0) + (specificList?.size ?: 0)
writePluginClassPathHeader(out = out, isJarOnly = true, pluginCount = pluginCount, moduleOutputPatcher = moduleOutputPatcher, context = context)
out.write(commonClassPath)
additionalClassPath?.let { out.write(it) }
specificClasspath?.let { out.write(it) }
out.close()
context.addDistFile(DistFile(relativePath = PLUGIN_CLASSPATH, content = InMemoryDistFileContent(byteOut.toByteArray()), os = supportedDist.os, arch = supportedDist.arch))
}
}
/**
* Validates module structure to be ensure all module dependencies are included.
*/
@@ -231,15 +239,16 @@ fun validateModuleStructure(platform: PlatformLayout, context: BuildContext) {
}
}
private fun getPluginDirs(context: BuildContext, isUpdateFromSources: Boolean): List<Pair<SupportedDistribution, Path>> =
private fun getPluginDirs(context: BuildContext, isUpdateFromSources: Boolean): List<Pair<SupportedDistribution, Path>> {
if (isUpdateFromSources) {
listOf(SupportedDistribution(OsFamily.currentOs, JvmArchitecture.currentJvmArch) to context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
return listOf(SupportedDistribution(OsFamily.currentOs, JvmArchitecture.currentJvmArch) to context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
}
else {
SUPPORTED_DISTRIBUTIONS.map {
it to getOsAndArchSpecificDistDirectory(it.os, it.arch, context).resolve(PLUGINS_DIRECTORY)
return SUPPORTED_DISTRIBUTIONS.map {
it to getOsAndArchSpecificDistDirectory(osFamily = it.os, arch = it.arch, context = context).resolve(PLUGINS_DIRECTORY)
}
}
}
suspend fun buildBundledPlugins(
state: DistributionBuilderState,
@@ -308,8 +317,9 @@ private suspend fun buildOsSpecificBundledPlugins(
isUpdateFromSources: Boolean,
buildPlatformJob: Job?,
context: BuildContext,
searchableOptionSetDescriptor: SearchableOptionSetDescriptor?,
searchableOptionSet: SearchableOptionSetDescriptor?,
pluginDirs: List<Pair<SupportedDistribution, Path>>,
moduleOutputPatcher: ModuleOutputPatcher,
): Map<SupportedDistribution, List<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>>> {
return spanBuilder("build os-specific bundled plugins")
.setAttribute("isUpdateFromSources", isUpdateFromSources)
@@ -328,28 +338,27 @@ private suspend fun buildOsSpecificBundledPlugins(
return@mapNotNull null
}
async(Dispatchers.IO) {
dist to async(Dispatchers.IO) {
spanBuilder("build bundled plugins")
.setAttribute("os", os.osName)
.setAttribute("arch", arch.name)
.setAttribute("count", osSpecificPlugins.size.toLong())
.setAttribute("outDir", targetDir.toString())
.useWithScope {
dist to buildPlugins(
moduleOutputPatcher = ModuleOutputPatcher(),
buildPlugins(
moduleOutputPatcher = moduleOutputPatcher,
plugins = osSpecificPlugins,
targetDir = targetDir,
state = state,
context = context,
buildPlatformJob = buildPlatformJob,
searchableOptionSet = searchableOptionSetDescriptor,
searchableOptionSet = searchableOptionSet,
)
}
}
}
}
.map { deferred -> deferred.getCompleted() }
.associateBy(keySelector = { it.first }, valueTransform = { it.second })
.associateBy(keySelector = { it.first }, valueTransform = { it.second.getCompleted() })
}
suspend fun copyAdditionalPlugins(context: BuildContext, pluginDir: Path): List<Pair<Path, List<Path>>>? {
@@ -925,6 +934,9 @@ fun satisfiesBundlingRequirements(plugin: PluginLayout, osFamily: OsFamily?, arc
}
val bundlingRestrictions = plugin.bundlingRestrictions
if (bundlingRestrictions == PluginBundlingRestrictions.MARKETPLACE) {
return false
}
if (context.options.useReleaseCycleRelatedBundlingRestrictionsForContentReport) {
val isNightly = context.options.isNightlyBuild
@@ -940,10 +952,6 @@ fun satisfiesBundlingRequirements(plugin: PluginLayout, osFamily: OsFamily?, arc
}
}
if (bundlingRestrictions == PluginBundlingRestrictions.MARKETPLACE) {
return false
}
return when {
osFamily == null && bundlingRestrictions.supportedOs != OsFamily.ALL -> false
osFamily != null && (bundlingRestrictions.supportedOs == OsFamily.ALL || !bundlingRestrictions.supportedOs.contains(osFamily)) -> false

View File

@@ -136,7 +136,7 @@ class JarPackager private constructor(
isCodesignEnabled: Boolean = true,
layout: BaseLayout?,
platformLayout: PlatformLayout?,
moduleOutputPatcher: ModuleOutputPatcher = ModuleOutputPatcher(),
moduleOutputPatcher: ModuleOutputPatcher,
dryRun: Boolean,
searchableOptionSet: SearchableOptionSetDescriptor? = null,
context: BuildContext,
@@ -343,7 +343,7 @@ class JarPackager private constructor(
}
}
}
else if (moduleName == (context.productProperties.productPluginSourceModuleName ?: context.productProperties.applicationInfoModule)) {
else if (moduleName == context.productProperties.applicationInfoModule) {
moduleSources.addAll(searchableOptionSet.createSourceByPlugin("com.intellij"))
}
}

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet", "LiftReturnOrAssignment")
@file:Suppress("ReplaceGetOrSet", "LiftReturnOrAssignment", "ReplacePutWithAssignment")
package org.jetbrains.intellij.build.impl
@@ -13,17 +13,27 @@ import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
enum class PatchOverwriteMode {
TRUE,
FALSE,
IF_EQUAL,
}
class ModuleOutputPatcher {
private val patchDirs = ConcurrentHashMap<String, CopyOnWriteArrayList<Path>>()
private val patches = ConcurrentHashMap<String, MutableMap<String, ByteArray>>()
fun patchModuleOutput(moduleName: String, path: String, content: String, overwrite: Boolean = false) {
fun patchModuleOutput(moduleName: String, path: String, content: String, overwrite: PatchOverwriteMode = PatchOverwriteMode.FALSE) {
patchModuleOutput(moduleName = moduleName, path = path, content = content.toByteArray(StandardCharsets.UTF_8), overwrite = overwrite)
}
fun patchModuleOutput(moduleName: String, path: String, content: ByteArray, overwrite: Boolean = false) {
fun patchModuleOutput(moduleName: String, path: String, content: ByteArray, overwrite: Boolean) {
patchModuleOutput(moduleName = moduleName, path = path, content = content, overwrite = if (overwrite) PatchOverwriteMode.TRUE else PatchOverwriteMode.FALSE)
}
fun patchModuleOutput(moduleName: String, path: String, content: ByteArray, overwrite: PatchOverwriteMode = PatchOverwriteMode.FALSE) {
val pathToData = patches.computeIfAbsent(moduleName) { Collections.synchronizedMap(LinkedHashMap()) }
if (overwrite) {
if (overwrite == PatchOverwriteMode.TRUE) {
val overwritten = pathToData.put(path, content) != null
Span.current().addEvent("patch module output", Attributes.of(
AttributeKey.stringKey("module"), moduleName,
@@ -36,12 +46,16 @@ class ModuleOutputPatcher {
val existing = pathToData.putIfAbsent(path, content)
val span = Span.current()
if (existing != null) {
span.addEvent("failed to patch because path is duplicated", Attributes.of(
AttributeKey.stringKey("path"), path,
AttributeKey.stringKey("oldContent"), byteArrayToTraceStringValue(existing),
AttributeKey.stringKey("newContent"), byteArrayToTraceStringValue(content),
))
error("Patched directory $path is already added for module $moduleName")
if (overwrite != PatchOverwriteMode.IF_EQUAL && !existing.contentEquals(content)) {
span.addEvent("failed to patch because path is duplicated", Attributes.of(
AttributeKey.stringKey("path"), path,
AttributeKey.stringKey("oldContent"), byteArrayToTraceStringValue(existing),
AttributeKey.stringKey("newContent"), byteArrayToTraceStringValue(content),
))
error("Patched directory $path is already added for module $moduleName")
}
pathToData.put(path, content)
}
span.addEvent("patch module output", Attributes.of(

View File

@@ -282,7 +282,6 @@ internal suspend fun createPlatformLayout(addPlatformCoverage: Boolean, projectL
context = context,
validateImplicitPlatformModule = false,
).asSequence().map { it.first } + explicitModuleNames).toList(),
productPluginSourceModuleName = context.productProperties.productPluginSourceModuleName ?: context.productProperties.applicationInfoModule,
)
val implicit = computeImplicitRequiredModules(
@@ -490,12 +489,12 @@ private fun computeTransitive(
// result _must be_ consistent, do not use Set.of or HashSet here
private suspend fun processAndGetProductPluginContentModules(
context: BuildContext,
productPluginSourceModuleName: String,
layout: PlatformLayout,
includedPlatformModulesPartialList: List<String>,
): Set<ModuleItem> {
val xIncludePathResolver = createXIncludePathResolver(includedPlatformModulesPartialList, context)
return withContext(Dispatchers.IO) {
val productPluginSourceModuleName = context.productProperties.applicationInfoModule
val file = requireNotNull(
context.findFileInModuleSources(productPluginSourceModuleName, "META-INF/plugin.xml")
?: context.findFileInModuleSources(moduleName = productPluginSourceModuleName, relativePath = "META-INF/${context.productProperties.platformPrefix}Plugin.xml")

View File

@@ -88,7 +88,8 @@ internal fun patchPluginXml(
catch (e: Throwable) {
throw RuntimeException("Could not patch descriptor (module=${plugin.mainModule})", e)
}
moduleOutputPatcher.patchModuleOutput(moduleName = plugin.mainModule, path = "META-INF/plugin.xml", content = content)
// os-specific plugins being built several times - we expect that plugin.xml must be the same
moduleOutputPatcher.patchModuleOutput(moduleName = plugin.mainModule, path = "META-INF/plugin.xml", content = content, overwrite = PatchOverwriteMode.IF_EQUAL)
}
@TestOnly

View File

@@ -16,13 +16,7 @@ class ClassPathXmlPathResolver(
override val isFlat: Boolean
get() = true
override fun loadXIncludeReference(
readInto: RawPluginDescriptor,
readContext: ReadModuleContext,
dataLoader: DataLoader,
base: String?,
relativePath: String,
): Boolean {
override fun loadXIncludeReference(readInto: RawPluginDescriptor, readContext: ReadModuleContext, dataLoader: DataLoader, base: String?, relativePath: String): Boolean {
val path = PluginXmlPathResolver.toLoadPath(relativePath, base)
val reader: XMLStreamReader2
if (classLoader is UrlClassLoader) {
@@ -65,19 +59,22 @@ class ClassPathXmlPathResolver(
if (resource == null) {
val log = logger<ClassPathXmlPathResolver>()
val moduleName = path.removeSuffix(".xml")
if (isRunningFromSources && path.startsWith("intellij.") && dataLoader.emptyDescriptorIfCannotResolve) {
log.trace("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader). ")
val descriptor = RawPluginDescriptor()
descriptor.`package` = "unresolved.$moduleName"
return descriptor
when {
isRunningFromSources && path.startsWith("intellij.") && dataLoader.emptyDescriptorIfCannotResolve -> {
log.trace("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader). ")
val descriptor = RawPluginDescriptor()
descriptor.`package` = "unresolved.$moduleName"
return descriptor
}
ProductLoadingStrategy.strategy.isOptionalProductModule(moduleName) -> {
// this check won't be needed when we are able to load optional modules directly from product-modules.xml
log.debug { "Skip module '$path' since its descriptor cannot be found and it's optional" }
return RawPluginDescriptor().apply { `package` = "unresolved.$moduleName" }
}
else -> {
throw RuntimeException("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader)")
}
}
if (ProductLoadingStrategy.strategy.isOptionalProductModule(moduleName)) {
// this check won't be needed when we are able to load optional modules directly from product-modules.xml
log.debug { "Skip module '$path' since its descriptor cannot be found and it's optional" }
return RawPluginDescriptor().apply { `package` = "unresolved.$moduleName" }
}
throw RuntimeException("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader)")
}
return readModuleDescriptor(

View File

@@ -111,7 +111,7 @@ fun loadDescriptorFromDir(
useCoreClassLoader = useCoreClassLoader,
)
context.debugData?.recordDescriptorPath(descriptor = descriptor, rawPluginDescriptor = raw, path = descriptorRelativePath)
initMainDescriptorByRaw(descriptor = descriptor, raw = raw, pathResolver = pathResolver, context = context, pluginDir = pluginDir ?: dir, dataLoader = dataLoader)
initMainDescriptorByRaw(descriptor = descriptor, raw = raw, pathResolver = pathResolver, context = context, pluginDir = pluginDir ?: dir, dataLoader = dataLoader)
descriptor.jarFiles = Collections.singletonList(dir)
return descriptor
}
@@ -575,46 +575,71 @@ internal fun CoroutineScope.loadPluginDescriptorsImpl(
else {
val effectiveBundledPluginDir = bundledPluginDir ?: Paths.get(PathManager.getPreInstalledPluginsPath())
val data = try {
Files.readAllBytes(effectiveBundledPluginDir.resolve("plugin-classpath.txt"))
// use only if the format is supported (first byte it is a version)
Files.readAllBytes(effectiveBundledPluginDir.resolve("plugin-classpath.txt")).takeIf { it[0] == 2.toByte() }
}
catch (ignored: NoSuchFileException) {
null
}
result.addAll(loadCoreModules(
context = context,
platformPrefix = platformPrefix,
isUnitTestMode = false,
isInDevServerMode = AppMode.isDevServer(),
isRunningFromSources = isRunningFromSources,
classLoader = mainClassLoader,
pool = zipFilePool,
result = result,
))
result.addAll(loadDescriptorsFromDir(dir = customPluginDir, context = context, isBundled = false, pool = zipFilePool))
if (data == null || data[0] != 1.toByte()) {
if (data == null) {
result.addAll(loadCoreModules(
context = context,
platformPrefix = platformPrefix,
isUnitTestMode = false,
isInDevServerMode = AppMode.isDevServer(),
isRunningFromSources = isRunningFromSources,
classLoader = mainClassLoader,
pool = zipFilePool,
result = result,
))
result.addAll(loadDescriptorsFromDir(dir = customPluginDir, context = context, isBundled = false, pool = zipFilePool))
result.addAll(loadDescriptorsFromDir(dir = effectiveBundledPluginDir, context = context, isBundled = true, pool = zipFilePool))
}
else {
result.addAll(loadFromPluginClasspathDescriptor(data = data, context = context, zipFilePool = zipFilePool, bundledPluginDir = effectiveBundledPluginDir, scope = this))
val byteInput = ByteArrayInputStream(data, 2, data.size)
val input = DataInputStream(byteInput)
val descriptorSize = input.readInt()
val descriptorStart = data.size - byteInput.available()
input.skipBytes(descriptorSize)
result.add(async {
loadCoreProductPlugin(
path = PluginManagerCore.PLUGIN_XML_PATH,
context = context,
pathResolver = ClassPathXmlPathResolver(classLoader = mainClassLoader, isRunningFromSources = false),
useCoreClassLoader = platformPrefix.startsWith("CodeServer") || java.lang.Boolean.getBoolean("idea.force.use.core.classloader"),
reader = createXmlStreamReader(data, descriptorStart, descriptorSize),
)
})
result.addAll(loadDescriptorsFromDir(dir = customPluginDir, context = context, isBundled = false, pool = zipFilePool))
loadFromPluginClasspathDescriptor(
input = input,
jarOnly = data[1] == 1.toByte(),
context = context,
zipFilePool = zipFilePool,
bundledPluginDir = effectiveBundledPluginDir,
scope = this,
result = result,
)
}
}
return result
}
private fun loadFromPluginClasspathDescriptor(
data: ByteArray,
input: DataInputStream,
jarOnly: Boolean,
context: DescriptorListLoadingContext,
zipFilePool: ZipFilePool,
bundledPluginDir: Path,
scope: CoroutineScope,
): Array<Deferred<IdeaPluginDescriptorImpl?>> {
val jarOnly = data[1] == 1.toByte()
val input = DataInputStream(ByteArrayInputStream(data, 2, data.size))
result: ArrayList<Deferred<IdeaPluginDescriptorImpl?>>,
) {
val pluginCount = input.readUnsignedShort()
return Array(pluginCount) {
result.ensureCapacity(result.size + pluginCount)
repeat(pluginCount) {
val fileCount = input.readUnsignedShort()
val pluginDir = bundledPluginDir.resolve(input.readUTF())
@@ -629,7 +654,7 @@ private fun loadFromPluginClasspathDescriptor(
FileItem(file = file, path = path)
}
scope.async {
result.add(scope.async {
try {
loadPluginDescriptor(
fileItems = fileItems,
@@ -647,7 +672,7 @@ private fun loadFromPluginClasspathDescriptor(
PluginManagerCore.logger.warn("Cannot load plugin descriptor, files:\n ${fileItems.joinToString(separator = "\n ")}", e)
null
}
}
})
}
}
@@ -791,7 +816,17 @@ private fun CoroutineScope.loadCoreModules(
): List<Deferred<IdeaPluginDescriptorImpl?>> {
val pathResolver = ClassPathXmlPathResolver(classLoader = classLoader, isRunningFromSources = isRunningFromSources && !isInDevServerMode)
val useCoreClassLoader = pathResolver.isRunningFromSources || platformPrefix.startsWith("CodeServer") || java.lang.Boolean.getBoolean("idea.force.use.core.classloader")
if (loadCorePlugin(platformPrefix, isInDevServerMode, isUnitTestMode, isRunningFromSources, context, pathResolver, useCoreClassLoader, classLoader, result)) {
if (loadCorePlugin(
platformPrefix = platformPrefix,
isInDevServerMode = isInDevServerMode,
isUnitTestMode = isUnitTestMode,
isRunningFromSources = isRunningFromSources,
context = context,
pathResolver = pathResolver,
useCoreClassLoader = useCoreClassLoader,
classLoader = classLoader,
result = result,
)) {
return result
}
@@ -887,7 +922,7 @@ private fun loadCoreProductPlugin(
override fun toString() = "product classpath"
}
val raw = readModuleDescriptor(reader = reader, readContext = context, pathResolver = pathResolver, dataLoader = dataLoader, includeBase = null, readInto = null)
val raw = readModuleDescriptor(reader = reader, readContext = context, pathResolver = pathResolver, dataLoader = dataLoader)
val libDir = Paths.get(PathManager.getLibPath())
val descriptor = IdeaPluginDescriptorImpl(raw = raw, path = libDir, isBundled = true, id = null, moduleName = null, useCoreClassLoader = useCoreClassLoader)
context.debugData?.recordDescriptorPath(descriptor = descriptor, rawPluginDescriptor = raw, path = path)
@@ -926,18 +961,13 @@ private fun loadModuleDescriptors(
pathResolver = pathResolver,
dataLoader = dataLoader,
containerDescriptor = descriptor,
)) {
)) {
continue
}
}
module.descriptor = descriptor.createSub(
raw = pathResolver.resolveModuleFile(
readContext = context,
dataLoader = dataLoader,
path = subDescriptorFile,
readInto = null,
),
raw = pathResolver.resolveModuleFile(readContext = context, dataLoader = dataLoader, path = subDescriptorFile, readInto = null),
descriptorPath = subDescriptorFile,
context = context,
moduleName = moduleName,
@@ -1075,8 +1105,8 @@ fun loadDescriptorsFromOtherIde(
bundledPluginDir = bundledPluginDir,
zipFilePool = ZipFilePool.POOL ?: NonShareableJavaZipFilePool(),
mainClassLoader = DescriptorListLoadingContext::class.java.classLoader,
isRunningFromSources = false,
isUnitTestMode = false,
isRunningFromSources = PluginManagerCore.isRunningFromSources(),
isUnitTestMode = PluginManagerCore.isUnitTestMode,
)
}, isMainProcess()),
overrideUseIfCompatible = false,

View File

@@ -23,7 +23,6 @@ import com.intellij.openapi.actionSystem.impl.ActionManagerImpl
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModernApplicationStarter
import com.intellij.openapi.components.serviceAsync
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.PluginDescriptor
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.keymap.impl.ui.KeymapPanel

View File

@@ -8,6 +8,7 @@ import com.fasterxml.aalto.`in`.ReaderConfig
import com.fasterxml.aalto.stax.StreamReaderImpl
import org.codehaus.stax2.XMLInputFactory2
import org.codehaus.stax2.XMLStreamReader2
import org.jetbrains.annotations.ApiStatus.Internal
import java.io.InputStream
import java.io.Reader
import javax.xml.stream.XMLInputFactory
@@ -43,6 +44,12 @@ fun createXmlStreamReader(bytes: ByteArray): XMLStreamReader2 {
return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(readerConfig, bytes, 0, bytes.size))
}
@Internal
fun createXmlStreamReader(bytes: ByteArray, start: Int, size: Int): XMLStreamReader2 {
val readerConfig = configWithCoalescing.createNonShared(null, null, "UTF-8")
return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(readerConfig, bytes, start, size))
}
@Throws(XMLStreamException::class)
fun createNonCoalescingXmlStreamReader(input: InputStream, locationSource: String?): XMLStreamReader2 {
return StreamReaderImpl.construct(ByteSourceBootstrapper.construct(config.createNonShared(null, locationSource, "UTF-8"), input))