rra/ilgonmic/ic-to-compiler
[JS IR] Fix compilation in tests [JS IR] Fix test after migrating IC to compiler [JS IR] Fix memory leak [JS IR] Move js ic to compiler Merge-request: KT-MR-5673 Merged-by: Ilya Goncharov <Ilya.Goncharov@jetbrains.com>
This commit is contained in:
@@ -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<K2JSCompilerArguments>() {
|
||||
// TODO: Handle non-empty main call arguments
|
||||
val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList<String>()
|
||||
|
||||
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<K2JSCompilerArguments>() {
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>): MutableMap<ModulePath, CacheInfo> {
|
||||
fun loadCacheInfo(cachePaths: Collection<String>): MutableMap<ModulePath, CacheInfo> {
|
||||
val caches = cachePaths.map { CacheInfo.load(it) ?: error("Cannot load IC cache from $it") }
|
||||
val result = mutableMapOf<ModulePath, CacheInfo>()
|
||||
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<ModulePath>,
|
||||
icCachePaths: Collection<String>,
|
||||
irFactory: () -> IrFactory,
|
||||
mainArguments: List<String>?,
|
||||
executor: CacheExecutor,
|
||||
callback: (CacheUpdateStatus) -> Unit
|
||||
): List<String> {
|
||||
val (libraries, dependencyGraph, configMD5) = CacheConfiguration(dependencies, compilerConfiguration)
|
||||
val cacheMap = libraries.values.zip(icCachePaths).toMap()
|
||||
|
||||
val icCacheMap: MutableMap<ModulePath, CacheInfo> = mutableMapOf<ModulePath, CacheInfo>()
|
||||
val resultCaches = mutableListOf<String>()
|
||||
|
||||
val visitedLibraries = mutableSetOf<KotlinLibrary>()
|
||||
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<ModulePath>,
|
||||
val compilerConfiguration: CompilerConfiguration
|
||||
) {
|
||||
val libraries: Map<ModulePath, KotlinLibrary> = loadLibraries(compilerConfiguration, dependencies)
|
||||
|
||||
val dependencyGraph: Map<KotlinLibrary, List<KotlinLibrary>>
|
||||
get() {
|
||||
val nameToKotlinLibrary: Map<ModuleName, KotlinLibrary> = 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<ModulePath>,
|
||||
icCachePaths: Collection<String>,
|
||||
configMD5: ULong,
|
||||
libraries: Map<ModulePath, KotlinLibrary>,
|
||||
dependencyGraph: Map<KotlinLibrary, List<KotlinLibrary>>,
|
||||
icCacheMap: Map<ModulePath, CacheInfo>,
|
||||
irFactory: IrFactory,
|
||||
mainArguments: List<String>?,
|
||||
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<ModulePath, CacheInfo> = loadCacheInfo(icCachePaths).also {
|
||||
it[modulePath] = cacheInfo
|
||||
}
|
||||
|
||||
val libraries: Map<ModulePath, KotlinLibrary> = loadLibraries(compilerConfiguration, dependencies)
|
||||
val nameToKotlinLibrary: Map<ModuleName, KotlinLibrary> = 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(
|
||||
|
||||
@@ -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<FqName>,
|
||||
mainArguments: List<String>?,
|
||||
) { }
|
||||
) {
|
||||
}
|
||||
|
||||
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<ModulePath>,
|
||||
icCachePaths: Collection<String>,
|
||||
irFactory: IrFactory,
|
||||
mainArguments: List<String>?,
|
||||
executor: CacheExecutor
|
||||
): CacheUpdateStatus {
|
||||
val (libraries, dependencyGraph, configMD5) = CacheConfiguration(dependencies, compilerConfiguration)
|
||||
|
||||
val modulePath = moduleName.toCanonicalPath()
|
||||
val icCacheMap: Map<ModulePath, CacheInfo> = 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
|
||||
|
||||
+19
-23
@@ -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("<kotlin-js-ir-ic-multiple-artifacts:lib>")) {
|
||||
if (lib) {
|
||||
error("lib should be only once in cache")
|
||||
}
|
||||
lib = true
|
||||
}
|
||||
|
||||
if (text.contains("<kotlin-js-ir-ic-multiple-artifacts:lib_other>")) {
|
||||
if (libOther) {
|
||||
error("libOther should be only once in cache")
|
||||
}
|
||||
libOther = true
|
||||
}
|
||||
.filter { it.isFile }
|
||||
.forEach {
|
||||
val text = it.readText()
|
||||
if (text.contains("<kotlin-js-ir-ic-multiple-artifacts:lib>")) {
|
||||
if (lib) {
|
||||
error("lib should be only once in cache")
|
||||
}
|
||||
lib = true
|
||||
}
|
||||
|
||||
if (text.contains("<kotlin-js-ir-ic-multiple-artifacts:lib_other>")) {
|
||||
if (libOther) {
|
||||
error("libOther should be only once in cache")
|
||||
}
|
||||
libOther = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assertTrue("lib and libOther should be once in cache") {
|
||||
|
||||
+38
-306
@@ -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<KotlinCompilation<*>>()
|
||||
|
||||
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<KotlinCompilation<*>>,
|
||||
): List<File> {
|
||||
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<String> {
|
||||
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<ResolvedDependency>()
|
||||
private val visitedFiles = mutableSetOf<File>()
|
||||
private val visitedCacheDirectories = mutableSetOf<File>()
|
||||
|
||||
private val objectFiles
|
||||
get() = objectFilesFactory()
|
||||
|
||||
fun buildCompilerArgs(
|
||||
compileClasspath: Configuration,
|
||||
additionalForResolve: FileCollection?,
|
||||
associatedCaches: List<File>
|
||||
): List<File> {
|
||||
|
||||
val allCacheDirectories = mutableListOf<File>()
|
||||
val visitedDependenciesForCache = mutableSetOf<File>()
|
||||
|
||||
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<File> {
|
||||
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<String, ResolvedArtifact> = mutableMapOf()
|
||||
val depsMap: MutableMap<ResolvedArtifact, MutableList<ResolvedArtifact>> = mutableMapOf()
|
||||
val cacheMap: MutableMap<ResolvedArtifact, File> = mutableMapOf()
|
||||
var newOrderArtifactsToAddToCache = mutableListOf<ResolvedArtifact>()
|
||||
|
||||
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<File>()
|
||||
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<File>,
|
||||
cacheDirectory: File,
|
||||
dependenciesCacheDirectories: Collection<File>
|
||||
) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
+7
@@ -1094,6 +1094,12 @@ abstract class Kotlin2JsCompile @Inject constructor(
|
||||
override val incrementalProps: List<FileCollection>
|
||||
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(),
|
||||
|
||||
Reference in New Issue
Block a user