[Native][tests] Support static cache as a separate compilation and dependency type
^KT-50775
This commit is contained in:
+155
-41
@@ -8,33 +8,31 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.compilation
|
|||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.*
|
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.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.*
|
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.*
|
||||||
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.ArgsBuilder
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildArgs
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildArgs
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.flatMapToSet
|
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
|
import java.io.File
|
||||||
|
|
||||||
internal abstract class TestCompilation<A : TestCompilationArtifact> {
|
internal abstract class TestCompilation<A : TestCompilationArtifact> {
|
||||||
abstract val result: TestCompilationResult<out A>
|
abstract val result: TestCompilationResult<out A>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class BaseCompilation<A : TestCompilationArtifact>(
|
internal abstract class BasicCompilation<A : TestCompilationArtifact>(
|
||||||
private val targets: KotlinNativeTargets,
|
private val targets: KotlinNativeTargets,
|
||||||
private val home: KotlinNativeHome,
|
|
||||||
private val classLoader: KotlinNativeClassLoader,
|
private val classLoader: KotlinNativeClassLoader,
|
||||||
private val optimizationMode: OptimizationMode,
|
private val optimizationMode: OptimizationMode,
|
||||||
private val memoryModel: MemoryModel,
|
protected val dependencies: Iterable<TestCompilationDependency<*>>,
|
||||||
private val threadStateChecker: ThreadStateChecker,
|
protected val expectedArtifact: A
|
||||||
private val gcType: GCType,
|
|
||||||
private val freeCompilerArgs: TestCompilerArgs,
|
|
||||||
private val sourceModules: Collection<TestModule>,
|
|
||||||
private val dependencies: Collection<TestCompilationDependency<*>>,
|
|
||||||
private val expectedArtifact: A
|
|
||||||
) : TestCompilation<A>() {
|
) : TestCompilation<A>() {
|
||||||
|
protected abstract val sourceModules: Collection<TestModule>
|
||||||
|
protected abstract val freeCompilerArgs: TestCompilerArgs
|
||||||
|
|
||||||
// Runs the compiler and memorizes the result on property access.
|
// Runs the compiler and memorizes the result on property access.
|
||||||
final override val result: TestCompilationResult<out A> by lazy {
|
final override val result: TestCompilationResult<out A> by lazy {
|
||||||
val failures = dependencies.collectFailures()
|
val failures = dependencies.collectFailures()
|
||||||
@@ -45,38 +43,37 @@ internal abstract class BaseCompilation<A : TestCompilationArtifact>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ArgsBuilder.applyCommonArgs() {
|
private fun ArgsBuilder.applyCommonArgs() {
|
||||||
|
add("-target", targets.testTarget.name)
|
||||||
|
optimizationMode.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
|
||||||
add(
|
add(
|
||||||
"-enable-assertions",
|
"-enable-assertions",
|
||||||
"-target", targets.testTarget.name,
|
|
||||||
"-repo", home.dir.resolve("klib").path,
|
|
||||||
"-output", expectedArtifact.path,
|
|
||||||
"-Xskip-prerelease-check",
|
"-Xskip-prerelease-check",
|
||||||
"-Xverify-ir",
|
"-Xverify-ir",
|
||||||
"-Xbinary=runtimeAssertionsMode=panic"
|
"-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() {
|
private fun ArgsBuilder.applySources() {
|
||||||
addFlattenedTwice(sourceModules, { it.files }) { it.location.path }
|
addFlattenedTwice(sourceModules, { it.files }) { it.location.path }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun doBeforeCompile() = Unit
|
||||||
|
|
||||||
private fun doCompile(): TestCompilationResult.ImmediateResult<out A> {
|
private fun doCompile(): TestCompilationResult.ImmediateResult<out A> {
|
||||||
|
doBeforeCompile()
|
||||||
|
|
||||||
val compilerArgs = buildArgs {
|
val compilerArgs = buildArgs {
|
||||||
applyCommonArgs()
|
applyCommonArgs()
|
||||||
applySpecificCompilerArgs()
|
applySpecificArgs(this)
|
||||||
|
applyDependencies(this)
|
||||||
|
applyFreeArgs()
|
||||||
applySources()
|
applySources()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +107,7 @@ internal abstract class BaseCompilation<A : TestCompilationArtifact>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun Collection<TestCompilationDependency<*>>.collectFailures() = flatMapToSet { dependency ->
|
private fun Iterable<TestCompilationDependency<*>>.collectFailures() = flatMapToSet { dependency ->
|
||||||
when (val result = (dependency as? TestCompilation<*>)?.result) {
|
when (val result = (dependency as? TestCompilation<*>)?.result) {
|
||||||
is TestCompilationResult.Failure -> listOf(result)
|
is TestCompilationResult.Failure -> listOf(result)
|
||||||
is TestCompilationResult.DependencyFailures -> result.causes
|
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() =
|
@JvmStatic
|
||||||
mapNotNull { dependency -> if (dependency.type is T) dependency.artifact as A else null }
|
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>()
|
return mapNotNull { dependency -> if (dependencyTypeMatcher(dependency.type)) dependency.artifact as A else null }
|
||||||
private val Collection<TestCompilationDependency<*>>.friends get() = collectArtifacts<KLIB, FriendLibrary>()
|
}
|
||||||
private val Collection<TestCompilationDependency<*>>.includedLibraries get() = collectArtifacts<KLIB, IncludedLibrary>()
|
|
||||||
|
@JvmStatic
|
||||||
|
protected val Iterable<TestCompilationDependency<*>>.cachedLibraries
|
||||||
|
get() = collectArtifacts<KLIBStaticCache, LibraryStaticCache>()
|
||||||
|
|
||||||
private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log")
|
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(
|
internal class LibraryCompilation(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
freeCompilerArgs: TestCompilerArgs,
|
freeCompilerArgs: TestCompilerArgs,
|
||||||
sourceModules: Collection<TestModule>,
|
sourceModules: Collection<TestModule>,
|
||||||
dependencies: Collection<TestCompilationDependency<*>>,
|
dependencies: Iterable<TestCompilationDependency<*>>,
|
||||||
expectedArtifact: KLIB
|
expectedArtifact: KLIB
|
||||||
) : BaseCompilation<KLIB>(
|
) : SourceBasedCompilation<KLIB>(
|
||||||
targets = settings.get(),
|
targets = settings.get(),
|
||||||
home = settings.get(),
|
home = settings.get(),
|
||||||
classLoader = settings.get(),
|
classLoader = settings.get(),
|
||||||
@@ -149,8 +199,9 @@ internal class LibraryCompilation(
|
|||||||
dependencies = dependencies,
|
dependencies = dependencies,
|
||||||
expectedArtifact = expectedArtifact
|
expectedArtifact = expectedArtifact
|
||||||
) {
|
) {
|
||||||
override fun ArgsBuilder.applySpecificCompilerArgs() {
|
override fun applySpecificArgs(argsBuilder: ArgsBuilder) = with(argsBuilder) {
|
||||||
add("-produce", "library")
|
add("-produce", "library")
|
||||||
|
super.applySpecificArgs(argsBuilder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,9 +210,9 @@ internal class ExecutableCompilation(
|
|||||||
freeCompilerArgs: TestCompilerArgs,
|
freeCompilerArgs: TestCompilerArgs,
|
||||||
sourceModules: Collection<TestModule>,
|
sourceModules: Collection<TestModule>,
|
||||||
private val extras: Extras,
|
private val extras: Extras,
|
||||||
dependencies: Collection<TestCompilationDependency<*>>,
|
dependencies: Iterable<TestCompilationDependency<*>>,
|
||||||
expectedArtifact: Executable
|
expectedArtifact: Executable
|
||||||
) : BaseCompilation<Executable>(
|
) : SourceBasedCompilation<Executable>(
|
||||||
targets = settings.get(),
|
targets = settings.get(),
|
||||||
home = settings.get(),
|
home = settings.get(),
|
||||||
classLoader = settings.get(),
|
classLoader = settings.get(),
|
||||||
@@ -176,7 +227,7 @@ internal class ExecutableCompilation(
|
|||||||
) {
|
) {
|
||||||
private val cacheKind: CacheKind = settings.get()
|
private val cacheKind: CacheKind = settings.get()
|
||||||
|
|
||||||
override fun ArgsBuilder.applySpecificCompilerArgs() {
|
override fun applySpecificArgs(argsBuilder: ArgsBuilder): Unit = with(argsBuilder) {
|
||||||
add("-produce", "program")
|
add("-produce", "program")
|
||||||
when (extras) {
|
when (extras) {
|
||||||
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
|
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
|
||||||
@@ -189,6 +240,69 @@ internal class ExecutableCompilation(
|
|||||||
add(testRunnerArg)
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -12,5 +12,6 @@ internal sealed interface TestCompilationArtifact {
|
|||||||
val path: String get() = file.path
|
val path: String get() = file.path
|
||||||
|
|
||||||
data class KLIB(override val file: File) : TestCompilationArtifact
|
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
|
data class Executable(override val file: File) : TestCompilationArtifact
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-7
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.konan.blackboxtest.support.compilation
|
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
|
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).
|
* [FriendLibrary] - similarly but friend modules (-friend-modules).
|
||||||
* [IncludedLibrary] - similarly but included modules (-Xinclude).
|
* [IncludedLibrary] - similarly but included modules (-Xinclude).
|
||||||
*/
|
*/
|
||||||
internal sealed interface TestCompilationDependencyType<A : TestCompilationArtifact> {
|
internal sealed class TestCompilationDependencyType<A : TestCompilationArtifact>(private val artifactClass: Class<A>) {
|
||||||
object Library : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
fun canYield(artifactClass: Class<out TestCompilationArtifact>): Boolean = this.artifactClass.isAssignableFrom(artifactClass)
|
||||||
object FriendLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
|
||||||
object IncludedLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
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> {
|
internal sealed interface TestCompilationDependency<A : TestCompilationArtifact> {
|
||||||
@@ -33,6 +39,6 @@ internal class CompiledDependency<A : TestCompilationArtifact>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class ExistingLibraryDependency(
|
internal class ExistingLibraryDependency(
|
||||||
override val artifact: TestCompilationArtifact.KLIB,
|
override val artifact: KLIB,
|
||||||
override val type: TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
override val type: TestCompilationDependencyType<KLIB>
|
||||||
) : TestCompilationDependency<TestCompilationArtifact.KLIB>
|
) : TestCompilationDependency<KLIB>
|
||||||
|
|||||||
+131
-79
@@ -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
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies
|
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.TestModule.Companion.allFriends
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
|
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
|
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.*
|
||||||
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.settings.Binaries
|
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.KotlinNativeTargets
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class TestCompilationFactory {
|
internal class TestCompilationFactory {
|
||||||
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, TestCompilation<KLIB>>()
|
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, KlibCompilations>()
|
||||||
private val cachedExecutableCompilations = ThreadSafeCache<ExecutableCacheKey, TestCompilation<Executable>>()
|
private val cachedExecutableCompilations = ThreadSafeCache<ExecutableCacheKey, TestCompilation<Executable>>()
|
||||||
|
|
||||||
private data class KlibCacheKey(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs)
|
private data class KlibCacheKey(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs)
|
||||||
private data class ExecutableCacheKey(val sourceModules: Set<TestModule>)
|
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> {
|
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation<Executable> {
|
||||||
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
|
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
|
||||||
val cacheKey = ExecutableCacheKey(rootModules)
|
val cacheKey = ExecutableCacheKey(rootModules)
|
||||||
@@ -38,11 +52,8 @@ internal class TestCompilationFactory {
|
|||||||
// Long pass.
|
// Long pass.
|
||||||
val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs // Should be identical inside the same test case group.
|
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 extras = testCases.first().extras // Should be identical inside the same test case group.
|
||||||
val dependencies = buildList {
|
val dependencies = collectDependencies(rootModules, freeCompilerArgs, settings).all()
|
||||||
rootModules.flatMapToSet { it.allDependencies }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
|
val executableArtifact = Executable(settings.artifactFileForExecutable(rootModules))
|
||||||
rootModules.flatMapToSet { it.allFriends }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
|
|
||||||
}
|
|
||||||
val expectedArtifact = Executable(settings.artifactFileForExecutable(rootModules))
|
|
||||||
|
|
||||||
return cachedExecutableCompilations.computeIfAbsent(cacheKey) {
|
return cachedExecutableCompilations.computeIfAbsent(cacheKey) {
|
||||||
ExecutableCompilation(
|
ExecutableCompilation(
|
||||||
@@ -51,12 +62,12 @@ internal class TestCompilationFactory {
|
|||||||
sourceModules = rootModules,
|
sourceModules = rootModules,
|
||||||
extras = extras,
|
extras = extras,
|
||||||
dependencies = dependencies,
|
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 sourceModules = setOf(sourceModule)
|
||||||
val cacheKey = KlibCacheKey(sourceModules, freeCompilerArgs)
|
val cacheKey = KlibCacheKey(sourceModules, freeCompilerArgs)
|
||||||
|
|
||||||
@@ -64,90 +75,131 @@ internal class TestCompilationFactory {
|
|||||||
cachedKlibCompilations[cacheKey]?.let { return it }
|
cachedKlibCompilations[cacheKey]?.let { return it }
|
||||||
|
|
||||||
// Long pass.
|
// Long pass.
|
||||||
val dependencies = buildList {
|
val dependencies = collectDependencies(sourceModules, freeCompilerArgs, settings)
|
||||||
sourceModule.allDependencies.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
|
val klibArtifact = KLIB(settings.artifactFileForKlib(sourceModule, freeCompilerArgs))
|
||||||
sourceModule.allFriends.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
|
|
||||||
}
|
val staticCacheArtifact: KLIBStaticCache? = if (settings.get<CacheKind>() is WithStaticCache)
|
||||||
val expectedArtifact = KLIB(settings.artifactFileForKlib(sourceModule, freeCompilerArgs))
|
KLIBStaticCache(file = klibArtifact.artifactFileForStaticCache(), klib = klibArtifact)
|
||||||
|
else
|
||||||
|
null // No artifact means no static cache should be compiled.
|
||||||
|
|
||||||
return cachedKlibCompilations.computeIfAbsent(cacheKey) {
|
return cachedKlibCompilations.computeIfAbsent(cacheKey) {
|
||||||
LibraryCompilation(
|
val klibCompilation = LibraryCompilation(
|
||||||
settings = settings,
|
settings = settings,
|
||||||
freeCompilerArgs = freeCompilerArgs,
|
freeCompilerArgs = freeCompilerArgs,
|
||||||
sourceModules = sourceModules,
|
sourceModules = sourceModules,
|
||||||
dependencies = dependencies,
|
dependencies = dependencies.klibDependencies,
|
||||||
expectedArtifact = expectedArtifact
|
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,
|
freeCompilerArgs: TestCompilerArgs,
|
||||||
settings: Settings,
|
settings: Settings
|
||||||
type: T
|
): SourceBasedCompilationDependencies {
|
||||||
) = CompiledDependency(moduleToKlib(this, freeCompilerArgs, settings), type)
|
val klibDependencies = mutableListOf<CompiledDependency<KLIB>>()
|
||||||
|
val staticCacheDependencies = mutableListOf<CompiledDependency<KLIBStaticCache>>()
|
||||||
|
|
||||||
private fun Settings.artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
|
fun <T : TestCompilationDependencyType<KLIB>> Set<TestModule>.collectDependencies(type: T) =
|
||||||
1 -> artifactFileForExecutable(modules.first())
|
forEach { dependencyModule: TestModule ->
|
||||||
else -> multiModuleArtifactFile(modules, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
|
val klibCompilations = moduleToKlib(dependencyModule, freeCompilerArgs, settings)
|
||||||
}
|
klibDependencies += klibCompilations.klib.asKlibDependency(type)
|
||||||
|
staticCacheDependencies.addIfNotNull(klibCompilations.staticCache?.asStaticCacheDependency())
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module is TestModule.Exclusive)
|
sourceModules.allDependencies().collectDependencies(Library)
|
||||||
uniquePackageNames += module.testCase.nominalPackageName
|
sourceModules.allFriends().collectDependencies(FriendLibrary)
|
||||||
}
|
|
||||||
|
|
||||||
val commonPackageName = uniquePackageNames.findCommonPackageName()
|
return SourceBasedCompilationDependencies(klibDependencies, staticCacheDependencies)
|
||||||
|
|
||||||
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 {
|
companion object {
|
||||||
val baseDir = get<Binaries>().testBinariesDir
|
private fun Set<TestModule>.allDependencies() = if (size == 1) first().allDependencies else flatMapToSet { it.allDependencies }
|
||||||
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.konan.blackboxtest.support.settings
|
package org.jetbrains.kotlin.konan.blackboxtest.support.settings
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@@ -124,8 +123,6 @@ internal sealed interface CacheKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CacheKind.rootCacheDir: File? get() = safeAs<WithStaticCache>()?.rootCacheDir
|
|
||||||
|
|
||||||
private fun computeCacheDirName(testTarget: KonanTarget, cacheKind: String, debuggable: Boolean) =
|
private fun computeCacheDirName(testTarget: KonanTarget, cacheKind: String, debuggable: Boolean) =
|
||||||
"$testTarget${if (debuggable) "-g" else ""}$cacheKind"
|
"$testTarget${if (debuggable) "-g" else ""}$cacheKind"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user