[Gradle, JS] Store RootNpmResolver's heavy state in build service params

That heavy state was duplicated in serialized state of each task that has access to it transitively.
#KT-45294 Fixed
This commit is contained in:
Alexander Likhachev
2021-03-16 20:03:52 +03:00
parent 14f6e5db6d
commit 6e333c4f3f
28 changed files with 311 additions and 178 deletions
@@ -5,11 +5,13 @@
package $package package $package
import java.io.Serializable
/** /**
* Package versions used by tasks * Package versions used by tasks
*/ */
// DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt // DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt
class $class { class $class : Serializable {
#foreach( $dep in $dependencies ) #foreach( $dep in $dependencies )
val $dep.camelize() = NpmPackageVersion("$dep.name", "$dep.version") val $dep.camelize() = NpmPackageVersion("$dep.name", "$dep.version")
#end #end
@@ -24,7 +24,6 @@ import java.security.MessageDigest
* @param version When updating logic in `compute`, `version` should be increased to invalidate cache * @param version When updating logic in `compute`, `version` should be increased to invalidate cache
*/ */
internal open class ProcessedFilesCache( internal open class ProcessedFilesCache(
val logger: Logger,
val projectDir: File, val projectDir: File,
val targetDir: File, val targetDir: File,
stateFileName: String, stateFileName: String,
@@ -174,7 +173,8 @@ internal open class ProcessedFilesCache(
try { try {
GsonBuilder().setPrettyPrinting().create().newJsonReader(stateFile.reader()).use { readFrom(it) } GsonBuilder().setPrettyPrinting().create().newJsonReader(stateFile.reader()).use { readFrom(it) }
} catch (e: Throwable) { } catch (e: Throwable) {
logger.warn("Cannot read $stateFile", e) System.err.println("Cannot read $stateFile")
e.printStackTrace()
if (targetDir.exists()) { if (targetDir.exists()) {
targetDir.deleteRecursively() targetDir.deleteRecursively()
} }
@@ -209,14 +209,14 @@ internal open class ProcessedFilesCache(
if (old != null) { if (old != null) {
if (checkTarget(old.target)) return old.target if (checkTarget(old.target)) return old.target
else logger.warn("Cannot find ${File(targetDir.relativeTo(projectDir), old.target!!)}, rebuilding") else System.err.println("Cannot find ${File(targetDir.relativeTo(projectDir), old.target!!)}, rebuilding")
} }
val key = compute()?.relativeTo(targetDir)?.toString() val key = compute()?.relativeTo(targetDir)?.toString()
val existedTarget = state.byTarget[key] val existedTarget = state.byTarget[key]
if (key != null && existedTarget != null) { if (key != null && existedTarget != null) {
if (!File(existedTarget.src).exists()) { if (!File(existedTarget.src).exists()) {
logger.warn("Removing cache for removed source `${existedTarget.src}`") System.err.println("Removing cache for removed source `${existedTarget.src}`")
state.remove(existedTarget) state.remove(existedTarget)
} }
} }
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.gradle.targets.js package org.jetbrains.kotlin.gradle.targets.js
import java.io.Serializable
/** /**
* Package versions used by tasks * Package versions used by tasks
*/ */
// DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt // DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt
class NpmVersions { class NpmVersions : Serializable {
val dukat = NpmPackageVersion("dukat", "0.5.8-rc.4") val dukat = NpmPackageVersion("dukat", "0.5.8-rc.4")
val webpack4 = NpmPackageVersion("webpack", "4.46.0") val webpack4 = NpmPackageVersion("webpack", "4.46.0")
val sourceMapLoader1 = NpmPackageVersion("source-map-loader", "1.1.3") val sourceMapLoader1 = NpmPackageVersion("source-map-loader", "1.1.3")
@@ -8,7 +8,8 @@ package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency
import java.io.Serializable
interface RequiredKotlinJsDependency { interface RequiredKotlinJsDependency : Serializable {
fun createDependency(project: Project, scope: NpmDependency.Scope = NpmDependency.Scope.DEV): Dependency fun createDependency(project: Project, scope: NpmDependency.Scope = NpmDependency.Scope.DEV): Dependency
} }
@@ -21,11 +21,13 @@ import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
internal class DukatCompilationResolverPlugin( internal class DukatCompilationResolverPlugin(
@Transient
private val resolver: KotlinCompilationNpmResolver private val resolver: KotlinCompilationNpmResolver
) : CompilationResolverPlugin { ) : CompilationResolverPlugin {
val project get() = resolver.project val project get() = resolver.project
val nodeJs get() = resolver.nodeJs val nodeJs get() = resolver.nodeJs
val npmProject get() = resolver.npmProject val versions by lazy { nodeJs.versions }
val npmProject by lazy { resolver.npmProject }
val compilation get() = npmProject.compilation val compilation get() = npmProject.compilation
val compilationName by lazy { val compilationName by lazy {
compilation.disambiguatedName compilation.disambiguatedName
@@ -83,7 +85,7 @@ internal class DukatCompilationResolverPlugin(
} }
override fun hookDependencies( override fun hookDependencies(
internalDependencies: Set<KotlinCompilationNpmResolver>, internalDependencies: Set<KotlinCompilationNpmResolver.InternalDependency>,
internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>, internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>,
externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>, externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>,
externalNpmDependencies: Set<NpmDependency>, externalNpmDependencies: Set<NpmDependency>,
@@ -107,7 +109,7 @@ internal class DukatCompilationResolverPlugin(
} }
DukatExecutor( DukatExecutor(
nodeJs.versions, versions,
DtsResolver(npmProject).getAllDts(externalNpmDependencies), DtsResolver(npmProject).getAllDts(externalNpmDependencies),
externalsOutputFormat, externalsOutputFormat,
npmProject, npmProject,
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResoluti
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver
internal class DukatRootResolverPlugin(val resolver: KotlinRootNpmResolver) : RootResolverPlugin { internal class DukatRootResolverPlugin(val forceFullResolve: Boolean) : RootResolverPlugin {
val compilations = mutableListOf<DukatCompilationResolverPlugin>() val compilations = mutableListOf<DukatCompilationResolverPlugin>()
override fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<DukatCompilationResolverPlugin> { override fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<DukatCompilationResolverPlugin> {
@@ -20,7 +20,7 @@ internal class DukatRootResolverPlugin(val resolver: KotlinRootNpmResolver) : Ro
} }
override fun close(resolution: KotlinRootNpmResolution) { override fun close(resolution: KotlinRootNpmResolution) {
if (resolver.forceFullResolve) { if (forceFullResolve) {
// inside idea import // inside idea import
compilations.forEach { compilations.forEach {
it.executeDukatIfNeeded(true, resolution) it.executeDukatIfNeeded(true, resolution)
@@ -20,8 +20,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.File import java.io.File
import java.io.Serializable
open class NodeJsRootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<NodeJsEnv>() { open class NodeJsRootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<NodeJsEnv>(), Serializable {
init { init {
check(rootProject.rootProject == rootProject) check(rootProject.rootProject == rootProject)
} }
@@ -2,25 +2,29 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat
import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import javax.inject.Inject
@CacheableTask @CacheableTask
open class NodeJsSetupTask : DefaultTask() { abstract class NodeJsSetupTask : DefaultTask() {
@Transient @Transient
private val settings = NodeJsRootPlugin.apply(project.rootProject) private val settings = NodeJsRootPlugin.apply(project.rootProject)
private val env by lazy { settings.requireConfigured() } private val env by lazy { settings.requireConfigured() }
private val fs = FileSystemOperationsCompat(project)
private val archiveOperations = ArchiveOperationsCompat(project) private val archiveOperations = ArchiveOperationsCompat(project)
private val shouldDownload = settings.download private val shouldDownload = settings.download
@get:Inject
internal open val fs: FileSystemOperations
get() = error("Should be injected")
val ivyDependency: String val ivyDependency: String
@Input get() = env.ivyDependency @Input get() = env.ivyDependency
@@ -10,8 +10,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependencyDeclaration import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependencyDeclaration
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
import org.jetbrains.kotlin.gradle.targets.js.npm.toDeclaration import org.jetbrains.kotlin.gradle.targets.js.npm.toDeclaration
import java.io.Serializable
class TasksRequirements { class TasksRequirements : Serializable {
private val _byTask = mutableMapOf<String, Set<RequiredKotlinJsDependency>>() private val _byTask = mutableMapOf<String, Set<RequiredKotlinJsDependency>>()
private val byCompilation = mutableMapOf<String, MutableSet<NpmDependencyDeclaration>>() private val byCompilation = mutableMapOf<String, MutableSet<NpmDependencyDeclaration>>()
@@ -6,25 +6,29 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.file.DirectoryProperty
import org.gradle.internal.hash.FileHasher import org.gradle.api.invocation.Gradle
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.kotlin.gradle.internal.ProcessedFilesCache import org.jetbrains.kotlin.gradle.internal.ProcessedFilesCache
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import java.io.File import java.io.File
/** /**
* Cache for storing already created [GradleNodeModule]s * Cache for storing already created [GradleNodeModule]s
*/ */
internal abstract class AbstractNodeModulesCache(nodeJs: NodeJsRootExtension) : AutoCloseable { internal abstract class AbstractNodeModulesCache : AutoCloseable, BuildService<AbstractNodeModulesCache.Parameters> {
internal interface Parameters : BuildServiceParameters {
val rootProjectDir: DirectoryProperty
val cacheDir: DirectoryProperty
}
companion object { companion object {
const val STATE_FILE_NAME = ".visited" const val STATE_FILE_NAME = ".visited"
} }
internal val dir = nodeJs.nodeModulesGradleCacheDir
private val cache = ProcessedFilesCache( private val cache = ProcessedFilesCache(
nodeJs.rootProject.logger, parameters.rootProjectDir.get().asFile,
nodeJs.rootProject.projectDir, parameters.cacheDir.get().asFile,
dir,
STATE_FILE_NAME, STATE_FILE_NAME,
"9" "9"
) )
@@ -52,8 +56,6 @@ internal abstract class AbstractNodeModulesCache(nodeJs: NodeJsRootExtension) :
} }
} }
// Synchronized as tasks from configuration cache run in parallel and every task has it's own modules cache
@Synchronized
fun makeNodeModule( fun makeNodeModule(
container: File, container: File,
packageJson: PackageJson, packageJson: PackageJson,
@@ -5,19 +5,20 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import java.io.File import java.io.File
/** /**
* Cache for storing already created [GradleNodeModule]s * Cache for storing already created [GradleNodeModule]s
*/ */
internal class CompositeNodeModulesCache(nodeJs: NodeJsRootExtension) : AbstractNodeModulesCache(nodeJs) { internal abstract class CompositeNodeModulesCache : AbstractNodeModulesCache() {
override fun buildImportedPackage( override fun buildImportedPackage(
name: String, name: String,
version: String, version: String,
file: File file: File
): File? { ): File? {
val module = CompositeNodeModuleBuilder(file, dir) val module = CompositeNodeModuleBuilder(file, parameters.cacheDir.get().asFile)
return module.rebuild() return module.rebuild()
} }
} }
@@ -6,11 +6,12 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import java.io.File import java.io.File
import java.io.Serializable
/** /**
* Fake NodeJS module directory created from Gradle external module * Fake NodeJS module directory created from Gradle external module
*/ */
data class GradleNodeModule(val name: String, val version: String, val path: File) { data class GradleNodeModule(val name: String, val version: String, val path: File) : Serializable {
val semver: SemVer val semver: SemVer
get() = SemVer.from(version) get() = SemVer.from(version)
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.targets.js.JS import org.jetbrains.kotlin.gradle.targets.js.JS
import org.jetbrains.kotlin.gradle.targets.js.JS_MAP import org.jetbrains.kotlin.gradle.targets.js.JS_MAP
import org.jetbrains.kotlin.gradle.targets.js.META_JS import org.jetbrains.kotlin.gradle.targets.js.META_JS
@@ -5,27 +5,32 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.gradle.api.file.FileSystemOperations
import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat
import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat
import java.io.File import java.io.File
import javax.inject.Inject
/** /**
* Cache for storing already created [GradleNodeModule]s * Cache for storing already created [GradleNodeModule]s
*/ */
internal class GradleNodeModulesCache(nodeJs: NodeJsRootExtension) : AbstractNodeModulesCache(nodeJs) { internal abstract class GradleNodeModulesCache : AbstractNodeModulesCache() {
@Transient
private val project = nodeJs.rootProject
private val fs = FileSystemOperationsCompat(project) // TODO: replace by injected service org.gradle.api.file.FileSystemOperations once min support Gradle is 6.2
private val archiveOperations = ArchiveOperationsCompat(project) // https://github.com/gradle/gradle/commit/d02b9d84c08dba64775fb9581e3280f88d319a21
@Transient
lateinit var fs: FileSystemOperationsCompat
// TODO: replace by injected service org.gradle.api.file.ArchiveOperations once min supported Gradle is 6.6
@Transient
lateinit var archiveOperations: ArchiveOperationsCompat
override fun buildImportedPackage( override fun buildImportedPackage(
name: String, name: String,
version: String, version: String,
file: File file: File
): File? { ): File? {
val module = GradleNodeModuleBuilder(fs, archiveOperations, name, version, listOf(file), dir) val module = GradleNodeModuleBuilder(fs, archiveOperations, name, version, listOf(file), parameters.cacheDir.get().asFile)
module.visitArtifacts() module.visitArtifacts()
return module.rebuild() return module.rebuild()
} }
@@ -10,6 +10,8 @@ import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.internal.service.ServiceRegistry import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.internal.isInIdeaSync import org.jetbrains.kotlin.gradle.internal.isInIdeaSync
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
@@ -85,9 +87,22 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
internal val resolver = KotlinRootNpmResolver(nodeJsSettings, forceFullResolve) internal val resolver = KotlinRootNpmResolver(nodeJsSettings, forceFullResolve)
@Volatile internal abstract class KotlinNpmResolutionManagerStateHolder : BuildService<BuildServiceParameters.None> {
private var state: ResolutionState = @Volatile
ResolutionState.Configuring(resolver) internal var state: ResolutionState? = null
}
private val stateHolderProvider = nodeJsSettings.rootProject.gradle.sharedServices.registerIfAbsent(
"npm-resolution-manager-state-holder", KotlinNpmResolutionManagerStateHolder::class.java) {
}
private val stateHolder get() = stateHolderProvider.get()
private var state: ResolutionState
get() = stateHolder.state ?: ResolutionState.Configuring(resolver)
set(value) {
stateHolder.state = value
}
internal sealed class ResolutionState { internal sealed class ResolutionState {
abstract val npmProjects: List<NpmProject> abstract val npmProjects: List<NpmProject>
@@ -139,7 +154,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
services: ServiceRegistry, services: ServiceRegistry,
logger: Logger logger: Logger
): KotlinRootNpmResolution { ): KotlinRootNpmResolution {
synchronized(this) { synchronized(stateHolder) {
if (state is ResolutionState.Installed) { if (state is ResolutionState.Installed) {
return (state as ResolutionState.Installed).resolved return (state as ResolutionState.Installed).resolved
} }
@@ -178,9 +193,11 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
val state0 = this.state val state0 = this.state
return when (state0) { return when (state0) {
is ResolutionState.Prepared -> alreadyResolved(state0.preparedInstallation) is ResolutionState.Prepared -> {
alreadyResolved(state0.preparedInstallation)
}
is ResolutionState.Configuring -> { is ResolutionState.Configuring -> {
synchronized(this) { synchronized(stateHolder) {
val state1 = this.state val state1 = this.state
when (state1) { when (state1) {
is ResolutionState.Prepared -> alreadyResolved(state1.preparedInstallation) is ResolutionState.Prepared -> alreadyResolved(state1.preparedInstallation)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import java.io.Serializable
data class NpmDependencyDeclaration( data class NpmDependencyDeclaration(
@Input @Input
@@ -16,7 +17,7 @@ data class NpmDependencyDeclaration(
val version: String, val version: String,
@Input @Input
val generateExternals: Boolean val generateExternals: Boolean
) ) : Serializable
fun NpmDependencyDeclaration.uniqueRepresentation() = fun NpmDependencyDeclaration.uniqueRepresentation() =
"$scope $name:$version, $generateExternals" "$scope $name:$version, $generateExternals"
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask
import java.io.File import java.io.File
import java.io.Serializable
val KotlinJsCompilation.npmProject: NpmProject val KotlinJsCompilation.npmProject: NpmProject
get() = NpmProject(this) get() = NpmProject(this)
@@ -24,7 +25,7 @@ val KotlinJsCompilation.npmProject: NpmProject
* *
* More info can be obtained from [KotlinCompilationNpmResolution], which is available after project resolution (after [KotlinNpmInstallTask] execution). * More info can be obtained from [KotlinCompilationNpmResolution], which is available after project resolution (after [KotlinNpmInstallTask] execution).
*/ */
open class NpmProject(@Transient val compilation: KotlinJsCompilation) { open class NpmProject(@Transient val compilation: KotlinJsCompilation) : Serializable {
val compilationName = compilation.disambiguatedName val compilationName = compilation.disambiguatedName
val name: String by lazy { val name: String by lazy {
@@ -59,6 +60,10 @@ open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
val packageJsonTask: KotlinPackageJsonTask val packageJsonTask: KotlinPackageJsonTask
get() = project.tasks.getByName(packageJsonTaskName) as KotlinPackageJsonTask get() = project.tasks.getByName(packageJsonTaskName) as KotlinPackageJsonTask
val packageJsonTaskPath by lazy {
packageJsonTask.path
}
val dist: File val dist: File
get() = dir.resolve(DIST_FOLDER) get() = dir.resolve(DIST_FOLDER)
@@ -10,6 +10,7 @@ import com.google.gson.JsonObject
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.NODE_MODULES import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.NODE_MODULES
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON
import java.io.File import java.io.File
import java.io.Serializable
/** /**
* Search modules in node_modules according to https://nodejs.org/api/modules.html. * Search modules in node_modules according to https://nodejs.org/api/modules.html.
@@ -19,7 +20,7 @@ open class NpmProjectModules(
val packageJsonEntries: Collection<String> = listOf("main", "module", "browser"), val packageJsonEntries: Collection<String> = listOf("main", "module", "browser"),
val indexFileNames: Collection<String> = listOf(INDEX_FILE_NAME), val indexFileNames: Collection<String> = listOf(INDEX_FILE_NAME),
val indexFileSuffixes: Collection<String> = listOf(JS_SUFFIX) val indexFileSuffixes: Collection<String> = listOf(JS_SUFFIX)
) { ) : Serializable {
/** /**
* Require [request] nodejs module and return canonical path to it's main js file. * Require [request] nodejs module and return canonical path to it's main js file.
*/ */
@@ -13,12 +13,13 @@ import org.gradle.api.GradleException
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import java.io.File import java.io.File
import java.io.Serializable
// Gson set nulls reflectively no matter on default values and non-null types // Gson set nulls reflectively no matter on default values and non-null types
class PackageJson( class PackageJson(
var name: String, var name: String,
var version: String var version: String
) { ) : Serializable {
internal val customFields = mutableMapOf<String, Any?>() internal val customFields = mutableMapOf<String, Any?>()
val empty: Boolean val empty: Boolean
@@ -7,10 +7,11 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.plugins
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver
import java.io.Serializable
internal interface CompilationResolverPlugin { internal interface CompilationResolverPlugin : Serializable {
fun hookDependencies( fun hookDependencies(
internalDependencies: Set<KotlinCompilationNpmResolver>, internalDependencies: Set<KotlinCompilationNpmResolver.InternalDependency>,
internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>, internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>,
externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>, externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>,
externalNpmDependencies: Set<NpmDependency>, externalNpmDependencies: Set<NpmDependency>,
@@ -7,8 +7,9 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.plugins
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver
import java.io.Serializable
internal interface RootResolverPlugin { internal interface RootResolverPlugin : Serializable {
fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<CompilationResolverPlugin> fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<CompilationResolverPlugin>
fun close(resolution: KotlinRootNpmResolution) fun close(resolution: KotlinRootNpmResolution)
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.targets.js.npm.* import org.jetbrains.kotlin.gradle.targets.js.npm.*
import java.io.Serializable
/** /**
* Resolved [NpmProject] * Resolved [NpmProject]
@@ -19,7 +20,7 @@ class KotlinCompilationNpmResolution(
val externalGradleDependencies: Collection<GradleNodeModule>, val externalGradleDependencies: Collection<GradleNodeModule>,
private val _externalNpmDependencies: Collection<NpmDependencyDeclaration>, private val _externalNpmDependencies: Collection<NpmDependencyDeclaration>,
val packageJson: PackageJson val packageJson: PackageJson
) { ) : Serializable {
val project val project
get() = _project!! get() = _project!!
@@ -5,20 +5,12 @@
package org.jetbrains.kotlin.gradle.targets.js.npm.resolver package org.jetbrains.kotlin.gradle.targets.js.npm.resolver
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileCollection
import org.gradle.api.initialization.IncludedBuild import org.gradle.api.initialization.IncludedBuild
import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
import org.gradle.api.tasks.Input import org.gradle.api.tasks.*
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.bundling.Zip
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
@@ -38,10 +30,8 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata
import org.jetbrains.kotlin.gradle.utils.`is` import org.jetbrains.kotlin.gradle.utils.`is`
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.topRealPath import org.jetbrains.kotlin.gradle.utils.topRealPath
import java.io.File import java.io.*
import java.io.Serializable
/** /**
* See [KotlinNpmResolutionManager] for details about resolution process. * See [KotlinNpmResolutionManager] for details about resolution process.
@@ -51,49 +41,27 @@ internal class KotlinCompilationNpmResolver(
val projectResolver: KotlinProjectNpmResolver, val projectResolver: KotlinProjectNpmResolver,
@Transient @Transient
val compilation: KotlinJsCompilation val compilation: KotlinJsCompilation
) { ) : Serializable {
@Transient @Transient
val resolver = projectResolver.resolver var rootResolver = projectResolver.resolver
private val gradleNodeModules by lazy {
resolver.gradleNodeModules
}
private val compositeNodeModules by lazy {
resolver.compositeNodeModules
}
val npmProject = compilation.npmProject val npmProject = compilation.npmProject
val compilationDisambiguatedName = compilation.disambiguatedName val compilationDisambiguatedName = compilation.disambiguatedName
val npmName by lazy {
npmProject.name
}
val npmVersion by lazy { val npmVersion by lazy {
project.version.toString() project.version.toString()
} }
val npmMain by lazy { val nodeJs get() = rootResolver.nodeJs
npmProject.main
}
val prePackageJsonFile by lazy {
npmProject.prePackageJsonFile
}
val nodeJs get() = resolver.nodeJs
val taskRequirements by lazy {
nodeJs.taskRequirements
}
val target get() = compilation.target val target get() = compilation.target
val project get() = target.project val project get() = target.project
private val packageJsonHandlers by project.provider { val projectPath = project.path
val packageJsonHandlers by lazy {
compilation.packageJsonHandlers compilation.packageJsonHandlers
} }
@@ -132,16 +100,24 @@ internal class KotlinCompilationNpmResolver(
override fun toString(): String = "KotlinCompilationNpmResolver(${npmProject.name})" override fun toString(): String = "KotlinCompilationNpmResolver(${npmProject.name})"
@delegate:Transient
private val aggregatedConfiguration: Configuration by lazy { private val aggregatedConfiguration: Configuration by lazy {
createAggregatedConfiguration() createAggregatedConfiguration()
} }
val packageJsonProducer: PackageJsonProducer by lazy { private val packageJsonProducer_: PackageJsonProducer by lazy {
val visitor = ConfigurationVisitor() val visitor = ConfigurationVisitor()
visitor.visit(aggregatedConfiguration) visitor.visit(aggregatedConfiguration)
visitor.toPackageJsonProducer() visitor.toPackageJsonProducer().also { it.compilationResolver = this }
} }
val packageJsonProducer: PackageJsonProducer
get() {
val packageJsonProducer = packageJsonProducer_
packageJsonProducer.compilationResolver = this
return packageJsonProducer
}
private var closed = false private var closed = false
private var resolution: KotlinCompilationNpmResolution? = null private var resolution: KotlinCompilationNpmResolution? = null
@@ -159,7 +135,7 @@ internal class KotlinCompilationNpmResolver(
fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? { fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? {
if (resolution != null) return resolution if (resolution != null) return resolution
if (packageJsonTaskHolder == null || packageJsonTaskHolder.get().state.upToDate) return resolve(skipWriting = true) if (packageJsonTaskHolder == null || packageJsonTaskHolder.get().state.upToDate) return resolve(skipWriting = true)
if (resolver.forceFullResolve && resolution == null) { if (rootResolver.forceFullResolve && resolution == null) {
// need to force all NPM tasks to be configured in IDEA import // need to force all NPM tasks to be configured in IDEA import
project.tasks.implementing(RequiresNpmDependencies::class).all {} project.tasks.implementing(RequiresNpmDependencies::class).all {}
return resolve() return resolve()
@@ -216,26 +192,35 @@ internal class KotlinCompilationNpmResolver(
data class ExternalGradleDependency( data class ExternalGradleDependency(
val dependency: ResolvedDependency, val dependency: ResolvedDependency,
val artifact: ResolvedArtifact val artifact: ResolvedArtifact
) ): Serializable
data class FileCollectionExternalGradleDependency( data class FileCollectionExternalGradleDependency(
val fileCollection: FileCollection, val files: Collection<File>,
val dependencyVersion: String? val dependencyVersion: String?
) ): Serializable
data class FileExternalGradleDependency( data class FileExternalGradleDependency(
val dependencyName: String, val dependencyName: String,
val dependencyVersion: String, val dependencyVersion: String,
val file: File val file: File
) ) : Serializable
data class CompositeDependency( data class CompositeDependency(
val dependency: ResolvedDependency, val dependencyName: String,
val includedBuild: IncludedBuild val dependencyVersion: String,
) val includedBuildDir: File,
@Transient
val includedBuild: IncludedBuild?
) : Serializable
data class InternalDependency(
val projectPath: String,
val compilationName: String,
val projectName: String
) : Serializable
inner class ConfigurationVisitor { inner class ConfigurationVisitor {
private val internalDependencies = mutableSetOf<KotlinCompilationNpmResolver>() private val internalDependencies = mutableSetOf<InternalDependency>()
private val internalCompositeDependencies = mutableSetOf<CompositeDependency>() private val internalCompositeDependencies = mutableSetOf<CompositeDependency>()
private val externalGradleDependencies = mutableSetOf<ExternalGradleDependency>() private val externalGradleDependencies = mutableSetOf<ExternalGradleDependency>()
private val externalNpmDependencies = mutableSetOf<NpmDependency>() private val externalNpmDependencies = mutableSetOf<NpmDependency>()
@@ -249,14 +234,14 @@ internal class KotlinCompilationNpmResolver(
configuration.allDependencies.forEach { dependency -> configuration.allDependencies.forEach { dependency ->
when (dependency) { when (dependency) {
is NpmDependency -> externalNpmDependencies.add(dependency) is NpmDependency -> externalNpmDependencies.add(dependency)
is FileCollectionDependency -> fileCollectionDependencies.add(FileCollectionExternalGradleDependency(dependency.files, dependency.version)) is FileCollectionDependency -> fileCollectionDependencies.add(FileCollectionExternalGradleDependency(dependency.files.files, dependency.version))
} }
} }
//TODO: rewrite when we get general way to have inter compilation dependencies //TODO: rewrite when we get general way to have inter compilation dependencies
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) {
val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsCompilation val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsCompilation
internalDependencies.add(projectResolver[main]) internalDependencies.add(InternalDependency(projectResolver.project.path, main.disambiguatedName, projectResolver[main].npmProject.name))
} }
val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty() val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty()
@@ -332,7 +317,9 @@ internal class KotlinCompilationNpmResolver(
(componentIdentifier as DefaultProjectComponentIdentifier).let { identifier -> (componentIdentifier as DefaultProjectComponentIdentifier).let { identifier ->
val includedBuild = project.gradle.includedBuild(identifier.identityPath.topRealPath().name!!) val includedBuild = project.gradle.includedBuild(identifier.identityPath.topRealPath().name!!)
internalCompositeDependencies.add(CompositeDependency(dependency, includedBuild)) internalCompositeDependencies.add(
CompositeDependency(dependency.moduleName, dependency.moduleVersion, includedBuild.projectDir, includedBuild)
)
} }
} }
@@ -342,17 +329,23 @@ internal class KotlinCompilationNpmResolver(
val dependentProject = project.findProject(componentIdentifier.projectPath) val dependentProject = project.findProject(componentIdentifier.projectPath)
?: error("Cannot find project ${componentIdentifier.projectPath}") ?: error("Cannot find project ${componentIdentifier.projectPath}")
resolver.findDependentResolver(project, dependentProject) rootResolver.findDependentResolver(project, dependentProject)
?.forEach { dependentResolver -> ?.forEach { dependentResolver ->
internalDependencies.add(dependentResolver) internalDependencies.add(InternalDependency(dependentResolver.projectPath, dependentResolver.compilationDisambiguatedName, dependentResolver.npmProject.name))
} }
} }
fun toPackageJsonProducer() = PackageJsonProducer( fun toPackageJsonProducer() = PackageJsonProducer(
internalDependencies, internalDependencies,
internalCompositeDependencies, internalCompositeDependencies,
externalGradleDependencies, externalGradleDependencies.map {
externalNpmDependencies, FileExternalGradleDependency(
it.dependency.moduleName,
it.dependency.moduleVersion,
it.artifact.file
)
},
externalNpmDependencies.map { it.toDeclaration() },
fileCollectionDependencies fileCollectionDependencies
) )
} }
@@ -374,56 +367,44 @@ internal class KotlinCompilationNpmResolver(
@get:Input @get:Input
val fileCollectionDependencies: Collection<File> val fileCollectionDependencies: Collection<File>
) : Serializable )
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
inner class PackageJsonProducer( class PackageJsonProducer(
val internalDependencies: Collection<KotlinCompilationNpmResolver>, var internalDependencies: Collection<InternalDependency>,
val internalCompositeDependencies: Collection<CompositeDependency>, var internalCompositeDependencies: Collection<CompositeDependency>,
@Transient var externalGradleDependencies: Collection<FileExternalGradleDependency>,
val externalGradleDependencies: Collection<ExternalGradleDependency>, var externalNpmDependencies: Collection<NpmDependencyDeclaration>,
@Transient var fileCollectionDependencies: Collection<FileCollectionExternalGradleDependency>
val externalNpmDependencies: Collection<NpmDependency>, ) : Serializable {
val fileCollectionDependencies: Collection<FileCollectionExternalGradleDependency> private val projectPackagesDir by lazy { compilationResolver.nodeJs.projectPackagesDir }
) { private val rootDir by lazy { compilationResolver.nodeJs.rootProject.rootDir }
val externalNpmDependencyDeclarations by lazy {
externalNpmDependencies.map {
it.toDeclaration()
}
}
val fileExternalGradleDependencies by lazy { @Transient
externalGradleDependencies.map { internal lateinit var compilationResolver: KotlinCompilationNpmResolver
FileExternalGradleDependency(
it.dependency.moduleName,
it.dependency.moduleVersion,
it.artifact.file
)
}
}
val inputs: PackageJsonProducerInputs val inputs: PackageJsonProducerInputs
get() = PackageJsonProducerInputs( get() = PackageJsonProducerInputs(
internalDependencies.map { it.npmProject.name }, internalDependencies.map { it.projectName },
internalCompositeDependencies.flatMap { it.getPackages() }, internalCompositeDependencies.flatMap { it.getPackages() },
fileExternalGradleDependencies.map { it.file }, externalGradleDependencies.map { it.file },
externalNpmDependencyDeclarations.map { it.uniqueRepresentation() }, externalNpmDependencies.map { it.uniqueRepresentation() },
fileCollectionDependencies.map{ it.fileCollection }.flatMap { it.files } fileCollectionDependencies.flatMap{ it.files }
) )
fun createPackageJson(skipWriting: Boolean): KotlinCompilationNpmResolution { fun createPackageJson(skipWriting: Boolean): KotlinCompilationNpmResolution {
val resolvedInternalDependencies = internalDependencies.map { val resolvedInternalDependencies = internalDependencies.map {
it.getResolutionOrResolveIfForced() compilationResolver.rootResolver[it.projectPath][it.compilationName].getResolutionOrResolveIfForced()
?: error("Unresolved dependent npm package: ${this@KotlinCompilationNpmResolver} -> $it") ?: error("Unresolved dependent npm package: ${compilationResolver} -> $it")
} }
val importedExternalGradleDependencies = fileExternalGradleDependencies.mapNotNull { val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull {
gradleNodeModules.get(it.dependencyName, it.dependencyVersion, it.file) compilationResolver.rootResolver.gradleNodeModules.get(it.dependencyName, it.dependencyVersion, it.file)
} + fileCollectionDependencies.flatMap { dependency -> } + fileCollectionDependencies.flatMap { dependency ->
dependency.fileCollection.files dependency.files
// Gradle can hash with FileHasher only files and only existed files // Gradle can hash with FileHasher only files and only existed files
.filter { it.isFile } .filter { it.isFile }
.map { file -> .map { file ->
gradleNodeModules.get( compilationResolver.rootResolver.gradleNodeModules.get(
file.name, file.name,
dependency.dependencyVersion ?: "0.0.1", dependency.dependencyVersion ?: "0.0.1",
file file
@@ -434,26 +415,26 @@ internal class KotlinCompilationNpmResolver(
val compositeDependencies = internalCompositeDependencies.flatMap { dependency -> val compositeDependencies = internalCompositeDependencies.flatMap { dependency ->
dependency.getPackages() dependency.getPackages()
.map { file -> .map { file ->
compositeNodeModules.get( compilationResolver.rootResolver.compositeNodeModules.get(
dependency.dependency.moduleName, dependency.dependencyName,
dependency.dependency.moduleVersion, dependency.dependencyVersion,
file file
) )
} }
} }
.filterNotNull() .filterNotNull()
val toolsNpmDependencies = taskRequirements val toolsNpmDependencies = compilationResolver.rootResolver.nodeJs.taskRequirements
.getCompilationNpmRequirements(compilationDisambiguatedName) .getCompilationNpmRequirements(compilationResolver.compilationDisambiguatedName)
val allNpmDependencies = externalNpmDependencyDeclarations + toolsNpmDependencies val allNpmDependencies = externalNpmDependencies + toolsNpmDependencies
val packageJson = packageJson( val packageJson = packageJson(
npmName, compilationResolver.npmProject.name,
npmVersion, compilationResolver.npmVersion,
npmMain, compilationResolver.npmProject.main,
allNpmDependencies, allNpmDependencies,
packageJsonHandlers compilationResolver.packageJsonHandlers
) )
compositeDependencies.forEach { compositeDependencies.forEach {
@@ -468,17 +449,18 @@ internal class KotlinCompilationNpmResolver(
packageJson.dependencies[it.name] = fileVersion(it.path) packageJson.dependencies[it.name] = fileVersion(it.path)
} }
packageJsonHandlers.forEach { compilationResolver.packageJsonHandlers.forEach {
it(packageJson) it(packageJson)
} }
if (!skipWriting) { if (!skipWriting) {
packageJson.saveTo(prePackageJsonFile) packageJson.saveTo(compilationResolver.npmProject.prePackageJsonFile)
} }
return KotlinCompilationNpmResolution( return KotlinCompilationNpmResolution(
if (compilation != null) project else null, @Suppress("SENSELESS_COMPARISON")
npmProject, if (compilationResolver.compilation != null) compilationResolver.project else null,
compilationResolver.npmProject,
compositeDependencies, compositeDependencies,
importedExternalGradleDependencies, importedExternalGradleDependencies,
allNpmDependencies, allNpmDependencies,
@@ -487,9 +469,7 @@ internal class KotlinCompilationNpmResolver(
} }
private fun CompositeDependency.getPackages(): List<File> { private fun CompositeDependency.getPackages(): List<File> {
val packages = includedBuild val packages = includedBuildDir.resolve(projectPackagesDir.relativeTo(rootDir))
.projectDir
.resolve(nodeJs.projectPackagesDir.relativeTo(nodeJs.rootProject.rootDir))
return packages return packages
.list() .list()
?.map { packages.resolve(it) } ?.map { packages.resolve(it) }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import java.io.Serializable
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@@ -27,8 +28,9 @@ import kotlin.reflect.KClass
internal class KotlinProjectNpmResolver( internal class KotlinProjectNpmResolver(
@Transient @Transient
val project: Project, val project: Project,
val resolver: KotlinRootNpmResolver @Transient
) { var resolver: KotlinRootNpmResolver
) : Serializable {
override fun toString(): String = "ProjectNpmResolver($project)" override fun toString(): String = "ProjectNpmResolver($project)"
private val projectPath by lazy { project.path } private val projectPath by lazy { project.path }
@@ -7,12 +7,14 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolver
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.provider.Provider
import org.gradle.internal.service.ServiceRegistry import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.targets.js.dukat.DukatRootResolverPlugin import org.jetbrains.kotlin.gradle.targets.js.dukat.DukatRootResolverPlugin
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements
import org.jetbrains.kotlin.gradle.targets.js.npm.CompositeNodeModulesCache import org.jetbrains.kotlin.gradle.targets.js.npm.CompositeNodeModulesCache
import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesCache import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesCache
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
@@ -23,6 +25,8 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResol
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
import org.jetbrains.kotlin.gradle.targets.js.yarn.toVersionString import org.jetbrains.kotlin.gradle.targets.js.yarn.toVersionString
import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat
import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat
/** /**
* See [KotlinNpmResolutionManager] for details about resolution process. * See [KotlinNpmResolutionManager] for details about resolution process.
@@ -42,22 +46,90 @@ internal class KotlinRootNpmResolver internal constructor(
rootProject.version.toString() rootProject.version.toString()
} }
val plugins = mutableListOf<RootResolverPlugin>().also {
it.add(DukatRootResolverPlugin(this))
}
enum class State { enum class State {
CONFIGURING, CONFIGURING,
PROJECTS_CLOSED, PROJECTS_CLOSED,
INSTALLED INSTALLED
} }
@Transient
@Volatile @Volatile
private var state: State = State.CONFIGURING private var state_: State? = State.CONFIGURING
val gradleNodeModules = GradleNodeModulesCache(nodeJs) private var state
val compositeNodeModules = CompositeNodeModulesCache(nodeJs) get() = state_ ?: resolverStateHolder.get().state
val projectResolvers = mutableMapOf<String, KotlinProjectNpmResolver>() set(value) {
if (state_ != null) {
state_ = value
} else {
resolverStateHolder.get().state = value
}
}
private val archiveOperations by lazy { ArchiveOperationsCompat(rootProject) }
private val fs by lazy { FileSystemOperationsCompat(rootProject) }
private val gradleNodeModulesProvider: Provider<GradleNodeModulesCache> =
rootProject.gradle.sharedServices.registerIfAbsent("gradle-node-modules", GradleNodeModulesCache::class.java) {
it.parameters.cacheDir.set(nodeJs.nodeModulesGradleCacheDir)
it.parameters.rootProjectDir.set(rootProject.projectDir)
}
val gradleNodeModules: GradleNodeModulesCache
get() = gradleNodeModulesProvider.get().also {
it.archiveOperations = archiveOperations
it.fs = fs
}
private val compositeNodeModulesProvider: Provider<CompositeNodeModulesCache> =
rootProject.gradle.sharedServices.registerIfAbsent("composite-node-modules", CompositeNodeModulesCache::class.java) {
it.parameters.cacheDir.set(nodeJs.nodeModulesGradleCacheDir)
it.parameters.rootProjectDir.set(rootProject.projectDir)
}
val compositeNodeModules: CompositeNodeModulesCache
get() = compositeNodeModulesProvider.get()
@Suppress("RedundantNullableReturnType")
@Transient
private val plugins_: MutableList<RootResolverPlugin>? = mutableListOf<RootResolverPlugin>().also {
it.add(DukatRootResolverPlugin(forceFullResolve))
}
@Suppress("RedundantNullableReturnType")
@Transient
private val projectResolvers_: MutableMap<String, KotlinProjectNpmResolver>? = mutableMapOf()
private val resolverStateHolder by lazy {
rootProject.gradle.sharedServices.registerIfAbsent(
KotlinRootNpmResolverStateHolder::class.qualifiedName,
KotlinRootNpmResolverStateHolder::class.java
) {
it.parameters.plugins.set(plugins_)
it.parameters.projectResolvers.set(projectResolvers_)
}
}
private val configurationCacheProjectResolvers: MutableMap<String, KotlinProjectNpmResolver>
get() {
val stateHolder = resolverStateHolder.get()
val projResolvers = stateHolder.parameters.projectResolvers.get()
if (stateHolder.initialized) return projResolvers
projResolvers.forEach { (_, value) ->
value.resolver = this
value.compilationResolvers.forEach { compResolver ->
compResolver.rootResolver = this
}
}
stateHolder.initialized = true
return projResolvers
}
val plugins
get() = plugins_ ?: resolverStateHolder.get().parameters.plugins.get()
val projectResolvers
get() = projectResolvers_ ?: configurationCacheProjectResolvers
val yarn by lazy { val yarn by lazy {
YarnPlugin.apply(rootProject) YarnPlugin.apply(rootProject)
@@ -120,6 +192,7 @@ internal class KotlinRootNpmResolver internal constructor(
val allNpmPackages = projectResolutions.values.flatMap { it.npmProjects } val allNpmPackages = projectResolutions.values.flatMap { it.npmProjects }
gradleNodeModules.close() gradleNodeModules.close()
compositeNodeModules.close()
nodeJs.packageManager.prepareRootProject( nodeJs.packageManager.prepareRootProject(
rootProject, rootProject,
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2021 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.gradle.targets.js.npm.resolver
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements
import org.jetbrains.kotlin.gradle.targets.js.npm.plugins.RootResolverPlugin
// We use this service as a single storage for duplicated data in configuration cache
internal abstract class KotlinRootNpmResolverStateHolder : BuildService<KotlinRootNpmResolverStateHolder.Parameters> {
interface Parameters : BuildServiceParameters {
val plugins: ListProperty<RootResolverPlugin>
val projectResolvers: MapProperty<String, KotlinProjectNpmResolver>
}
var initialized = false
@Volatile
var state = KotlinRootNpmResolver.State.CONFIGURING
}
@@ -59,10 +59,10 @@ open class KotlinPackageJsonTask : DefaultTask() {
} }
private fun findDependentTasks(): Collection<Any> = private fun findDependentTasks(): Collection<Any> =
producer.internalDependencies.map { dependentResolver -> producer.internalDependencies.map { dependency ->
dependentResolver.npmProject.packageJsonTask npmResolutionManager.resolver[dependency.projectPath][dependency.compilationName].npmProject.packageJsonTaskPath
} + producer.internalCompositeDependencies.map { dependency -> } + producer.internalCompositeDependencies.map { dependency ->
dependency.includedBuild.task(":$PACKAGE_JSON_UMBRELLA_TASK_NAME") dependency.includedBuild?.task(":$PACKAGE_JSON_UMBRELLA_TASK_NAME") ?: "includedBuild instance is not available"
} }
@get:Input @get:Input
@@ -32,6 +32,7 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.testing.TestDescriptor import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
import java.io.File import java.io.File
import java.io.Serializable
const val minSupportedGradleVersion = "6.1" const val minSupportedGradleVersion = "6.1"
@@ -75,7 +76,7 @@ internal fun checkGradleCompatibility(
internal val AbstractArchiveTask.archivePathCompatible: File internal val AbstractArchiveTask.archivePathCompatible: File
get() = archiveFile.get().asFile get() = archiveFile.get().asFile
internal class ArchiveOperationsCompat(@Transient private val project: Project) { internal class ArchiveOperationsCompat(@Transient private val project: Project) : Serializable {
private val archiveOperations: Any? = try { private val archiveOperations: Any? = try {
(project as ProjectInternal).services.get(ArchiveOperations::class.java) (project as ProjectInternal).services.get(ArchiveOperations::class.java)
} catch (e: NoClassDefFoundError) { } catch (e: NoClassDefFoundError) {
@@ -98,7 +99,7 @@ internal class ArchiveOperationsCompat(@Transient private val project: Project)
} }
} }
internal class FileSystemOperationsCompat(@Transient private val project: Project) { internal class FileSystemOperationsCompat(@Transient private val project: Project) : Serializable {
private val fileSystemOperations: Any? = try { private val fileSystemOperations: Any? = try {
(project as ProjectInternal).services.get(FileSystemOperations::class.java) (project as ProjectInternal).services.get(FileSystemOperations::class.java)
} catch (e: NoClassDefFoundError) { } catch (e: NoClassDefFoundError) {
@@ -124,4 +125,4 @@ internal interface LegacyTestDescriptorInternal : TestDescriptor {
fun getOwnerBuildOperationId(): Any? fun getOwnerBuildOperationId(): Any?
fun getClassDisplayName(): String? fun getClassDisplayName(): String?
} }