diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt new file mode 100644 index 00000000000..95b78234f0a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt @@ -0,0 +1,37 @@ +/* + * 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.internal + +import kotlin.reflect.KProperty + +abstract class ConfigurationPhaseAware { + + private var configured: C? = null + + @Synchronized + fun requireConfigured(): C { + if (configured == null) { + configured = finalizeConfiguration() + } + + return configured!! + } + + protected fun requireNotConfigured() { + check(configured == null) { "Configuration already finalized for previous property values" } + } + + inner class Property(var value: T) { + operator fun getValue(receiver: Any, property: KProperty<*>): T = value + + operator fun setValue(receiver: Any, property: KProperty<*>, value: T) { + requireNotConfigured() + this.value = value + } + } + + protected abstract fun finalizeConfiguration(): C +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt index f21d9d55621..04b521e96c1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt @@ -1,8 +1,10 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs +import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File -internal data class NodeJsEnv( +data class NodeJsEnv( + val cleanableStore: CleanableStore, val nodeDir: File, val nodeBinDir: File, val nodeExecutable: String, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt index e21c4ffc7b4..68d3c73362a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt @@ -72,7 +72,7 @@ open class NodeJsExec : AbstractExecTask(NodeJsExec::class.java), Re return project.registerTask(name) { it.nodeJs = nodeJs it.compilation = compilation - it.executable = nodeJs.environment.nodeExecutable + it.executable = nodeJs.requireConfigured().nodeExecutable it.dependsOn(nodeJs.npmInstallTask) val compileKotlinTask = compilation.compileKotlinTask diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt index 4d762ca67a2..43b8cdfb24e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.targets.js.NpmVersions @@ -11,7 +12,7 @@ import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File -open class NodeJsRootExtension(val rootProject: Project) { +open class NodeJsRootExtension(val rootProject: Project) : ConfigurationPhaseAware() { init { check(rootProject.rootProject == rootProject) } @@ -20,16 +21,16 @@ open class NodeJsRootExtension(val rootProject: Project) { rootProject.logger.kotlinInfo("Storing cached files in $it") } - var installationDir = gradleHome.resolve("nodejs") - var cleanableStore = CleanableStore[installationDir.absolutePath] + var installationDir by Property(gradleHome.resolve("nodejs")) - var download = true - var nodeDownloadBaseUrl = "https://nodejs.org/dist" - var nodeVersion = "12.14.0" + var download by Property(true) - var nodeCommand = "node" + var nodeDownloadBaseUrl by Property("https://nodejs.org/dist") + var nodeVersion by Property("12.14.0") - var packageManager: NpmApi = Yarn + var nodeCommand by Property("node") + + var packageManager: NpmApi by Property(Yarn) private val projectProperties = PropertiesProvider(rootProject) @@ -61,38 +62,39 @@ open class NodeJsRootExtension(val rootProject: Project) { val nodeModulesGradleCacheDir: File get() = rootPackageDir.resolve("packages_imported") - internal val environment: NodeJsEnv - get() { - val platform = NodeJsPlatform.name - val architecture = NodeJsPlatform.architecture + override fun finalizeConfiguration(): NodeJsEnv { + val platform = NodeJsPlatform.name + val architecture = NodeJsPlatform.architecture - val nodeDirName = "node-v$nodeVersion-$platform-$architecture" - val nodeDir = cleanableStore[nodeDirName].use() - val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN - val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin") + val nodeDirName = "node-v$nodeVersion-$platform-$architecture" + val cleanableStore = CleanableStore[installationDir.absolutePath] + val nodeDir = cleanableStore[nodeDirName].use() + val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN + val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin") - fun getExecutable(command: String, customCommand: String, windowsExtension: String): String { - val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand - return if (download) File(nodeBinDir, finalCommand).absolutePath else finalCommand - } - - fun getIvyDependency(): String { - val type = if (isWindows) "zip" else "tar.gz" - return "org.nodejs:node:$nodeVersion:$platform-$architecture@$type" - } - - return NodeJsEnv( - nodeDir = nodeDir, - nodeBinDir = nodeBinDir, - nodeExecutable = getExecutable("node", nodeCommand, "exe"), - platformName = platform, - architectureName = architecture, - ivyDependency = getIvyDependency() - ) + fun getExecutable(command: String, customCommand: String, windowsExtension: String): String { + val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand + return if (download) File(nodeBinDir, finalCommand).absolutePath else finalCommand } + fun getIvyDependency(): String { + val type = if (isWindows) "zip" else "tar.gz" + return "org.nodejs:node:$nodeVersion:$platform-$architecture@$type" + } + + return NodeJsEnv( + cleanableStore = cleanableStore, + nodeDir = nodeDir, + nodeBinDir = nodeBinDir, + nodeExecutable = getExecutable("node", nodeCommand, "exe"), + platformName = platform, + architectureName = architecture, + ivyDependency = getIvyDependency() + ) + } + internal fun executeSetup() { - val nodeJsEnv = environment + val nodeJsEnv = requireConfigured() if (download) { if (!nodeJsEnv.nodeBinDir.isDirectory) { nodeJsSetupTask.exec() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt index 9619478fa9e..a9651e3017d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt @@ -22,14 +22,14 @@ open class NodeJsRootPlugin : Plugin { it.description = "Download and install a local node/npm version" } - tasks.create(KotlinNpmInstallTask.NAME, KotlinNpmInstallTask::class.java) { + tasks.register(KotlinNpmInstallTask.NAME, KotlinNpmInstallTask::class.java) { it.dependsOn(setupTask) it.group = TASKS_GROUP_NAME it.description = "Find, download and link NPM dependencies and projects" } - tasks.create("node" + CleanDataTask.NAME, CleanDataTask::class.java) { - it.cleanableStore = settings.cleanableStore + tasks.register("node" + CleanDataTask.NAME_SUFFIX, CleanDataTask::class.java) { + it.cleanableStore = settings.requireConfigured().cleanableStore it.group = TASKS_GROUP_NAME it.description = "Clean unused local node version" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt index 16bf3ab0d9a..061d6c5b4f9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt @@ -15,7 +15,7 @@ import java.net.URI @CacheableTask open class NodeJsSetupTask : DefaultTask() { private val settings get() = NodeJsRootPlugin.apply(project.rootProject) - private val env by lazy { settings.environment } + private val env by lazy { settings.requireConfigured() } val ivyDependency: String @Input get() = env.ivyDependency diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt index 95450898369..a2c22978c0f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt @@ -70,7 +70,7 @@ open class NpmProject(val compilation: KotlinJsCompilation) { fun useTool(exec: ExecSpec, tool: String, vararg args: String) { exec.workingDir = dir - exec.executable = nodeJs.environment.nodeExecutable + exec.executable = nodeJs.requireConfigured().nodeExecutable exec.args = listOf(require(tool)) + args } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt index 527ef92480e..91af08a9abf 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt @@ -110,7 +110,7 @@ open class KotlinJsTest : KotlinTest(), RequiresNpmDependencies { override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec { val forkOptions = DefaultProcessForkOptions(fileResolver) forkOptions.workingDir = compilation.npmProject.dir - forkOptions.executable = nodeJs.environment.nodeExecutable + forkOptions.executable = nodeJs.requireConfigured().nodeExecutable val nodeJsArgs = mutableListOf() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt index 3ca92fe9f22..4a39610f065 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt @@ -24,13 +24,11 @@ abstract class YarnBasics : NpmApi { vararg args: String ) { val nodeJs = NodeJsRootPlugin.apply(project) - val nodeJsEnv = nodeJs.environment val yarnPlugin = YarnPlugin.apply(project) - val yarnEnv = yarnPlugin.environment project.execWithProgress(description) { exec -> - exec.executable = nodeJsEnv.nodeExecutable - exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + + exec.executable = nodeJs.requireConfigured().nodeExecutable + exec.args = listOf(yarnPlugin.requireConfigured().home.resolve("bin/yarn.js").absolutePath) + args + if (project.logger.isDebugEnabled) "--verbose" else "" exec.workingDir = dir diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt index 991eddca7a4..e1f23b8f694 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt @@ -5,9 +5,11 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn +import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File -internal data class YarnEnv( +data class YarnEnv( val downloadUrl: String, + val cleanableStore: CleanableStore, val home: File ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt index 22029732370..9f2643a134a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt @@ -9,7 +9,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.tasks.CleanDataTask -import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore open class YarnPlugin : Plugin { override fun apply(project: Project): Unit = project.run { @@ -25,8 +24,8 @@ open class YarnPlugin : Plugin { it.dependsOn(nodeJs.nodeJsSetupTask) } - tasks.create("yarn" + CleanDataTask.NAME, CleanDataTask::class.java) { - it.cleanableStore = yarnRootExtension.cleanableStore + tasks.create("yarn" + CleanDataTask.NAME_SUFFIX, CleanDataTask::class.java) { + it.cleanableStore = yarnRootExtension.requireConfigured().cleanableStore it.description = "Clean unused local yarn version" } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt index 7eb054d5dc1..4dd89a128a4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt @@ -6,11 +6,12 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore -open class YarnRootExtension(val project: Project) { +open class YarnRootExtension(val project: Project) : ConfigurationPhaseAware() { init { check(project == project.rootProject) } @@ -19,32 +20,33 @@ open class YarnRootExtension(val project: Project) { project.logger.kotlinInfo("Storing cached files in $it") } - var installationDir = gradleHome.resolve("yarn") + var installationDir by Property(gradleHome.resolve("yarn")) - // TODO: Split configuration and execution phases to support changed installationDir - val cleanableStore = CleanableStore[installationDir.path] - - var downloadBaseUrl = "https://github.com/yarnpkg/yarn/releases/download" - var version = "1.21.1" + var downloadBaseUrl by Property("https://github.com/yarnpkg/yarn/releases/download") + var version by Property("1.21.1") val yarnSetupTask: YarnSetupTask get() = project.tasks.getByName(YarnSetupTask.NAME) as YarnSetupTask - var disableWorkspaces: Boolean = false + var disableWorkspaces: Boolean by Property(false) val useWorkspaces: Boolean get() = !disableWorkspaces - // TODO: Split configuration and execution phases to support changed settings - internal val environment = YarnEnv( - downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz", - home = cleanableStore["yarn-v$version"].use() - ) + override fun finalizeConfiguration(): YarnEnv { + val cleanableStore = CleanableStore[installationDir.path] + + return YarnEnv( + downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz", + cleanableStore = cleanableStore, + home = cleanableStore["yarn-v$version"].use() + ) + } internal fun executeSetup() { NodeJsRootPlugin.apply(project).executeSetup() - if (!environment.home.isDirectory) { + if (!finalizeConfiguration().home.isDirectory) { yarnSetupTask.setup() } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt index 066fbcabbfa..7591b391b46 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt @@ -17,7 +17,7 @@ import java.io.File @CacheableTask open class YarnSetupTask : DefaultTask() { private val settings = project.yarn - private val env by lazy { settings.environment } + private val env by lazy { settings.requireConfigured() } @Suppress("MemberVisibilityCanBePrivate") val downloadUrl diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTask.kt index 2a2304cc021..e5a53d3e0f9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTask.kt @@ -41,7 +41,7 @@ open class CleanDataTask : DefaultTask() { } companion object { - const val NAME: String = "KotlinClean" + const val NAME_SUFFIX: String = "KotlinClean" } } \ No newline at end of file