IJPL-866 separate product modules on a class loader level

GitOrigin-RevId: 5bfe0607aec78d560348b0fb749fe73ad6bb4c2a
This commit is contained in:
Vladimir Krivosheev
2024-03-31 21:08:04 +03:00
committed by intellij-monorepo-bot
parent 6c47a866c4
commit 6fb57f5b2d
16 changed files with 235 additions and 86 deletions
@@ -1,12 +1,12 @@
// 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.
@file:Suppress("unused", "ReplaceJavaStaticMethodWithKotlinAnalog")
@file:JvmName("DevMainImpl")
package org.jetbrains.intellij.build.devServer
import com.intellij.openapi.application.PathManager
import com.intellij.platform.diagnostic.telemetry.exporters.BatchSpanProcessor
import com.intellij.util.SystemProperties
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.SystemProperties
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.sdk.OpenTelemetrySdk
@@ -17,6 +17,10 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import org.jetbrains.intellij.build.ConsoleSpanExporter
import org.jetbrains.intellij.build.dependencies.BuildDependenciesDownloader
import org.jetbrains.intellij.build.dev.BuildRequest
import org.jetbrains.intellij.build.dev.buildProductInProcess
import org.jetbrains.intellij.build.dev.getAdditionalModules
import org.jetbrains.intellij.build.dev.getIdeSystemProperties
import org.jetbrains.intellij.build.traceManagerInitializer
import java.io.File
import java.nio.file.Path
@@ -47,21 +51,23 @@ fun buildDevMain(): Collection<Path> {
tracer to spanProcessor
}
buildProductInProcess(BuildRequest(
platformPrefix = System.getProperty("idea.platform.prefix", "idea"),
additionalModules = getAdditionalModules()?.toList() ?: emptyList(),
homePath = ideaProjectRoot,
keepHttpClient = false,
platformClassPathConsumer = { classPath, runDir ->
newClassPath = classPath
homePath = runDir.toString().replace(File.separator, "/")
buildProductInProcess(
BuildRequest(
platformPrefix = System.getProperty("idea.platform.prefix", "idea"),
additionalModules = getAdditionalModules()?.toList() ?: emptyList(),
homePath = ideaProjectRoot,
keepHttpClient = false,
platformClassPathConsumer = { classPath, runDir ->
newClassPath = classPath
homePath = runDir.toString().replace(File.separator, "/")
for ((name, value) in getIdeSystemProperties(runDir)) {
System.setProperty(name, value)
}
},
generateRuntimeModuleRepository = SystemProperties.getBooleanProperty("intellij.build.generate.runtime.module.repository", false)
))
for ((name, value) in getIdeSystemProperties(runDir)) {
System.setProperty(name, value)
}
},
generateRuntimeModuleRepository = SystemProperties.getBooleanProperty("intellij.build.generate.runtime.module.repository", false),
)
)
}
finally {
batchSpanProcessorScope.cancel()
@@ -284,5 +284,6 @@
<orderEntry type="library" scope="RUNTIME" name="jackson-module-kotlin" level="project" />
<orderEntry type="library" scope="RUNTIME" name="jackson-dataformat-yaml" level="project" />
<orderEntry type="library" name="ion" level="project" />
<orderEntry type="module" module-name="intellij.platform.boot" />
</component>
</module>
@@ -1,7 +1,7 @@
// 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", "ReplacePutWithAssignment", "LiftReturnOrAssignment")
package org.jetbrains.intellij.build.devServer
package org.jetbrains.intellij.build.dev
import com.intellij.platform.diagnostic.telemetry.helpers.useWithScope
import kotlinx.coroutines.NonCancellable
@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplacePutWithAssignment")
package org.jetbrains.intellij.build.devServer
package org.jetbrains.intellij.build.dev
import com.dynatrace.hash4j.hashing.HashFunnel
import com.dynatrace.hash4j.hashing.Hashing
@@ -41,12 +41,11 @@ import java.time.temporal.TemporalAdjusters
import kotlin.String
import kotlin.time.Duration.Companion.seconds
private val isUnpackedDist = System.getProperty("idea.dev.build.unpacked").toBoolean()
data class BuildRequest(
@JvmField val platformPrefix: String,
@JvmField val additionalModules: List<String>,
@JvmField val homePath: Path,
@JvmField val devRootPath: Path = homePath.normalize().toAbsolutePath().resolve("out/dev-run"),
@JvmField val productionClassOutput: Path = Path.of(System.getenv("CLASSES_DIR")
?: homePath.resolve("out/classes/production").toString()).toAbsolutePath(),
@JvmField val keepHttpClient: Boolean = true,
@@ -57,6 +56,8 @@ data class BuildRequest(
* and its generation makes build a little longer, so it should be enabled only if needed.
*/
@JvmField val generateRuntimeModuleRepository: Boolean = false,
@JvmField val isUnpackedDist: Boolean = System.getProperty("idea.dev.build.unpacked").toBoolean()
) {
override fun toString(): String {
return "BuildRequest(platformPrefix='$platformPrefix', " +
@@ -70,7 +71,7 @@ data class BuildRequest(
@OptIn(ExperimentalCoroutinesApi::class)
internal suspend fun buildProduct(productConfiguration: ProductConfiguration, request: BuildRequest): Path {
val rootDir = withContext(Dispatchers.IO) {
val rootDir = request.homePath.normalize().toAbsolutePath().resolve("out/dev-run")
val rootDir = request.devRootPath
// if symlinked to ram disk, use a real path for performance reasons and avoid any issues in ant/other code
if (Files.exists(rootDir)) {
// toRealPath must be called only on existing file
@@ -108,7 +109,7 @@ internal suspend fun buildProduct(productConfiguration: ProductConfiguration, re
val runDir = buildDir.resolve(productDirNameWithoutClassifier)
val context = createBuildContext(
productConfiguration = productConfiguration,
createProductProperties = { createProductProperties(productConfiguration = productConfiguration, request = request) },
request = request,
runDir = runDir,
buildDir = buildDir,
@@ -160,13 +161,13 @@ internal suspend fun buildProduct(productConfiguration: ProductConfiguration, re
launch {
val (pluginEntries, additionalEntries) = pluginDistributionEntriesDeferred.await()
spanBuilder("generate plugin classpath").useWithScope(Dispatchers.IO) {
val mainData = generatePluginClassPath(pluginEntries, writeDescriptor = !isUnpackedDist)
val additionalData = additionalEntries?.let { generatePluginClassPathFromFiles(it, writeDescriptor = !isUnpackedDist) }
val mainData = generatePluginClassPath(pluginEntries, writeDescriptor = !request.isUnpackedDist)
val additionalData = additionalEntries?.let { generatePluginClassPathFromFiles(it, writeDescriptor = !request.isUnpackedDist) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = pluginEntries.size + (additionalEntries?.size ?: 0)
writePluginClassPathHeader(out = out, isJarOnly = !isUnpackedDist, pluginCount = pluginCount)
writePluginClassPathHeader(out = out, isJarOnly = !request.isUnpackedDist, pluginCount = pluginCount)
out.write(mainData)
additionalData?.let { out.write(it) }
out.close()
@@ -332,7 +333,7 @@ private suspend fun buildPlugins(
}
private suspend fun createBuildContext(
productConfiguration: ProductConfiguration,
createProductProperties: suspend () -> ProductProperties,
request: BuildRequest,
runDir: Path,
jarCacheDir: Path,
@@ -342,7 +343,7 @@ private suspend fun createBuildContext(
// ~1 second
val productProperties = async {
withTimeout(30.seconds) {
createProductProperties(productConfiguration = productConfiguration, request = request)
createProductProperties()
}
}
@@ -369,7 +370,7 @@ private suspend fun createBuildContext(
BuildOptions.FUS_METADATA_BUNDLE_STEP,
)
if (isUnpackedDist && options.enableEmbeddedJetBrainsClient) {
if (request.isUnpackedDist && options.enableEmbeddedJetBrainsClient) {
options.enableEmbeddedJetBrainsClient = false
}
@@ -497,7 +498,7 @@ private fun getBundledMainModuleNames(productProperties: ProductProperties, addi
return LinkedHashSet(productProperties.productLayout.bundledPluginModules) + additionalModules
}
internal fun getAdditionalModules(): Sequence<String>? {
fun getAdditionalModules(): Sequence<String>? {
return System.getProperty("additional.modules")?.splitToSequence(',')?.map(String::trim)?.filter { it.isNotEmpty() }
}
@@ -2,7 +2,7 @@
@file:Suppress("PrivatePropertyName", "LiftReturnOrAssignment")
@file:OptIn(ExperimentalCoroutinesApi::class)
package org.jetbrains.intellij.build.devServer
package org.jetbrains.intellij.build.dev
import com.intellij.platform.diagnostic.telemetry.helpers.useWithScope
import io.opentelemetry.api.common.AttributeKey
@@ -54,6 +54,8 @@ sealed class BaseLayout {
fun hasLibrary(name: String): Boolean = includedProjectLibraries.any { it.libraryName == name }
fun findProjectLibrary(name: String): ProjectLibraryData? = includedProjectLibraries.firstOrNull { it.libraryName == name }
@TestOnly
fun isLibraryExcluded(name: String): Boolean = excludedLibraries.get(null)?.contains(name) ?: false
@@ -1,6 +1,7 @@
// 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.impl
import com.intellij.openapi.application.PathManager
import com.intellij.platform.diagnostic.telemetry.helpers.use
import com.intellij.platform.diagnostic.telemetry.helpers.useWithScope
import com.intellij.util.JavaModuleOptions
@@ -83,17 +84,20 @@ suspend fun runIdea(context: CompilationContext,
onError = onError)
}
suspend fun runApplicationStarter(context: BuildContext,
tempDir: Path,
ideClasspath: Set<String>,
arguments: List<String>,
systemProperties: Map<String, Any> = emptyMap(),
vmOptions: List<String> = emptyList(),
timeout: Duration = DEFAULT_TIMEOUT) {
suspend fun runApplicationStarter(
context: BuildContext,
tempDir: Path,
ideClasspath: Collection<String>,
arguments: List<String>,
systemProperties: Map<String, Any> = emptyMap(),
vmOptions: List<String> = emptyList(),
homePath: Path = context.paths.projectHome,
timeout: Duration = DEFAULT_TIMEOUT,
) {
Files.createDirectories(tempDir)
val jvmArgs = mutableListOf<String>()
val systemDir = tempDir.resolve("system")
BuildUtils.addVmProperty(jvmArgs, "idea.home.path", context.paths.projectHome.toString())
BuildUtils.addVmProperty(jvmArgs, PathManager.PROPERTY_HOME_PATH, homePath.toString())
BuildUtils.addVmProperty(jvmArgs, "idea.system.path", systemDir.toString())
BuildUtils.addVmProperty(jvmArgs, "idea.config.path", "$tempDir/config")
BuildUtils.addVmProperty(jvmArgs, "idea.builtin.server.disabled", "true")
@@ -120,12 +124,14 @@ suspend fun runApplicationStarter(context: BuildContext,
}
}
disableCompatibleIgnoredPlugins(context = context, configDir = tempDir.resolve("config"), explicitlyEnabledPlugins = additionalPluginIds)
runIdea(context = context,
mainClass = context.productProperties.mainClassName,
args = arguments,
jvmArgs = jvmArgs,
classPath = effectiveIdeClasspath.toList(),
timeout = timeout) {
runIdea(
context = context,
mainClass = context.productProperties.mainClassName,
args = arguments,
jvmArgs = jvmArgs,
classPath = effectiveIdeClasspath.toList(),
timeout = timeout
) {
val logFile = systemDir.resolve("log").resolve("idea.log")
if (Files.exists(logFile)) {
val logFileToPublish = Files.createTempFile("idea-", ".log")
@@ -29,6 +29,9 @@ import org.jetbrains.idea.maven.aether.ArtifactRepositoryManager
import org.jetbrains.idea.maven.aether.ProgressConsumer
import org.jetbrains.intellij.build.*
import org.jetbrains.intellij.build.TraceManager.spanBuilder
import org.jetbrains.intellij.build.dev.BuildRequest
import org.jetbrains.intellij.build.dev.buildProductInProcess
import org.jetbrains.intellij.build.dev.getIdeSystemProperties
import org.jetbrains.intellij.build.impl.productInfo.PRODUCT_INFO_FILE_NAME
import org.jetbrains.intellij.build.impl.productInfo.ProductInfoLaunchData
import org.jetbrains.intellij.build.impl.productInfo.checkInArchive
@@ -660,17 +663,41 @@ private suspend fun compileModulesForDistribution(context: BuildContext): Distri
compilationTasks.compileModules(moduleNames = it)
localizeModules(context, moduleNames = it)
}
val builtinModuleData = spanBuilder("build provided module list").useWithScope {
val ideClasspath = createIdeClassPath(platform = platform, context = context)
//val ideClasspath = createIdeClassPath(platform = platform, context = context)
var homePath: Path? = null
var newClassPath: Collection<Path>? = null
val systemProperties = HashMap<String, Any>()
val tempDir = context.paths.tempDir.resolve("builtinModules")
buildProductInProcess(
BuildRequest(
devRootPath = tempDir,
isUnpackedDist = true,
platformPrefix = context.productProperties.platformPrefix ?: "idea",
additionalModules = emptyList(),
homePath = context.paths.projectHome,
platformClassPathConsumer = { classPath, runDir ->
newClassPath = classPath
homePath = runDir
for ((name, value) in getIdeSystemProperties(runDir)) {
systemProperties.put(name, value)
}
}
)
)
Files.deleteIfExists(providedModuleFile)
// start the product in headless mode using com.intellij.ide.plugins.BundledPluginsLister
runApplicationStarter(
context = context,
tempDir = context.paths.tempDir.resolve("builtinModules"),
ideClasspath = ideClasspath,
tempDir = tempDir,
ideClasspath = newClassPath!!.map { it.toString() },
arguments = listOf("listBundledPlugins", providedModuleFile.toString()),
timeout = 30.seconds
timeout = 30.seconds,
homePath = homePath ?: context.paths.projectHome,
systemProperties = systemProperties,
)
context.productProperties.customizeBuiltinModules(context = context, builtinModulesFile = providedModuleFile)
try {
@@ -390,7 +390,7 @@ class JarPackager private constructor(
}
moduleSources.add(DirSource(dir = moduleOutDir, excludes = excludes))
if (layout != null && !item.relativeOutputFile.contains('/') && !layout.modulesWithExcludedModuleLibraries.contains(moduleName)) {
if (layout != null && !layout.modulesWithExcludedModuleLibraries.contains(moduleName)) {
val jarAsset = if (packToDir) {
getJarAsset(
targetFile = outFile,
@@ -438,8 +438,8 @@ class JarPackager private constructor(
libToMetadata.put(element.library!!, projectLibraryData)
}
else if (isLibraryAlwaysPackedIntoPlugin(libName)) {
check(!platformLayout!!.hasLibrary(libName)) {
"Library $libName must not be included into platform layout"
platformLayout!!.findProjectLibrary(libName)?.let {
throw IllegalStateException("Library $libName must not be included into platform layout: $it")
}
if (layout.hasLibrary(libName)) {
@@ -469,11 +469,7 @@ class JarPackager private constructor(
for (i in (files.size - 1) downTo 0) {
val file = files.get(i)
val fileName = file.fileName.toString()
val jarName = when (layout) {
is PluginLayout -> layout.getMainJarName()
is PlatformLayout -> PlatformJarNames.APP_JAR
}
if (item.relativeOutputFile.contains('/') || isSeparateJar(fileName = fileName, file = file, jarName = jarName)) {
if (isSeparateJar(fileName = fileName, file = file, jarPath = asset.relativePath)) {
files.removeAt(i)
addLibrary(
library = library,
@@ -728,7 +724,11 @@ class JarPackager private constructor(
}
}
private suspend fun isSeparateJar(fileName: String, file: Path, jarName: String): Boolean {
private suspend fun isSeparateJar(fileName: String, file: Path, jarPath: String): Boolean {
if (jarPath.contains('/') && !jarPath.startsWith("modules/")) {
return true
}
if (fileName.endsWith("-rt.jar") || fileName.contains("-agent")) {
return true
}
@@ -744,7 +744,7 @@ private suspend fun isSeparateJar(fileName: String, file: Path, jarName: String)
}
}
if (result) {
Span.current().addEvent("$fileName contains file '$filePreventingMerging' that prevent its merging into $jarName")
Span.current().addEvent("$fileName contains file '$filePreventingMerging' that prevent its merging into $jarPath")
}
return result
}
@@ -1,4 +1,4 @@
// Copyright 2000-2022 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.impl
import org.jetbrains.annotations.ApiStatus.Internal
@@ -36,9 +36,20 @@ class PlatformLayout: BaseLayout() {
excludedProjectLibraries.add(libraryName)
}
inline fun collectProjectLibrariesFromIncludedModules(context: BuildContext, consumer: (JpsLibrary, JpsModule) -> Unit) {
fun collectProjectLibrariesFromIncludedModules(context: BuildContext, consumer: (JpsLibrary, JpsModule) -> Unit) {
val libsToUnpack = includedProjectLibraries.mapTo(HashSet()) { it.libraryName }
for (moduleName in includedModules.asSequence().map { it.moduleName }.distinct()) {
val uniqueGuard = HashSet<String>()
for (item in includedModules) {
// libraries are packed into product module
if (item.reason == ModuleIncludeReasons.PRODUCT_MODULES) {
continue
}
val moduleName = item.moduleName
if (!uniqueGuard.add(moduleName)) {
continue
}
val module = context.findRequiredModule(moduleName)
for (library in JpsJavaExtensionService.dependencies(module).includedIn(JpsJavaClasspathKind.PRODUCTION_RUNTIME).libraries) {
if (!isSkippedLibrary(library, libsToUnpack)) {
@@ -48,7 +59,7 @@ class PlatformLayout: BaseLayout() {
}
}
fun isSkippedLibrary(library: JpsLibrary, libsToUnpack: Collection<String>): Boolean {
private fun isSkippedLibrary(library: JpsLibrary, libsToUnpack: Collection<String>): Boolean {
return library.createReference().parentReference is JpsModuleReference ||
libsToUnpack.contains(library.name) ||
excludedProjectLibraries.contains(library.name)
@@ -9,7 +9,6 @@ import io.opentelemetry.api.trace.Span
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -99,9 +98,9 @@ private val PLATFORM_IMPLEMENTATION_MODULES = persistentListOf(
"intellij.platform.util.commonsLangV2Shim"
)
internal val PLATFORM_CUSTOM_PACK_MODE: Map<String, LibraryPackMode> = persistentMapOf(
"jetbrains-annotations" to LibraryPackMode.STANDALONE_SEPARATE_WITHOUT_VERSION_NAME,
"intellij-coverage" to LibraryPackMode.STANDALONE_SEPARATE,
internal val PLATFORM_CUSTOM_PACK_MODE: Map<String, LibraryPackMode> = java.util.Map.of(
"jetbrains-annotations", LibraryPackMode.STANDALONE_SEPARATE_WITHOUT_VERSION_NAME,
"intellij-coverage", LibraryPackMode.STANDALONE_SEPARATE,
)
internal fun collectPlatformModules(to: MutableCollection<String>) {
@@ -323,7 +322,8 @@ internal suspend fun createPlatformLayout(addPlatformCoverage: Boolean,
return layout
}
fun isLibraryAlwaysPackedIntoPlugin(name: String): Boolean = name == "flexmark" || name == "okhttp"
// sqlite - used by DB and "import settings" (temporarily)
fun isLibraryAlwaysPackedIntoPlugin(name: String): Boolean = name == "flexmark" || name == "okhttp" || name == "sqlite"
internal fun computeProjectLibsUsedByPlugins(enabledPluginModules: Set<String>, context: BuildContext): SortedSet<ProjectLibraryData> {
val result = ObjectLinkedOpenHashSet<ProjectLibraryData>()
@@ -467,10 +467,7 @@ private fun compute(list: List<Pair<String, PersistentList<String>>>,
}
if (oldSize != result.size) {
compute(list = result.subList(oldSize, result.size).sortedBy { it.first },
context = context,
unique = unique,
result = result)
compute(list = result.subList(oldSize, result.size).sortedBy { it.first }, context = context, unique = unique, result = result)
}
}
@@ -507,8 +504,17 @@ private suspend fun getProductPluginContentModules(context: BuildContext, produc
private fun collectProductModules(content: XmlElement, result: LinkedHashSet<ModuleItem>) {
for (module in content.children("module")) {
result.add(ModuleItem(moduleName = module.attributes.get("name") ?: continue,
relativeOutputFile = "modules.jar",
reason = "productModule"))
val moduleName = module.attributes.get("name") ?: continue
result.add(
ModuleItem(
moduleName = moduleName,
relativeOutputFile = "modules/$moduleName.jar",
reason = ModuleIncludeReasons.PRODUCT_MODULES
),
)
}
}
internal object ModuleIncludeReasons {
const val PRODUCT_MODULES: String = "productModule"
}
@@ -37,5 +37,5 @@ class ProjectLibraryData(
override fun hashCode() = libraryName.hashCode()
override fun toString() = "ProjectLibraryData(name=$libraryName, packMode=$packMode, relativeOutputPath=$outPath)"
override fun toString() = "ProjectLibraryData(name=$libraryName, packMode=$packMode, relativeOutputPath=$outPath, reason=$reason)"
}
@@ -194,6 +194,20 @@ class ClassLoaderConfigurator(
}
private fun configureCorePluginModuleClassLoader(module: IdeaPluginDescriptorImpl, deps: Array<IdeaPluginDescriptorImpl>) {
val jarFiles = module.jarFiles
if (jarFiles != null) {
module.pluginClassLoader = PluginClassLoader(
classPath = ClassPath(jarFiles, DEFAULT_CLASSLOADER_CONFIGURATION, resourceFileFactory, false),
parents = deps,
pluginDescriptor = module,
coreLoader = coreLoader,
resolveScopeManager = null,
packagePrefix = module.packagePrefix,
libDirectories = ArrayList(),
)
return
}
val coreUrlClassLoader = getCoreUrlClassLoaderIfPossible()
if (coreUrlClassLoader == null) {
setPluginClassLoaderForModuleAndOldSubDescriptors(module, coreLoader)
@@ -1,12 +1,15 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.plugins
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.util.lang.UrlClassLoader
import com.intellij.util.xml.dom.createNonCoalescingXmlStreamReader
import org.codehaus.stax2.XMLStreamReader2
internal class ClassPathXmlPathResolver(private val classLoader: ClassLoader, @JvmField val isRunningFromSources: Boolean) : PathResolver {
internal class ClassPathXmlPathResolver(
private val classLoader: ClassLoader,
@JvmField val isRunningFromSources: Boolean,
) : PathResolver {
override val isFlat: Boolean
get() = true
@@ -68,17 +71,17 @@ internal class ClassPathXmlPathResolver(private val classLoader: ClassLoader, @J
return descriptor
}
val logger = Logger.getInstance(ClassPathXmlPathResolver::class.java)
val log = logger<ClassPathXmlPathResolver>()
val moduleName = path.removeSuffix(".xml")
if (isRunningFromSources && path.startsWith("intellij.") && dataLoader.emptyDescriptorIfCannotResolve) {
logger.trace("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader). ")
log.trace("Cannot resolve $path (dataLoader=$dataLoader, classLoader=$classLoader). ")
val descriptor = RawPluginDescriptor()
descriptor.`package` = "unresolved.$moduleName"
return descriptor
}
if (ProductLoadingStrategy.strategy.isOptionalProductModule(moduleName)) {
//this check won't be needed when we are able to load optional modules directly from product-modules.xml
logger.debug("Skip module '$path' since its descriptor cannot be found and it's optional")
log.debug("Skip module '$path' since its descriptor cannot be found and it's optional")
return RawPluginDescriptor().apply { `package` = "unresolved.$moduleName" }
}
@@ -194,7 +194,7 @@ class IdeaPluginDescriptorImpl(
override fun getPluginPath(): Path = path
private fun createSub(
internal fun createSub(
raw: RawPluginDescriptor,
descriptorPath: String,
context: DescriptorListLoadingContext,
@@ -232,7 +232,7 @@ class IdeaPluginDescriptorImpl(
// must be first because merged into raw descriptor
for (module in content.modules) {
val subDescriptorFile = module.configFile ?: "${module.name}.xml"
val subDescriptor = createSub(
module.descriptor = createSub(
raw = pathResolver.resolveModuleFile(
readContext = context,
dataLoader = dataLoader,
@@ -243,9 +243,17 @@ class IdeaPluginDescriptorImpl(
context = context,
moduleName = module.name,
)
module.descriptor = subDescriptor
}
initByRawDescriptor(raw = raw, context = context, pathResolver = pathResolver, dataLoader = dataLoader)
}
internal fun initByRawDescriptor(
raw: RawPluginDescriptor,
context: DescriptorListLoadingContext,
pathResolver: PathResolver,
dataLoader: DataLoader,
) {
if (raw.resourceBundleBaseName != null) {
if (id == PluginManagerCore.CORE_ID) {
LOG.warn("<resource-bundle>${raw.resourceBundleBaseName}</resource-bundle> tag is found in an xml descriptor" +
@@ -617,6 +617,7 @@ private fun CoroutineScope.loadCoreModules(
context = context,
pathResolver = pathResolver,
useCoreClassLoader = useCoreClassLoader,
pool = pool,
)!!
})
}
@@ -628,6 +629,7 @@ private fun CoroutineScope.loadCoreModules(
path = "${PluginManagerCore.META_INF}$fileName",
classLoader = classLoader,
context = context,
pool = pool,
pathResolver = pathResolver,
useCoreClassLoader = useCoreClassLoader,
)
@@ -663,6 +665,7 @@ private fun loadCoreProductPlugin(
context: DescriptorListLoadingContext,
pathResolver: ClassPathXmlPathResolver,
useCoreClassLoader: Boolean,
pool: ZipFilePool,
): IdeaPluginDescriptorImpl? {
val reader = getResourceReader(path, classLoader) ?: return null
val dataLoader = object : DataLoader {
@@ -682,16 +685,76 @@ private fun loadCoreProductPlugin(
includeBase = null,
readInto = null,
)
val libDir = Paths.get(PathManager.getLibPath())
val descriptor = IdeaPluginDescriptorImpl(
raw = raw,
path = Paths.get(PathManager.getLibPath()),
path = libDir,
isBundled = true,
id = null,
moduleName = null,
useCoreClassLoader = useCoreClassLoader,
)
context.debugData?.recordDescriptorPath(descriptor, raw, path)
descriptor.readExternal(raw = raw, pathResolver = pathResolver, context = context, dataLoader = dataLoader)
val moduleDir = libDir.resolve("modules")
val moduleDirExists = Files.isDirectory(moduleDir)
for (module in descriptor.content.modules) {
check(module.configFile == null) {
"product module must not use `/` notation for module descriptor file (configFile=${module.configFile})"
}
val moduleName = module.name
var moduleRaw: RawPluginDescriptor?
val subDescriptorFile = "$moduleName.xml"
if (moduleDirExists && !pathResolver.isRunningFromSources && moduleName.startsWith("intellij.")) {
val jarFile = Paths.get(PathManager.getLibPath(), "modules", "$moduleName.jar")
val resolver = pool.load(jarFile)
try {
moduleRaw = resolver.loadZipEntry(subDescriptorFile)?.let {
readModuleDescriptor(
input = it,
readContext = context,
pathResolver = pathResolver,
dataLoader = dataLoader,
includeBase = null,
readInto = null,
locationSource = jarFile.toString(),
)
}
}
finally {
(resolver as? Closeable)?.close()
}
if (moduleRaw != null) {
val subDescriptor = descriptor.createSub(
raw = moduleRaw,
descriptorPath = subDescriptorFile,
context = context,
moduleName = moduleName,
)
subDescriptor.jarFiles = Java11Shim.INSTANCE.listOf(jarFile)
module.descriptor = subDescriptor
continue
}
}
moduleRaw = pathResolver.resolveModuleFile(
readContext = context,
dataLoader = dataLoader,
path = subDescriptorFile,
readInto = null,
)
module.descriptor = descriptor.createSub(
raw = moduleRaw,
descriptorPath = subDescriptorFile,
context = context,
moduleName = moduleName,
)
}
descriptor.initByRawDescriptor(raw = raw, context = context, pathResolver = pathResolver, dataLoader = dataLoader)
return descriptor
}
@@ -818,12 +881,13 @@ fun testLoadDescriptorsFromClassPath(loader: ClassLoader): List<IdeaPluginDescri
)
val result = PluginLoadingResult(checkModuleDependencies = false)
result.addAll(toSequence(runBlocking {
val pool = ZipFilePool.POOL?.takeIf { !context.transient } ?: NonShareableJavaZipFilePool()
loadDescriptorsFromClassPath(
urlToFilename = urlToFilename,
context = context,
pathResolver = ClassPathXmlPathResolver(loader, isRunningFromSources = false),
useCoreClassLoader = true,
pool = ZipFilePool.POOL?.takeIf { !context.transient } ?: NonShareableJavaZipFilePool(),
pool = pool,
)
}, isMainProcess()), overrideUseIfCompatible = false, productBuildNumber = buildNumber)
return result.enabledPlugins