diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelJsKlibCompilerRunnerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelCompilerRunnerTest.kt similarity index 58% rename from compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelJsKlibCompilerRunnerTest.kt rename to compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelCompilerRunnerTest.kt index b4ce8cae0f6..c46bee28e71 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelJsKlibCompilerRunnerTest.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementaMultiModulelCompilerRunnerTest.kt @@ -5,35 +5,45 @@ package org.jetbrains.kotlin.incremental -import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments -import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryJs -import org.jetbrains.kotlin.incremental.utils.* -import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION +import org.jetbrains.kotlin.build.report.ICReporter +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory +import org.jetbrains.kotlin.incremental.utils.TestCompilationResult +import org.jetbrains.kotlin.incremental.utils.TestICReporter +import org.jetbrains.kotlin.incremental.utils.TestMessageCollector import org.jetbrains.kotlin.utils.DFS import java.io.File import java.util.regex.Pattern -abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : AbstractIncrementalJsKlibCompilerRunnerTest() { +abstract class AbstractIncrementalMultiModuleCompilerRunnerTest : + AbstractIncrementalCompilerRunnerTestBase() { - private class ModuleBuildConfiguration(val srcDir: File, val dependencies: List) + private class ModuleDependency(val moduleName: String, val flags: Set) + private class ModuleBuildConfiguration(val srcDir: File, val dependencies: List) - private val modulesDir: File by lazy { File(workingDir, "modules") } + protected val repository: File by lazy { File(workingDir, "repository") } private val modulesInfo: MutableMap = mutableMapOf() private val modulesOrder: MutableList = mutableListOf() private val dirToModule = mutableMapOf() private val nameToModules = mutableMapOf>() + private val jarToClassListFile = mutableMapOf() private val jarToModule = mutableMapOf() - private val modulesApiHistory: ModulesApiHistoryJs by lazy { - ModulesApiHistoryJs(IncrementalModuleInfo(workingDir, dirToModule, nameToModules, emptyMap(), jarToModule)) + protected val incrementalModuleInfo: IncrementalModuleInfo by lazy { + IncrementalModuleInfo(workingDir, dirToModule, nameToModules, jarToClassListFile, jarToModule) } + protected abstract val modulesApiHistory: ApiHistory + override val moduleNames: Collection? get() = modulesOrder + protected abstract val scopeExpansionMode: CompileScopeExpansionMode + override fun resetTest(testDir: File, newOutDir: File, newCacheDir: File) { - modulesDir.deleteRecursively() - modulesDir.mkdirs() + repository.deleteRecursively() + repository.mkdirs() dirToModule.clear() nameToModules.clear() @@ -43,6 +53,7 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract } override fun setupTest(testDir: File, srcDir: File, cacheDir: File, outDir: File): List { + repository.mkdirs() val ktFiles = srcDir.getFiles().filter { it.extension == "kt" } val results = mutableMapOf>>() @@ -59,7 +70,7 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract val dependencyGraph = parseDependencies(testDir) DFS.topologicalOrder(dependencyGraph.keys) { m -> - dependencyGraph[m] ?: error("Expected dependencies for module $m") + (dependencyGraph[m] ?: error("Expected dependencies for module $m")).map { it.moduleName } }.reversed().mapTo(modulesOrder) { it } for ((moduleName, fileEntries) in results) { @@ -81,8 +92,8 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract return listOf(srcDir) } - private fun setupModuleApiHistory(moduleName: String, outDir: File, cacheDir: File) { - val depKlibFile = File(modulesDir, moduleName.klib) + protected open fun setupModuleApiHistory(moduleName: String, outDir: File, cacheDir: File) { + val depArtifactFile = File(repository, moduleName.asArtifactFileName()) val moduleBuildDir = File(outDir, moduleName) val moduleCacheDir = File(cacheDir, moduleName) val moduleBuildHistoryFile = buildHistoryFile(moduleCacheDir) @@ -91,7 +102,7 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract dirToModule[moduleBuildDir] = moduleEntry nameToModules.getOrPut(moduleName) { mutableSetOf() }.add(moduleEntry) - jarToModule[depKlibFile] = moduleEntry + jarToModule[depArtifactFile] = moduleEntry } companion object { @@ -103,9 +114,7 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract else listOf(this) } - private val String.klib: String get() = "$this.$KLIB_FILE_EXTENSION" - - private fun parseDependencies(testDir: File): Map> { + private fun parseDependencies(testDir: File): Map> { val actualModulesTxtFile = File(testDir, "dependencies.txt") @@ -113,71 +122,91 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract error("${actualModulesTxtFile.path} is expected") } - val result = mutableMapOf>() + val result = mutableMapOf>() val lines = actualModulesTxtFile.readLines() - lines.map { it.split("->") }.map { + lines.map { it.split("->") }.forEach { assert(it.size == 2) val moduleName = it[0] val dependencyPart = it[1] - val idx = dependencyPart.indexOf('[') - val dependencyName = if (idx >= 0) { - // skip annotations - dependencyPart.substring(0, idx) - } else dependencyPart - val dependencies = result.getOrPut(moduleName) { mutableListOf() } - if (dependencyName.isNotBlank()) { - dependencies.add(dependencyName) + + if (dependencyPart.isNotBlank()) { + val idx = dependencyPart.indexOf('[') + val dependency = if (idx >= 0) { + // skip annotations + val depModuleName = dependencyPart.substring(0, idx) + val flagsString = dependencyPart.substring(idx + 1, dependencyPart.length - 1) + val flags = flagsString.split(",").map { s -> s.trim() }.filter { s -> s.isNotEmpty() }.toSet() + ModuleDependency(depModuleName, flags) + } else ModuleDependency(dependencyPart, emptySet()) + dependencies.add(dependency) } } return result } + + private const val EXPORTED = "exported" } - private fun K2JSCompilerArguments.updateCompilerArguments( - moduleDependencies: List, - initialDeps: String, - destinationFile: File - ) { - val additionalDeps = moduleDependencies.joinToString(File.pathSeparator) { - File(modulesDir, it.klib).absolutePath + protected abstract fun makeForSingleModule( + moduleCacheDir: File, + sourceRoots: Iterable, + args: Args, + moduleBuildHistoryFile: File, + messageCollector: MessageCollector, + reporter: ICReporter, + scopeExpansion: CompileScopeExpansionMode, + modulesApiHistory: ApiHistory, + providedChangedFiles: ChangedFiles? + ) + + private fun collectEffectiveDependencies(moduleName: String): List { + val result = mutableSetOf() + + val moduleInfo = modulesInfo[moduleName] ?: error("Cannot find module info for $moduleName") + + for (dep in moduleInfo.dependencies) { + val depName = dep.moduleName + result.add(depName) + + val depInfo = modulesInfo[depName] ?: error("Cannot find module info for dependency $moduleName -> $depName") + for (depdep in depInfo.dependencies) { + if (EXPORTED in depdep.flags) { + result.add(depdep.moduleName) + } + } } - val sb = StringBuilder(initialDeps) - if (additionalDeps.isNotBlank()) { - sb.append(File.pathSeparator) - sb.append(additionalDeps) - } - - libraries = sb.toString() - outputFile = destinationFile.path + return result.toList() } + protected abstract fun Args.updateForSingleModule(moduleDependencies: List, outFile: File) + + protected abstract fun String.asOutputFileName(): String + protected abstract fun String.asArtifactFileName(): String + + protected abstract fun transformToDependency(moduleName: String, rawArtifact: File): File + override fun make( cacheDir: File, outDir: File, sourceRoots: Iterable, - args: K2JSCompilerArguments + args: Args ): TestCompilationResult { val reporter = TestICReporter() val messageCollector = TestMessageCollector() - val initialDeps = args.libraries ?: "" - - args.repositries = modulesDir.path val modifiedLibraries = mutableListOf>() val deletedLibraries = mutableListOf>() var compilationIsEnabled = true - val isInitial = modulesDir.list()?.isEmpty() ?: true + val isInitial = repository.list()?.isEmpty() ?: true for (module in modulesOrder) { - val moduleBuildInfo = modulesInfo[module] ?: error("Cannot find config for $module") - - val moduleDependencies = moduleBuildInfo.dependencies + val moduleDependencies = collectEffectiveDependencies(module) val moduleModifiedDependencies = modifiedLibraries.filter { it.first in moduleDependencies }.map { it.second } val moduleDeletedDependencies = deletedLibraries.filter { it.first in moduleDependencies }.map { it.second } @@ -187,15 +216,15 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract val moduleOutDir = File(outDir, module) val moduleCacheDir = File(cacheDir, module) val moduleBuildHistory = buildHistoryFile(moduleCacheDir) + + val moduleBuildInfo = modulesInfo[module] ?: error("Cannot find config for $module") val sources = moduleBuildInfo.srcDir.getFiles() - val outputKlibFile = File(moduleOutDir, module.klib) - val dependencyFile = File(modulesDir, module.klib) - - args.updateCompilerArguments(moduleDependencies, initialDeps, outputKlibFile) + val outputFile = File(moduleOutDir, module.asOutputFileName()) if (compilationIsEnabled) { - makeJsIncrementally( + args.updateForSingleModule(moduleDependencies, outputFile) + makeForSingleModule( moduleCacheDir, sources, args, @@ -208,6 +237,7 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract ) } + val dependencyFile = File(repository, module.asArtifactFileName()) val oldMD5 = if (dependencyFile.exists()) { val bytes = dependencyFile.readBytes() dependencyFile.delete() @@ -215,11 +245,11 @@ abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : Abstract } else 0 if (!messageCollector.hasErrors()) { - val newMD5 = outputKlibFile.readBytes().md5() + transformToDependency(module, outputFile) + val newMD5 = dependencyFile.readBytes().md5() if (oldMD5 != newMD5) { modifiedLibraries.add(module to dependencyFile) } - outputKlibFile.copyTo(dependencyFile) } else { compilationIsEnabled = false } diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalCompilerRunnerTestBase.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalCompilerRunnerTestBase.kt index 4a57fee88eb..07b078d0ce4 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalCompilerRunnerTestBase.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalCompilerRunnerTestBase.kt @@ -81,7 +81,7 @@ abstract class AbstractIncrementalCompilerRunnerTestBase { diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJsCompilerRunnerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJsCompilerRunnerTest.kt index 100b3039f15..c7f2c1d5929 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJsCompilerRunnerTest.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJsCompilerRunnerTest.kt @@ -44,7 +44,5 @@ abstract class AbstractIncrementalJsCompilerRunnerTest : AbstractIncrementalComp metaInfo = true } - protected fun buildHistoryFile(cacheDir: File): File = File(cacheDir, "build-history.bin") - protected open val scopeExpansionMode = CompileScopeExpansionMode.NEVER } \ No newline at end of file diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest.kt new file mode 100644 index 00000000000..e36e7d574ce --- /dev/null +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.incremental + +import org.jetbrains.kotlin.build.report.ICReporter +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryJs +import org.jetbrains.kotlin.incremental.testingUtils.BuildLogFinder +import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION +import java.io.File + +abstract class AbstractIncrementalMultiModuleJsKlibCompilerRunnerTest : + AbstractIncrementalMultiModuleCompilerRunnerTest() { + + override fun createCompilerArguments(destinationDir: File, testDir: File): K2JSCompilerArguments = + K2JSCompilerArguments().apply { + libraries = STDLIB_DEPENDENCY + outputFile = File(destinationDir, "${testDir.name}.$KLIB_FILE_EXTENSION").path + sourceMap = false + irProduceKlibDir = false + irProduceKlibFile = true + irOnly = true + repositries = repository.absolutePath + } + + override val buildLogFinder: BuildLogFinder + get() = super.buildLogFinder.copy(isJsIrEnabled = true, isKlibEnabled = true) + + override fun makeForSingleModule( + moduleCacheDir: File, + sourceRoots: Iterable, + args: K2JSCompilerArguments, + moduleBuildHistoryFile: File, + messageCollector: MessageCollector, + reporter: ICReporter, + scopeExpansion: CompileScopeExpansionMode, + modulesApiHistory: ModulesApiHistoryJs, + providedChangedFiles: ChangedFiles? + ) { + makeJsIncrementally( + moduleCacheDir, + sourceRoots, + args, + moduleBuildHistoryFile, + messageCollector, + reporter, + scopeExpansionMode, + modulesApiHistory, + providedChangedFiles + ) + } + + override val modulesApiHistory: ModulesApiHistoryJs by lazy { + ModulesApiHistoryJs(incrementalModuleInfo) + } + + override val scopeExpansionMode: CompileScopeExpansionMode get() = CompileScopeExpansionMode.NEVER + + override fun String.asOutputFileName(): String = klib + override fun String.asArtifactFileName(): String = klib + + override fun transformToDependency(moduleName: String, rawArtifact: File): File { + val dependencyFile = File(repository, moduleName.klib) + rawArtifact.copyTo(dependencyFile) + return dependencyFile + } + + override fun K2JSCompilerArguments.updateForSingleModule(moduleDependencies: List, outFile: File) { + val additionalDeps = moduleDependencies.joinToString(File.pathSeparator) { + File(repository, it.klib).absolutePath + } + + val sb = StringBuilder(STDLIB_DEPENDENCY) + if (additionalDeps.isNotBlank()) { + sb.append(File.pathSeparator) + sb.append(additionalDeps) + } + + libraries = sb.toString() + outputFile = outFile.path + } + + companion object { + private val String.klib: String get() = "$this.$KLIB_FILE_EXTENSION" + + private const val STDLIB_DEPENDENCY = "build/js-ir-runtime/full-runtime.klib" + } +} \ No newline at end of file