Commit 592612c8 authored by Vladimir Dolzhenko's avatar Vladimir Dolzhenko Committed by intellij-monorepo-bot
Browse files

[kotlin] Rework computeLibrariesAndSdksUsedIn to reduce computation complexity

#KTIJ-23501

GitOrigin-RevId: d3d51c60a298558889b283284ade1be38b9694c5
parent 28b43ff7
Branches unavailable Tags unavailable
Showing with 761 additions and 59 deletions
+761 -59
......@@ -15,7 +15,6 @@ import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.Disposer
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
......@@ -39,7 +38,21 @@ import org.jetbrains.kotlin.idea.caches.trackers.ModuleModificationTracker
import org.jetbrains.kotlin.idea.configuration.isMavenized
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
typealias LibraryDependencyCandidatesAndSdkInfos = Pair<Set<LibraryDependencyCandidate>, Set<SdkInfo>>
private class LibraryDependencyCandidatesAndSdkInfos(
val libraryDependencyCandidates: MutableSet<LibraryDependencyCandidate> = linkedSetOf(),
val sdkInfos: MutableSet<SdkInfo> = linkedSetOf()
) {
operator fun plusAssign(other: LibraryDependencyCandidatesAndSdkInfos) {
libraryDependencyCandidates += other.libraryDependencyCandidates
sdkInfos += other.sdkInfos
}
override fun toString(): String {
return "[${Integer.toHexString(System.identityHashCode(this))}] libraryDependencyCandidates: ${
libraryDependencyCandidates.map { it.libraries.map(LibraryInfo::name) }
} sdkInfos: $sdkInfos"
}
}
class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDependenciesCache, Disposable {
companion object {
......@@ -60,75 +73,35 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
override fun dispose() = Unit
private fun computeLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibraryDependencies {
val (dependencyCandidates, sdks) = computeLibrariesAndSdksUsedWithNoFilter(libraryInfo)
val libraryDependencyCandidatesAndSdkInfos = computeLibrariesAndSdksUsedWithNoFilter(libraryInfo)
// Maven is Gradle Metadata unaware, and therefore needs stricter filter. See KTIJ-15758
val libraryDependenciesFilter = if (project.isMavenized)
StrictEqualityForPlatformSpecificCandidatesFilter
else
DefaultLibraryDependenciesFilter union SharedNativeLibraryToNativeInteropFallbackDependenciesFilter
val libraries = libraryDependenciesFilter(libraryInfo.platform, dependencyCandidates).flatMap { it.libraries }
return LibraryDependencies(libraryInfo, libraries, sdks.toList())
val libraries = libraryDependenciesFilter(
libraryInfo.platform,
libraryDependencyCandidatesAndSdkInfos.libraryDependencyCandidates
).flatMap { it.libraries }
return LibraryDependencies(libraryInfo, libraries, libraryDependencyCandidatesAndSdkInfos.sdkInfos.toList())
}
//NOTE: used LibraryRuntimeClasspathScope as reference
private fun computeLibrariesAndSdksUsedWithNoFilter(libraryInfo: LibraryInfo): LibraryDependencyCandidatesAndSdkInfos {
val libraries = LinkedHashSet<LibraryDependencyCandidate>()
val sdks = LinkedHashSet<SdkInfo>()
val libraryDependencyCandidatesAndSdkInfos = LibraryDependencyCandidatesAndSdkInfos()
val modulesLibraryIsUsedIn =
getLibraryUsageIndex().getModulesLibraryIsUsedIn(libraryInfo)
for (module in modulesLibraryIsUsedIn) {
checkCanceled()
val (moduleLibraries, moduleSdks) = moduleDependenciesCache[module]
libraries.addAll(moduleLibraries)
sdks.addAll(moduleSdks)
}
val filteredLibraries = filterForBuiltins(libraryInfo, libraries)
return filteredLibraries to sdks
}
private fun computeLibrariesAndSdksUsedIn(module: Module): LibraryDependencyCandidatesAndSdkInfos {
val libraries = LinkedHashSet<LibraryDependencyCandidate>()
val sdks = LinkedHashSet<SdkInfo>()
val processedModules = HashSet<Module>()
val condition = Condition<OrderEntry> { orderEntry ->
checkCanceled()
orderEntry.safeAs<ModuleOrderEntry>()?.let {
it.module?.run { this !in processedModules } ?: false
} ?: true
libraryDependencyCandidatesAndSdkInfos += moduleDependenciesCache[module]
}
val infoCache = LibraryInfoCache.getInstance(project)
ModuleRootManager.getInstance(module).orderEntries()
// TODO: it results into O(n^2)
.recursively()
.satisfying(condition).process(object : RootPolicy<Unit>() {
override fun visitModuleSourceOrderEntry(moduleSourceOrderEntry: ModuleSourceOrderEntry, value: Unit) {
processedModules.add(moduleSourceOrderEntry.ownerModule)
}
override fun visitLibraryOrderEntry(libraryOrderEntry: LibraryOrderEntry, value: Unit) {
checkCanceled()
val libraryEx = libraryOrderEntry.library.safeAs<LibraryEx>()?.takeUnless { it.isDisposed } ?: return
val candidate = LibraryDependencyCandidate.fromLibraryOrNull(infoCache[libraryEx]) ?: return
libraries += candidate
}
val filteredLibraries = filterForBuiltins(libraryInfo, libraryDependencyCandidatesAndSdkInfos.libraryDependencyCandidates)
override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) {
checkCanceled()
jdkOrderEntry.jdk?.let { jdk ->
sdks += SdkInfo(project, jdk)
}
}
}, Unit)
return libraries to sdks
return LibraryDependencyCandidatesAndSdkInfos(filteredLibraries, libraryDependencyCandidatesAndSdkInfos.sdkInfos)
}
/*
......@@ -146,8 +119,8 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
*/
private fun filterForBuiltins(
libraryInfo: LibraryInfo,
dependencyLibraries: Set<LibraryDependencyCandidate>
): Set<LibraryDependencyCandidate> {
dependencyLibraries: MutableSet<LibraryDependencyCandidate>
): MutableSet<LibraryDependencyCandidate> {
return if (!IdeBuiltInsLoadingState.isFromClassLoader && libraryInfo.isCoreKotlinLibrary(project)) {
dependencyLibraries.filterTo(mutableSetOf()) { dep ->
dep.libraries.any { it.isCoreKotlinLibrary(project) }
......@@ -228,19 +201,205 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
connection.subscribe(ProjectTopics.PROJECT_ROOTS, this)
}
override fun get(key: Module): LibraryDependencyCandidatesAndSdkInfos {
return internalGet(key, hashMapOf(), linkedSetOf(), hashMapOf())
}
private fun internalGet(
key: Module,
tmpResults: MutableMap<Module, LibraryDependencyCandidatesAndSdkInfos>,
trace: LinkedHashSet<Module>,
loops: MutableMap<Module, Set<Module>>
): LibraryDependencyCandidatesAndSdkInfos {
checkKeyAndDisposeIllegalEntry(key)
useCache { cache ->
checkEntitiesIfRequired(cache)
cache[key]
}?.let { return it }
checkCanceled()
val newValue = computeLibrariesAndSdksUsedIn(key, tmpResults, trace, loops)
if (isValidityChecksEnabled) {
checkValueValidity(newValue)
}
val existedValue = if (trace.isNotEmpty()) {
dumpLoopsIfPossible(key, newValue, tmpResults, trace, loops)
} else {
// it is possible to dump results when all dependencies are resolved hence trace is empty
useCache { cache ->
val existedValue = cache.putIfAbsent(key, newValue)
for (entry in tmpResults.entries) {
cache.putIfAbsent(entry.key, entry.value)
}
existedValue
}
}
return existedValue ?: newValue
}
/**
* It is possible to dump loops from the subtree from the last module from the loop
*
* @param trace is not empty trace
* @return existed value if applicable
*/
private fun dumpLoopsIfPossible(
key: Module,
newValue: LibraryDependencyCandidatesAndSdkInfos,
tmpResults: MutableMap<Module, LibraryDependencyCandidatesAndSdkInfos>,
trace: LinkedHashSet<Module>,
loops: MutableMap<Module, Set<Module>>,
): LibraryDependencyCandidatesAndSdkInfos? {
val currentLoop = loops[key] ?: return null
if (trace.last() in loops) return null
return useCache { cache ->
val existedValue = cache.putIfAbsent(key, newValue)
tmpResults.remove(key)
for (loopModule in currentLoop) {
tmpResults.remove(loopModule)?.let {
cache.putIfAbsent(loopModule, it)
}
loops.remove(loopModule)
}
existedValue
}
}
private fun computeLibrariesAndSdksUsedIn(
module: Module,
tmpResults: MutableMap<Module, LibraryDependencyCandidatesAndSdkInfos>,
trace: LinkedHashSet<Module>,
loops: MutableMap<Module, Set<Module>>
): LibraryDependencyCandidatesAndSdkInfos {
checkCanceled()
check(trace.add(module)) { "recursion detected" }
val libraryDependencyCandidatesAndSdkInfos = LibraryDependencyCandidatesAndSdkInfos()
tmpResults[module] = libraryDependencyCandidatesAndSdkInfos
val modulesToVisit = HashSet<Module>()
val infoCache = LibraryInfoCache.getInstance(project)
ModuleRootManager.getInstance(module).orderEntries()
.process(object : RootPolicy<Unit>() {
override fun visitModuleSourceOrderEntry(moduleSourceOrderEntry: ModuleSourceOrderEntry, value: Unit) {
modulesToVisit.addAll(moduleSourceOrderEntry.rootModel.getModuleDependencies(true).toList())
}
override fun visitLibraryOrderEntry(libraryOrderEntry: LibraryOrderEntry, value: Unit) {
checkCanceled()
val libraryEx = libraryOrderEntry.library.safeAs<LibraryEx>()?.takeUnless { it.isDisposed } ?: return
val candidate = LibraryDependencyCandidate.fromLibraryOrNull(infoCache[libraryEx]) ?: return
libraryDependencyCandidatesAndSdkInfos.libraryDependencyCandidates += candidate
}
override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) {
checkCanceled()
jdkOrderEntry.jdk?.let { jdk ->
libraryDependencyCandidatesAndSdkInfos.sdkInfos += SdkInfo(project, jdk)
}
}
}, Unit)
// handle circular dependency case
for (moduleToVisit in modulesToVisit) {
checkCanceled()
if (moduleToVisit == module) continue
if (moduleToVisit !in trace) continue
// circular dependency found
val reversedTrace = trace.toList().asReversed()
val sharedLibraryDependencyCandidatesAndSdkInfos: LibraryDependencyCandidatesAndSdkInfos = run {
var shared: LibraryDependencyCandidatesAndSdkInfos? = null
val loop = hashSetOf<Module>()
val duplicates = hashSetOf<LibraryDependencyCandidatesAndSdkInfos>()
for (traceModule in reversedTrace) {
loop += traceModule
loops[traceModule]?.let { loop += it }
loops[traceModule] = loop
val traceModuleLibraryDependencyCandidatesAndSdkInfos = tmpResults.getValue(traceModule)
if (shared == null && !duplicates.add(traceModuleLibraryDependencyCandidatesAndSdkInfos)) {
shared = traceModuleLibraryDependencyCandidatesAndSdkInfos
}
if (traceModule === moduleToVisit) {
break
}
}
shared ?: duplicates.first()
}
sharedLibraryDependencyCandidatesAndSdkInfos += libraryDependencyCandidatesAndSdkInfos
for (traceModule in reversedTrace) {
val traceModuleLibraryDependencyCandidatesAndSdkInfos: LibraryDependencyCandidatesAndSdkInfos =
tmpResults.getValue(traceModule)
if (traceModuleLibraryDependencyCandidatesAndSdkInfos === sharedLibraryDependencyCandidatesAndSdkInfos) {
if (traceModule === moduleToVisit) {
break
}
continue
}
sharedLibraryDependencyCandidatesAndSdkInfos += traceModuleLibraryDependencyCandidatesAndSdkInfos
tmpResults[traceModule] = sharedLibraryDependencyCandidatesAndSdkInfos
loops[traceModule]?.let { loop ->
for (loopModule in loop) {
if (loopModule == traceModule) continue
val value = tmpResults.getValue(loopModule)
if (value === sharedLibraryDependencyCandidatesAndSdkInfos) continue
sharedLibraryDependencyCandidatesAndSdkInfos += value
tmpResults[loopModule] = sharedLibraryDependencyCandidatesAndSdkInfos
}
}
if (traceModule === moduleToVisit) {
break
}
}
}
// merge
for (moduleToVisit in modulesToVisit) {
checkCanceled()
if (moduleToVisit == module || moduleToVisit in trace) continue
val moduleToVisitLibraryDependencyCandidatesAndSdkInfos =
tmpResults[moduleToVisit] ?: internalGet(moduleToVisit, tmpResults, trace, loops = loops)
val moduleLibraryDependencyCandidatesAndSdkInfos = tmpResults.getValue(module)
moduleLibraryDependencyCandidatesAndSdkInfos += moduleToVisitLibraryDependencyCandidatesAndSdkInfos
}
trace.remove(module)
return tmpResults.getValue(module)
}
override fun calculate(key: Module): LibraryDependencyCandidatesAndSdkInfos =
computeLibrariesAndSdksUsedIn(key)
throw UnsupportedOperationException("calculate(Module) should not be invoked due to custom impl of get()")
override fun checkKeyValidity(key: Module) {
key.checkValidity()
}
override fun checkValueValidity(value: LibraryDependencyCandidatesAndSdkInfos) {
value.first.forEach { it.libraries.forEach { libraryInfo -> libraryInfo.checkValidity() } }
value.libraryDependencyCandidates.forEach { it.libraries.forEach { libraryInfo -> libraryInfo.checkValidity() } }
}
override fun jdkRemoved(jdk: Sdk) {
invalidateEntries({ _, candidates -> candidates.second.any { it.sdk == jdk } })
invalidateEntries({ _, candidates -> candidates.sdkInfos.any { it.sdk == jdk } })
}
override fun jdkNameChanged(jdk: Sdk, previousName: String) {
......@@ -254,7 +413,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
val sdks = project.allSdks()
invalidateEntries(
{ _, (_, sdkInfos) -> sdkInfos.any { it.sdk !in sdks } },
{ _, candidates -> candidates.sdkInfos.any { it.sdk !in sdks } },
// unable to check entities properly: an event could be not the last
validityCondition = null
)
......@@ -277,7 +436,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
val infos = libraryInfos.toHashSet()
invalidateEntries(
{ _, v ->
v.first.any { candidate -> candidate.libraries.any { it in infos } }
v.libraryDependencyCandidates.any { candidate -> candidate.libraries.any { it in infos } }
},
// unable to check entities properly: an event could be not the last
validityCondition = null
......
......@@ -342,7 +342,55 @@ class IdeaModuleInfoTest8 : JavaModuleTestCase() {
b.production.assertDependenciesEqual(b.production, a.production)
}
fun testModuleCircularDependencyLibraries() {
val s0 = module("s0")
val (a, b, c) = modules()
s0.addDependency(a)
a.addDependency(b)
b.addDependency(c)
c.addDependency(a)
val kotlinStdlib = stdlibJvm()
val fakeLib = projectLibraryWithFakeRoot("fake")
val myLib = projectLibrary(
libraryName = "myLib",
classesRoot = TestKotlinArtifacts.kotlinDaemon.jarRoot,
sourcesRoot = TestKotlinArtifacts.kotlinDaemon.jarRoot,
)
s0.addDependency(myLib)
val myLibInfo = myLib.toLibraryInfo()
val kotlinStdlibLibraryInfo = kotlinStdlib.toLibraryInfo()
val fakeLibraryInfo = fakeLib.toLibraryInfo()
c.addDependency(kotlinStdlib)
b.addDependency(fakeLib)
s0.production.assertDependenciesEqual(s0.production, a.production, myLibInfo)
a.production.assertDependenciesEqual(a.production, b.production)
b.production.assertDependenciesEqual(b.production, c.production, fakeLibraryInfo)
c.production.assertDependenciesEqual(c.production, a.production, kotlinStdlibLibraryInfo)
val dependenciesCache = LibraryDependenciesCache.getInstance(project)
val dependencies = dependenciesCache.getLibraryDependencies(myLibInfo)
assertEquals(dependencies.libraries.sortedBy { it.name },
listOf(fakeLibraryInfo, kotlinStdlibLibraryInfo, myLibInfo))
}
fun testModuleCircularDependencyLibraries2() {
/**
```mermaid
graph TD
S0 --> A
A --> D
A --> B
B --> C
C --> A
```
*/
val s0 = module("s0")
val (a, b, c) = modules()
val d = module("d")
......@@ -456,6 +504,16 @@ class IdeaModuleInfoTest8 : JavaModuleTestCase() {
}
fun testModuleCircularDependencyLibraries3() {
/**
```mermaid
graph
S0 --> A
A --> D
A --> B
B --> C
C --> A
```
*/
val s0 = module("s0")
val (a, b, c) = modules()
val d = module("d")
......@@ -568,6 +626,491 @@ class IdeaModuleInfoTest8 : JavaModuleTestCase() {
)
}
fun testModuleCircularDependencyLibraries4() {
/**
```mermaid
graph
M0 --> M1
M1 --> M2
M2 --> M3
M3 --> M5
M5 --> M6
M6 --> M1
M3 --> M4
M4 --> M2
```
*/
val m0 = module("m0")
val m1 = module("m1")
val m2 = module("m2")
val m3 = module("m3")
val m4 = module("m4")
val m5 = module("m5")
val m6 = module("m6")
m0.addDependency(m1)
m1.addDependency(m2)
m2.addDependency(m3)
m3.addDependency(m5)
m5.addDependency(m6)
m6.addDependency(m1)
m3.addDependency(m4)
m4.addDependency(m2)
val m0Lib = projectLibrary("m0Lib", classesRoot = TestKotlinArtifacts.kotlinDaemon.jarRoot)
val m0LibInfo = m0Lib.toLibraryInfo()
m0.addDependency(m0Lib)
val m1Lib = projectLibrary("m1Lib", classesRoot = TestKotlinArtifacts.kotlinReflect.jarRoot)
val m1LibInfo = m1Lib.toLibraryInfo()
m1.addDependency(m1Lib)
val m2Lib = projectLibrary("m2Lib", classesRoot = TestKotlinArtifacts.kotlinAnnotationsJvm.jarRoot)
val m2LibInfo = m2Lib.toLibraryInfo()
m2.addDependency(m2Lib)
val m3Lib = projectLibrary("m3Lib", classesRoot = TestKotlinArtifacts.kotlinTestJunit.jarRoot)
val m3LibInfo = m3Lib.toLibraryInfo()
m3.addDependency(m3Lib)
val m4Lib = projectLibrary("m4Lib", classesRoot = TestKotlinArtifacts.parcelizeRuntime.jarRoot)
val m4LibInfo = m4Lib.toLibraryInfo()
m4.addDependency(m4Lib)
val m5Lib = projectLibrary("m5Lib", classesRoot = TestKotlinArtifacts.jsr305.jarRoot)
val m5LibInfo = m5Lib.toLibraryInfo()
m5.addDependency(m5Lib)
val m6Lib = projectLibrary("m6Lib", classesRoot = TestKotlinArtifacts.kotlinTest.jarRoot)
val m6LibInfo = m6Lib.toLibraryInfo()
m6.addDependency(m6Lib)
m0.production.assertDependenciesEqual(
m0.production,
m1.production,
m0LibInfo,
)
m1.production.assertDependenciesEqual(
m1.production,
m2.production,
m1LibInfo,
)
m2.production.assertDependenciesEqual(
m2.production,
m3.production,
m2LibInfo,
)
m3.production.assertDependenciesEqual(
m3.production,
m5.production,
m4.production,
m3LibInfo,
)
m4.production.assertDependenciesEqual(
m4.production,
m2.production,
m4LibInfo,
)
m5.production.assertDependenciesEqual(
m5.production,
m6.production,
m5LibInfo,
)
m6.production.assertDependenciesEqual(
m6.production,
m1.production,
m6LibInfo,
)
val dependenciesCache = LibraryDependenciesCache.getInstance(project)
fun assertDependencies(lib: LibraryInfo, vararg expectedLibraries: LibraryInfo) {
val dependencies = dependenciesCache.getLibraryDependencies(lib)
assertEquals(
"LibraryInfo '${lib.name}' dependencies",
expectedLibraries.joinToString(separator = "\n") { it.name.asString() },
dependencies.libraries.map { it.name.asString() }.sorted().joinToString(separator = "\n"),
)
}
assertDependencies(
lib = m0LibInfo,
m0LibInfo,
m1LibInfo,
m2LibInfo,
m3LibInfo,
m4LibInfo,
m5LibInfo,
m6LibInfo,
)
for (libraryInfo in listOf(m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo)) {
assertDependencies(
lib = libraryInfo,
m1LibInfo,
m2LibInfo,
m3LibInfo,
m4LibInfo,
m5LibInfo,
m6LibInfo,
)
}
}
fun testModuleCircularDependencyLibraries5() {
/**
```mermaid
graph
M01 --> M0
M0 --> M1
M1 --> M2
M2 --> M3
M3 --> M4
M3 --> M7
M4 --> M5
M5 --> M3
M4 --> M6
M6 --> M10
M7 --> M8
M8 --> M1
```
*/
val m01 = module("m01")
val m0 = module("m0")
val m1 = module("m1")
val m2 = module("m2")
val m3 = module("m3")
val m4 = module("m4")
val m5 = module("m5")
val m6 = module("m6")
val m7 = module("m7")
val m8 = module("m8")
val m10 = module("m10")
m01.addDependency(m0)
m0.addDependency(m1)
m1.addDependency(m2)
m2.addDependency(m3)
m3.addDependency(m4)
m3.addDependency(m7)
m4.addDependency(m5)
m4.addDependency(m6)
m5.addDependency(m3)
m6.addDependency(m10)
m7.addDependency(m8)
m8.addDependency(m1)
val m01Lib = projectLibraryWithFakeRoot("fake")
val m01LibInfo = m01Lib.toLibraryInfo()
m01.addDependency(m01Lib)
val m0Lib = projectLibrary("m0Lib", classesRoot = TestKotlinArtifacts.kotlinDaemon.jarRoot)
val m0LibInfo = m0Lib.toLibraryInfo()
m0.addDependency(m0Lib)
val m1Lib = projectLibrary("m1Lib", classesRoot = TestKotlinArtifacts.kotlinReflect.jarRoot)
val m1LibInfo = m1Lib.toLibraryInfo()
m1.addDependency(m1Lib)
val m2Lib = projectLibrary("m2Lib", classesRoot = TestKotlinArtifacts.kotlinAnnotationsJvm.jarRoot)
val m2LibInfo = m2Lib.toLibraryInfo()
m2.addDependency(m2Lib)
val m3Lib = projectLibrary("m3Lib", classesRoot = TestKotlinArtifacts.kotlinTestJunit.jarRoot)
val m3LibInfo = m3Lib.toLibraryInfo()
m3.addDependency(m3Lib)
val m4Lib = projectLibrary("m4Lib", classesRoot = TestKotlinArtifacts.parcelizeRuntime.jarRoot)
val m4LibInfo = m4Lib.toLibraryInfo()
m4.addDependency(m4Lib)
val m5Lib = projectLibrary("m5Lib", classesRoot = TestKotlinArtifacts.jsr305.jarRoot)
val m5LibInfo = m5Lib.toLibraryInfo()
m5.addDependency(m5Lib)
val m6Lib = projectLibrary("m6Lib", classesRoot = TestKotlinArtifacts.kotlinTest.jarRoot)
val m6LibInfo = m6Lib.toLibraryInfo()
m6.addDependency(m6Lib)
val m7Lib = projectLibrary("m7Lib", classesRoot = TestKotlinArtifacts.kotlinScriptRuntime.jarRoot)
val m7LibInfo = m7Lib.toLibraryInfo()
m7.addDependency(m7Lib)
val m8Lib = projectLibrary("m8Lib", classesRoot = TestKotlinArtifacts.junit3.jarRoot)
val m8LibInfo = m8Lib.toLibraryInfo()
m8.addDependency(m8Lib)
val m10Lib = projectLibrary("m10Lib", classesRoot = TestKotlinArtifacts.jetbrainsAnnotations.jarRoot)
val m10LibInfo = m10Lib.toLibraryInfo()
m10.addDependency(m10Lib)
m01.production.assertDependenciesEqual(
m01.production,
m0.production,
m01LibInfo,
)
m0.production.assertDependenciesEqual(
m0.production,
m1.production,
m0LibInfo,
)
m1.production.assertDependenciesEqual(
m1.production,
m2.production,
m1LibInfo,
)
m2.production.assertDependenciesEqual(
m2.production,
m3.production,
m2LibInfo,
)
m3.production.assertDependenciesEqual(
m3.production,
m4.production,
m7.production,
m3LibInfo,
)
m4.production.assertDependenciesEqual(
m4.production,
m5.production,
m6.production,
m4LibInfo,
)
m5.production.assertDependenciesEqual(
m5.production,
m3.production,
m5LibInfo,
)
m6.production.assertDependenciesEqual(
m6.production,
m10.production,
m6LibInfo,
)
m10.production.assertDependenciesEqual(
m10.production,
m10LibInfo,
)
m7.production.assertDependenciesEqual(
m7.production,
m8.production,
m7LibInfo,
)
m8.production.assertDependenciesEqual(
m8.production,
m1.production,
m8LibInfo,
)
val dependenciesCache = LibraryDependenciesCache.getInstance(project)
fun assertDependencies(lib: LibraryInfo, vararg expectedLibraries: LibraryInfo) {
val dependencies = dependenciesCache.getLibraryDependencies(lib)
assertEquals(
"LibraryInfo '${lib.name}' dependencies",
expectedLibraries.joinToString(separator = "\n") { it.name.asString() },
dependencies.libraries.map { it.name.asString() }.sorted().joinToString(separator = "\n"),
)
}
assertDependencies(
lib = m01LibInfo,
m01LibInfo, m0LibInfo, m10LibInfo, m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo, m7LibInfo, m8LibInfo
)
assertDependencies(
lib = m0LibInfo,
m0LibInfo, m10LibInfo, m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo, m7LibInfo, m8LibInfo
)
for (libraryInfo in listOf(m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m7LibInfo, m8LibInfo)) {
assertDependencies(
lib = libraryInfo,
m10LibInfo, m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo, m7LibInfo, m8LibInfo
)
}
assertDependencies(lib = m10LibInfo, m10LibInfo)
assertDependencies(lib = m6LibInfo, m10LibInfo, m6LibInfo)
}
fun testModuleCircularDependencyLibraries6() {
/**
```mermaid
graph
M0 --> M1
M1 --> M2
M2 --> M3
M3 --> M4
M4 --> M5
M5 --> M3
M5 --> M6
M6 --> M7
M7 --> M1
```
*/
val m0 = module("m0")
val m1 = module("m1")
val m2 = module("m2")
val m3 = module("m3")
val m4 = module("m4")
val m5 = module("m5")
val m6 = module("m6")
val m7 = module("m7")
m0.addDependency(m1)
m1.addDependency(m2)
m2.addDependency(m3)
m3.addDependency(m4)
m4.addDependency(m5)
m5.addDependency(m3)
m5.addDependency(m6)
m6.addDependency(m7)
m7.addDependency(m1)
val m0Lib = projectLibrary("m0Lib", classesRoot = TestKotlinArtifacts.kotlinDaemon.jarRoot)
val m0LibInfo = m0Lib.toLibraryInfo()
m0.addDependency(m0Lib)
val m1Lib = projectLibrary("m1Lib", classesRoot = TestKotlinArtifacts.kotlinReflect.jarRoot)
val m1LibInfo = m1Lib.toLibraryInfo()
m1.addDependency(m1Lib)
val m2Lib = projectLibrary("m2Lib", classesRoot = TestKotlinArtifacts.kotlinAnnotationsJvm.jarRoot)
val m2LibInfo = m2Lib.toLibraryInfo()
m2.addDependency(m2Lib)
val m3Lib = projectLibrary("m3Lib", classesRoot = TestKotlinArtifacts.kotlinTestJunit.jarRoot)
val m3LibInfo = m3Lib.toLibraryInfo()
m3.addDependency(m3Lib)
val m4Lib = projectLibrary("m4Lib", classesRoot = TestKotlinArtifacts.parcelizeRuntime.jarRoot)
val m4LibInfo = m4Lib.toLibraryInfo()
m4.addDependency(m4Lib)
val m5Lib = projectLibrary("m5Lib", classesRoot = TestKotlinArtifacts.jsr305.jarRoot)
val m5LibInfo = m5Lib.toLibraryInfo()
m5.addDependency(m5Lib)
val m6Lib = projectLibrary("m6Lib", classesRoot = TestKotlinArtifacts.kotlinTest.jarRoot)
val m6LibInfo = m6Lib.toLibraryInfo()
m6.addDependency(m6Lib)
val m7Lib = projectLibrary("m7Lib", classesRoot = TestKotlinArtifacts.kotlinScriptRuntime.jarRoot)
val m7LibInfo = m7Lib.toLibraryInfo()
m7.addDependency(m7Lib)
m0.production.assertDependenciesEqual(
m0.production,
m1.production,
m0LibInfo,
)
m1.production.assertDependenciesEqual(
m1.production,
m2.production,
m1LibInfo,
)
m2.production.assertDependenciesEqual(
m2.production,
m3.production,
m2LibInfo,
)
m3.production.assertDependenciesEqual(
m3.production,
m4.production,
m3LibInfo,
)
m4.production.assertDependenciesEqual(
m4.production,
m5.production,
m4LibInfo,
)
m5.production.assertDependenciesEqual(
m5.production,
m3.production,
m6.production,
m5LibInfo,
)
m6.production.assertDependenciesEqual(
m6.production,
m7.production,
m6LibInfo,
)
m7.production.assertDependenciesEqual(
m7.production,
m1.production,
m7LibInfo,
)
val dependenciesCache = LibraryDependenciesCache.getInstance(project)
fun assertDependencies(lib: LibraryInfo, vararg expectedLibraries: LibraryInfo) {
val dependencies = dependenciesCache.getLibraryDependencies(lib)
assertEquals(
"LibraryInfo '${lib.name}' dependencies",
expectedLibraries.joinToString(separator = "\n") { it.name.asString() },
dependencies.libraries.map { it.name.asString() }.sorted().joinToString(separator = "\n"),
)
}
assertDependencies(
lib = m0LibInfo,
m0LibInfo, m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo, m7LibInfo
)
for (libraryInfo in listOf(m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m7LibInfo)) {
assertDependencies(
lib = libraryInfo,
m1LibInfo, m2LibInfo, m3LibInfo, m4LibInfo, m5LibInfo, m6LibInfo, m7LibInfo
)
}
}
fun testExportedDependency() {
val (a, b, c) = modules()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment