diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index eaf9c1b73e3..2dfcfe4cd99 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -40,7 +40,7 @@ import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity -import org.jetbrains.kotlin.ir.backend.js.ic.actualizeCacheForModule +import org.jetbrains.kotlin.ir.backend.js.ic.actualizeCaches import org.jetbrains.kotlin.ir.backend.js.ic.buildCacheForModuleFiles import org.jetbrains.kotlin.ir.backend.js.ic.loadModuleCaches import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer @@ -196,9 +196,9 @@ class K2JsIrCompiler : CLICompiler() { // TODO: Handle non-empty main call arguments val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList() - val icCaches = configureLibraries(arguments.cacheDirectories) + val cacheDirectories = configureLibraries(arguments.cacheDirectories) - if (arguments.irBuildCache) { + val icCaches = if (cacheDirectories.isNotEmpty()) { messageCollector.report(INFO, "") messageCollector.report(INFO, "Building cache:") messageCollector.report(INFO, "to: ${outputFilePath}") @@ -209,24 +209,22 @@ class K2JsIrCompiler : CLICompiler() { val start = System.currentTimeMillis() - val updateStatus = actualizeCacheForModule( + actualizeCaches( includes, - outputFilePath, configurationJs, libraries, - icCaches, - IrFactoryImplForJsIC(WholeWorldStageController()), + cacheDirectories, + { IrFactoryImplForJsIC(WholeWorldStageController()) }, mainCallArguments, - ::buildCacheForModuleFiles - ) - - if (updateStatus.upToDate) { - messageCollector.report(INFO, "IC per-file cache up-to-date check duration: ${System.currentTimeMillis() - start}ms") - } else { - messageCollector.report(INFO, "IC per-file cache building duration: ${System.currentTimeMillis() - start}ms") + ::buildCacheForModuleFiles, + ) { updateStatus -> + if (updateStatus.upToDate) { + messageCollector.report(INFO, "IC per-file cache up-to-date check duration: ${System.currentTimeMillis() - start}ms") + } else { + messageCollector.report(INFO, "IC per-file cache building duration: ${System.currentTimeMillis() - start}ms") + } } - return OK - } + } else emptyList() // Run analysis if main module is sources lateinit var sourceModule: ModulesStructure diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/fileUtil.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/fileUtil.kt index dd2c317bc7c..c2773eaa7d6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/fileUtil.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/fileUtil.kt @@ -31,5 +31,15 @@ data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong, val configHashULong = configHash.toULongOrNull(16) ?: 0UL return CacheInfo(path, libPath, flatHash.toULong(16), transHash.toULong(16), configHashULong) } + + fun loadOrCreate( + path: String, + moduleName: String, + flatHash: ULong = 0UL, + transHash: ULong = 0UL, + configHash: ULong = 0UL + ): CacheInfo { + return load(path) ?: CacheInfo(path, moduleName, flatHash, transHash, configHash) + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt index a1519693e4d..f4e98c1561a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt @@ -40,7 +40,9 @@ import java.security.MessageDigest import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile as ProtoFile private fun KotlinLibrary.fingerprint(fileIndex: Int): Hash { - return ((((types(fileIndex).md5() * 31) + signatures(fileIndex).md5()) * 31 + strings(fileIndex).md5()) * 31 + declarations(fileIndex).md5()) * 31 + bodies(fileIndex).md5() + return ((((types(fileIndex).md5() * 31) + signatures(fileIndex).md5()) * 31 + strings(fileIndex).md5()) * 31 + declarations(fileIndex).md5()) * 31 + bodies( + fileIndex + ).md5() } private fun invalidateCacheForModule( @@ -295,7 +297,7 @@ private fun createCacheConsumer(path: String): PersistentCacheConsumer { return PersistentCacheConsumerImpl(path) } -private fun loadCacheInfo(cachePaths: Collection): MutableMap { +fun loadCacheInfo(cachePaths: Collection): MutableMap { val caches = cachePaths.map { CacheInfo.load(it) ?: error("Cannot load IC cache from $it") } val result = mutableMapOf() return caches.associateByTo(result) { it.libPath.toCanonicalPath() } @@ -311,7 +313,7 @@ private fun loadLibraries(configuration: CompilerConfiguration, dependencies: Co return allResolvedDependencies.getFullList().associateBy { it.libraryFile.path.toCanonicalPath() } } -private fun String.toCanonicalPath(): String = File(this).canonicalPath +fun String.toCanonicalPath(): String = File(this).canonicalPath typealias ModuleName = String typealias ModulePath = String @@ -408,35 +410,101 @@ enum class CacheUpdateStatus(val upToDate: Boolean) { } +fun actualizeCaches( + includes: String, + compilerConfiguration: CompilerConfiguration, + dependencies: Collection, + icCachePaths: Collection, + irFactory: () -> IrFactory, + mainArguments: List?, + executor: CacheExecutor, + callback: (CacheUpdateStatus) -> Unit +): List { + val (libraries, dependencyGraph, configMD5) = CacheConfiguration(dependencies, compilerConfiguration) + val cacheMap = libraries.values.zip(icCachePaths).toMap() + + val icCacheMap: MutableMap = mutableMapOf() + val resultCaches = mutableListOf() + + val visitedLibraries = mutableSetOf() + fun visitDependency(library: KotlinLibrary) { + if (library in visitedLibraries) return + visitedLibraries.add(library) + + val libraryDeps = dependencyGraph[library] ?: error("Unknown library ${library.libraryName}") + libraryDeps.forEach { visitDependency(it) } + + val cachePath = cacheMap[library] ?: error("Unknown cache for library ${library.libraryName}") + resultCaches.add(cachePath) + + val moduleName = library.libraryFile.path.toCanonicalPath() + val cacheInfo = CacheInfo.loadOrCreate(cachePath, moduleName, configHash = configMD5) + icCacheMap[moduleName] = cacheInfo + + val updateStatus = actualizeCacheForModule( + moduleName = moduleName, + cachePath = cachePath, + compilerConfiguration = compilerConfiguration, + configMD5 = configMD5, + libraries = libraries, + dependencyGraph = dependencyGraph, + icCacheMap = icCacheMap, + irFactory = irFactory(), + mainArguments = mainArguments, + executor = executor + ) + callback(updateStatus) + } + + val canonicalIncludes = includes.toCanonicalPath() + val mainLibrary = libraries[canonicalIncludes] ?: error("Main library not found in libraries: $canonicalIncludes") + visitDependency(mainLibrary) + return resultCaches +} + +class CacheConfiguration( + private val dependencies: Collection, + val compilerConfiguration: CompilerConfiguration +) { + val libraries: Map = loadLibraries(compilerConfiguration, dependencies) + + val dependencyGraph: Map> + get() { + val nameToKotlinLibrary: Map = libraries.values.associateBy { it.moduleName } + + return libraries.values.associateWith { + it.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { depName -> + nameToKotlinLibrary[depName] ?: error("No Library found for $depName") + } + } + } + + val configMD5 + get() = compilerConfiguration.calcMD5() + + operator fun component1() = libraries + operator fun component2() = dependencyGraph + operator fun component3() = configMD5 +} + // Returns true if caches up-to-date fun actualizeCacheForModule( moduleName: String, cachePath: String, compilerConfiguration: CompilerConfiguration, - dependencies: Collection, - icCachePaths: Collection, + configMD5: ULong, + libraries: Map, + dependencyGraph: Map>, + icCacheMap: Map, irFactory: IrFactory, mainArguments: List?, executor: CacheExecutor ): CacheUpdateStatus { - val configMD5 = compilerConfiguration.calcMD5() - val modulePath = moduleName.toCanonicalPath() - val cacheInfo = CacheInfo.load(cachePath) ?: CacheInfo(cachePath, modulePath, 0UL, 0UL, configMD5) - val icCacheMap: Map = loadCacheInfo(icCachePaths).also { - it[modulePath] = cacheInfo - } - - val libraries: Map = loadLibraries(compilerConfiguration, dependencies) - val nameToKotlinLibrary: Map = libraries.values.associateBy { it.moduleName } - val dependencyGraph = libraries.values.associateWith { - it.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { depName -> - nameToKotlinLibrary[depName] ?: error("No Library found for $depName") - } - } + val cacheInfo = icCacheMap[moduleName] ?: error("Cache for $moduleName not found") val configUpdated = configMD5 != cacheInfo.configHash cacheInfo.configHash = configMD5 - if (checkLibrariesHash(libraries, dependencyGraph, icCacheMap, modulePath) && !configUpdated) { + if (checkLibrariesHash(libraries, dependencyGraph, icCacheMap, moduleName) && !configUpdated) { return CacheUpdateStatus.FAST_PATH // up-to-date } @@ -444,7 +512,7 @@ fun actualizeCacheForModule( libraries[lib.toCanonicalPath()]!! to createCacheProvider(cache.path) }.toMap() - val currentModule = libraries[moduleName.toCanonicalPath()] ?: error("No loaded library found for path $moduleName") + val currentModule = libraries[moduleName] ?: error("No loaded library found for path $moduleName") val persistentCacheConsumer = createCacheConsumer(cachePath) return actualizeCacheForModule( diff --git a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt index 471c03de237..a4277919289 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt @@ -17,17 +17,15 @@ import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.ir.backend.js.generateKLib -import org.jetbrains.kotlin.ir.backend.js.ic.CacheUpdateStatus -import org.jetbrains.kotlin.ir.backend.js.ic.PersistentCacheConsumer -import org.jetbrains.kotlin.ir.backend.js.ic.actualizeCacheForModule +import org.jetbrains.kotlin.ir.backend.js.ic.* import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.prepareAnalyzedSourceModule +import org.jetbrains.kotlin.ir.declarations.IrFactory import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.KotlinTestWithEnvironment -import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator import org.jetbrains.kotlin.test.util.JUnit4Assertions import java.io.File import kotlin.io.path.createTempDirectory @@ -58,7 +56,8 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() { cacheConsumer: PersistentCacheConsumer, exportedDeclarations: Set, mainArguments: List?, - ) { } + ) { + } private fun initializeStdlibCache() { if (stdlibCacheDir != null) return @@ -67,7 +66,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() { val configuration = createConfiguration("stdlib") - actualizeCacheForModule( + actualizeCacheForModuleWithPrepare( stdlibKlibPath, cacheDir.canonicalPath, configuration, @@ -158,7 +157,17 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() { val moduleCacheDir = resolveModuleCache(module, buildDir) - buildCachesAndCheck(moduleStep, configuration, moduleSourceDir, outputKlibFile, moduleCacheDir, dependencies, icCaches, dirtyFiles, deletedFiles) + buildCachesAndCheck( + moduleStep, + configuration, + moduleSourceDir, + outputKlibFile, + moduleCacheDir, + dependencies, + icCaches, + dirtyFiles, + deletedFiles + ) } } } @@ -212,7 +221,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() { dependencies.mapTo(dependenciesPaths) { it.canonicalPath } dependenciesPaths.add(moduleKlibFile.canonicalPath) - val updateStatus = actualizeCacheForModule( + val updateStatus = actualizeCacheForModuleWithPrepare( moduleKlibFile.canonicalPath, moduleCacheDir.canonicalPath, configuration, @@ -235,6 +244,41 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() { } } + private fun actualizeCacheForModuleWithPrepare( + moduleName: String, + cachePath: String, + compilerConfiguration: CompilerConfiguration, + dependencies: Collection, + icCachePaths: Collection, + irFactory: IrFactory, + mainArguments: List?, + executor: CacheExecutor + ): CacheUpdateStatus { + val (libraries, dependencyGraph, configMD5) = CacheConfiguration(dependencies, compilerConfiguration) + + val modulePath = moduleName.toCanonicalPath() + val icCacheMap: Map = loadCacheInfo(icCachePaths).also { + it[modulePath] = CacheInfo.loadOrCreate( + cachePath, + moduleName, + configHash = configMD5 + ) + } + + return actualizeCacheForModule( + moduleName = modulePath, + cachePath = cachePath, + compilerConfiguration = compilerConfiguration, + configMD5 = configMD5, + libraries = libraries, + dependencyGraph = dependencyGraph, + icCacheMap = icCacheMap, + irFactory = irFactory, + mainArguments = mainArguments, + executor = executor + ) + } + private fun KotlinCoreEnvironment.createPsiFile(file: File): KtFile { val psiManager = PsiManager.getInstance(project) val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 012e4cfdc9f..892503f2321 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.gradle.util.normalizePath import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.condition.DisabledIf -import java.io.File import java.nio.file.Files import java.nio.file.Paths import java.util.zip.ZipFile @@ -178,7 +177,7 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) build("compileDevelopmentExecutableKotlinJs") { - val cacheDir = projectPath.resolve("app/build/klib/cache/lib/unspecified/") + val cacheDir = projectPath.resolve("app/build/klib/cache/lib/") .toFile() assertTrue("Lib cache size should be 2") { cacheDir @@ -186,33 +185,30 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { ?.size == 2 } - var lib: Boolean = false - var libOther: Boolean = false + var lib = false + var libOther = false cacheDir.listFiles()!! .forEach { it.listFiles()!! - .single() - .let { - it.listFiles()!! - .filter { it.isFile } - .forEach { - val text = it.readText() - if (text.contains("")) { - if (lib) { - error("lib should be only once in cache") - } - lib = true - } - - if (text.contains("")) { - if (libOther) { - error("libOther should be only once in cache") - } - libOther = true - } + .filter { it.isFile } + .forEach { + val text = it.readText() + if (text.contains("")) { + if (lib) { + error("lib should be only once in cache") } + lib = true + } + + if (text.contains("")) { + if (libOther) { + error("libOther should be only once in cache") + } + libOther = true + } } + } assertTrue("lib and libOther should be once in cache") { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt index d47add81bab..e064f208a57 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt @@ -5,47 +5,27 @@ package org.jetbrains.kotlin.gradle.targets.js.ir -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileCollection import org.gradle.api.file.FileTree -import org.gradle.api.logging.Logger import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.* -import org.gradle.work.InputChanges +import org.gradle.internal.hash.FileHasher import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments import org.jetbrains.kotlin.compilerRunner.ArgumentUtils -import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment -import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner -import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptionsImpl -import org.jetbrains.kotlin.gradle.dsl.copyFreeCompilerArgsToArgs -import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService -import org.jetbrains.kotlin.gradle.report.ReportingSettings import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.DEVELOPMENT import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.PRODUCTION import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile -import org.jetbrains.kotlin.gradle.tasks.SourceRoots -import org.jetbrains.kotlin.gradle.tasks.TaskOutputsBackup -import org.jetbrains.kotlin.gradle.utils.getAllDependencies -import org.jetbrains.kotlin.gradle.utils.getCacheDirectory -import org.jetbrains.kotlin.gradle.utils.getDependenciesCacheDirectories import org.jetbrains.kotlin.gradle.utils.getValue -import org.jetbrains.kotlin.library.resolveSingleFileKlib -import org.jetbrains.kotlin.library.uniqueName -import org.jetbrains.kotlin.library.unresolvedDependencies import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.StringMetrics import java.io.File @@ -77,10 +57,18 @@ abstract class KotlinJsIrLink @Inject constructor( @get:Internal internal lateinit var compilation: KotlinCompilationData<*> + private val platformType by project.provider { + compilation.platformType + } + @Transient @get:Internal internal val propertiesProvider = PropertiesProvider(project) + @get:Inject + open val fileHasher: FileHasher + get() = throw UnsupportedOperationException() + @get:Input internal val incrementalJsIr: Boolean = propertiesProvider.incrementalJsIr @@ -117,12 +105,13 @@ abstract class KotlinJsIrLink @Inject constructor( return !entryModule.get().asFile.exists() } - override fun callCompilerAsync( - args: K2JSCompilerArguments, - sourceRoots: SourceRoots, - inputChanges: InputChanges, - taskOutputsBackup: TaskOutputsBackup? - ) { + @get:Internal + val rootCacheDirectory by lazy { + buildDir.resolve("klib/cache") + } + + override fun processArgs(args: K2JSCompilerArguments) { + super.processArgs(args) KotlinBuildStatsService.applyIfInitialised { it.report(BooleanMetrics.JS_IR_INCREMENTAL, incrementalJsIr) val newArgs = K2JSCompilerArguments() @@ -136,55 +125,31 @@ abstract class KotlinJsIrLink @Inject constructor( ) } if (incrementalJsIr && mode == DEVELOPMENT) { - val visitedCompilations = mutableSetOf>() - - val cacheBuilder = CacheBuilder( - buildDir, - kotlinOptions, - libraryFilter, - compilerRunner.get(), - { createCompilerArgs() }, - { objects.fileCollection() }, - defaultCompilerClasspath, - logger, - reportingSettings() - ) - val cacheArgs = visitCompilation( - compilation as KotlinCompilation<*>, - cacheBuilder, - visitedCompilations, - ) - - args.cacheDirectories = cacheArgs.joinToString(File.pathSeparator) { - it.normalize().absolutePath - } + args.cacheDirectories = args.libraries?.splitByPathSeparator() + ?.map { + val file = File(it) + rootCacheDirectory + .resolve(file.nameWithoutExtension) + .also { + it.mkdirs() + } + .resolve(fileHasher.hash(file).toString()) + } + ?.plus(rootCacheDirectory.resolve(entryModule.get().asFile.name)) + ?.let { + if (it.isNotEmpty()) + it.joinToString(File.pathSeparator) + else + null + } } - super.callCompilerAsync(args, sourceRoots, inputChanges, taskOutputsBackup) } - private fun visitCompilation( - compilation: KotlinCompilation<*>, - cacheBuilder: CacheBuilder, - visitedCompilations: MutableSet>, - ): List { - if (compilation in visitedCompilations) return emptyList() - visitedCompilations.add(compilation) - - val associatedCaches = compilation.associateWith - .flatMap { - visitCompilation( - it, - cacheBuilder, - visitedCompilations, - ) - } - - return cacheBuilder - .buildCompilerArgs( - project.configurations.getByName(compilation.compileDependencyConfigurationName), - compilation.output.classesDirs, - associatedCaches - ) + private fun String.splitByPathSeparator(): List { + return this.split(File.pathSeparator.toRegex()) + .dropLastWhile { it.isEmpty() } + .toTypedArray() + .filterNot { it.isEmpty() } } override fun setupCompilerArgs(args: K2JSCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { @@ -204,10 +169,6 @@ abstract class KotlinJsIrLink @Inject constructor( super.setupCompilerArgs(args, defaultsOnly, ignoreClasspathResolutionErrors) } - private val platformType by project.provider { - compilation.platformType - } - private fun KotlinJsOptions.configureOptions(vararg additionalCompilerArgs: String) { freeCompilerArgs += additionalCompilerArgs.toList() + PRODUCE_JS + @@ -217,233 +178,4 @@ abstract class KotlinJsIrLink @Inject constructor( freeCompilerArgs += WASM_BACKEND } } -} - -internal class CacheBuilder( - private val buildDir: File, - private val kotlinOptions: KotlinJsOptions, - private val libraryFilter: (File) -> Boolean, - private val compilerRunner: GradleCompilerRunner, - private val compilerArgsFactory: () -> K2JSCompilerArguments, - private val objectFilesFactory: () -> FileCollection, - private val computedCompilerClasspath: FileCollection, - private val logger: Logger, - private val reportingSettings: ReportingSettings -) { - val rootCacheDirectory by lazy { - buildDir.resolve("klib/cache") - } - - private val visitedDependencies = mutableSetOf() - private val visitedFiles = mutableSetOf() - private val visitedCacheDirectories = mutableSetOf() - - private val objectFiles - get() = objectFilesFactory() - - fun buildCompilerArgs( - compileClasspath: Configuration, - additionalForResolve: FileCollection?, - associatedCaches: List - ): List { - - val allCacheDirectories = mutableListOf() - val visitedDependenciesForCache = mutableSetOf() - - compileClasspath.resolvedConfiguration.firstLevelModuleDependencies - .forEach { dependency -> - ensureDependencyCached( - dependency - ).forEach { - if (it !in visitedDependenciesForCache) { - visitedDependenciesForCache.add(it) - allCacheDirectories.add(it) - } - } - } - - additionalForResolve?.files?.forEach { file -> - val cacheDirectory = rootCacheDirectory.resolve(file.name) - cacheDirectory.mkdirs() - runCompiler( - file, - compileClasspath.files, - cacheDirectory, - (allCacheDirectories + associatedCaches).distinct() - ) - allCacheDirectories.add(cacheDirectory) - } - - return associatedCaches + allCacheDirectories - .filter { it !in visitedCacheDirectories } - .also { visitedCacheDirectories.addAll(it) } - } - - private fun ensureDependencyCached( - dependency: ResolvedDependency - ): List { - if (dependency in visitedDependencies) { - return dependency.moduleArtifacts - .filter { libraryFilter(it.file) } - .map { - getCacheDirectory(rootCacheDirectory, dependency, it, { libraryFilter(it.file) }) - } - } - visitedDependencies.add(dependency) - - val depCacheDirs = dependency.children - .flatMap { ensureDependencyCached(it) } - .distinct() - - val artifactsToAddToCache = dependency.moduleArtifacts - .filter { libraryFilter(it.file) } - - if (artifactsToAddToCache.isEmpty()) return depCacheDirs - - val dependenciesCacheDirectories = getDependenciesCacheDirectories( - rootCacheDirectory, - dependency, - libraryFilter = { libraryFilter(it.file) }, - considerArtifact = true - ) ?: return depCacheDirs - - val nameMap: MutableMap = mutableMapOf() - val depsMap: MutableMap> = mutableMapOf() - val cacheMap: MutableMap = mutableMapOf() - var newOrderArtifactsToAddToCache = mutableListOf() - - if (artifactsToAddToCache.size > 1) { - artifactsToAddToCache.forEach { - val klib = resolveSingleFileKlib(org.jetbrains.kotlin.konan.file.File(it.file.absolutePath)) - nameMap[klib.uniqueName] = it - } - artifactsToAddToCache.forEach { artifact -> - val klib = resolveSingleFileKlib(org.jetbrains.kotlin.konan.file.File(artifact.file.absolutePath)) - klib.unresolvedDependencies - .forEach { depLib -> - val depName = depLib.path.substringAfterLast("/") - nameMap[depName]?.let { - val depsSet = depsMap.getOrPut(artifact) { - mutableListOf() - } - depsSet.add(it) - } - } - } - fun traverseDependency(library: ResolvedArtifact) { - depsMap[library]?.let { - it.forEach { - traverseDependency(it) - } - } - if (library !in newOrderArtifactsToAddToCache) { - newOrderArtifactsToAddToCache.add(library) - } - } - depsMap.forEach { key, deps -> - traverseDependency(key) - } - } else { - newOrderArtifactsToAddToCache = artifactsToAddToCache.toMutableList() - } - - val additionalCacheDirs = mutableListOf() - for (library in newOrderArtifactsToAddToCache) { - val cacheDirectory = getCacheDirectory(rootCacheDirectory, dependency, library, { libraryFilter(it.file) }).also { - additionalCacheDirs.add(it) - } - cacheDirectory.mkdirs() - cacheMap[library] = cacheDirectory - val additionalDependencies = depsMap[library] ?: emptyList() - val additionalCacheDirectories = if (additionalDependencies.isNotEmpty()) { - additionalDependencies.map { cacheMap.getValue(it) } - } else emptyList() - runCompiler( - library.file, - (getAllDependencies(dependency) - .flatMap { it.moduleArtifacts } + additionalDependencies) - .map { it.file }, - cacheDirectory, - dependenciesCacheDirectories + additionalCacheDirectories - ) - } - return depCacheDirs + additionalCacheDirs - } - - fun runCompiler( - file: File, - dependencies: Collection, - cacheDirectory: File, - dependenciesCacheDirectories: Collection - ) { - if (file in visitedFiles) return - val compilerArgs = compilerArgsFactory() - kotlinOptions.copyFreeCompilerArgsToArgs(compilerArgs) - var prevIndex: Int? = null - compilerArgs.freeArgs = compilerArgs.freeArgs - .filterIndexed { index, arg -> - !listOf("-source-map-base-dirs", "-source-map-prefix").any { - if (prevIndex != null) { - prevIndex = null - return@any true - } - if (arg.startsWith(it)) { - prevIndex = index - return@any true - } - - false - } - } - - compilerArgs.freeArgs = compilerArgs.freeArgs - .filterNot { arg -> - IGNORED_ARGS.any { - arg.startsWith(it) - } - } - - visitedFiles.add(file) - compilerArgs.includes = file.normalize().absolutePath - compilerArgs.outputFile = cacheDirectory.normalize().absolutePath - if (dependenciesCacheDirectories.isNotEmpty()) { - compilerArgs.cacheDirectories = dependenciesCacheDirectories.joinToString(File.pathSeparator) - } - compilerArgs.irBuildCache = true - - compilerArgs.libraries = dependencies - .filter { it.exists() && libraryFilter(it) } - .distinct() - .filterNot { it == file } - .joinToString(File.pathSeparator) { it.normalize().absolutePath } - - val messageCollector = GradlePrintingMessageCollector(logger, false) - val outputItemCollector = OutputItemsCollectorImpl() - val environment = GradleCompilerEnvironment( - computedCompilerClasspath, - messageCollector, - outputItemCollector, - outputFiles = objectFiles.files.toList(), - reportingSettings = reportingSettings - ) - - compilerRunner - .runJsCompilerAsync( - emptyList(), - emptyList(), - compilerArgs, - environment, - null - )?.await() - } - - companion object { - private val IGNORED_ARGS = listOf( - ENTRY_IR_MODULE, - PRODUCE_JS, - PRODUCE_UNZIPPED_KLIB, - ENABLE_DCE, - GENERATE_D_TS - ) - } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index eecad7c7e42..b2a39cf191c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -1094,6 +1094,12 @@ abstract class Kotlin2JsCompile @Inject constructor( override val incrementalProps: List get() = super.incrementalProps + listOf(friendDependencies) + open fun processArgs( + args: K2JSCompilerArguments + ) { + + } + override fun callCompilerAsync( args: K2JSCompilerArguments, sourceRoots: SourceRoots, @@ -1147,6 +1153,7 @@ abstract class Kotlin2JsCompile @Inject constructor( reportingSettings = reportingSettings(), incrementalCompilationEnvironment = icEnv ) + processArgs(args) compilerRunner.runJsCompilerAsync( sourceRoots.kotlinSourceFiles.files.toList(), commonSourceSet.toList(),