From ff1fa7481d318164c94a80f5a5b657b6c80ebb21 Mon Sep 17 00:00:00 2001 From: Ilia Kirianovskii Date: Mon, 29 Sep 2025 10:14:12 +0200 Subject: [PATCH] [build-scripts] Don't use multiple output roots in ArchivedCompilationContextUtil#getArchivedCompiledClassesMapping (IJI-3011) Partly reverts commit f3ba70cad4845ef3bf50754aaf1cf6b9959fbcd2. GitOrigin-RevId: b8e715e75b0331dff2865f77cb91fdd7f089b907 --- .../tools/launch/ide/ClassPathBuilder.kt | 6 +- .../impl/BuildProcessClasspathManager.kt | 31 +++++----- .../jps/cmdline/ClasspathBootstrap.java | 7 +-- .../build/impl/ModuleStructureValidator.kt | 25 +++------ .../execution/eel/JavaMainClassExecutor.kt | 8 +-- .../tests/MonorepoProjectStructure.kt | 30 ++++------ .../monorepo/tests/api/ModuleApi.kt | 6 +- .../openapi/application/ex/PathManagerEx.java | 6 +- .../ArchivedCompilationContextUtil.kt | 16 +++--- .../test/UrlClassLoaderSplitPackageTest.kt | 24 ++++---- .../testSrc/LibrariesRequiredForWorkspace.kt | 31 ++++------ .../incremental/groovy/GroovyRtJarPaths.java | 8 +-- .../configuration/JUnitModulePathTest.java | 5 +- .../intellij/execution/junit/TestObject.java | 12 ++-- .../idea/maven/maven3/Maven3Support.java | 13 ++--- .../idea/maven/maven4/Maven4Support.java | 9 ++- .../idea/maven/server/MavenIndexerCMDState.kt | 6 +- .../maven/server/MavenServerManagerImpl.kt | 56 +------------------ .../jetbrains/idea/maven/utils/MavenUtil.kt | 11 ++-- tools/apiDump/src/impl.kt | 21 ++++--- 20 files changed, 125 insertions(+), 206 deletions(-) diff --git a/build/launch/src/com/intellij/tools/launch/ide/ClassPathBuilder.kt b/build/launch/src/com/intellij/tools/launch/ide/ClassPathBuilder.kt index 8dd49ececa87..8c1e3282f26e 100644 --- a/build/launch/src/com/intellij/tools/launch/ide/ClassPathBuilder.kt +++ b/build/launch/src/com/intellij/tools/launch/ide/ClassPathBuilder.kt @@ -114,10 +114,10 @@ class ClassPathBuilder(private val paths: PathsProvider, private val modulesToSc private fun Collection.replaceWithArchivedIfNeeded(): Collection { val mapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping ?: return this - return flatMap { path -> - if (Files.isRegularFile(path)) listOf(path) + return map { path -> + if (Files.isRegularFile(path)) path // path is absolute, mapping contains only the last two path elements - else mapping[path.parent.name + "/" + path.name]?.map(Path::of) ?: listOf(path) + else mapping[path.parent.name + "/" + path.name]?.let { Path.of(it) } ?: path } } } \ No newline at end of file diff --git a/java/compiler/impl/src/com/intellij/compiler/server/impl/BuildProcessClasspathManager.kt b/java/compiler/impl/src/com/intellij/compiler/server/impl/BuildProcessClasspathManager.kt index 22758fb5e252..437765890d93 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/impl/BuildProcessClasspathManager.kt +++ b/java/compiler/impl/src/com/intellij/compiler/server/impl/BuildProcessClasspathManager.kt @@ -137,21 +137,21 @@ class BuildProcessClasspathManager(parentDisposable: Disposable) { private val LOG = logger() -private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, baseFile: Path): List { +private fun findClassesRoot(relativePath: String, plugin: IdeaPluginDescriptor, baseFile: Path): String? { val jarFile = baseFile.resolve("lib/$relativePath") if (Files.exists(jarFile)) { - return listOf(jarFile.toString()) + return jarFile.toString() } if (AppMode.isRunningFromDevBuild()) { check(Files.isDirectory(baseFile)) - return listOf(baseFile.toString()) + return baseFile.toString() } // ... 'plugin run configuration': all module outputs are copied to 'classes' folder val classesDir = baseFile.resolve("classes") if (Files.isDirectory(classesDir)) { - return listOf(classesDir.toString()) + return classesDir.toString() } // development mode @@ -163,9 +163,10 @@ private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, if (mapping != null) { // baseFile is ".../idea-compile-parts-v2/production//" // We should take ".../idea-compile-parts-v2/production//.jar" - mapping["production/$moduleName"]?.let { - if (it.map(Path::of).all(Files::exists)) { - return it + val moduleJar = mapping["production/$moduleName"]?.let(Path::of) + if (moduleJar != null) { + if (Files.exists(moduleJar)) { + return moduleJar.toString() } } } @@ -177,7 +178,7 @@ private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, } val moduleDir = baseOutputDir.resolve(moduleName) if (Files.isDirectory(moduleDir)) { - return listOf(moduleDir.toString()) + return moduleDir.toString() } } // ... try "/lib/", assuming that is a module library committed to VCS @@ -185,7 +186,7 @@ private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, if (pluginDir != null) { val libraryFile = File(pluginDir, "lib/" + PathUtilRt.getFileName(relativePath)) if (libraryFile.exists()) { - return listOf(libraryFile.path) + return libraryFile.path } } // ... look for on the classpath, assuming that is an external (read: Maven) library @@ -193,7 +194,7 @@ private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, val urls = BuildProcessClasspathManager::class.java.classLoader.getResources(JarFile.MANIFEST_NAME).asSequence() val jarPath = urls.mapNotNull { URLUtil.splitJarUrl(it.file)?.first }.firstOrNull { PathUtilRt.getFileName(it) == relativePath } if (jarPath != null) { - return listOf(jarPath) + return jarPath } } catch (ignored: IOException) { @@ -201,7 +202,7 @@ private fun findClassesRoots(relativePath: String, plugin: IdeaPluginDescriptor, } LOG.error(PluginException("Cannot add '$relativePath' from '${plugin.name} ${plugin.version}'" + " (plugin path: $baseFile) to compiler classpath", plugin.pluginId)) - return emptyList() + return null } private fun computeCompileServerPluginsClasspath(): List { @@ -216,12 +217,12 @@ private fun computeCompileServerPluginsClasspath(): List { LOG.trace { "$baseFile added to process classpath from $pluginId" } } else { - serverPlugin.classpath.splitToSequence(';').flatMapTo(classpath) { - val classesRoots = findClassesRoots(relativePath = it, plugin = plugin, baseFile = baseFile) - for (classesRoot in classesRoots) { + serverPlugin.classpath.splitToSequence(';').mapNotNullTo(classpath) { + val classesRoot = findClassesRoot(relativePath = it, plugin = plugin, baseFile = baseFile) + if (classesRoot != null) { LOG.trace { "$classesRoot added to process classpath from $pluginId" } } - classesRoots + classesRoot } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index 89b77c0622c0..bb098b4b5f09 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -306,12 +306,9 @@ public final class ClasspathBootstrap { } else { var relevantJarsRoot = ArchivedCompilationContextUtil.getArchivedCompiledClassesLocation(); - Map> mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); + Map mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); if (relevantJarsRoot != null && mapping != null && instrumentationUtilPath.startsWith(relevantJarsRoot)) { - List result = new ArrayList<>(); - result.add(instrumentationUtilPath); - result.addAll(mapping.get("production/intellij.java.compiler.instrumentationUtil.java8")); - return result; + return Arrays.asList(instrumentationUtilPath, mapping.get("production/intellij.java.compiler.instrumentationUtil.java8")); } //running from jars: intellij.java.compiler.instrumentationUtil.java8 is located in the same jar return Collections.singletonList(instrumentationUtilPath); diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleStructureValidator.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleStructureValidator.kt index 3efd107add11..c03b6982aa2f 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleStructureValidator.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/ModuleStructureValidator.kt @@ -14,7 +14,6 @@ import org.jetbrains.jps.model.module.JpsLibraryDependency import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.module.JpsModuleDependency import org.jetbrains.jps.util.JpsPathUtil -import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.Path @@ -239,9 +238,9 @@ class ModuleStructureValidator(private val context: BuildContext, private val al } var hasOutput = false - jpsModule.processProductionOutput { outputRoots -> - hasOutput = outputRoots.all(Path::exists) - outputRoots.forEach { outputRoot -> outputRoot + jpsModule.processProductionOutput { outputRoot -> + hasOutput = outputRoot.exists() + outputRoot .walk() .filter { path -> path.isRegularFile() && path.name.endsWith(".class") && !path.name.endsWith("Kt.class") @@ -250,7 +249,6 @@ class ModuleStructureValidator(private val context: BuildContext, private val al val className = removeSuffixStrict(normalizedPath, ".class").replace('/', '.') classes.add(className) } - } } if (!hasOutput) { throw IllegalStateException("Module '$moduleName' output is missing while module has source roots") @@ -322,22 +320,17 @@ class ModuleStructureValidator(private val context: BuildContext, private val al * Calls [processor] for the path containing the production output of [this@processModuleProductionOutput]. * Works both when module output is located in a directory and when it's packed in a JAR. */ -private fun JpsModule.processProductionOutput(processor: (outputRoots: List) -> T): T { +private fun JpsModule.processProductionOutput(processor: (outputRoot: Path) -> T): T { val archivedCompiledClassesMapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping - val outputJarPaths = archivedCompiledClassesMapping?.get("production/$name") - if (outputJarPaths == null) { + val outputJarPath = archivedCompiledClassesMapping?.get("production/$name") + if (outputJarPath == null) { val outputDirectoryPath = JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, false) ?: error("Output directory is not specified for '$name'") - return processor(listOf(outputDirectoryPath)) + return processor(outputDirectoryPath) } else { - outputJarPaths.map(Path::of).map(FileSystems::newFileSystem).let { - try { - return processor(it.map { it.rootDirectories.single() }) - } - finally { - it.forEach(FileSystem::close) - } + return FileSystems.newFileSystem(Path(outputJarPath)).use { + processor(it.rootDirectories.single()) } } } diff --git a/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt b/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt index 2162e2c30069..258b9ad5ac41 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt +++ b/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt @@ -90,7 +90,7 @@ internal class JavaMainClassExecutor(clazz: Class<*>, vararg args: String) { // archived compilation output val mapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping checkNotNull(mapping) { "Mapping cannot be null at this point" } - val key = mapping.entries.firstOrNull { (_, value) -> value.contains(jarPathForClass) }?.key + val key = mapping.entries.firstOrNull { (_, value) -> value == jarPathForClass }?.key if (key == null) { throw IllegalStateException("Cannot find path '$jarPathForClass' in mapping values:'$mapping'") } @@ -115,11 +115,11 @@ internal class JavaMainClassExecutor(clazz: Class<*>, vararg args: String) { // archived compilation output, assume we need 'production' output val mapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping checkNotNull(mapping) { "Mapping cannot be null at this point" } - return moduleNames.flatMap { + return moduleNames.mapNotNull { val key = "production/$it" val value = mapping[key] - if (value == null) logger.value.warn("Not found jars mapping for '$key'") - value?.map(Path::of) ?: emptyList() + if (value == null) logger.value.warn("Not found jar mapping for '$key'") + value?.let { Path(value) } } } else { diff --git a/platform/testFramework/monorepo/tests/MonorepoProjectStructure.kt b/platform/testFramework/monorepo/tests/MonorepoProjectStructure.kt index 129878b2ec7e..d728ae7d0a69 100644 --- a/platform/testFramework/monorepo/tests/MonorepoProjectStructure.kt +++ b/platform/testFramework/monorepo/tests/MonorepoProjectStructure.kt @@ -12,7 +12,6 @@ import org.jetbrains.jps.model.library.JpsLibrary import org.jetbrains.jps.model.library.JpsOrderRootType import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService -import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Path import kotlin.io.path.Path @@ -33,25 +32,20 @@ object MonorepoProjectStructure { fun JpsModule.hasProductionSources(): Boolean = getSourceRoots(JavaSourceRootType.SOURCE).iterator().hasNext() /** - * Calls [processor] for the paths containing the production output of [this@processModuleProductionOutput]. - * Works both when module output is located in a directory and when it's packed in JARs. + * Calls [processor] for the path containing the production output of [this@processModuleProductionOutput]. + * Works both when module output is located in a directory and when it's packed in a JAR. */ -fun JpsModule.processProductionOutputs(processor: (outputRoots: List) -> T): T { +fun JpsModule.processProductionOutput(processor: (outputRoot: Path) -> T): T { val archivedCompiledClassesMapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping - val outputJarPaths = archivedCompiledClassesMapping?.get("production/$name") - if (outputJarPaths == null) { + val outputJarPath = archivedCompiledClassesMapping?.get("production/$name") + if (outputJarPath == null) { val outputDirectoryPath = JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, false) ?: error("Output directory is not specified for '$name'") - return processor(listOf(outputDirectoryPath)) + return processor(outputDirectoryPath) } else { - outputJarPaths.map(Path::of).map(FileSystems::newFileSystem).let { - try { - return processor(it.map { it.rootDirectories.single() }) - } - finally { - it.forEach(FileSystem::close) - } + return FileSystems.newFileSystem(Path(outputJarPath)).use { + processor(it.rootDirectories.single()) } } } @@ -60,14 +54,14 @@ val JpsModule.productionOutputPaths: List get() { val archivedCompiledClassesMapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping val outputJarPath = archivedCompiledClassesMapping?.get("production/$name") - return outputJarPath?.map(Path::of) ?: listOf(JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, false) - ?: error("Output directory is not specified for '$name'")) + return outputJarPath?.let { listOf(Path(it)) } ?: listOf(JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, false) + ?: error("Output directory is not specified for '$name'")) } val JpsModule.testOutputPaths: List get() { val archivedCompiledClassesMapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping val outputJarPath = archivedCompiledClassesMapping?.get("test/$name") - return outputJarPath?.map(Path::of) ?: listOf(JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, true) - ?: error("Test output directory is not specified for '$name'")) + return outputJarPath?.let { listOf(Path(it)) } ?: listOf(JpsJavaExtensionService.getInstance().getOutputDirectoryPath(this, true) + ?: error("Test output directory is not specified for '$name'")) } diff --git a/platform/testFramework/monorepo/tests/api/ModuleApi.kt b/platform/testFramework/monorepo/tests/api/ModuleApi.kt index 2f931a930e2b..1fb82f0385c3 100644 --- a/platform/testFramework/monorepo/tests/api/ModuleApi.kt +++ b/platform/testFramework/monorepo/tests/api/ModuleApi.kt @@ -1,7 +1,7 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.platform.testFramework.monorepo.api -import com.intellij.platform.testFramework.monorepo.processProductionOutputs +import com.intellij.platform.testFramework.monorepo.processProductionOutput import com.intellij.tools.apiDump.API import com.intellij.tools.apiDump.api import com.intellij.tools.apiDump.emptyApiIndex @@ -47,8 +47,8 @@ class ModuleApi(private val cs: CoroutineScope) { acc + item } - return module.processProductionOutputs { outputRoots -> - api(dependencyIndex, outputRoots) + return module.processProductionOutput { outputRoot -> + api(dependencyIndex, outputRoot) } } } diff --git a/platform/testFramework/src/com/intellij/openapi/application/ex/PathManagerEx.java b/platform/testFramework/src/com/intellij/openapi/application/ex/PathManagerEx.java index 30a2b6413854..eca2a35f260e 100644 --- a/platform/testFramework/src/com/intellij/openapi/application/ex/PathManagerEx.java +++ b/platform/testFramework/src/com/intellij/openapi/application/ex/PathManagerEx.java @@ -326,7 +326,7 @@ public final class PathManagerEx { } if (!root.isDirectory()) { String relevantJarsRoot = ArchivedCompilationContextUtil.getArchivedCompiledClassesLocation(); - Map> mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); + Map mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); if (relevantJarsRoot != null && mapping != null && root.toPath().toAbsolutePath().startsWith(relevantJarsRoot)) { // .../idea-compile-parts-v2/test/intellij.java.compiler.tests/$sha256.jar String moduleName = root.getParentFile().getName(); @@ -336,8 +336,8 @@ public final class PathManagerEx { // .../out/bazel-out/jvm-fastbuild/bin/external/community+/java/compiler/compiler-tests_test_lib.jar return ContainerUtil.exists(getCommunityModules(), - s -> mapping.getOrDefault("production/" + s, Collections.emptyList()).contains(classRootPath) || - mapping.getOrDefault("test/" + s, Collections.emptyList()).contains(classRootPath)) + s -> Objects.equals(mapping.get("production/" + s), classRootPath) || + Objects.equals(mapping.get("test/" + s), classRootPath)) ? FileSystemLocation.COMMUNITY : FileSystemLocation.ULTIMATE; } //this means that clazz is located in a library; perhaps we should throw exception here diff --git a/platform/util/src/com/intellij/openapi/application/ArchivedCompilationContextUtil.kt b/platform/util/src/com/intellij/openapi/application/ArchivedCompilationContextUtil.kt index c6f0bf281cb6..1b145114896f 100644 --- a/platform/util/src/com/intellij/openapi/application/ArchivedCompilationContextUtil.kt +++ b/platform/util/src/com/intellij/openapi/application/ArchivedCompilationContextUtil.kt @@ -41,7 +41,7 @@ object ArchivedCompilationContextUtil { } @JvmStatic - val archivedCompiledClassesMapping: Map>? by lazy(LazyThreadSafetyMode.PUBLICATION) { + val archivedCompiledClassesMapping: Map? by lazy(LazyThreadSafetyMode.PUBLICATION) { /** * Returns a map of IntelliJ modules to .jar absolute paths, e.g.: * "production/intellij.platform.util" => ".../production/intellij.platform.util/$hash.jar" @@ -49,7 +49,7 @@ object ArchivedCompilationContextUtil { computeArchivedCompiledClassesMapping() } - private fun computeArchivedCompiledClassesMapping(): Map>? { + private fun computeArchivedCompiledClassesMapping(): Map? { val filePath = System.getProperty("intellij.test.jars.mapping.file") if (filePath.isNullOrBlank()) { if (getArchivedCompiledClassesLocationIfIsRunningFromBazelOut() != null) { @@ -65,19 +65,19 @@ object ArchivedCompilationContextUtil { log("Failed to load jars mappings from $filePath") return null } - val mapping: MutableMap> = mutableMapOf() + val mapping: MutableMap = mutableMapOf() for (line in lines) { val split = line.split("=", limit = 2) if (split.size < 2) { log("Ignored jars mapping line: $line") continue } - mapping[split[0]] = listOf(split[1]) + mapping[split[0]] = split[1] } return mapping } - private fun computeArchivedCompiledClassesMappingIfIsRunningFromBazelOut(): Map>? { + private fun computeArchivedCompiledClassesMappingIfIsRunningFromBazelOut(): Map? { val targetsFile: BazelTargetsInfo.TargetsFile try { targetsFile = BazelTargetsInfo.loadTargetsFileFromBazelTargetsJson(PathManager.getHomeDir()) @@ -87,13 +87,13 @@ object ArchivedCompilationContextUtil { return null } - val mapping: MutableMap> = mutableMapOf() + val mapping: MutableMap = mutableMapOf() targetsFile.modules.forEach { (moduleName, targetsFileModuleDescription) -> if (targetsFileModuleDescription.productionJars.isNotEmpty()) { - mapping["production/$moduleName"] = targetsFileModuleDescription.productionJars.map { PathManager.getHomeDir().resolve(it).toString() } + mapping["production/$moduleName"] = targetsFileModuleDescription.productionJars.map { PathManager.getHomeDir().resolve(it).toString() }.single() } if (targetsFileModuleDescription.testJars.isNotEmpty()) { - mapping["test/$moduleName"] = targetsFileModuleDescription.testJars.map { PathManager.getHomeDir().resolve(it).toString() } + mapping["test/$moduleName"] = targetsFileModuleDescription.testJars.map { PathManager.getHomeDir().resolve(it).toString() }.single() } } return mapping diff --git a/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderSplitPackageTest.kt b/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderSplitPackageTest.kt index 16005899ced6..60effbf0f770 100644 --- a/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderSplitPackageTest.kt +++ b/platform/util/testSrc/com/intellij/util/lang/test/UrlClassLoaderSplitPackageTest.kt @@ -2,7 +2,7 @@ package com.intellij.util.lang.test import com.intellij.openapi.application.PathManager -import com.intellij.platform.testFramework.monorepo.processProductionOutputs +import com.intellij.platform.testFramework.monorepo.processProductionOutput import com.intellij.project.IntelliJProjectConfiguration import org.junit.Assert import org.junit.Test @@ -29,18 +29,16 @@ class UrlClassLoaderSplitPackageTest { ) val project = IntelliJProjectConfiguration.loadIntelliJProject(PathManager.getHomePath()) project.modules.filterNot { it.name in platformLoaderModules }.forEach { module -> - module.processProductionOutputs { outputRoots -> - outputRoots.forEach { outputRoot -> - urlClassLoaderPackages.forEach { packageName -> - val packageDir = outputRoot.resolve(packageName.replace('.', '/')) - if (packageDir.exists()) { - val classNames = packageDir.listDirectoryEntries("*.class").map { it.fileName.toString().removeSuffix(".class") } - val incorrectClasses = classNames - knownClassesFromOtherModules - Assert.assertTrue(""" - |Module '${module.name}' defines classes (${incorrectClasses.joinToString()}) in '$packageName' which is treated in a special way by UrlClassLoader::findClass: - |Move these classes to a different package to avoid problems (see IDEA-331043 for details). - """.trimMargin(), incorrectClasses.isEmpty()) - } + module.processProductionOutput { outputRoot -> + urlClassLoaderPackages.forEach { packageName -> + val packageDir = outputRoot.resolve(packageName.replace('.', '/')) + if (packageDir.exists()) { + val classNames = packageDir.listDirectoryEntries("*.class").map { it.fileName.toString().removeSuffix(".class") } + val incorrectClasses = classNames - knownClassesFromOtherModules + Assert.assertTrue(""" + |Module '${module.name}' defines classes (${incorrectClasses.joinToString()}) in '$packageName' which is treated in a special way by UrlClassLoader::findClass: + |Move these classes to a different package to avoid problems (see IDEA-331043 for details). + """.trimMargin(), incorrectClasses.isEmpty()) } } } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/LibrariesRequiredForWorkspace.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/LibrariesRequiredForWorkspace.kt index d632fb9ce13c..841eb72ab8b2 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/LibrariesRequiredForWorkspace.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/LibrariesRequiredForWorkspace.kt @@ -124,15 +124,13 @@ private fun addDependencyFromCompilationOutput(model: ModifiableRootModel, libra val library = model.moduleLibraryTable.modifiableModel.createLibrary(libraryName) val modifiableModel = library.modifiableModel - val classpathRootVirtualFiles = mutableListOf() + var classpathRootVirtualFile: VirtualFile? val mapping = ArchivedCompilationContextUtil.archivedCompiledClassesMapping if (mapping != null) { - (mapping["production/$classpathFolder"] ?: error("No jars found for $classpathFolder production classes")).mapTo(classpathRootVirtualFiles) { jar -> - val classpathRootVirtualFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(VfsUtil.pathToUrl(jar)) - assertNotNull("Cannot find $classpathFolder in production classes jars. Possibly, project was partially compiled", classpathRootVirtualFile) - classpathRootVirtualFile!! - } + val jar = mapping["production/$classpathFolder"] ?: error("No jar found for $classpathFolder production classes") + classpathRootVirtualFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(VfsUtil.pathToUrl(jar)) + assertNotNull("Cannot find $classpathFolder in production classes jars. Possibly, project was partially compiled", classpathRootVirtualFile) } else { val classesPathUrl = VfsUtil.pathToUrl(PathUtil.getJarPathForClass(WorkspaceEntity::class.java)) @@ -144,23 +142,18 @@ private fun addDependencyFromCompilationOutput(model: ModifiableRootModel, libra VfsUtil.iterateChildrenRecursively(sharedClassesRootVirtualFile!!, null) { true } - val classpathRootVirtualFile = sharedClassesRootVirtualFile.children?.find { it.name == classpathFolder } + classpathRootVirtualFile = sharedClassesRootVirtualFile.children?.find { it.name == classpathFolder } assertNotNull("Cannot find $classpathFolder in $sharedClassesRootVirtualFile. Possibly, project was partially compiled", classpathRootVirtualFile) - classpathRootVirtualFiles.add(classpathRootVirtualFile!!) } + VfsUtil.markDirtyAndRefresh(false, true, true, classpathRootVirtualFile) - for (classpathRootVirtualFile in classpathRootVirtualFiles) { + if (classpathRootVirtualFile!!.isFile && classpathRootVirtualFile.extension == "jar") { + val file = classpathRootVirtualFile + classpathRootVirtualFile = JarFileSystem.getInstance().getJarRootForLocalFile(file) + assertNotNull("Cannot convert $file to a jar VirtualFile", classpathRootVirtualFile) VfsUtil.markDirtyAndRefresh(false, true, true, classpathRootVirtualFile) - - if (classpathRootVirtualFile.isFile && classpathRootVirtualFile.extension == "jar") { - val jarRootVirtualFile = JarFileSystem.getInstance().getJarRootForLocalFile(classpathRootVirtualFile) - assertNotNull("Cannot convert $classpathRootVirtualFile to a jar VirtualFile", jarRootVirtualFile) - VfsUtil.markDirtyAndRefresh(false, true, true, jarRootVirtualFile) - modifiableModel.addDependency(jarRootVirtualFile!!) - } - else { - modifiableModel.addDependency(classpathRootVirtualFile) - } } + + modifiableModel.addDependency(classpathRootVirtualFile!!) modifiableModel.commit() } \ No newline at end of file diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyRtJarPaths.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyRtJarPaths.java index 3981a6c933f9..6eb00d372ab2 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyRtJarPaths.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyRtJarPaths.java @@ -33,16 +33,16 @@ public final class GroovyRtJarPaths { String relevantJarsRoot = ArchivedCompilationContextUtil.getArchivedCompiledClassesLocation(); if (relevantJarsRoot != null && jpsPluginClassesRoot.getAbsolutePath().startsWith(relevantJarsRoot)) { // running from archived compilation output - Map> mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); + Map mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); if (mapping == null) { throw new IllegalStateException("Mapping cannot be null at this point. 'intellij.test.jars.location' is not null"); } for (String moduleName : moduleNames) { - List paths = mapping.get("production/" + moduleName); - if (paths == null) { + String path = mapping.get("production/" + moduleName); + if (path == null) { throw new IllegalStateException("Mapping for module '" + moduleName + "' not found in " + mapping); } - to.addAll(paths); + to.add(path); } return; } diff --git a/plugins/junit/java-tests/test/com/intellij/execution/junit/configuration/JUnitModulePathTest.java b/plugins/junit/java-tests/test/com/intellij/execution/junit/configuration/JUnitModulePathTest.java index 7dcb254d0a2a..92567cee206c 100644 --- a/plugins/junit/java-tests/test/com/intellij/execution/junit/configuration/JUnitModulePathTest.java +++ b/plugins/junit/java-tests/test/com/intellij/execution/junit/configuration/JUnitModulePathTest.java @@ -30,7 +30,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor; -import java.io.File; import java.util.Arrays; import java.util.List; @@ -154,7 +153,7 @@ public class JUnitModulePathTest extends BaseConfigurationTestCase { PathsList classPath = params4Tests.getClassPath(); assertContainsElements(classPath.getPathList(), TestObject.getJUnitRtPath().getPath()); - assertContainsElements(classPath.getPathList(), ContainerUtil.map(TestObject.getJUnitRtFiles(JUnitStarter.JUNIT5_PARAMETER), File::getPath)); + assertContainsElements(classPath.getPathList(), TestObject.getJUnitRtFile(JUnitStarter.JUNIT5_PARAMETER).getPath()); PathsList modulePath = params4Tests.getModulePath(); checkLibrariesOnPathList(module, modulePath); @@ -186,7 +185,7 @@ public class JUnitModulePathTest extends BaseConfigurationTestCase { PathsList classPath = params4Tests.getClassPath(); assertContainsElements(classPath.getPathList(), TestObject.getJUnitRtPath().getPath()); - assertContainsElements(classPath.getPathList(), ContainerUtil.map(TestObject.getJUnitRtFiles(JUnitStarter.JUNIT6_PARAMETER), File::getPath)); + assertContainsElements(classPath.getPathList(), TestObject.getJUnitRtFile(JUnitStarter.JUNIT6_PARAMETER).getPath()); PathsList modulePath = params4Tests.getModulePath(); checkLibrariesOnPathList(module, modulePath); diff --git a/plugins/junit/src/com/intellij/execution/junit/TestObject.java b/plugins/junit/src/com/intellij/execution/junit/TestObject.java index 799fc4da9a55..21bba34e1943 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestObject.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestObject.java @@ -249,9 +249,9 @@ public abstract class TestObject extends JavaTestFrameworkRunnableState getJUnitRtFiles(@NotNull String runner) { + public static File getJUnitRtFile(@NotNull String runner) { String version = RUNNER_VERSIONS.getOrDefault(runner, "5"); File junit4Rt = getJUnitRtPath(); String junit4Name = junit4Rt.getName(); @@ -284,15 +284,15 @@ public abstract class TestObject extends JavaTestFrameworkRunnableState> mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); + Map mapping = ArchivedCompilationContextUtil.getArchivedCompiledClassesMapping(); if (relevantJarsRoot != null && junit4Rt.toPath().startsWith(relevantJarsRoot) && mapping != null) { - return ContainerUtil.map(mapping.get("production/intellij.junit.v" + version + ".rt"), File::new); + return new File(mapping.get("production/intellij.junit.v" + version + ".rt")); } else { junitCurrentName = junit4Name.replace("junit", "junit" + version); } } - return Collections.singletonList(new File(junit4Rt.getParent(), junitCurrentName)); + return new File(junit4Rt.getParent(), junitCurrentName); } public static File getJUnitRtPath() { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java index bb6af7d084f5..9e679157b519 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven3/Maven3Support.java @@ -27,10 +27,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Predicate; -import static org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutputs; +import static org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutput; public class Maven3Support implements MavenVersionAwareSupportExtension { private static final @NonNls String MAIN_CLASS36 = "org.jetbrains.idea.maven.server.RemoteMavenServer36"; @@ -100,18 +99,18 @@ public class Maven3Support implements MavenVersionAwareSupportExtension { BundledMavenDownloader.INSTANCE.downloadMaven3LibsSync(communityRoot); classpath.add(PathManager.getJarForClass(MavenId.class)); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server"))); + classpath.add(locateModuleOutput("intellij.maven.server")); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.telemetry"))); + classpath.add(locateModuleOutput("intellij.maven.server.telemetry")); classpath.addAll(MavenUtil.collectClasspath(MavenServerTelemetryClasspathUtil.TELEMETRY_CLASSES)); Path parentFile = MavenUtil.getMavenPluginParentFile(); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.m3.common"))); + classpath.add(locateModuleOutput("intellij.maven.server.m3.common")); addDir(classpath, parentFile.resolve("maven3-server-common/lib"), f -> true); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.m3.impl"))); + classpath.add(locateModuleOutput("intellij.maven.server.m3.impl")); if (StringUtil.compareVersionNumbers(mavenVersion, "3.6") >= 0) { - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.m36.impl"))); + classpath.add(locateModuleOutput("intellij.maven.server.m36.impl")); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java index 63e1f853a85d..8f9740f0bf02 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/maven4/Maven4Support.java @@ -27,10 +27,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Predicate; -import static org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutputs; +import static org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutput; final class Maven4Support implements MavenVersionAwareSupportExtension { @@ -96,15 +95,15 @@ final class Maven4Support implements MavenVersionAwareSupportExtension { BundledMavenDownloader.INSTANCE.downloadMaven4LibsSync(communityRoot); classpath.add(PathManager.getJarForClass(MavenId.class)); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server"))); + classpath.add(locateModuleOutput("intellij.maven.server")); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.telemetry"))); + classpath.add(locateModuleOutput("intellij.maven.server.telemetry")); classpath.addAll(MavenUtil.collectClasspath(MavenServerTelemetryClasspathUtil.TELEMETRY_CLASSES)); Path parentPath = MavenUtil.getMavenPluginParentFile(); addDir(classpath, parentPath.resolve("maven40-server-impl/lib"), f -> true); - classpath.addAll(Objects.requireNonNull(locateModuleOutputs("intellij.maven.server.m40"))); + classpath.add(locateModuleOutput("intellij.maven.server.m40")); } private static void addMavenLibs(List classpath, Path mavenHome) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenIndexerCMDState.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenIndexerCMDState.kt index 9d2d57b497d2..7bc823e60aa7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenIndexerCMDState.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenIndexerCMDState.kt @@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull import org.jetbrains.idea.maven.model.MavenId import org.jetbrains.idea.maven.utils.MavenLog import org.jetbrains.idea.maven.utils.MavenUtil -import org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutputs +import org.jetbrains.idea.maven.utils.MavenUtil.locateModuleOutput import java.io.IOException import java.io.InputStreamReader import java.net.URI @@ -194,8 +194,8 @@ class MavenIndexerCMDState( private fun prepareClassPathForLocalRunAndUnitTests(classpath: MutableList) { classpath.add(PathManager.getJarForClass(MavenId::class.java)!!) - classpath.addAll(locateModuleOutputs("intellij.maven.server")!!) - classpath.addAll(locateModuleOutputs("intellij.maven.server.indexer")!!) + classpath.add(locateModuleOutput("intellij.maven.server")!!) + classpath.add(locateModuleOutput("intellij.maven.server.indexer")!!) } private fun addMavenLibs(classpath: MutableList, mavenHome: Path) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManagerImpl.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManagerImpl.kt index 0c8b8460f75b..2dc8941a9119 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManagerImpl.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManagerImpl.kt @@ -16,8 +16,6 @@ import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.registry.Registry import com.intellij.util.ObjectUtils import com.intellij.util.PathUtil -import com.intellij.util.io.DigestUtil.digestToHash -import com.intellij.util.io.DigestUtil.sha256 import com.intellij.util.net.NetUtils import org.apache.commons.lang3.SystemUtils import org.jetbrains.annotations.SystemIndependent @@ -33,20 +31,14 @@ import org.jetbrains.idea.maven.utils.MavenUtil import org.jetbrains.idea.maven.utils.MavenUtil.isCompatibleWith import java.io.File import java.io.IOException -import java.nio.file.Files import java.nio.file.Path -import java.nio.file.StandardCopyOption import java.rmi.RemoteException import java.util.* import java.util.concurrent.Callable import java.util.concurrent.atomic.AtomicBoolean import java.util.function.Consumer import java.util.function.Predicate -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream import kotlin.io.path.exists -import kotlin.io.path.nameWithoutExtension internal class MavenServerManagerImpl : MavenServerManager { private val myMultimoduleDirToConnectorMap: MutableMap = HashMap() @@ -464,53 +456,7 @@ internal class MavenServerManagerImpl : MavenServerManager { private val eventSpyPathForLocalBuild: Path get() { - val result = MavenUtil.locateModuleOutputs("intellij.maven.server.eventListener")!! - if (result.size == 1) { - return result.single() - } - - // merge the result into a single jar, check if already exists - val digest = sha256() - result.forEach { - Files.newInputStream(it).use { - digest.update(it.readAllBytes()) - } - } - val eventListenerJar = result.first().resolveSibling("${result.joinToString("-") { it.nameWithoutExtension }}-${digestToHash(digest)}.jar") - if (eventListenerJar.exists()) { - return eventListenerJar - } - - // merge - var tmp = Files.createTempFile(eventListenerJar.parent, null, null) - try { - ZipOutputStream(Files.newOutputStream(tmp)).use { out -> - result.forEach { - ZipFile(it.toFile()).use { input -> - input.entries().asSequence().forEach { - out.putNextEntry(ZipEntry(it.name)) - input.getInputStream(it).copyTo(out) - } - } - } - } - try { - Files.move(tmp, eventListenerJar, StandardCopyOption.ATOMIC_MOVE) - tmp = null - } - catch (_: AccessDeniedException) { - // ATOMIC_MOVE uses MOVEFILE_REPLACE_EXISTING, ignore - } - catch (_: FileAlreadyExistsException) { - // ignore - } - } - finally { - if (tmp != null) { - Files.delete(tmp) - } - } - return eventListenerJar + return MavenUtil.locateModuleOutput("intellij.maven.server.eventListener")!! } } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.kt index ba30166da95c..520d0f17ac27 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.kt @@ -43,6 +43,7 @@ import com.intellij.openapi.startup.StartupManager import com.intellij.openapi.util.* import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry.Companion.`is` import com.intellij.openapi.util.text.StringUtil @@ -1960,18 +1961,18 @@ object MavenUtil { private val path = PathManager.getJarForClass(MavenServerManager::class.java)?.parent /** - * Locate outputs of an IDEA module if running from sources. - * @return module output paths: can point to a directory or jar files. + * Locate output of an IDEA module if running from sources. + * @return path to the module output: can point to a directory or a jar file. * `null` if not running from sources or if module cannot be located */ @JvmStatic - fun locateModuleOutputs(moduleName: String): List? { + fun locateModuleOutput(moduleName: String): Path? { if (!isRunningFromSources()) return null if (archivedClassesLocation != null && mapping != null) { - return mapping["production/$moduleName"]?.map(Path::of) + return mapping["production/$moduleName"]?.toNioPathOrNull() } else { - return path?.resolve(moduleName)?.let(::listOf) + return path?.resolve(moduleName) } } diff --git a/tools/apiDump/src/impl.kt b/tools/apiDump/src/impl.kt index 316fa5e6442d..563d06a4bd64 100644 --- a/tools/apiDump/src/impl.kt +++ b/tools/apiDump/src/impl.kt @@ -51,12 +51,12 @@ class ApiIndex private constructor( return classes[className] } - internal fun discoverPackages(packages: Map, roots: List): ApiIndex { + internal fun discoverPackages(packages: Map, root: Path): ApiIndex { val builder = this.packages.builder() for ((packageName, packageAnnotations) in packages) { val existingAnnotations = this.packages[packageName] if (existingAnnotations != null && existingAnnotations != packageAnnotations) { - error("$packageName has different annotations in different modules. The current roots = $roots") + error("$packageName has different annotations in different modules. The current root = $root") } builder[packageName] = packageAnnotations } @@ -66,7 +66,7 @@ class ApiIndex private constructor( ) } - internal fun discoverClass(signature: ClassBinarySignature, roots: List): ApiIndex { + internal fun discoverClass(signature: ClassBinarySignature, root: Path): ApiIndex { val className = signature.name if (className.endsWith("/package-info")) { // ignore package-info.java @@ -74,7 +74,7 @@ class ApiIndex private constructor( } check(classes[className] == null) { - "$className already declared. The current roots = $roots" + "$className already declared. The current root = $root" } return ApiIndex( @@ -105,13 +105,13 @@ class API internal constructor( /** * @return a list of classes (with members) in [root], which are considered API */ -fun api(index: ApiIndex, roots: List): API { +fun api(index: ApiIndex, root: Path): API { @Suppress("NAME_SHADOWING") var index = index - val classFilePaths: Sequence = classFilePaths(roots) + val classFilePaths: Sequence = classFilePaths(root) val packages: Map = classFilePaths.packages() - index = index.discoverPackages(packages, roots) + index = index.discoverPackages(packages, root) val signatures: List = classFilePaths .map { it.inputStream() } @@ -126,7 +126,7 @@ fun api(index: ApiIndex, roots: List): API { * because the next [handleAnnotationsAndVisibility] call relies on it * to resolve the outer class name. */ - index = index.discoverClass(it, roots) + index = index.discoverClass(it, root) } } return API(index, signatures) @@ -307,14 +307,13 @@ private fun stableAndExperimentalApi(classSignatures: List): Pair): Sequence { - return classRoots.flatMap { classRoot -> classRoot +private fun classFilePaths(classRoot: Path): Sequence { + return classRoot .walk() .filter { path -> path.extension == "class" && !classRoot.relativize(path).startsWith("META-INF/") } - }.asSequence() } internal data class ApiAnnotations(val isInternal: Boolean, val isExperimental: Boolean) {