[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
import java.io.Serializable
/**
* Package versions used by tasks
*/
// DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt
class $class {
class $class : Serializable {
#foreach( $dep in $dependencies )
val $dep.camelize() = NpmPackageVersion("$dep.name", "$dep.version")
#end
@@ -24,7 +24,6 @@ import java.security.MessageDigest
* @param version When updating logic in `compute`, `version` should be increased to invalidate cache
*/
internal open class ProcessedFilesCache(
val logger: Logger,
val projectDir: File,
val targetDir: File,
stateFileName: String,
@@ -174,7 +173,8 @@ internal open class ProcessedFilesCache(
try {
GsonBuilder().setPrettyPrinting().create().newJsonReader(stateFile.reader()).use { readFrom(it) }
} catch (e: Throwable) {
logger.warn("Cannot read $stateFile", e)
System.err.println("Cannot read $stateFile")
e.printStackTrace()
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
@@ -209,14 +209,14 @@ internal open class ProcessedFilesCache(
if (old != null) {
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 existedTarget = state.byTarget[key]
if (key != null && existedTarget != null) {
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)
}
}
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.gradle.targets.js
import java.io.Serializable
/**
* Package versions used by tasks
*/
// 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 webpack4 = NpmPackageVersion("webpack", "4.46.0")
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.artifacts.Dependency
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
}
@@ -21,11 +21,13 @@ import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.registerTask
internal class DukatCompilationResolverPlugin(
@Transient
private val resolver: KotlinCompilationNpmResolver
) : CompilationResolverPlugin {
val project get() = resolver.project
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 compilationName by lazy {
compilation.disambiguatedName
@@ -83,7 +85,7 @@ internal class DukatCompilationResolverPlugin(
}
override fun hookDependencies(
internalDependencies: Set<KotlinCompilationNpmResolver>,
internalDependencies: Set<KotlinCompilationNpmResolver.InternalDependency>,
internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>,
externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>,
externalNpmDependencies: Set<NpmDependency>,
@@ -107,7 +109,7 @@ internal class DukatCompilationResolverPlugin(
}
DukatExecutor(
nodeJs.versions,
versions,
DtsResolver(npmProject).getAllDts(externalNpmDependencies),
externalsOutputFormat,
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.KotlinRootNpmResolver
internal class DukatRootResolverPlugin(val resolver: KotlinRootNpmResolver) : RootResolverPlugin {
internal class DukatRootResolverPlugin(val forceFullResolve: Boolean) : RootResolverPlugin {
val compilations = mutableListOf<DukatCompilationResolverPlugin>()
override fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<DukatCompilationResolverPlugin> {
@@ -20,7 +20,7 @@ internal class DukatRootResolverPlugin(val resolver: KotlinRootNpmResolver) : Ro
}
override fun close(resolution: KotlinRootNpmResolution) {
if (resolver.forceFullResolve) {
if (forceFullResolve) {
// inside idea import
compilations.forEach {
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.tasks.internal.CleanableStore
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 {
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.artifacts.Configuration
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat
import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import java.io.File
import java.net.URI
import javax.inject.Inject
@CacheableTask
open class NodeJsSetupTask : DefaultTask() {
abstract class NodeJsSetupTask : DefaultTask() {
@Transient
private val settings = NodeJsRootPlugin.apply(project.rootProject)
private val env by lazy { settings.requireConfigured() }
private val fs = FileSystemOperationsCompat(project)
private val archiveOperations = ArchiveOperationsCompat(project)
private val shouldDownload = settings.download
@get:Inject
internal open val fs: FileSystemOperations
get() = error("Should be injected")
val ivyDependency: String
@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.RequiresNpmDependencies
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 byCompilation = mutableMapOf<String, MutableSet<NpmDependencyDeclaration>>()
@@ -6,25 +6,29 @@
package org.jetbrains.kotlin.gradle.targets.js.npm
import com.google.gson.GsonBuilder
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.hash.FileHasher
import org.gradle.api.file.DirectoryProperty
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.targets.js.nodejs.NodeJsRootExtension
import java.io.File
/**
* 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 {
const val STATE_FILE_NAME = ".visited"
}
internal val dir = nodeJs.nodeModulesGradleCacheDir
private val cache = ProcessedFilesCache(
nodeJs.rootProject.logger,
nodeJs.rootProject.projectDir,
dir,
parameters.rootProjectDir.get().asFile,
parameters.cacheDir.get().asFile,
STATE_FILE_NAME,
"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(
container: File,
packageJson: PackageJson,
@@ -5,19 +5,20 @@
package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import java.io.File
/**
* Cache for storing already created [GradleNodeModule]s
*/
internal class CompositeNodeModulesCache(nodeJs: NodeJsRootExtension) : AbstractNodeModulesCache(nodeJs) {
internal abstract class CompositeNodeModulesCache : AbstractNodeModulesCache() {
override fun buildImportedPackage(
name: String,
version: String,
file: File
): File? {
val module = CompositeNodeModuleBuilder(file, dir)
val module = CompositeNodeModuleBuilder(file, parameters.cacheDir.get().asFile)
return module.rebuild()
}
}
@@ -6,11 +6,12 @@
package org.jetbrains.kotlin.gradle.targets.js.npm
import java.io.File
import java.io.Serializable
/**
* 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
get() = SemVer.from(version)
}
@@ -5,7 +5,6 @@
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_MAP
import org.jetbrains.kotlin.gradle.targets.js.META_JS
@@ -5,27 +5,32 @@
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.FileSystemOperationsCompat
import java.io.File
import javax.inject.Inject
/**
* Cache for storing already created [GradleNodeModule]s
*/
internal class GradleNodeModulesCache(nodeJs: NodeJsRootExtension) : AbstractNodeModulesCache(nodeJs) {
@Transient
private val project = nodeJs.rootProject
internal abstract class GradleNodeModulesCache : AbstractNodeModulesCache() {
private val fs = FileSystemOperationsCompat(project)
private val archiveOperations = ArchiveOperationsCompat(project)
// TODO: replace by injected service org.gradle.api.file.FileSystemOperations once min support Gradle is 6.2
// 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(
name: String,
version: String,
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()
return module.rebuild()
}
@@ -10,6 +10,8 @@ import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.project.ProjectInternal
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.jetbrains.kotlin.gradle.internal.isInIdeaSync
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)
@Volatile
private var state: ResolutionState =
ResolutionState.Configuring(resolver)
internal abstract class KotlinNpmResolutionManagerStateHolder : BuildService<BuildServiceParameters.None> {
@Volatile
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 {
abstract val npmProjects: List<NpmProject>
@@ -139,7 +154,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
services: ServiceRegistry,
logger: Logger
): KotlinRootNpmResolution {
synchronized(this) {
synchronized(stateHolder) {
if (state is ResolutionState.Installed) {
return (state as ResolutionState.Installed).resolved
}
@@ -178,9 +193,11 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
val state0 = this.state
return when (state0) {
is ResolutionState.Prepared -> alreadyResolved(state0.preparedInstallation)
is ResolutionState.Prepared -> {
alreadyResolved(state0.preparedInstallation)
}
is ResolutionState.Configuring -> {
synchronized(this) {
synchronized(stateHolder) {
val state1 = this.state
when (state1) {
is ResolutionState.Prepared -> alreadyResolved(state1.preparedInstallation)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.tasks.Input
import java.io.Serializable
data class NpmDependencyDeclaration(
@Input
@@ -16,7 +17,7 @@ data class NpmDependencyDeclaration(
val version: String,
@Input
val generateExternals: Boolean
)
) : Serializable
fun NpmDependencyDeclaration.uniqueRepresentation() =
"$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.npm.tasks.KotlinPackageJsonTask
import java.io.File
import java.io.Serializable
val KotlinJsCompilation.npmProject: NpmProject
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).
*/
open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
open class NpmProject(@Transient val compilation: KotlinJsCompilation) : Serializable {
val compilationName = compilation.disambiguatedName
val name: String by lazy {
@@ -59,6 +60,10 @@ open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
val packageJsonTask: KotlinPackageJsonTask
get() = project.tasks.getByName(packageJsonTaskName) as KotlinPackageJsonTask
val packageJsonTaskPath by lazy {
packageJsonTask.path
}
val dist: File
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.PACKAGE_JSON
import java.io.File
import java.io.Serializable
/**
* 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 indexFileNames: Collection<String> = listOf(INDEX_FILE_NAME),
val indexFileSuffixes: Collection<String> = listOf(JS_SUFFIX)
) {
) : Serializable {
/**
* 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.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import java.io.File
import java.io.Serializable
// Gson set nulls reflectively no matter on default values and non-null types
class PackageJson(
var name: String,
var version: String
) {
) : Serializable {
internal val customFields = mutableMapOf<String, Any?>()
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.resolver.KotlinCompilationNpmResolver
import java.io.Serializable
internal interface CompilationResolverPlugin {
internal interface CompilationResolverPlugin : Serializable {
fun hookDependencies(
internalDependencies: Set<KotlinCompilationNpmResolver>,
internalDependencies: Set<KotlinCompilationNpmResolver.InternalDependency>,
internalCompositeDependencies: Set<KotlinCompilationNpmResolver.CompositeDependency>,
externalGradleDependencies: Set<KotlinCompilationNpmResolver.ExternalGradleDependency>,
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.resolver.KotlinCompilationNpmResolver
import java.io.Serializable
internal interface RootResolverPlugin {
internal interface RootResolverPlugin : Serializable {
fun createCompilationResolverPlugins(resolver: KotlinCompilationNpmResolver): List<CompilationResolverPlugin>
fun close(resolution: KotlinRootNpmResolution)
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.targets.js.npm.*
import java.io.Serializable
/**
* Resolved [NpmProject]
@@ -19,7 +20,7 @@ class KotlinCompilationNpmResolution(
val externalGradleDependencies: Collection<GradleNodeModule>,
private val _externalNpmDependencies: Collection<NpmDependencyDeclaration>,
val packageJson: PackageJson
) {
) : Serializable {
val project
get() = _project!!
@@ -5,20 +5,12 @@
package org.jetbrains.kotlin.gradle.targets.js.npm.resolver
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileCollection
import org.gradle.api.initialization.IncludedBuild
import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
import org.gradle.api.tasks.Input
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.*
import org.gradle.api.tasks.bundling.Zip
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
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.utils.CompositeProjectComponentArtifactMetadata
import org.jetbrains.kotlin.gradle.utils.`is`
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.topRealPath
import java.io.File
import java.io.Serializable
import java.io.*
/**
* See [KotlinNpmResolutionManager] for details about resolution process.
@@ -51,49 +41,27 @@ internal class KotlinCompilationNpmResolver(
val projectResolver: KotlinProjectNpmResolver,
@Transient
val compilation: KotlinJsCompilation
) {
) : Serializable {
@Transient
val resolver = projectResolver.resolver
private val gradleNodeModules by lazy {
resolver.gradleNodeModules
}
private val compositeNodeModules by lazy {
resolver.compositeNodeModules
}
var rootResolver = projectResolver.resolver
val npmProject = compilation.npmProject
val compilationDisambiguatedName = compilation.disambiguatedName
val npmName by lazy {
npmProject.name
}
val npmVersion by lazy {
project.version.toString()
}
val npmMain by lazy {
npmProject.main
}
val prePackageJsonFile by lazy {
npmProject.prePackageJsonFile
}
val nodeJs get() = resolver.nodeJs
val taskRequirements by lazy {
nodeJs.taskRequirements
}
val nodeJs get() = rootResolver.nodeJs
val target get() = compilation.target
val project get() = target.project
private val packageJsonHandlers by project.provider {
val projectPath = project.path
val packageJsonHandlers by lazy {
compilation.packageJsonHandlers
}
@@ -132,16 +100,24 @@ internal class KotlinCompilationNpmResolver(
override fun toString(): String = "KotlinCompilationNpmResolver(${npmProject.name})"
@delegate:Transient
private val aggregatedConfiguration: Configuration by lazy {
createAggregatedConfiguration()
}
val packageJsonProducer: PackageJsonProducer by lazy {
private val packageJsonProducer_: PackageJsonProducer by lazy {
val visitor = ConfigurationVisitor()
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 resolution: KotlinCompilationNpmResolution? = null
@@ -159,7 +135,7 @@ internal class KotlinCompilationNpmResolver(
fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? {
if (resolution != null) return resolution
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
project.tasks.implementing(RequiresNpmDependencies::class).all {}
return resolve()
@@ -216,26 +192,35 @@ internal class KotlinCompilationNpmResolver(
data class ExternalGradleDependency(
val dependency: ResolvedDependency,
val artifact: ResolvedArtifact
)
): Serializable
data class FileCollectionExternalGradleDependency(
val fileCollection: FileCollection,
val files: Collection<File>,
val dependencyVersion: String?
)
): Serializable
data class FileExternalGradleDependency(
val dependencyName: String,
val dependencyVersion: String,
val file: File
)
) : Serializable
data class CompositeDependency(
val dependency: ResolvedDependency,
val includedBuild: IncludedBuild
)
val dependencyName: String,
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 {
private val internalDependencies = mutableSetOf<KotlinCompilationNpmResolver>()
private val internalDependencies = mutableSetOf<InternalDependency>()
private val internalCompositeDependencies = mutableSetOf<CompositeDependency>()
private val externalGradleDependencies = mutableSetOf<ExternalGradleDependency>()
private val externalNpmDependencies = mutableSetOf<NpmDependency>()
@@ -249,14 +234,14 @@ internal class KotlinCompilationNpmResolver(
configuration.allDependencies.forEach { dependency ->
when (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
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) {
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()
@@ -332,7 +317,9 @@ internal class KotlinCompilationNpmResolver(
(componentIdentifier as DefaultProjectComponentIdentifier).let { identifier ->
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)
?: error("Cannot find project ${componentIdentifier.projectPath}")
resolver.findDependentResolver(project, dependentProject)
rootResolver.findDependentResolver(project, dependentProject)
?.forEach { dependentResolver ->
internalDependencies.add(dependentResolver)
internalDependencies.add(InternalDependency(dependentResolver.projectPath, dependentResolver.compilationDisambiguatedName, dependentResolver.npmProject.name))
}
}
fun toPackageJsonProducer() = PackageJsonProducer(
internalDependencies,
internalCompositeDependencies,
externalGradleDependencies,
externalNpmDependencies,
externalGradleDependencies.map {
FileExternalGradleDependency(
it.dependency.moduleName,
it.dependency.moduleVersion,
it.artifact.file
)
},
externalNpmDependencies.map { it.toDeclaration() },
fileCollectionDependencies
)
}
@@ -374,56 +367,44 @@ internal class KotlinCompilationNpmResolver(
@get:Input
val fileCollectionDependencies: Collection<File>
) : Serializable
)
@Suppress("MemberVisibilityCanBePrivate")
inner class PackageJsonProducer(
val internalDependencies: Collection<KotlinCompilationNpmResolver>,
val internalCompositeDependencies: Collection<CompositeDependency>,
@Transient
val externalGradleDependencies: Collection<ExternalGradleDependency>,
@Transient
val externalNpmDependencies: Collection<NpmDependency>,
val fileCollectionDependencies: Collection<FileCollectionExternalGradleDependency>
) {
val externalNpmDependencyDeclarations by lazy {
externalNpmDependencies.map {
it.toDeclaration()
}
}
class PackageJsonProducer(
var internalDependencies: Collection<InternalDependency>,
var internalCompositeDependencies: Collection<CompositeDependency>,
var externalGradleDependencies: Collection<FileExternalGradleDependency>,
var externalNpmDependencies: Collection<NpmDependencyDeclaration>,
var fileCollectionDependencies: Collection<FileCollectionExternalGradleDependency>
) : Serializable {
private val projectPackagesDir by lazy { compilationResolver.nodeJs.projectPackagesDir }
private val rootDir by lazy { compilationResolver.nodeJs.rootProject.rootDir }
val fileExternalGradleDependencies by lazy {
externalGradleDependencies.map {
FileExternalGradleDependency(
it.dependency.moduleName,
it.dependency.moduleVersion,
it.artifact.file
)
}
}
@Transient
internal lateinit var compilationResolver: KotlinCompilationNpmResolver
val inputs: PackageJsonProducerInputs
get() = PackageJsonProducerInputs(
internalDependencies.map { it.npmProject.name },
internalDependencies.map { it.projectName },
internalCompositeDependencies.flatMap { it.getPackages() },
fileExternalGradleDependencies.map { it.file },
externalNpmDependencyDeclarations.map { it.uniqueRepresentation() },
fileCollectionDependencies.map{ it.fileCollection }.flatMap { it.files }
externalGradleDependencies.map { it.file },
externalNpmDependencies.map { it.uniqueRepresentation() },
fileCollectionDependencies.flatMap{ it.files }
)
fun createPackageJson(skipWriting: Boolean): KotlinCompilationNpmResolution {
val resolvedInternalDependencies = internalDependencies.map {
it.getResolutionOrResolveIfForced()
?: error("Unresolved dependent npm package: ${this@KotlinCompilationNpmResolver} -> $it")
compilationResolver.rootResolver[it.projectPath][it.compilationName].getResolutionOrResolveIfForced()
?: error("Unresolved dependent npm package: ${compilationResolver} -> $it")
}
val importedExternalGradleDependencies = fileExternalGradleDependencies.mapNotNull {
gradleNodeModules.get(it.dependencyName, it.dependencyVersion, it.file)
val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull {
compilationResolver.rootResolver.gradleNodeModules.get(it.dependencyName, it.dependencyVersion, it.file)
} + fileCollectionDependencies.flatMap { dependency ->
dependency.fileCollection.files
dependency.files
// Gradle can hash with FileHasher only files and only existed files
.filter { it.isFile }
.map { file ->
gradleNodeModules.get(
compilationResolver.rootResolver.gradleNodeModules.get(
file.name,
dependency.dependencyVersion ?: "0.0.1",
file
@@ -434,26 +415,26 @@ internal class KotlinCompilationNpmResolver(
val compositeDependencies = internalCompositeDependencies.flatMap { dependency ->
dependency.getPackages()
.map { file ->
compositeNodeModules.get(
dependency.dependency.moduleName,
dependency.dependency.moduleVersion,
compilationResolver.rootResolver.compositeNodeModules.get(
dependency.dependencyName,
dependency.dependencyVersion,
file
)
}
}
.filterNotNull()
val toolsNpmDependencies = taskRequirements
.getCompilationNpmRequirements(compilationDisambiguatedName)
val toolsNpmDependencies = compilationResolver.rootResolver.nodeJs.taskRequirements
.getCompilationNpmRequirements(compilationResolver.compilationDisambiguatedName)
val allNpmDependencies = externalNpmDependencyDeclarations + toolsNpmDependencies
val allNpmDependencies = externalNpmDependencies + toolsNpmDependencies
val packageJson = packageJson(
npmName,
npmVersion,
npmMain,
compilationResolver.npmProject.name,
compilationResolver.npmVersion,
compilationResolver.npmProject.main,
allNpmDependencies,
packageJsonHandlers
compilationResolver.packageJsonHandlers
)
compositeDependencies.forEach {
@@ -468,17 +449,18 @@ internal class KotlinCompilationNpmResolver(
packageJson.dependencies[it.name] = fileVersion(it.path)
}
packageJsonHandlers.forEach {
compilationResolver.packageJsonHandlers.forEach {
it(packageJson)
}
if (!skipWriting) {
packageJson.saveTo(prePackageJsonFile)
packageJson.saveTo(compilationResolver.npmProject.prePackageJsonFile)
}
return KotlinCompilationNpmResolution(
if (compilation != null) project else null,
npmProject,
@Suppress("SENSELESS_COMPARISON")
if (compilationResolver.compilation != null) compilationResolver.project else null,
compilationResolver.npmProject,
compositeDependencies,
importedExternalGradleDependencies,
allNpmDependencies,
@@ -487,9 +469,7 @@ internal class KotlinCompilationNpmResolver(
}
private fun CompositeDependency.getPackages(): List<File> {
val packages = includedBuild
.projectDir
.resolve(nodeJs.projectPackagesDir.relativeTo(nodeJs.rootProject.rootDir))
val packages = includedBuildDir.resolve(projectPackagesDir.relativeTo(rootDir))
return packages
.list()
?.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.resolved.KotlinProjectNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import java.io.Serializable
import kotlin.reflect.KClass
/**
@@ -27,8 +28,9 @@ import kotlin.reflect.KClass
internal class KotlinProjectNpmResolver(
@Transient
val project: Project,
val resolver: KotlinRootNpmResolver
) {
@Transient
var resolver: KotlinRootNpmResolver
) : Serializable {
override fun toString(): String = "ProjectNpmResolver($project)"
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.logging.Logger
import org.gradle.api.provider.Provider
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
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.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.GradleNodeModulesCache
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.yarn.YarnPlugin
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.
@@ -42,22 +46,90 @@ internal class KotlinRootNpmResolver internal constructor(
rootProject.version.toString()
}
val plugins = mutableListOf<RootResolverPlugin>().also {
it.add(DukatRootResolverPlugin(this))
}
enum class State {
CONFIGURING,
PROJECTS_CLOSED,
INSTALLED
}
@Transient
@Volatile
private var state: State = State.CONFIGURING
private var state_: State? = State.CONFIGURING
val gradleNodeModules = GradleNodeModulesCache(nodeJs)
val compositeNodeModules = CompositeNodeModulesCache(nodeJs)
val projectResolvers = mutableMapOf<String, KotlinProjectNpmResolver>()
private var state
get() = state_ ?: resolverStateHolder.get().state
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 {
YarnPlugin.apply(rootProject)
@@ -120,6 +192,7 @@ internal class KotlinRootNpmResolver internal constructor(
val allNpmPackages = projectResolutions.values.flatMap { it.npmProjects }
gradleNodeModules.close()
compositeNodeModules.close()
nodeJs.packageManager.prepareRootProject(
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> =
producer.internalDependencies.map { dependentResolver ->
dependentResolver.npmProject.packageJsonTask
producer.internalDependencies.map { dependency ->
npmResolutionManager.resolver[dependency.projectPath][dependency.compilationName].npmProject.packageJsonTaskPath
} + 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
@@ -32,6 +32,7 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.util.GradleVersion
import java.io.File
import java.io.Serializable
const val minSupportedGradleVersion = "6.1"
@@ -75,7 +76,7 @@ internal fun checkGradleCompatibility(
internal val AbstractArchiveTask.archivePathCompatible: File
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 {
(project as ProjectInternal).services.get(ArchiveOperations::class.java)
} 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 {
(project as ProjectInternal).services.get(FileSystemOperations::class.java)
} catch (e: NoClassDefFoundError) {
@@ -124,4 +125,4 @@ internal interface LegacyTestDescriptorInternal : TestDescriptor {
fun getOwnerBuildOperationId(): Any?
fun getClassDisplayName(): String?
}
}