[Gradle, JS] Divide creating root package and install tasks

#KT-34832 fixed
This commit is contained in:
Ilya Goncharov
2020-04-15 17:16:34 +03:00
parent 3ad597ca5c
commit 86573c6dd1
11 changed files with 159 additions and 48 deletions
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2020 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.nodejs package org.jetbrains.kotlin.gradle.targets.js.nodejs
import org.gradle.api.Project import org.gradle.api.Project
@@ -7,6 +12,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
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.tasks.KotlinNpmInstallTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
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
@@ -53,6 +59,9 @@ open class NodeJsRootExtension(val rootProject: Project) : ConfigurationPhaseAwa
val rootPackageDir: File val rootPackageDir: File
get() = rootProject.buildDir.resolve("js") get() = rootProject.buildDir.resolve("js")
val rootPackageJson: File
get() = rootPackageDir.resolve(NpmProject.PACKAGE_JSON)
internal val rootNodeModulesStateFile: File internal val rootNodeModulesStateFile: File
get() = rootPackageDir.resolve("node_modules.state") get() = rootPackageDir.resolve("node_modules.state")
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2020 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.nodejs package org.jetbrains.kotlin.gradle.targets.js.nodejs
import org.gradle.api.Plugin import org.gradle.api.Plugin
@@ -5,6 +10,7 @@ import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.BasePlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.EXTENSION_NAME import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.EXTENSION_NAME
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
open class NodeJsRootPlugin : Plugin<Project> { open class NodeJsRootPlugin : Plugin<Project> {
@@ -22,8 +28,13 @@ open class NodeJsRootPlugin : Plugin<Project> {
it.description = "Download and install a local node/npm version" it.description = "Download and install a local node/npm version"
} }
val rootPackageJson = tasks.register(RootPackageJsonTask.NAME, RootPackageJsonTask::class.java) {
it.group = TASKS_GROUP_NAME
it.description = "Create root package.json"
}
tasks.register(KotlinNpmInstallTask.NAME, KotlinNpmInstallTask::class.java) { tasks.register(KotlinNpmInstallTask.NAME, KotlinNpmInstallTask::class.java) {
it.dependsOn(setupTask) it.dependsOn(setupTask, rootPackageJson)
it.group = TASKS_GROUP_NAME it.group = TASKS_GROUP_NAME
it.description = "Find, download and link NPM dependencies and projects" it.description = "Find, download and link NPM dependencies and projects"
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -84,8 +84,19 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
) )
internal sealed class ResolutionState { internal sealed class ResolutionState {
class Configuring(val resolver: KotlinRootNpmResolver) : ResolutionState() abstract val npmProjects: List<NpmProject>
class Installed(val resolved: KotlinRootNpmResolution) : ResolutionState()
class Configuring(val resolver: KotlinRootNpmResolver) : ResolutionState() {
override val npmProjects: List<NpmProject>
get() = resolver.compilations.map { it.npmProject }
}
class Installed(val resolved: KotlinRootNpmResolution) : ResolutionState() {
override val npmProjects: List<NpmProject>
get() = resolved.projects.values.flatMap { it.npmProjects.map { it.npmProject } }
}
} }
@Incubating @Incubating
@@ -100,7 +111,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
installIfNeeded(requireUpToDateReason = reason)[project] installIfNeeded(requireUpToDateReason = reason)[project]
internal val packageJsonFiles: Collection<File> internal val packageJsonFiles: Collection<File>
get() = (state as ResolutionState.Configuring).resolver.compilations.map { it.npmProject.packageJsonFile } get() = state.npmProjects.map { it.packageJsonFile }
/** /**
* @param requireUpToDateReason Check that project already resolved, * @param requireUpToDateReason Check that project already resolved,
@@ -1,6 +1,6 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* that can be found in the license/LICENSE.txt file. * 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 package org.jetbrains.kotlin.gradle.targets.js.npm
@@ -17,13 +17,20 @@ interface NpmApi {
fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution)
fun resolveRootProject( fun prepareRootProject(
rootProject: Project, rootProject: Project,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
) )
fun resolveRootProject(
rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean,
cliArgs: List<String>
)
fun resolveDependency( fun resolveDependency(
npmResolution: KotlinCompilationNpmResolution, npmResolution: KotlinCompilationNpmResolution,
dependency: NpmDependency, dependency: NpmDependency,
@@ -1,17 +1,15 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.resolved package org.jetbrains.kotlin.gradle.targets.js.npm.resolved
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
class KotlinRootNpmResolution( class KotlinRootNpmResolution(
val rootProject: Project, val rootProject: Project,
private val projects: Map<Project, KotlinProjectNpmResolution> val projects: Map<Project, KotlinProjectNpmResolution>
) { ) {
operator fun get(project: Project) = projects[project] ?: KotlinProjectNpmResolution.empty(project) operator fun get(project: Project) = projects[project] ?: KotlinProjectNpmResolution.empty(project)
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -42,7 +42,7 @@ internal class KotlinRootNpmResolver internal constructor(
val gradleNodeModules = GradleNodeModulesCache(nodeJs) val gradleNodeModules = GradleNodeModulesCache(nodeJs)
val compositeNodeModules = CompositeNodeModulesCache(nodeJs) val compositeNodeModules = CompositeNodeModulesCache(nodeJs)
val packageJsonUmbrella = rootProject.registerTask(PACKAGE_JSON_UMBRELLA_TASK_NAME, Task::class.java) {} val packageJsonUmbrella = rootProject.registerTask(PACKAGE_JSON_UMBRELLA_TASK_NAME, Task::class.java) {}
private val projectResolvers = mutableMapOf<Project, KotlinProjectNpmResolver>() val projectResolvers = mutableMapOf<Project, KotlinProjectNpmResolver>()
fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved." fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved."
@@ -86,7 +86,7 @@ internal class KotlinRootNpmResolver internal constructor(
} }
/** /**
* Don't use directly, use [NodeJsRootExtension.resolveIfNeeded] instead. * Don't use directly, use [KotlinNpmResolutionManager.installIfNeeded] instead.
*/ */
internal fun close(forceUpToDate: Boolean): KotlinRootNpmResolution { internal fun close(forceUpToDate: Boolean): KotlinRootNpmResolution {
check(!closed) check(!closed)
@@ -108,15 +108,13 @@ internal class KotlinRootNpmResolver internal constructor(
} }
val upToDate = forceUpToDate || upToDateChecks.all { it.upToDate } val upToDate = forceUpToDate || upToDateChecks.all { it.upToDate }
nodeJs.packageManager.resolveRootProject( nodeJs.packageManager.prepareRootProject(
rootProject, rootProject,
allNpmPackages, allNpmPackages,
upToDate, upToDate,
nodeJs.npmInstallTask.args nodeJs.npmInstallTask.args
) )
nodeJs.rootNodeModulesStateFile.writeText(System.currentTimeMillis().toString())
upToDateChecks.forEach { it.commit() } upToDateChecks.forEach { it.commit() }
return KotlinRootNpmResolution(rootProject, projectResolutions) return KotlinRootNpmResolution(rootProject, projectResolutions)
@@ -1,15 +1,12 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.tasks package org.jetbrains.kotlin.gradle.targets.js.npm.tasks
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input import org.gradle.api.tasks.*
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
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 java.io.File import java.io.File
@@ -29,6 +26,10 @@ open class KotlinNpmInstallTask : DefaultTask() {
val packageJsonFiles: Collection<File> val packageJsonFiles: Collection<File>
get() = resolutionManager.packageJsonFiles get() = resolutionManager.packageJsonFiles
@get:InputFile
val rootPackageJson: File
get() = nodeJs.rootPackageJson
// 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
@get:OutputFile @get:OutputFile
val nodeModulesState: File val nodeModulesState: File
@@ -40,7 +41,15 @@ open class KotlinNpmInstallTask : DefaultTask() {
@TaskAction @TaskAction
fun resolve() { fun resolve() {
resolutionManager.install() val npmResolutions = resolutionManager.requireInstalled()
.projects
.values
nodeJs.packageManager.resolveRootProject(
project,
npmResolutions.flatMap { it.npmProjects },
false,
args
)
} }
companion object { companion object {
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2020 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.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import java.io.File
open class RootPackageJsonTask : DefaultTask() {
init {
check(project == project.rootProject)
outputs.upToDateWhen {
false
}
}
private val nodeJs get() = NodeJsRootPlugin.apply(project.rootProject)
private val resolutionManager get() = nodeJs.npmResolutionManager
@get:OutputFile
val rootPackageJson: File
get() = nodeJs.rootPackageJson
@TaskAction
fun resolve() {
resolutionManager.install()
}
companion object {
const val NAME = "rootPackageJson"
}
}
@@ -1,6 +1,6 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* that can be found in the license/LICENSE.txt file. * 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.yarn package org.jetbrains.kotlin.gradle.targets.js.yarn
@@ -24,6 +24,16 @@ 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 resolveRootProject(
rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean,
cliArgs: List<String>
) {
getDelegate(rootProject.project)
.resolveRootProject(rootProject, npmProjects, skipExecution, cliArgs)
}
override fun resolveDependency( override fun resolveDependency(
npmResolution: KotlinCompilationNpmResolution, npmResolution: KotlinCompilationNpmResolution,
dependency: NpmDependency, dependency: NpmDependency,
@@ -35,13 +45,13 @@ class Yarn : NpmApi {
transitive transitive
) )
override fun resolveRootProject( override fun prepareRootProject(
rootProject: Project, rootProject: Project,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
) = getDelegate(rootProject.project) ) = getDelegate(rootProject.project)
.resolveRootProject( .prepareRootProject(
rootProject, rootProject,
subProjects, subProjects,
skipExecution, skipExecution,
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -28,6 +28,15 @@ class YarnSimple : YarnBasics() {
} }
override fun resolveRootProject( override fun resolveRootProject(
rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean,
cliArgs: List<String>
) {
TODO("Not yet implemented")
}
override fun prepareRootProject(
rootProject: Project, rootProject: Project,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
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.PackageJson import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
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
@@ -16,7 +15,7 @@ import java.io.File
class YarnWorkspaces : YarnBasics() { class YarnWorkspaces : YarnBasics() {
override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) = Unit override fun resolveProject(resolvedNpmProject: KotlinCompilationNpmResolution) = Unit
override fun resolveRootProject( override fun prepareRootProject(
rootProject: Project, rootProject: Project,
subProjects: Collection<KotlinCompilationNpmResolution>, subProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
@@ -24,31 +23,42 @@ class YarnWorkspaces : YarnBasics() {
) { ) {
check(rootProject == rootProject.rootProject) check(rootProject == rootProject.rootProject)
if (!skipExecution) setup(rootProject) if (!skipExecution) setup(rootProject)
return resolveWorkspaces( return prepareRootPackageJson(
rootProject, rootProject,
subProjects, subProjects,
skipExecution, skipExecution
cliArgs
) )
} }
private fun resolveWorkspaces( private fun prepareRootPackageJson(
rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean
) {
val nodeJs = NodeJsRootPlugin.apply(rootProject)
val rootPackageJsonFile = nodeJs.rootPackageJson
if (!skipExecution) {
saveRootProjectWorkspacesPackageJson(rootProject, npmProjects, rootPackageJsonFile)
}
}
override fun resolveRootProject(
rootProject: Project, rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
skipExecution: Boolean, skipExecution: Boolean,
cliArgs: List<String> cliArgs: List<String>
) { ) {
val nodeJsWorldDir = NodeJsRootPlugin.apply(rootProject).rootPackageDir val nodeJs = NodeJsRootPlugin.apply(rootProject)
val nodeJsWorldDir = nodeJs.rootPackageDir
if (!skipExecution) { yarnExec(
saveRootProjectWorkspacesPackageJson(rootProject, npmProjects, nodeJsWorldDir) rootProject,
yarnExec( nodeJsWorldDir,
rootProject, NpmApi.resolveOperationDescription("yarn"),
nodeJsWorldDir, cliArgs
NpmApi.resolveOperationDescription("yarn"), )
cliArgs nodeJs.rootNodeModulesStateFile.writeText(System.currentTimeMillis().toString())
)
}
yarnLockReadTransitiveDependencies(nodeJsWorldDir, npmProjects.flatMap { it.externalNpmDependencies }) yarnLockReadTransitiveDependencies(nodeJsWorldDir, npmProjects.flatMap { it.externalNpmDependencies })
} }
@@ -56,8 +66,9 @@ class YarnWorkspaces : YarnBasics() {
private fun saveRootProjectWorkspacesPackageJson( private fun saveRootProjectWorkspacesPackageJson(
rootProject: Project, rootProject: Project,
npmProjects: Collection<KotlinCompilationNpmResolution>, npmProjects: Collection<KotlinCompilationNpmResolution>,
nodeJsWorldDir: File rootPackageJsonFile: File
) { ) {
val nodeJsWorldDir = rootPackageJsonFile.parentFile
val rootPackageJson = PackageJson(rootProject.name, rootProject.version.toString()) val rootPackageJson = PackageJson(rootProject.name, rootProject.version.toString())
rootPackageJson.private = true rootPackageJson.private = true
@@ -67,7 +78,7 @@ class YarnWorkspaces : YarnBasics() {
rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces
rootPackageJson.saveTo( rootPackageJson.saveTo(
nodeJsWorldDir.resolve(NpmProject.PACKAGE_JSON) rootPackageJsonFile
) )
} }
} }