mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-201068 runtime module repository: include paths to module-level libraries to classpath of corresponding modules
Before, module-level libraries were translated to separate RuntimeModuleDescriptor in the runtime module repository, and dependencies on them were added to RuntimeModuleDescriptor instance corresponding to the JPS module. However, it didn't work well when IDE is started from source code, and output of the content module isn't merged with the module's libraries, causing problems like IJPL-201068 and IJPL-233281. Also, it greatly increased number of elements in the runtime module repository. Now paths to library JARs are added to the module classpath, and no separate RuntimeModuleDescriptor instances are created for module-level libraries. This will also make it simpler to reuse the runtime module repository for computing dependencies of modules at runtime (IJPL-157803). GitOrigin-RevId: ce62725e98a8202bd80afaa3639f1c53fd50521c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cec0416128
commit
2d8a1208d6
+2
-1
@@ -6,6 +6,7 @@ import com.intellij.devkit.runtimeModuleRepository.generator.RuntimeModuleReposi
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.RuntimeModuleRepositoryGenerator.COMPACT_REPOSITORY_FILE_NAME
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.RuntimeModuleRepositoryGenerator.JAR_REPOSITORY_FILE_NAME
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.RuntimeModuleRepositoryValidator
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.isProjectLevel
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleId
|
||||
import com.intellij.platform.runtime.repository.serialization.RawRuntimeModuleDescriptor
|
||||
@@ -183,7 +184,7 @@ private suspend fun generateRepositoryForDistribution(
|
||||
val distDescriptors = RuntimeModuleRepositoryGenerator.generateRuntimeModuleDescriptors(
|
||||
includedProduction = moduleProductionPaths.keySet(),
|
||||
includedTests = moduleTestPaths.keySet(),
|
||||
includedLibraries = libraryPaths.keySet(),
|
||||
includedProjectLibraries = libraryPaths.keySet().filter { it.isProjectLevel },
|
||||
resourcePathsSchema = DistributionResourcePathsSchema(moduleProductionPaths, moduleTestPaths, libraryPaths),
|
||||
).map { descriptor ->
|
||||
//this is a temporary workaround to skip optional dependencies which aren't included in the distribution
|
||||
|
||||
+3
-2
@@ -57,9 +57,10 @@ public final class RuntimeModuleId {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ID of a runtime module corresponding to the module-level library {@code libraryName} from module {@code moduleName} in intellij
|
||||
* project configuration.
|
||||
* @deprecated module-level libraries are now merged with corresponding modules at runtime, it doesn't make sense to have separate IDs for
|
||||
* them.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static @NotNull RuntimeModuleId moduleLibrary(@NotNull String moduleName, @NotNull String libraryName) {
|
||||
return new RuntimeModuleId(LIB_NAME_PREFIX + moduleName + "." + libraryName);
|
||||
}
|
||||
|
||||
+3
-2
@@ -3,6 +3,7 @@ package com.intellij.platform.testFramework.monorepo.runtimeModuleRepository
|
||||
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.ResourcePathsSchema
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.RuntimeModuleRepositoryGenerator
|
||||
import com.intellij.devkit.runtimeModuleRepository.generator.isProjectLevel
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleRepository
|
||||
import com.intellij.platform.runtime.repository.impl.RuntimeModuleRepositoryImpl
|
||||
import com.intellij.platform.runtime.repository.serialization.RawRuntimeModuleRepositoryData
|
||||
@@ -31,11 +32,11 @@ fun generateRuntimeModuleRepositoryForTests(monorepoProject: JpsProject): Runtim
|
||||
}
|
||||
}
|
||||
|
||||
val allLibraries = JpsJavaExtensionService.dependencies(monorepoProject).productionOnly().runtimeOnly().libraries
|
||||
val allProjectLibraries = JpsJavaExtensionService.dependencies(monorepoProject).productionOnly().runtimeOnly().libraries.filter { it.isProjectLevel }
|
||||
val moduleDescriptors = RuntimeModuleRepositoryGenerator.generateRuntimeModuleDescriptors(
|
||||
includedProduction = monorepoProject.modules,
|
||||
includedTests = emptyList(),
|
||||
includedLibraries = allLibraries,
|
||||
includedProjectLibraries = allProjectLibraries,
|
||||
resourcePathsSchema = ResourcePathsSchemaForTests,
|
||||
)
|
||||
|
||||
|
||||
+111
-49
@@ -12,7 +12,6 @@ import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator
|
||||
import org.jetbrains.jps.model.java.JpsJavaDependencyScope
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.library.JpsLibrary
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType
|
||||
import org.jetbrains.jps.model.module.JpsDependencyElement
|
||||
import org.jetbrains.jps.model.module.JpsLibraryDependency
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
@@ -30,30 +29,30 @@ object RuntimeModuleRepositoryGenerator {
|
||||
* Generates the runtime module descriptors for all modules and libraries in [project].
|
||||
*/
|
||||
fun generateRuntimeModuleDescriptorsForWholeProject(project: JpsProject, resourcePathsSchema: ResourcePathsSchema): List<RawRuntimeModuleDescriptor> {
|
||||
val libraries = LinkedHashSet<JpsLibrary>()
|
||||
val projectLibraries = LinkedHashSet<JpsLibrary>()
|
||||
for (module in project.modules) {
|
||||
libraries.addAll(enumerateRuntimeDependencies(module).libraries)
|
||||
projectLibraries.addAll(enumerateRuntimeDependencies(module).libraries.filter { it.isProjectLevel })
|
||||
}
|
||||
return generateRuntimeModuleDescriptors(
|
||||
includedProduction = project.modules,
|
||||
includedTests = project.modules,
|
||||
includedLibraries = libraries,
|
||||
includedProjectLibraries = projectLibraries,
|
||||
resourcePathsSchema = resourcePathsSchema
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the runtime module descriptors for production parts of [includedProduction], test parts of [includedTests] and
|
||||
* [includedLibraries].
|
||||
* [includedProjectLibraries].
|
||||
*/
|
||||
fun generateRuntimeModuleDescriptors(includedProduction: Collection<JpsModule>,
|
||||
includedTests: Collection<JpsModule>,
|
||||
includedLibraries: Collection<JpsLibrary>,
|
||||
includedProjectLibraries: Collection<JpsLibrary>,
|
||||
resourcePathsSchema: ResourcePathsSchema): List<RawRuntimeModuleDescriptor> {
|
||||
val descriptors = ArrayList<RawRuntimeModuleDescriptor>()
|
||||
generateDescriptorsForModules(descriptors, includedProduction, includedTests, resourcePathsSchema)
|
||||
for (library in includedLibraries) {
|
||||
val moduleId = getLibraryId(library)
|
||||
for (library in includedProjectLibraries) {
|
||||
val moduleId = getProjectLibraryId(library) ?: error("Project-level library expected, but found: $library")
|
||||
descriptors.add(RawRuntimeModuleDescriptor.create(moduleId.stringId, resourcePathsSchema.libraryPaths(library), emptyList()))
|
||||
}
|
||||
return descriptors
|
||||
@@ -119,8 +118,8 @@ private fun generateDescriptorsForModules(
|
||||
}
|
||||
}
|
||||
if (includedTests.isNotEmpty()) {
|
||||
val additionalDependenciesForTestsCache = HashMap<JpsModule, Set<RuntimeModuleId>>()
|
||||
val productionDependenciesCache = HashMap<JpsModule, Set<RuntimeModuleId>>()
|
||||
val additionalDependenciesForTestsCache = HashMap<JpsModule, DependenciesAndResources>()
|
||||
val productionDependenciesCache = HashMap<JpsModule, DependenciesAndResources>()
|
||||
for (module in includedTests) {
|
||||
if (module.hasTestSources) {
|
||||
descriptors.add(createTestPartDescriptor(module = module,
|
||||
@@ -145,12 +144,20 @@ private val JpsModule.hasProductionSources
|
||||
|
||||
private fun createProductionPartDescriptor(module: JpsModule, runtimeModuleNameGenerator: (JpsModule, Boolean) -> String, resourcePathsSchema: ResourcePathsSchema): RawRuntimeModuleDescriptor {
|
||||
val dependencies = LinkedHashSet<String>()
|
||||
val resourcePaths = if (module.hasProductionSources) resourcePathsSchema.moduleOutputPaths(module).toMutableSet() else mutableSetOf()
|
||||
enumerateRuntimeDependencies(module).productionOnly().processModuleAndLibraries(
|
||||
{ dependencies.add(runtimeModuleNameGenerator(it, false)) },
|
||||
{ dependencies.add(getLibraryId(it).stringId) }
|
||||
{ library ->
|
||||
val projectLibraryId = getProjectLibraryId(library)
|
||||
if (projectLibraryId != null) {
|
||||
dependencies.add(projectLibraryId.stringId)
|
||||
}
|
||||
else {
|
||||
resourcePaths.addAll(resourcePathsSchema.libraryPaths(library))
|
||||
}
|
||||
}
|
||||
)
|
||||
val resourcePaths = if (module.hasProductionSources) resourcePathsSchema.moduleOutputPaths(module) else emptyList()
|
||||
return RawRuntimeModuleDescriptor.create(runtimeModuleNameGenerator(module, false), resourcePaths, dependencies.toList())
|
||||
return RawRuntimeModuleDescriptor.create(runtimeModuleNameGenerator(module, false), resourcePaths.toList(), dependencies.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,15 +170,24 @@ private fun createProductionPartDescriptor(module: JpsModule, runtimeModuleNameG
|
||||
private fun createTestPartDescriptor(
|
||||
module: JpsModule,
|
||||
runtimeModuleNameGenerator: (JpsModule, Boolean) -> String,
|
||||
additionalDependenciesForTestsCache: MutableMap<JpsModule, Set<RuntimeModuleId>>,
|
||||
productionDependenciesCache: MutableMap<JpsModule, Set<RuntimeModuleId>>,
|
||||
additionalDependenciesForTestsCache: MutableMap<JpsModule, DependenciesAndResources>,
|
||||
productionDependenciesCache: MutableMap<JpsModule, DependenciesAndResources>,
|
||||
resourcePathsSchema: ResourcePathsSchema,
|
||||
): RawRuntimeModuleDescriptor {
|
||||
val resourcePaths = if (module.hasTestSources) resourcePathsSchema.moduleTestOutputPaths(module).toMutableSet() else mutableSetOf()
|
||||
val dependencies = LinkedHashSet<RuntimeModuleId>()
|
||||
dependencies.addAll(collectProductionDependenciesForModule(module, productionDependenciesCache, runtimeModuleNameGenerator))
|
||||
dependencies.addAll(collectAdditionalRuntimeDependenciesForTests(module, productionDependenciesCache, additionalDependenciesForTestsCache, runtimeModuleNameGenerator))
|
||||
val resourcePaths = if (module.hasTestSources) resourcePathsSchema.moduleTestOutputPaths(module) else emptyList()
|
||||
return RawRuntimeModuleDescriptor.create(runtimeModuleNameGenerator(module, true), resourcePaths, dependencies.map { it.stringId })
|
||||
val forProduction = collectProductionDependenciesForModule(module, productionDependenciesCache, runtimeModuleNameGenerator, resourcePathsSchema)
|
||||
forProduction.copyTo(dependencies, resourcePaths)
|
||||
|
||||
val forTests = collectAdditionalRuntimeDependenciesAndResourcesForTests(
|
||||
module,
|
||||
productionDependenciesCache,
|
||||
additionalDependenciesForTestsCache,
|
||||
runtimeModuleNameGenerator,
|
||||
resourcePathsSchema
|
||||
)
|
||||
forTests.copyTo(dependencies, resourcePaths)
|
||||
return RawRuntimeModuleDescriptor.create(runtimeModuleNameGenerator(module, true), resourcePaths.toList(), dependencies.map { it.stringId })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,48 +195,77 @@ private fun createTestPartDescriptor(
|
||||
*/
|
||||
private fun collectProductionDependenciesForModule(
|
||||
module: JpsModule,
|
||||
productionDependenciesCache: MutableMap<JpsModule, Set<RuntimeModuleId>>,
|
||||
productionDependenciesCache: MutableMap<JpsModule, DependenciesAndResources>,
|
||||
runtimeModuleNameGenerator: (JpsModule, Boolean) -> String,
|
||||
): Set<RuntimeModuleId> {
|
||||
resourcePathsSchema: ResourcePathsSchema,
|
||||
) : DependenciesAndResources {
|
||||
val cached = productionDependenciesCache[module]
|
||||
if (cached != null) {
|
||||
return cached
|
||||
}
|
||||
if (module.hasDescriptorForProduction) {
|
||||
val result = setOf(RuntimeModuleId.raw(runtimeModuleNameGenerator(module, false)))
|
||||
val result = DependenciesAndResources(setOf(RuntimeModuleId.raw(runtimeModuleNameGenerator(module, false))), emptySet())
|
||||
productionDependenciesCache[module] = result
|
||||
return result
|
||||
}
|
||||
|
||||
//if a module doesn't have its own descriptor, its dependencies should be added instead
|
||||
productionDependenciesCache[module] = emptySet() //to prevent StackOverflowError in case of circular dependencies
|
||||
val result = LinkedHashSet<RuntimeModuleId>()
|
||||
productionDependenciesCache[module] = emptyDependenciesAndResources //to prevent StackOverflowError in case of circular dependencies
|
||||
val dependencies = LinkedHashSet<RuntimeModuleId>()
|
||||
val resourcePaths = LinkedHashSet<String>()
|
||||
enumerateRuntimeDependencies(module).productionOnly().processModuleAndLibraries(
|
||||
{ dependency ->
|
||||
result.addAll(collectProductionDependenciesForModule(dependency, productionDependenciesCache, runtimeModuleNameGenerator))
|
||||
collectProductionDependenciesForModule(
|
||||
dependency,
|
||||
productionDependenciesCache,
|
||||
runtimeModuleNameGenerator,
|
||||
resourcePathsSchema
|
||||
).copyTo(dependencies, resourcePaths)
|
||||
},
|
||||
{ result.add(getLibraryId(it)) }
|
||||
{ dependency ->
|
||||
val projectLibraryId = getProjectLibraryId(dependency)
|
||||
if (projectLibraryId != null) {
|
||||
dependencies.add(projectLibraryId)
|
||||
}
|
||||
else {
|
||||
resourcePaths.addAll(resourcePathsSchema.libraryPaths(dependency))
|
||||
}
|
||||
}
|
||||
)
|
||||
val result = DependenciesAndResources(dependencies, resourcePaths)
|
||||
productionDependenciesCache[module] = result
|
||||
return result
|
||||
}
|
||||
|
||||
private data class DependenciesAndResources(
|
||||
val dependencies: Set<RuntimeModuleId>,
|
||||
val resourcePaths: Set<String>,
|
||||
) {
|
||||
fun copyTo(dependencies: MutableSet<RuntimeModuleId>, resourcePaths: MutableCollection<String>) {
|
||||
dependencies.addAll(this.dependencies)
|
||||
resourcePaths.addAll(this.resourcePaths)
|
||||
}
|
||||
}
|
||||
|
||||
private val emptyDependenciesAndResources = DependenciesAndResources(emptySet(), emptySet())
|
||||
|
||||
/**
|
||||
* Returns IDs of descriptors which should be added to tests dependencies of [module] in addition to production dependencies.
|
||||
*/
|
||||
private fun collectAdditionalRuntimeDependenciesForTests(
|
||||
private fun collectAdditionalRuntimeDependenciesAndResourcesForTests(
|
||||
module: JpsModule,
|
||||
productionDependenciesCache: MutableMap<JpsModule, Set<RuntimeModuleId>>,
|
||||
additionalDependenciesForTestsCache: MutableMap<JpsModule, Set<RuntimeModuleId>>,
|
||||
runtimeModuleNameGenerator: (JpsModule, Boolean) -> String
|
||||
): Collection<RuntimeModuleId> {
|
||||
productionDependenciesCache: MutableMap<JpsModule, DependenciesAndResources>,
|
||||
additionalDependenciesForTestsCache: MutableMap<JpsModule, DependenciesAndResources>,
|
||||
runtimeModuleNameGenerator: (JpsModule, Boolean) -> String,
|
||||
resourcePathsSchema: ResourcePathsSchema
|
||||
): DependenciesAndResources {
|
||||
val cached = additionalDependenciesForTestsCache[module]
|
||||
if (cached != null) {
|
||||
return cached
|
||||
}
|
||||
additionalDependenciesForTestsCache[module] = emptySet() ////to prevent StackOverflowError in case of circular dependencies
|
||||
val result = LinkedHashSet<RuntimeModuleId>()
|
||||
additionalDependenciesForTestsCache[module] = emptyDependenciesAndResources ////to prevent StackOverflowError in case of circular dependencies
|
||||
val dependencies = LinkedHashSet<RuntimeModuleId>()
|
||||
val resourcePaths = LinkedHashSet<String>()
|
||||
for (dependency in module.dependenciesList.dependencies) {
|
||||
when (dependency) {
|
||||
is JpsModuleDependency -> {
|
||||
@@ -229,38 +274,55 @@ private fun collectAdditionalRuntimeDependenciesForTests(
|
||||
if (scope == JpsJavaDependencyScope.PROVIDED) continue
|
||||
|
||||
if (dependencyModule.hasTestSources) {
|
||||
result.add(RuntimeModuleId.raw(runtimeModuleNameGenerator(dependencyModule, true)))
|
||||
dependencies.add(RuntimeModuleId.raw(runtimeModuleNameGenerator(dependencyModule, true)))
|
||||
}
|
||||
else {
|
||||
if (scope == JpsJavaDependencyScope.TEST) {
|
||||
result.addAll(collectProductionDependenciesForModule(dependencyModule, productionDependenciesCache, runtimeModuleNameGenerator))
|
||||
collectProductionDependenciesForModule(
|
||||
dependencyModule,
|
||||
productionDependenciesCache,
|
||||
runtimeModuleNameGenerator,
|
||||
resourcePathsSchema
|
||||
).copyTo(dependencies, resourcePaths)
|
||||
}
|
||||
result.addAll(collectAdditionalRuntimeDependenciesForTests(dependencyModule, productionDependenciesCache, additionalDependenciesForTestsCache, runtimeModuleNameGenerator))
|
||||
collectAdditionalRuntimeDependenciesAndResourcesForTests(
|
||||
dependencyModule,
|
||||
productionDependenciesCache,
|
||||
additionalDependenciesForTestsCache,
|
||||
runtimeModuleNameGenerator,
|
||||
resourcePathsSchema
|
||||
).copyTo(dependencies, resourcePaths)
|
||||
}
|
||||
}
|
||||
is JpsLibraryDependency -> {
|
||||
if (dependency.scope == JpsJavaDependencyScope.TEST) {
|
||||
dependency.library?.let { result.add(getLibraryId(it)) }
|
||||
dependency.library?.let { library ->
|
||||
val projectLibraryId = getProjectLibraryId(library)
|
||||
if (projectLibraryId != null) {
|
||||
dependencies.add(projectLibraryId)
|
||||
}
|
||||
else {
|
||||
resourcePaths.addAll(resourcePathsSchema.libraryPaths(library))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
additionalDependenciesForTestsCache[module] = result
|
||||
return result
|
||||
val dependenciesAndResources = DependenciesAndResources(dependencies, resourcePaths)
|
||||
additionalDependenciesForTestsCache[module] = dependenciesAndResources
|
||||
return dependenciesAndResources
|
||||
}
|
||||
|
||||
private val JpsDependencyElement.scope: JpsJavaDependencyScope?
|
||||
get() = JpsJavaExtensionService.getInstance().getDependencyExtension(this)?.scope
|
||||
|
||||
private fun getLibraryId(library: JpsLibrary): RuntimeModuleId {
|
||||
var name = library.name
|
||||
val element = (library as JpsElementBase<*>).parent.parent
|
||||
if (element is JpsModule) {
|
||||
val files = library.getFiles(JpsOrderRootType.COMPILED)
|
||||
if (name.startsWith("#") && files.size == 1) {
|
||||
name = files[0].name
|
||||
}
|
||||
return RuntimeModuleId.moduleLibrary((element as JpsModule).name, name)
|
||||
private fun getProjectLibraryId(library: JpsLibrary): RuntimeModuleId? {
|
||||
if (library.isProjectLevel) {
|
||||
return RuntimeModuleId.projectLibrary(library.name)
|
||||
}
|
||||
return RuntimeModuleId.projectLibrary(name)
|
||||
return null
|
||||
}
|
||||
|
||||
val JpsLibrary.isProjectLevel: Boolean
|
||||
get() = (this as JpsElementBase<*>).parent.parent is JpsProject
|
||||
+19
-3
@@ -90,7 +90,24 @@ class RuntimeModuleRepositoryBuilderTest : RuntimeModuleRepositoryTestCase() {
|
||||
testDescriptor("c.tests", "c", "a.tests")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun `test transitive dependency via module without tests but with test module-level library`() {
|
||||
val a = addModule("a", withTests = true)
|
||||
val b = addModule("b", a, withTests = false)
|
||||
val lib = b.libraryCollection.addLibrary("lib", JpsJavaLibraryType.INSTANCE)
|
||||
val dependency = b.dependenciesList.addLibraryDependency(lib)
|
||||
JpsJavaExtensionService.getInstance().getOrCreateDependencyExtension(dependency).scope = JpsJavaDependencyScope.TEST
|
||||
lib.addRoot(getUrl("project/lib"), JpsOrderRootType.COMPILED)
|
||||
addModule("c", b, withTests = true)
|
||||
buildAndCheck {
|
||||
descriptor("a")
|
||||
descriptor("b", "a")
|
||||
descriptor("c", "b")
|
||||
testDescriptor("a.tests", "a")
|
||||
descriptor("c.tests", listOf("test/c", $$"$PROJECT_DIR$/lib"), listOf("c", "a.tests"))
|
||||
}
|
||||
}
|
||||
|
||||
fun `test do not add unnecessary transitive dependencies via module without tests`() {
|
||||
val a = addModule("a", withTests = true)
|
||||
val b = addModule("b", withTests = false)
|
||||
@@ -157,8 +174,7 @@ class RuntimeModuleRepositoryBuilderTest : RuntimeModuleRepositoryTestCase() {
|
||||
a.dependenciesList.addLibraryDependency(lib)
|
||||
lib.addRoot(getUrl("project/lib"), JpsOrderRootType.COMPILED)
|
||||
buildAndCheck {
|
||||
descriptor("a", "lib.a.lib")
|
||||
descriptor("lib.a.lib", listOf($$"$PROJECT_DIR$/lib"), emptyList())
|
||||
descriptor("a",listOf("production/a", $$"$PROJECT_DIR$/lib"), emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user