[Gradle, JS] rootPackageJson and kotlinNpmInstall cache-friendly

This commit is contained in:
Ilya Goncharov
2020-12-17 19:08:30 +03:00
committed by Alexander Likhachev
parent 8c79baa998
commit 4566b7c6e5
21 changed files with 203 additions and 100 deletions
@@ -44,7 +44,10 @@ abstract class DukatTask(
@get:Internal @get:Internal
@delegate:Transient @delegate:Transient
val dts by lazy { val dts by lazy {
val resolvedCompilation = nodeJs.npmResolutionManager.requireInstalled()[project.path][compilation] val resolvedCompilation = nodeJs.npmResolutionManager.requireInstalled(
services,
logger
)[project.path][compilation]
val dtsResolver = DtsResolver(resolvedCompilation.npmProject) val dtsResolver = DtsResolver(resolvedCompilation.npmProject)
dtsResolver.getAllDts( dtsResolver.getAllDts(
resolvedCompilation.externalNpmDependencies, resolvedCompilation.externalNpmDependencies,
@@ -57,8 +57,8 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat
val nodeJsSetupTaskProvider: TaskProvider<out NodeJsSetupTask> val nodeJsSetupTaskProvider: TaskProvider<out NodeJsSetupTask>
get() = rootProject.tasks.withType(NodeJsSetupTask::class.java).named(NodeJsSetupTask.NAME) get() = rootProject.tasks.withType(NodeJsSetupTask::class.java).named(NodeJsSetupTask.NAME)
val npmInstallTaskProvider: TaskProvider<out KotlinNpmInstallTask> val npmInstallTaskProvider: TaskProvider<out KotlinNpmInstallTask>?
get() = rootProject.tasks.withType(KotlinNpmInstallTask::class.java).named(KotlinNpmInstallTask.NAME) get() = rootProject?.tasks?.withType(KotlinNpmInstallTask::class.java)?.named(KotlinNpmInstallTask.NAME)
val packageJsonUmbrellaTaskProvider: TaskProvider<Task> val packageJsonUmbrellaTaskProvider: TaskProvider<Task>
get() = rootProject.tasks.named(PACKAGE_JSON_UMBRELLA_TASK_NAME) get() = rootProject.tasks.named(PACKAGE_JSON_UMBRELLA_TASK_NAME)
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.Incubating import org.gradle.api.Incubating
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.logging.Logger
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
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
@@ -116,7 +118,10 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
} }
@Incubating @Incubating
internal fun requireInstalled() = installIfNeeded(reason = "") internal fun requireInstalled(
services: ServiceRegistry,
logger: Logger
) = installIfNeeded(reason = "", services = services, logger = logger)
internal fun requireConfiguringState(): KotlinRootNpmResolver = internal fun requireConfiguringState(): KotlinRootNpmResolver =
(this.state as? ResolutionState.Configuring ?: error("NPM Dependencies already resolved and installed")).resolver (this.state as? ResolutionState.Configuring ?: error("NPM Dependencies already resolved and installed")).resolver
@@ -124,23 +129,25 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
internal fun isConfiguringState(): Boolean = internal fun isConfiguringState(): Boolean =
this.state is ResolutionState.Configuring this.state is ResolutionState.Configuring
internal fun prepare() = prepareIfNeeded(requireNotPrepared = true) internal fun prepare(logger: Logger) = prepareIfNeeded(requireNotPrepared = true, logger = logger)
internal fun installIfNeeded( internal fun installIfNeeded(
reason: String? = "", reason: String? = "",
args: List<String> = emptyList() args: List<String> = emptyList(),
services: ServiceRegistry,
logger: Logger
): KotlinRootNpmResolution { ): KotlinRootNpmResolution {
synchronized(this) { synchronized(this) {
if (state is ResolutionState.Installed) { if (state is ResolutionState.Installed) {
return (state as ResolutionState.Installed).resolved return (state as ResolutionState.Installed).resolved
} }
val installUpToDate = nodeJsSettings.npmInstallTaskProvider.get().state.upToDate val installUpToDate = nodeJsSettings.npmInstallTaskProvider?.get()?.state?.upToDate ?: false
val forceUpToDate = installUpToDate && !forceFullResolve val forceUpToDate = installUpToDate && !forceFullResolve
val installation = prepareIfNeeded(requireUpToDateReason = reason) val installation = prepareIfNeeded(requireUpToDateReason = reason, logger = logger)
val resolution = installation val resolution = installation
.install(forceUpToDate, args) .install(forceUpToDate, args, services, logger)
state = ResolutionState.Installed(resolution) state = ResolutionState.Installed(resolution)
installation.closePlugins(resolution) installation.closePlugins(resolution)
@@ -149,8 +156,8 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
} }
} }
internal fun requireAlreadyInstalled(project: Project, reason: String = ""): KotlinProjectNpmResolution = // internal fun requireAlreadyInstalled(project: Project, reason: String = ""): KotlinProjectNpmResolution =
installIfNeeded(reason = reason)[project.path] // installIfNeeded(reason = reason)[project.path]
internal val packageJsonFiles: Collection<File> internal val packageJsonFiles: Collection<File>
get() = state.npmProjects.map { it.packageJsonFile } get() = state.npmProjects.map { it.packageJsonFile }
@@ -162,7 +169,8 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
*/ */
private fun prepareIfNeeded( private fun prepareIfNeeded(
requireUpToDateReason: String? = null, requireUpToDateReason: String? = null,
requireNotPrepared: Boolean = false requireNotPrepared: Boolean = false,
logger: Logger
): KotlinRootNpmResolver.Installation { ): KotlinRootNpmResolver.Installation {
fun alreadyResolved(installation: KotlinRootNpmResolver.Installation): KotlinRootNpmResolver.Installation { fun alreadyResolved(installation: KotlinRootNpmResolver.Installation): KotlinRootNpmResolver.Installation {
if (requireNotPrepared) error("Project already prepared") if (requireNotPrepared) error("Project already prepared")
@@ -183,7 +191,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
error("NPM dependencies should be resolved $requireUpToDateReason") error("NPM dependencies should be resolved $requireUpToDateReason")
} }
state1.resolver.prepareInstallation().also { state1.resolver.prepareInstallation(logger).also {
this.state = ResolutionState.Prepared(it) this.state = ResolutionState.Prepared(it)
} }
} }
@@ -196,23 +204,26 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
} }
internal fun getNpmDependencyResolvedCompilation(npmDependency: NpmDependency): KotlinCompilationNpmResolution? { internal fun getNpmDependencyResolvedCompilation(npmDependency: NpmDependency): KotlinCompilationNpmResolution? {
val project = npmDependency.project.path val project = npmDependency.project!!
val projectPath = project.path
val services = (project as ProjectInternal).services
val logger = project.logger
val resolvedProject = val resolvedProject =
if (forceFullResolve) { if (forceFullResolve) {
installIfNeeded(reason = null)[project] installIfNeeded(reason = null, services = services, logger = logger)[projectPath]
} else { } else {
// may return null only during npm resolution // may return null only during npm resolution
// (it can be called since NpmDependency added to configuration that // (it can be called since NpmDependency added to configuration that
// requires resolve to build package.json, in this case we should just skip this call) // requires resolve to build package.json, in this case we should just skip this call)
val state0 = state val state0 = state
when (state0) { when (state0) {
is ResolutionState.Prepared -> state0.preparedInstallation[project] is ResolutionState.Prepared -> state0.preparedInstallation[projectPath]
is ResolutionState.Configuring -> { is ResolutionState.Configuring -> {
return null return null
//error("Cannot use NpmDependency before :kotlinNpmInstall task execution") //error("Cannot use NpmDependency before :kotlinNpmInstall task execution")
} }
is ResolutionState.Installed -> state0.resolved[project] is ResolutionState.Installed -> state0.resolved[projectPath]
} }
} }
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.gradle.targets.js.npm package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
import java.io.File import java.io.File
@@ -17,16 +20,23 @@ interface NpmApi {
fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution)
fun preparedFiles(project: Project): Collection<File> fun preparedFiles(nodeJs: NodeJsRootExtension): Collection<File>
fun prepareRootProject( fun prepareRootProject(
rootProject: Project, rootProject: Project?,
nodeJs: NodeJsRootExtension,
rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String> resolutions: Map<String, String>
) )
fun resolveRootProject( fun resolveRootProject(
rootProject: Project, services: ServiceRegistry,
logger: Logger,
nodeJs: NodeJsRootExtension,
yarnHome: File,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
@@ -22,7 +22,7 @@ import java.io.File
data class NpmDependency( data class NpmDependency(
@Transient @Transient
internal val project: Project, internal val project: Project?,
private val name: String, private val name: String,
private val version: String, private val version: String,
val scope: Scope = Scope.NORMAL, val scope: Scope = Scope.NORMAL,
@@ -82,7 +82,7 @@ data class NpmDependency(
// (it can be called since NpmDependency added to configuration that // (it can be called since NpmDependency added to configuration that
// requires resolve to build package.json, in this case we should just skip this call) // requires resolve to build package.json, in this case we should just skip this call)
private fun resolveProject(): KotlinCompilationNpmResolution? { private fun resolveProject(): KotlinCompilationNpmResolution? {
val nodeJs = NodeJsRootPlugin.apply(project.rootProject) val nodeJs = NodeJsRootPlugin.apply(project!!.rootProject)
return nodeJs.npmResolutionManager.getNpmDependencyResolvedCompilation(this) return nodeJs.npmResolutionManager.getNpmDependencyResolvedCompilation(this)
} }
@@ -90,7 +90,7 @@ data class NpmDependency(
override fun toString() = "$key: $version" override fun toString() = "$key: $version"
override fun getFiles(): FileCollection = project.files(resolve(true)) override fun getFiles(): FileCollection = project!!.files(resolve(true))
override fun getName() = name override fun getName() = name
@@ -100,7 +100,7 @@ data class NpmDependency(
override fun contentEquals(dependency: Dependency) = this == dependency override fun contentEquals(dependency: Dependency) = this == dependency
override fun getTargetComponentId() = DefaultLibraryBinaryIdentifier(project.path, key, "npm") override fun getTargetComponentId() = DefaultLibraryBinaryIdentifier(project!!.path, key, "npm")
override fun copy(): Dependency = this.copy(name = name) override fun copy(): Dependency = this.copy(name = name)
@@ -8,13 +8,12 @@ package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.hash.FileHasher import org.gradle.internal.hash.FileHasher
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.daemon.common.toHexString import org.jetbrains.kotlin.daemon.common.toHexString
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import java.io.File import java.io.File
class PackageJsonUpToDateCheck(val npmProject: NpmProject) { class PackageJsonUpToDateCheck(val services: ServiceRegistry, val npmProject: NpmProject) {
val project: Project
get() = npmProject.project
private val NpmProject.packageJsonHashFile: File private val NpmProject.packageJsonHashFile: File
get() = dir.resolve("package.json.hash") get() = dir.resolve("package.json.hash")
@@ -24,10 +23,10 @@ class PackageJsonUpToDateCheck(val npmProject: NpmProject) {
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
} }
private val packageJsonFile = npmProject.prePackageJsonFile private val packageJsonFile = npmProject.packageJsonFile
private val hash by lazy { private val hash by lazy {
val hasher = (project as ProjectInternal).services.get(FileHasher::class.java) val hasher = services.get(FileHasher::class.java)
hasher.hash(packageJsonFile).toByteArray().toHexString() hasher.hash(packageJsonFile).toByteArray().toHexString()
} }
@@ -27,7 +27,10 @@ constructor(
private val nodeJs = npmProject.nodeJs private val nodeJs = npmProject.nodeJs
private val compilationResolution private val compilationResolution
get() = nodeJs.npmResolutionManager.requireInstalled()[project.path][npmProject.compilation] get() = nodeJs.npmResolutionManager.requireInstalled(
services,
logger
)[project.path][npmProject.compilation]
init { init {
// TODO: temporary workaround for configuration cache enabled builds // TODO: temporary workaround for configuration cache enabled builds
@@ -14,8 +14,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.*
class KotlinCompilationNpmResolution( class KotlinCompilationNpmResolution(
@Transient @Transient
private val _project: Project?, private val _project: Project?,
@Transient val npmProject: NpmProject,
private val _npmProject: NpmProject?,
val internalDependencies: Collection<KotlinCompilationNpmResolution>, val internalDependencies: Collection<KotlinCompilationNpmResolution>,
val internalCompositeDependencies: Collection<GradleNodeModule>, val internalCompositeDependencies: Collection<GradleNodeModule>,
val externalGradleDependencies: Collection<GradleNodeModule>, val externalGradleDependencies: Collection<GradleNodeModule>,
@@ -24,14 +23,12 @@ class KotlinCompilationNpmResolution(
) { ) {
val project val project
get() = _project!! get() = _project!!
val npmProject
get() = _npmProject!!
val externalNpmDependencies val externalNpmDependencies
get() = _externalNpmDependencies get() = _externalNpmDependencies
.map { .map {
NpmDependency( NpmDependency(
project = project, project = _project,
name = it.name, name = it.name,
version = it.version, version = it.version,
scope = it.scope, scope = it.scope,
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved
import org.gradle.api.Project import org.gradle.api.Project
internal class KotlinRootNpmResolution( internal class KotlinRootNpmResolution(
val rootProject: Project, val rootProject: Project?,
internal val projects: Map<String, KotlinProjectNpmResolution> internal val projects: Map<String, KotlinProjectNpmResolution>
) { ) {
operator fun get(project: String) = projects[project] ?: KotlinProjectNpmResolution.empty(project) operator fun get(project: String) = projects[project] ?: KotlinProjectNpmResolution.empty(project)
@@ -59,7 +59,6 @@ internal class KotlinCompilationNpmResolver(
resolver.compositeNodeModules resolver.compositeNodeModules
} }
@Transient
val npmProject = compilation.npmProject val npmProject = compilation.npmProject
val compilationName = compilation.name val compilationName = compilation.name
@@ -463,7 +462,7 @@ internal class KotlinCompilationNpmResolver(
return KotlinCompilationNpmResolution( return KotlinCompilationNpmResolution(
if (compilation != null) project else null, if (compilation != null) project else null,
if (compilation != null) npmProject else null, npmProject,
resolvedInternalDependencies, resolvedInternalDependencies,
compositeDependencies, compositeDependencies,
importedExternalGradleDependencies, importedExternalGradleDependencies,
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.gradle.targets.js.npm.resolver package org.jetbrains.kotlin.gradle.targets.js.npm.resolver
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.logging.Logger
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
@@ -22,7 +23,6 @@ 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.tasks.registerTask
/** /**
* See [KotlinNpmResolutionManager] for details about resolution process. * See [KotlinNpmResolutionManager] for details about resolution process.
@@ -34,6 +34,14 @@ internal class KotlinRootNpmResolver internal constructor(
val rootProject: Project val rootProject: Project
get() = nodeJs.rootProject get() = nodeJs.rootProject
val rootProjectName by lazy {
rootProject.name
}
val rootProjectVersion by lazy {
rootProject.version.toString()
}
val plugins = mutableListOf<RootResolverPlugin>().also { val plugins = mutableListOf<RootResolverPlugin>().also {
it.add(DukatRootResolverPlugin(this)) it.add(DukatRootResolverPlugin(this))
} }
@@ -51,6 +59,10 @@ internal class KotlinRootNpmResolver internal constructor(
val compositeNodeModules = CompositeNodeModulesCache(nodeJs) val compositeNodeModules = CompositeNodeModulesCache(nodeJs)
val projectResolvers = mutableMapOf<String, KotlinProjectNpmResolver>() val projectResolvers = mutableMapOf<String, KotlinProjectNpmResolver>()
val yarn by lazy {
YarnPlugin.apply(rootProject)
}
fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved." fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved."
@Synchronized @Synchronized
@@ -95,7 +107,7 @@ internal class KotlinRootNpmResolver internal constructor(
/** /**
* Don't use directly, use [KotlinNpmResolutionManager.installIfNeeded] instead. * Don't use directly, use [KotlinNpmResolutionManager.installIfNeeded] instead.
*/ */
internal fun prepareInstallation(): Installation { internal fun prepareInstallation(logger: Logger): Installation {
synchronized(this@KotlinRootNpmResolver) { synchronized(this@KotlinRootNpmResolver) {
check(state == State.CONFIGURING) { check(state == State.CONFIGURING) {
"Projects must be configuring" "Projects must be configuring"
@@ -109,14 +121,15 @@ internal class KotlinRootNpmResolver internal constructor(
gradleNodeModules.close() gradleNodeModules.close()
val yarn = YarnPlugin.apply(rootProject)
nodeJs.packageManager.prepareRootProject( nodeJs.packageManager.prepareRootProject(
rootProject, rootProject,
nodeJs,
rootProjectName,
rootProjectVersion,
logger,
allNpmPackages, allNpmPackages,
yarn.resolutions yarn.resolutions
.associate { it.path to it.toVersionString() } .associate { it.path to it.toVersionString() })
)
return Installation( return Installation(
projectResolutions projectResolutions
@@ -130,7 +143,9 @@ internal class KotlinRootNpmResolver internal constructor(
internal fun install( internal fun install(
forceUpToDate: Boolean, forceUpToDate: Boolean,
args: List<String> args: List<String>,
services: ServiceRegistry,
logger: Logger
): KotlinRootNpmResolution { ): KotlinRootNpmResolution {
synchronized(this@KotlinRootNpmResolver) { synchronized(this@KotlinRootNpmResolver) {
check(state == State.PROJECTS_CLOSED) { check(state == State.PROJECTS_CLOSED) {
@@ -147,12 +162,15 @@ internal class KotlinRootNpmResolver internal constructor(
// we should call it even kotlinNpmInstall task is up-to-date (skipPackageManager is true) // we should call it even kotlinNpmInstall task is up-to-date (skipPackageManager is true)
// because our upToDateChecks saves state for next execution // because our upToDateChecks saves state for next execution
val upToDateChecks = allNpmPackages.map { val upToDateChecks = allNpmPackages.map {
PackageJsonUpToDateCheck(it.npmProject) PackageJsonUpToDateCheck(services, it.npmProject)
} }
val upToDate = forceUpToDate || upToDateChecks.all { it.upToDate } val upToDate = forceUpToDate || upToDateChecks.all { it.upToDate }
nodeJs.packageManager.resolveRootProject( nodeJs.packageManager.resolveRootProject(
nodeJs.rootProject, services,
logger,
nodeJs,
yarn.requireConfigured().home,
allNpmPackages, allNpmPackages,
upToDate, upToDate,
args args
@@ -11,7 +11,6 @@ import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.utils.disableTaskOnConfigurationCacheBuild
import java.io.File import java.io.File
open class KotlinNpmInstallTask : DefaultTask() { open class KotlinNpmInstallTask : DefaultTask() {
@@ -44,7 +43,7 @@ open class KotlinNpmInstallTask : DefaultTask() {
@get:InputFiles @get:InputFiles
val preparedFiles: Collection<File> by lazy { val preparedFiles: Collection<File> by lazy {
nodeJs.packageManager.preparedFiles(project) nodeJs.packageManager.preparedFiles(nodeJs)
} }
// avoid using node_modules as output directory, as it is significantly slows down build // avoid using node_modules as output directory, as it is significantly slows down build
@@ -58,7 +57,11 @@ open class KotlinNpmInstallTask : DefaultTask() {
@TaskAction @TaskAction
fun resolve() { fun resolve() {
resolutionManager.installIfNeeded(args = args) resolutionManager.installIfNeeded(
args = args,
services = services,
logger = logger
)
} }
companion object { companion object {
@@ -107,9 +107,9 @@ open class KotlinPackageJsonTask : DefaultTask() {
task.inputs.file(packageJsonTask.map { it.packageJson }) task.inputs.file(packageJsonTask.map { it.packageJson })
} }
nodeJs.rootPackageJsonTaskProvider?.configure { it.mustRunAfter(packageJsonTask) } nodeJs.rootPackageJsonTaskProvider!!.configure { it.mustRunAfter(packageJsonTask) }
compilation.compileKotlinTaskProvider.dependsOn(npmInstallTask) compilation.compileKotlinTaskProvider.dependsOn(npmInstallTask!!)
compilation.compileKotlinTaskProvider.dependsOn(packageJsonTask) compilation.compileKotlinTaskProvider.dependsOn(packageJsonTask)
return packageJsonTask return packageJsonTask
@@ -40,7 +40,7 @@ open class RootPackageJsonTask : DefaultTask() {
@TaskAction @TaskAction
fun resolve() { fun resolve() {
resolutionManager.prepare() resolutionManager.prepare(logger)
} }
companion object { companion object {
@@ -250,7 +250,6 @@ constructor(
@TaskAction @TaskAction
fun doExecute() { fun doExecute() {
println(services.get(org.gradle.internal.logging.progress.ProgressLoggerFactory::class.java))
// nodeJs.npmResolutionManager?.checkRequiredDependencies(this) // nodeJs.npmResolutionManager?.checkRequiredDependencies(this)
val runner = createRunner() val runner = createRunner()
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
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.resolved.KotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
@@ -25,29 +28,43 @@ class Yarn : NpmApi {
override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) = override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) =
getDelegate(resolvedNpmProject.project).resolveProject(resolvedNpmProject) getDelegate(resolvedNpmProject.project).resolveProject(resolvedNpmProject)
override fun preparedFiles(project: Project): Collection<File> = override fun preparedFiles(nodeJs: NodeJsRootExtension): Collection<File> =
getDelegate(project).preparedFiles(project) yarnWorkspaces.preparedFiles(nodeJs)
override fun prepareRootProject( override fun prepareRootProject(
rootProject: Project, rootProject: Project?,
nodeJs: NodeJsRootExtension,
rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String> resolutions: Map<String, String>
) = getDelegate(rootProject.project) ) = yarnWorkspaces
.prepareRootProject( .prepareRootProject(
rootProject, rootProject,
nodeJs,
rootProjectName,
rootProjectVersion,
logger,
subProjects, subProjects,
resolutions resolutions
) )
override fun resolveRootProject( override fun resolveRootProject(
rootProject: Project, services: ServiceRegistry,
logger: Logger,
nodeJs: NodeJsRootExtension,
yarnHome: File,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
) { ) {
getDelegate(rootProject.project) yarnWorkspaces
.resolveRootProject( .resolveRootProject(
rootProject, services,
logger,
nodeJs,
yarnHome,
npmProjects, npmProjects,
skipExecution, skipExecution,
cliArgs cliArgs
@@ -7,7 +7,10 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.Logger
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.internal.execWithProgress import org.jetbrains.kotlin.gradle.internal.execWithProgress
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
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.NpmApi import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency
@@ -25,19 +28,19 @@ abstract class YarnBasics : NpmApi {
} }
fun yarnExec( fun yarnExec(
project: Project, services: ServiceRegistry,
logger: Logger,
nodeJs: NodeJsRootExtension,
yarnHome: File,
dir: File, dir: File,
description: String, description: String,
args: List<String> args: List<String>
) { ) {
val nodeJs = NodeJsRootPlugin.apply(project) services.execWithProgress(description) { exec ->
val yarnPlugin = YarnPlugin.apply(project)
(project as ProjectInternal).services.execWithProgress(description) { exec ->
exec.executable = nodeJs.requireConfigured().nodeExecutable exec.executable = nodeJs.requireConfigured().nodeExecutable
exec.args = listOf(yarnPlugin.requireConfigured().home.resolve("bin/yarn.js").absolutePath) + exec.args = listOf(yarnHome.resolve("bin/yarn.js").absolutePath) +
args + args +
if (project.logger.isDebugEnabled) "--verbose" else "" if (logger.isDebugEnabled) "--verbose" else ""
exec.workingDir = dir exec.workingDir = dir
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
import com.google.gson.Gson import com.google.gson.Gson
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModule import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModule
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
@@ -15,7 +16,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmR
import java.io.File import java.io.File
class YarnImportedPackagesVersionResolver( class YarnImportedPackagesVersionResolver(
private val rootProject: Project, private val logger: Logger,
private val npmProjects: Collection<KotlinCompilationNpmResolution>, private val npmProjects: Collection<KotlinCompilationNpmResolution>,
private val nodeJsWorldDir: File private val nodeJsWorldDir: File
) { ) {
@@ -53,7 +54,7 @@ class YarnImportedPackagesVersionResolver(
modules.groupBy { it.name }.forEach { (name, versions) -> modules.groupBy { it.name }.forEach { (name, versions) ->
val selected: GradleNodeModule = if (versions.size > 1) { val selected: GradleNodeModule = if (versions.size > 1) {
val sorted = versions.sortedBy { it.semver } val sorted = versions.sortedBy { it.semver }
rootProject.logger.warn( logger.warn(
"There are multiple versions of \"$name\" used in nodejs build: ${sorted.joinToString(", ") { it.version }}. " + "There are multiple versions of \"$name\" used in nodejs build: ${sorted.joinToString(", ") { it.version }}. " +
"Only latest version will be used." "Only latest version will be used."
) )
@@ -17,7 +17,10 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
open class YarnRootExtension(val project: Project) : ConfigurationPhaseAware<YarnEnv>() { open class YarnRootExtension(
@Transient
val project: Project
) : ConfigurationPhaseAware<YarnEnv>() {
init { init {
check(project == project.rootProject) check(project == project.rootProject)
} }
@@ -6,6 +6,11 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.Logger
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJsonUpToDateCheck import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJsonUpToDateCheck
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
@@ -17,28 +22,38 @@ class YarnSimple : YarnBasics() {
val project = resolvedNpmProject.project val project = resolvedNpmProject.project
PackageJsonUpToDateCheck(resolvedNpmProject.npmProject).updateIfNeeded { // PackageJsonUpToDateCheck(resolvedNpmProject.npmProject).updateIfNeeded {
yarnExec( // yarnExec(
project, // (project as ProjectInternal).services,
resolvedNpmProject.npmProject.dir, // project.logger,
NpmApi.resolveOperationDescription("yarn for ${project.path}"), // NodeJsRootPlugin.apply(project),
emptyList() // YarnPlugin.apply(project).requireConfigured().home,
) // resolvedNpmProject.npmProject.dir,
yarnLockReadTransitiveDependencies(resolvedNpmProject.npmProject.dir, resolvedNpmProject.externalNpmDependencies) // NpmApi.resolveOperationDescription("yarn for ${project.path}"),
} // emptyList()
// )
// yarnLockReadTransitiveDependencies(resolvedNpmProject.npmProject.dir, resolvedNpmProject.externalNpmDependencies)
// }
} }
override fun preparedFiles(project: Project): Collection<File> = override fun preparedFiles(nodeJs: NodeJsRootExtension): Collection<File> =
emptyList() emptyList()
override fun prepareRootProject( override fun prepareRootProject(
rootProject: Project, rootProject: Project?,
nodeJs: NodeJsRootExtension,
rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String> resolutions: Map<String, String>
) = Unit ) = Unit
override fun resolveRootProject( override fun resolveRootProject(
rootProject: Project, services: ServiceRegistry,
logger: Logger,
nodeJs: NodeJsRootExtension,
yarnHome: File,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
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.NpmApi import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
@@ -16,37 +19,49 @@ import java.io.File
class YarnWorkspaces : YarnBasics() { class YarnWorkspaces : YarnBasics() {
override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) = Unit override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) = Unit
override fun preparedFiles(project: Project): Collection<File> { override fun preparedFiles(nodeJs: NodeJsRootExtension): Collection<File> {
return listOf( return listOf(
NodeJsRootPlugin.apply(project.rootProject) nodeJs
.rootPackageDir .rootPackageDir
.resolve(NpmProject.PACKAGE_JSON) .resolve(NpmProject.PACKAGE_JSON)
) )
} }
override fun prepareRootProject( override fun prepareRootProject(
rootProject: Project, rootProject: Project?,
nodeJs: NodeJsRootExtension,
rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String> resolutions: Map<String, String>
) { ) {
check(rootProject == rootProject.rootProject) // check(rootProject == rootProject.rootProject)
setup(rootProject) rootProject?.let { setup(it) }
return prepareRootPackageJson( return prepareRootPackageJson(
rootProject, nodeJs,
rootProjectName,
rootProjectVersion,
logger,
subProjects, subProjects,
resolutions resolutions
) )
} }
private fun prepareRootPackageJson( private fun prepareRootPackageJson(
rootProject: Project, nodeJs: NodeJsRootExtension,
rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String> resolutions: Map<String, String>
) { ) {
val rootPackageJsonFile = preparedFiles(rootProject).single() val rootPackageJsonFile = preparedFiles(nodeJs).single()
saveRootProjectWorkspacesPackageJson( saveRootProjectWorkspacesPackageJson(
rootProject, rootProjectName,
rootProjectVersion,
logger,
npmProjects, npmProjects,
resolutions, resolutions,
rootPackageJsonFile rootPackageJsonFile
@@ -54,16 +69,21 @@ class YarnWorkspaces : YarnBasics() {
} }
override fun resolveRootProject( override fun resolveRootProject(
rootProject: Project, services: ServiceRegistry,
logger: Logger,
nodeJs: NodeJsRootExtension,
yarnHome: File,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
) { ) {
val nodeJs = NodeJsRootPlugin.apply(rootProject)
val nodeJsWorldDir = nodeJs.rootPackageDir val nodeJsWorldDir = nodeJs.rootPackageDir
yarnExec( yarnExec(
rootProject, services,
logger,
nodeJs,
yarnHome,
nodeJsWorldDir, nodeJsWorldDir,
NpmApi.resolveOperationDescription("yarn"), NpmApi.resolveOperationDescription("yarn"),
cliArgs cliArgs
@@ -74,18 +94,20 @@ class YarnWorkspaces : YarnBasics() {
} }
private fun saveRootProjectWorkspacesPackageJson( private fun saveRootProjectWorkspacesPackageJson(
rootProject: Project, rootProjectName: String,
rootProjectVersion: String,
logger: Logger,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
resolutions: Map<String, String>, resolutions: Map<String, String>,
rootPackageJsonFile: File rootPackageJsonFile: File
) { ) {
val nodeJsWorldDir = rootPackageJsonFile.parentFile val nodeJsWorldDir = rootPackageJsonFile.parentFile
val rootPackageJson = PackageJson(rootProject.name, rootProject.version.toString()) val rootPackageJson = PackageJson(rootProjectName, rootProjectVersion)
rootPackageJson.private = true rootPackageJson.private = true
val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path } val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path }
val importedProjectWorkspaces = val importedProjectWorkspaces =
YarnImportedPackagesVersionResolver(rootProject, npmProjects, nodeJsWorldDir).resolveAndUpdatePackages() YarnImportedPackagesVersionResolver(logger, npmProjects, nodeJsWorldDir).resolveAndUpdatePackages()
rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces
rootPackageJson.resolutions = resolutions rootPackageJson.resolutions = resolutions