diff --git a/build/src/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.kt b/build/src/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.kt index 65e23c4ce24b..ed6cf8a2bc6a 100644 --- a/build/src/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.kt +++ b/build/src/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.kt @@ -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") diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/ProductProperties.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/ProductProperties.kt index b70da124e510..9733988986de 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/ProductProperties.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/ProductProperties.kt @@ -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). diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/classpath.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/classpath.kt index 205af8a96c8a..06981775e236 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/classpath.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/classpath.kt @@ -59,7 +59,7 @@ fun reorderJar(relativePath: String, file: Path) { internal val excludedLibJars: Set = java.util.Set.of(PlatformJarNames.TEST_FRAMEWORK_JAR, "junit.jar") -fun generateClasspath(homeDir: Path, libDir: Path): List { +internal fun generateClasspath(homeDir: Path, libDir: Path): List { spanBuilder("generate classpath") .setAttribute("dir", homeDir.toString()) .useWithoutActiveScope { span -> @@ -165,11 +165,23 @@ internal data class PluginBuildDescriptor( @JvmField val moduleNames: List, ) -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) } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt index 9d25b9650962..85babc15021d 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt @@ -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, Set> { val entries = layoutPlatformDistribution( - moduleOutputPatcher = ModuleOutputPatcher(), + moduleOutputPatcher = moduleOutputPatcher, targetDirectory = runDir, platform = platformLayout, searchableOptionSet = searchableOptionSet, diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt index 0ed5d5a18368..015b85a62e6a 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuiltInHelpPlugin.kt @@ -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) } } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.kt index e33ed8e387d3..1a3aa997cca9 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.kt @@ -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> = 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>, searchableOptionSetDescriptor: SearchableOptionSetDescriptor?, + moduleOutputPatcher: ModuleOutputPatcher, context: BuildContext, ): List { - 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>, + common: List>>, + specific: Map>>>, + additional: List>>?, + 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> = +private fun getPluginDirs(context: BuildContext, isUpdateFromSources: Boolean): List> { 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>, + moduleOutputPatcher: ModuleOutputPatcher, ): Map>>> { 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>>? { @@ -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 diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt index e7c406c7b49e..affee4ba85d9 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt @@ -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")) } } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleOutputPatcher.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleOutputPatcher.kt index 508cd270d1bd..836b271f37f0 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleOutputPatcher.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleOutputPatcher.kt @@ -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>() private val patches = ConcurrentHashMap>() - 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( diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt index b6453105424a..f902ee65f70e 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PlatformModules.kt @@ -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, ): Set { 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") diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt index 802435bfb0ca..dae7c605008a 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt @@ -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 diff --git a/platform/core-impl/src/com/intellij/ide/plugins/ClassPathXmlPathResolver.kt b/platform/core-impl/src/com/intellij/ide/plugins/ClassPathXmlPathResolver.kt index a1d1e1b88599..bf4c79b02e71 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/ClassPathXmlPathResolver.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/ClassPathXmlPathResolver.kt @@ -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() 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( diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginDescriptorLoader.kt b/platform/core-impl/src/com/intellij/ide/plugins/PluginDescriptorLoader.kt index af0f62109779..40a137c520b6 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginDescriptorLoader.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginDescriptorLoader.kt @@ -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> { - val jarOnly = data[1] == 1.toByte() - val input = DataInputStream(ByteArrayInputStream(data, 2, data.size)) + result: ArrayList>, +) { 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> { 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, diff --git a/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.kt b/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.kt index 8b0894459e7d..dc2201024035 100644 --- a/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.kt +++ b/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.kt @@ -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 diff --git a/platform/util/xmlDom/src/com/intellij/util/xml/dom/StaxFactory.kt b/platform/util/xmlDom/src/com/intellij/util/xml/dom/StaxFactory.kt index c1bb33132e54..6c17c856ff5d 100644 --- a/platform/util/xmlDom/src/com/intellij/util/xml/dom/StaxFactory.kt +++ b/platform/util/xmlDom/src/com/intellij/util/xml/dom/StaxFactory.kt @@ -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))