[Native][tests] Support static cache as a separate compilation and dependency type

^KT-50775
This commit is contained in:
Dmitriy Dolovov
2022-01-24 12:12:02 +03:00
parent e03bfaee0b
commit 33ca8b528c
5 changed files with 300 additions and 130 deletions
@@ -8,33 +8,31 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.compilation
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheKind.Companion.rootCacheDir
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheKind.WithStaticCache
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ArgsBuilder
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildArgs
import org.jetbrains.kotlin.konan.blackboxtest.support.util.flatMapToSet
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
internal abstract class TestCompilation<A : TestCompilationArtifact> {
abstract val result: TestCompilationResult<out A>
}
internal abstract class BaseCompilation<A : TestCompilationArtifact>(
internal abstract class BasicCompilation<A : TestCompilationArtifact>(
private val targets: KotlinNativeTargets,
private val home: KotlinNativeHome,
private val classLoader: KotlinNativeClassLoader,
private val optimizationMode: OptimizationMode,
private val memoryModel: MemoryModel,
private val threadStateChecker: ThreadStateChecker,
private val gcType: GCType,
private val freeCompilerArgs: TestCompilerArgs,
private val sourceModules: Collection<TestModule>,
private val dependencies: Collection<TestCompilationDependency<*>>,
private val expectedArtifact: A
protected val dependencies: Iterable<TestCompilationDependency<*>>,
protected val expectedArtifact: A
) : TestCompilation<A>() {
protected abstract val sourceModules: Collection<TestModule>
protected abstract val freeCompilerArgs: TestCompilerArgs
// Runs the compiler and memorizes the result on property access.
final override val result: TestCompilationResult<out A> by lazy {
val failures = dependencies.collectFailures()
@@ -45,38 +43,37 @@ internal abstract class BaseCompilation<A : TestCompilationArtifact>(
}
private fun ArgsBuilder.applyCommonArgs() {
add("-target", targets.testTarget.name)
optimizationMode.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
add(
"-enable-assertions",
"-target", targets.testTarget.name,
"-repo", home.dir.resolve("klib").path,
"-output", expectedArtifact.path,
"-Xskip-prerelease-check",
"-Xverify-ir",
"-Xbinary=runtimeAssertionsMode=panic"
)
optimizationMode.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
memoryModel.compilerFlags?.let { compilerFlags -> add(compilerFlags) }
threadStateChecker.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
gcType.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
addFlattened(dependencies.libraries) { library -> listOf("-l", library.path) }
dependencies.friends.takeIf(Collection<*>::isNotEmpty)?.let { friends ->
add("-friend-modules", friends.joinToString(File.pathSeparator) { friend -> friend.path })
}
add(dependencies.includedLibraries) { include -> "-Xinclude=${include.path}" }
add(freeCompilerArgs.compilerArgs)
}
protected abstract fun ArgsBuilder.applySpecificCompilerArgs()
protected abstract fun applySpecificArgs(argsBuilder: ArgsBuilder)
protected abstract fun applyDependencies(argsBuilder: ArgsBuilder)
private fun ArgsBuilder.applyFreeArgs() {
add(freeCompilerArgs.compilerArgs)
}
private fun ArgsBuilder.applySources() {
addFlattenedTwice(sourceModules, { it.files }) { it.location.path }
}
protected open fun doBeforeCompile() = Unit
private fun doCompile(): TestCompilationResult.ImmediateResult<out A> {
doBeforeCompile()
val compilerArgs = buildArgs {
applyCommonArgs()
applySpecificCompilerArgs()
applySpecificArgs(this)
applyDependencies(this)
applyFreeArgs()
applySources()
}
@@ -110,7 +107,7 @@ internal abstract class BaseCompilation<A : TestCompilationArtifact>(
}
companion object {
private fun Collection<TestCompilationDependency<*>>.collectFailures() = flatMapToSet { dependency ->
private fun Iterable<TestCompilationDependency<*>>.collectFailures() = flatMapToSet { dependency ->
when (val result = (dependency as? TestCompilation<*>)?.result) {
is TestCompilationResult.Failure -> listOf(result)
is TestCompilationResult.DependencyFailures -> result.causes
@@ -119,24 +116,77 @@ internal abstract class BaseCompilation<A : TestCompilationArtifact>(
}
}
private inline fun <reified A : TestCompilationArtifact, reified T : TestCompilationDependencyType<A>> Collection<TestCompilationDependency<*>>.collectArtifacts() =
mapNotNull { dependency -> if (dependency.type is T) dependency.artifact as A else null }
@JvmStatic
protected inline fun <reified A : TestCompilationArtifact, reified T : TestCompilationDependencyType<A>> Iterable<TestCompilationDependency<*>>.collectArtifacts(): List<A> {
val concreteDependencyType = T::class.objectInstance
val dependencyTypeMatcher: (TestCompilationDependencyType<*>) -> Boolean = if (concreteDependencyType != null) {
{ it == concreteDependencyType }
} else {
{ it.canYield(A::class.java) }
}
private val Collection<TestCompilationDependency<*>>.libraries get() = collectArtifacts<KLIB, Library>()
private val Collection<TestCompilationDependency<*>>.friends get() = collectArtifacts<KLIB, FriendLibrary>()
private val Collection<TestCompilationDependency<*>>.includedLibraries get() = collectArtifacts<KLIB, IncludedLibrary>()
return mapNotNull { dependency -> if (dependencyTypeMatcher(dependency.type)) dependency.artifact as A else null }
}
@JvmStatic
protected val Iterable<TestCompilationDependency<*>>.cachedLibraries
get() = collectArtifacts<KLIBStaticCache, LibraryStaticCache>()
private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log")
}
}
internal abstract class SourceBasedCompilation<A : TestCompilationArtifact>(
targets: KotlinNativeTargets,
private val home: KotlinNativeHome,
classLoader: KotlinNativeClassLoader,
optimizationMode: OptimizationMode,
private val memoryModel: MemoryModel,
private val threadStateChecker: ThreadStateChecker,
private val gcType: GCType,
override val freeCompilerArgs: TestCompilerArgs,
override val sourceModules: Collection<TestModule>,
dependencies: Iterable<TestCompilationDependency<*>>,
expectedArtifact: A
) : BasicCompilation<A>(
targets = targets,
classLoader = classLoader,
optimizationMode = optimizationMode,
dependencies = dependencies,
expectedArtifact = expectedArtifact
) {
override fun applySpecificArgs(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
add(
"-repo", home.dir.resolve("klib").path,
"-output", expectedArtifact.path
)
memoryModel.compilerFlags?.let { compilerFlags -> add(compilerFlags) }
threadStateChecker.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
gcType.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
}
override fun applyDependencies(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
addFlattened(dependencies.libraries) { library -> listOf("-l", library.path) }
dependencies.friends.takeIf(Collection<*>::isNotEmpty)?.let { friends ->
add("-friend-modules", friends.joinToString(File.pathSeparator) { friend -> friend.path })
}
add(dependencies.includedLibraries) { include -> "-Xinclude=${include.path}" }
}
companion object {
private val Iterable<TestCompilationDependency<*>>.libraries get() = collectArtifacts<KLIB, Library>()
private val Iterable<TestCompilationDependency<*>>.friends get() = collectArtifacts<KLIB, FriendLibrary>()
private val Iterable<TestCompilationDependency<*>>.includedLibraries get() = collectArtifacts<KLIB, IncludedLibrary>()
}
}
internal class LibraryCompilation(
settings: Settings,
freeCompilerArgs: TestCompilerArgs,
sourceModules: Collection<TestModule>,
dependencies: Collection<TestCompilationDependency<*>>,
dependencies: Iterable<TestCompilationDependency<*>>,
expectedArtifact: KLIB
) : BaseCompilation<KLIB>(
) : SourceBasedCompilation<KLIB>(
targets = settings.get(),
home = settings.get(),
classLoader = settings.get(),
@@ -149,8 +199,9 @@ internal class LibraryCompilation(
dependencies = dependencies,
expectedArtifact = expectedArtifact
) {
override fun ArgsBuilder.applySpecificCompilerArgs() {
override fun applySpecificArgs(argsBuilder: ArgsBuilder) = with(argsBuilder) {
add("-produce", "library")
super.applySpecificArgs(argsBuilder)
}
}
@@ -159,9 +210,9 @@ internal class ExecutableCompilation(
freeCompilerArgs: TestCompilerArgs,
sourceModules: Collection<TestModule>,
private val extras: Extras,
dependencies: Collection<TestCompilationDependency<*>>,
dependencies: Iterable<TestCompilationDependency<*>>,
expectedArtifact: Executable
) : BaseCompilation<Executable>(
) : SourceBasedCompilation<Executable>(
targets = settings.get(),
home = settings.get(),
classLoader = settings.get(),
@@ -176,7 +227,7 @@ internal class ExecutableCompilation(
) {
private val cacheKind: CacheKind = settings.get()
override fun ArgsBuilder.applySpecificCompilerArgs() {
override fun applySpecificArgs(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
add("-produce", "program")
when (extras) {
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
@@ -189,6 +240,69 @@ internal class ExecutableCompilation(
add(testRunnerArg)
}
}
cacheKind.rootCacheDir?.let { rootCacheDir -> add("-Xcache-directory=$rootCacheDir") }
super.applySpecificArgs(argsBuilder)
}
override fun applyDependencies(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
super.applyDependencies(argsBuilder)
cacheKind.safeAs<WithStaticCache>()?.rootCacheDir?.let { rootCacheDir -> add("-Xcache-directory=$rootCacheDir") }
add(dependencies.cachedLibraries) { (libraryCacheDir, _) -> "-Xcache-directory=${libraryCacheDir.path}" }
}
}
internal class StaticCacheCompilation(
settings: Settings,
dependencies: Iterable<TestCompilationDependency<*>>,
expectedArtifact: KLIBStaticCache
) : BasicCompilation<KLIBStaticCache>(
targets = settings.get(),
classLoader = settings.get(),
optimizationMode = settings.get(),
dependencies = dependencies,
expectedArtifact = expectedArtifact
) {
override val sourceModules get() = emptyList<TestModule>()
override val freeCompilerArgs get() = TestCompilerArgs.EMPTY
private val cacheDir = expectedArtifact.file
private val cacheKind: WithStaticCache = run {
val cacheKind = settings.get<CacheKind>()
cacheKind.safeAs<WithStaticCache>() ?: fail {
"${WithStaticCache::class.java} is expected as the current cache kind in ${StaticCacheCompilation::class.java}, found: $cacheKind"
}
}
override fun doBeforeCompile() {
cacheDir.mkdirs() // Make sure the directory to hold the cache exists.
}
override fun applySpecificArgs(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
add("-produce", "static_cache")
// TODO: additional cache flags: konanProperties.resolvablePropertyList("additionalCacheFlags", target.visibleName)
add(
"-Xadd-cache=${dependencies.libraryToCache.path}",
"-Xcache-directory=${cacheDir.path}"
)
cacheKind.rootCacheDir?.let { rootCacheDir -> add("-Xcache-directory=$rootCacheDir") }
}
override fun applyDependencies(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
addFlattened(dependencies.cachedLibraries) { (libraryCacheDir, library) ->
listOf(
"-l", library.path,
"-Xcache-directory=${libraryCacheDir.path}"
)
}
}
companion object {
private val Iterable<TestCompilationDependency<*>>.libraryToCache: KLIB
get() {
val libraries = collectArtifacts<KLIB, TestCompilationDependencyType<KLIB>>()
return libraries.singleOrNull() ?: fail {
"Only one library is expected as input for ${StaticCacheCompilation::class.java}, found: $libraries"
}
}
}
}
@@ -12,5 +12,6 @@ internal sealed interface TestCompilationArtifact {
val path: String get() = file.path
data class KLIB(override val file: File) : TestCompilationArtifact
data class KLIBStaticCache(override val file: File, val klib: KLIB) : TestCompilationArtifact
data class Executable(override val file: File) : TestCompilationArtifact
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.konan.blackboxtest.support.compilation
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIBStaticCache
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
/**
@@ -14,10 +16,14 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilati
* [FriendLibrary] - similarly but friend modules (-friend-modules).
* [IncludedLibrary] - similarly but included modules (-Xinclude).
*/
internal sealed interface TestCompilationDependencyType<A : TestCompilationArtifact> {
object Library : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
object FriendLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
object IncludedLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
internal sealed class TestCompilationDependencyType<A : TestCompilationArtifact>(private val artifactClass: Class<A>) {
fun canYield(artifactClass: Class<out TestCompilationArtifact>): Boolean = this.artifactClass.isAssignableFrom(artifactClass)
object Library : TestCompilationDependencyType<KLIB>(KLIB::class.java)
object FriendLibrary : TestCompilationDependencyType<KLIB>(KLIB::class.java)
object IncludedLibrary : TestCompilationDependencyType<KLIB>(KLIB::class.java)
object LibraryStaticCache : TestCompilationDependencyType<KLIBStaticCache>(KLIBStaticCache::class.java)
}
internal sealed interface TestCompilationDependency<A : TestCompilationArtifact> {
@@ -33,6 +39,6 @@ internal class CompiledDependency<A : TestCompilationArtifact>(
}
internal class ExistingLibraryDependency(
override val artifact: TestCompilationArtifact.KLIB,
override val type: TestCompilationDependencyType<TestCompilationArtifact.KLIB>
) : TestCompilationDependency<TestCompilationArtifact.KLIB>
override val artifact: KLIB,
override val type: TestCompilationDependencyType<KLIB>
) : TestCompilationDependency<KLIB>
@@ -11,23 +11,37 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilerArgs
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allFriends
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.FriendLibrary
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.Library
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Binaries
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheKind
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheKind.WithStaticCache
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
internal class TestCompilationFactory {
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, TestCompilation<KLIB>>()
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, KlibCompilations>()
private val cachedExecutableCompilations = ThreadSafeCache<ExecutableCacheKey, TestCompilation<Executable>>()
private data class KlibCacheKey(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs)
private data class ExecutableCacheKey(val sourceModules: Set<TestModule>)
// A pair of compilations for a KLIB itself and for its static cache that are created together.
private data class KlibCompilations(val klib: TestCompilation<KLIB>, val staticCache: TestCompilation<KLIBStaticCache>?)
private data class SourceBasedCompilationDependencies(
val klibDependencies: List<CompiledDependency<KLIB>>,
val staticCacheDependencies: List<CompiledDependency<KLIBStaticCache>>
) {
fun all(): Iterable<CompiledDependency<*>> = (klibDependencies.asSequence() + staticCacheDependencies).asIterable()
fun staticCacheDependenciesWith(klib: CompiledDependency<KLIB>): Iterable<CompiledDependency<*>> =
(staticCacheDependencies.asSequence() + klib).asIterable()
}
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation<Executable> {
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
val cacheKey = ExecutableCacheKey(rootModules)
@@ -38,11 +52,8 @@ internal class TestCompilationFactory {
// Long pass.
val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs // Should be identical inside the same test case group.
val extras = testCases.first().extras // Should be identical inside the same test case group.
val dependencies = buildList {
rootModules.flatMapToSet { it.allDependencies }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
rootModules.flatMapToSet { it.allFriends }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
}
val expectedArtifact = Executable(settings.artifactFileForExecutable(rootModules))
val dependencies = collectDependencies(rootModules, freeCompilerArgs, settings).all()
val executableArtifact = Executable(settings.artifactFileForExecutable(rootModules))
return cachedExecutableCompilations.computeIfAbsent(cacheKey) {
ExecutableCompilation(
@@ -51,12 +62,12 @@ internal class TestCompilationFactory {
sourceModules = rootModules,
extras = extras,
dependencies = dependencies,
expectedArtifact = expectedArtifact
expectedArtifact = executableArtifact
)
}
}
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs, settings: Settings): TestCompilation<KLIB> {
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs, settings: Settings): KlibCompilations {
val sourceModules = setOf(sourceModule)
val cacheKey = KlibCacheKey(sourceModules, freeCompilerArgs)
@@ -64,90 +75,131 @@ internal class TestCompilationFactory {
cachedKlibCompilations[cacheKey]?.let { return it }
// Long pass.
val dependencies = buildList {
sourceModule.allDependencies.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
sourceModule.allFriends.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
}
val expectedArtifact = KLIB(settings.artifactFileForKlib(sourceModule, freeCompilerArgs))
val dependencies = collectDependencies(sourceModules, freeCompilerArgs, settings)
val klibArtifact = KLIB(settings.artifactFileForKlib(sourceModule, freeCompilerArgs))
val staticCacheArtifact: KLIBStaticCache? = if (settings.get<CacheKind>() is WithStaticCache)
KLIBStaticCache(file = klibArtifact.artifactFileForStaticCache(), klib = klibArtifact)
else
null // No artifact means no static cache should be compiled.
return cachedKlibCompilations.computeIfAbsent(cacheKey) {
LibraryCompilation(
val klibCompilation = LibraryCompilation(
settings = settings,
freeCompilerArgs = freeCompilerArgs,
sourceModules = sourceModules,
dependencies = dependencies,
expectedArtifact = expectedArtifact
dependencies = dependencies.klibDependencies,
expectedArtifact = klibArtifact
)
val staticCacheCompilation: StaticCacheCompilation? = if (staticCacheArtifact != null) {
StaticCacheCompilation(
settings = settings,
dependencies = dependencies.staticCacheDependenciesWith(klibCompilation.asKlibDependency(type = /* does not matter in fact*/ Library)),
expectedArtifact = staticCacheArtifact
)
} else
null
KlibCompilations(klibCompilation, staticCacheCompilation)
}
}
private fun <T : TestCompilationDependencyType<KLIB>> TestModule.asKlibDependency(
private fun collectDependencies(
sourceModules: Set<TestModule>,
freeCompilerArgs: TestCompilerArgs,
settings: Settings,
type: T
) = CompiledDependency(moduleToKlib(this, freeCompilerArgs, settings), type)
settings: Settings
): SourceBasedCompilationDependencies {
val klibDependencies = mutableListOf<CompiledDependency<KLIB>>()
val staticCacheDependencies = mutableListOf<CompiledDependency<KLIBStaticCache>>()
private fun Settings.artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
1 -> artifactFileForExecutable(modules.first())
else -> multiModuleArtifactFile(modules, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
}
private fun Settings.artifactFileForExecutable(module: TestModule.Exclusive) =
singleModuleArtifactFile(module, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
private fun Settings.artifactFileForKlib(module: TestModule, freeCompilerArgs: TestCompilerArgs) = when (module) {
is TestModule.Exclusive -> singleModuleArtifactFile(module, "klib")
is TestModule.Shared -> get<Binaries>().sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
}
private fun Settings.singleModuleArtifactFile(module: TestModule.Exclusive, extension: String): File {
val artifactFileName = buildString {
append(module.testCase.nominalPackageName.compressedPackageName).append('.')
if (extension == "klib") append(module.name).append('.')
append(extension)
}
return artifactDirForPackageName(module.testCase.nominalPackageName).resolve(artifactFileName)
}
private fun Settings.multiModuleArtifactFile(modules: Collection<TestModule>, extension: String): File {
var filesCount = 0
var hash = 0
val uniquePackageNames = hashSetOf<PackageName>()
modules.forEach { module ->
module.files.forEach { file ->
filesCount++
hash = hash * 31 + file.hashCode()
fun <T : TestCompilationDependencyType<KLIB>> Set<TestModule>.collectDependencies(type: T) =
forEach { dependencyModule: TestModule ->
val klibCompilations = moduleToKlib(dependencyModule, freeCompilerArgs, settings)
klibDependencies += klibCompilations.klib.asKlibDependency(type)
staticCacheDependencies.addIfNotNull(klibCompilations.staticCache?.asStaticCacheDependency())
}
if (module is TestModule.Exclusive)
uniquePackageNames += module.testCase.nominalPackageName
}
sourceModules.allDependencies().collectDependencies(Library)
sourceModules.allFriends().collectDependencies(FriendLibrary)
val commonPackageName = uniquePackageNames.findCommonPackageName()
val artifactFileName = buildString {
val prefix = filesCount.toString()
repeat(4 - prefix.length) { append('0') }
append(prefix).append('-')
if (!commonPackageName.isEmpty())
append(commonPackageName.compressedPackageName).append('-')
append(prettyHash(hash))
append('.').append(extension)
}
return artifactDirForPackageName(commonPackageName).resolve(artifactFileName)
return SourceBasedCompilationDependencies(klibDependencies, staticCacheDependencies)
}
private fun Settings.artifactDirForPackageName(packageName: PackageName?): File {
val baseDir = get<Binaries>().testBinariesDir
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
companion object {
private fun Set<TestModule>.allDependencies() = if (size == 1) first().allDependencies else flatMapToSet { it.allDependencies }
private fun Set<TestModule>.allFriends() = if (size == 1) first().allFriends else flatMapToSet { it.allFriends }
outputDir.mkdirs()
private fun <T : TestCompilationDependencyType<KLIB>> TestCompilation<KLIB>.asKlibDependency(type: T): CompiledDependency<KLIB> =
CompiledDependency(this, type)
return outputDir
private fun TestCompilation<KLIBStaticCache>.asStaticCacheDependency(): CompiledDependency<KLIBStaticCache> =
CompiledDependency(this, LibraryStaticCache)
private fun Settings.artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
1 -> artifactFileForExecutable(modules.first())
else -> multiModuleArtifactFile(modules, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
}
private fun Settings.artifactFileForExecutable(module: TestModule.Exclusive) =
singleModuleArtifactFile(module, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
private fun Settings.artifactFileForKlib(module: TestModule, freeCompilerArgs: TestCompilerArgs) = when (module) {
is TestModule.Exclusive -> singleModuleArtifactFile(module, "klib")
is TestModule.Shared -> get<Binaries>().sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
}
private fun KLIB.artifactFileForStaticCache() = file.resolveSibling("${file.name}-cache")
private fun Settings.singleModuleArtifactFile(module: TestModule.Exclusive, extension: String): File {
val artifactFileName = buildString {
append(module.testCase.nominalPackageName.compressedPackageName).append('.')
if (extension == "klib") append(module.name).append('.')
append(extension)
}
return artifactDirForPackageName(module.testCase.nominalPackageName).resolve(artifactFileName)
}
private fun Settings.multiModuleArtifactFile(modules: Collection<TestModule>, extension: String): File {
var filesCount = 0
var hash = 0
val uniquePackageNames = hashSetOf<PackageName>()
modules.forEach { module ->
module.files.forEach { file ->
filesCount++
hash = hash * 31 + file.hashCode()
}
if (module is TestModule.Exclusive)
uniquePackageNames += module.testCase.nominalPackageName
}
val commonPackageName = uniquePackageNames.findCommonPackageName()
val artifactFileName = buildString {
val prefix = filesCount.toString()
repeat(4 - prefix.length) { append('0') }
append(prefix).append('-')
if (!commonPackageName.isEmpty())
append(commonPackageName.compressedPackageName).append('-')
append(prettyHash(hash))
append('.').append(extension)
}
return artifactDirForPackageName(commonPackageName).resolve(artifactFileName)
}
private fun Settings.artifactDirForPackageName(packageName: PackageName?): File {
val baseDir = get<Binaries>().testBinariesDir
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
outputDir.mkdirs()
return outputDir
}
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.konan.blackboxtest.support.settings
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
import kotlin.time.Duration
@@ -124,8 +123,6 @@ internal sealed interface CacheKind {
}
companion object {
val CacheKind.rootCacheDir: File? get() = safeAs<WithStaticCache>()?.rootCacheDir
private fun computeCacheDirName(testTarget: KonanTarget, cacheKind: String, debuggable: Boolean) =
"$testTarget${if (debuggable) "-g" else ""}$cacheKind"
}