[Gradle, JS] Fix K/JS project build with configuration cache (part 3)

Now configuration cache enabled builds with JS tasks does not fails but prints warning and skips execution on subsequent builds.
Relates to KT-42494
This commit is contained in:
Alexander Likhachev
2020-10-20 21:57:44 +03:00
parent 97d39c102a
commit ac7b7abcc0
14 changed files with 112 additions and 42 deletions
@@ -140,8 +140,8 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
project.locateOrRegisterTask<ProcessResources>(compilation.processResourcesTaskName) { resourcesTask ->
resourcesTask.description = "Processes $resourceSet."
DslObject(resourcesTask).conventionMapping.map("destinationDir") { project.file(compilation.output.resourcesDir) }
resourcesTask.from(resourceSet)
resourcesTask.into(project.file(compilation.output.resourcesDir))
}
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
import org.jetbrains.kotlin.gradle.utils.disableTaskOnConfigurationCacheBuild
import java.io.File
abstract class DukatTask(
@@ -21,6 +22,11 @@ abstract class DukatTask(
@get:Internal
protected val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
init {
// TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { nodeJs.npmResolutionManager.toString() }
}
@get:Internal
override val nodeModulesRequired: Boolean
get() = true
@@ -21,8 +21,9 @@ constructor(
override val considerGeneratingFlag: Boolean = true
@get:OutputDirectory
override val destinationDir: File
get() = compilation.npmProject.externalsDir
override val destinationDir: File by lazy {
compilation.npmProject.externalsDir
}
@delegate:Transient
private val executor by lazy {
@@ -66,8 +66,9 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat
val rootPackageJsonTaskProvider: TaskProvider<RootPackageJsonTask>
get() = rootProject.tasks.withType(RootPackageJsonTask::class.java).named(RootPackageJsonTask.NAME)
val rootPackageDir: File
get() = rootProject.buildDir.resolve("js")
val rootPackageDir: File by lazy {
rootProject.buildDir.resolve("js")
}
internal val rootNodeModulesStateFile: File
get() = rootPackageDir.resolve("node_modules.state")
@@ -13,7 +13,7 @@ import java.net.URI
@CacheableTask
open class NodeJsSetupTask : DefaultTask() {
private val settings get() = NodeJsRootPlugin.apply(project.rootProject)
private val settings = NodeJsRootPlugin.apply(project.rootProject)
private val env by lazy { settings.requireConfigured() }
val ivyDependency: String
@@ -16,12 +16,12 @@ import org.gradle.api.internal.artifacts.dependencies.SelfResolvingDependencyInt
import org.gradle.api.tasks.TaskDependency
import org.gradle.internal.component.local.model.DefaultLibraryBinaryIdentifier
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency.Scope.DEV
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
import java.io.File
data class NpmDependency(
@Transient
internal val project: Project,
private val name: String,
private val version: String,
@@ -24,7 +24,7 @@ val KotlinJsCompilation.npmProject: NpmProject
*
* More info can be obtained from [KotlinCompilationNpmResolution], which is available after project resolution (after [KotlinNpmInstallTask] execution).
*/
open class NpmProject(val compilation: KotlinJsCompilation) {
open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
val name: String
get() = buildNpmProjectName()
@@ -13,6 +13,7 @@ import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON
import org.jetbrains.kotlin.gradle.utils.disableTaskOnConfigurationCacheBuild
import org.jetbrains.kotlin.gradle.utils.property
import java.io.File
import javax.inject.Inject
@@ -23,13 +24,17 @@ constructor(
@Transient
private val compilation: KotlinJsCompilation
) : DefaultTask() {
@Transient
private val npmProject = compilation.npmProject
private val nodeJs = npmProject.nodeJs
private val compilationResolution
get() = nodeJs.npmResolutionManager.requireInstalled()[project][npmProject.compilation]
init {
// TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { nodeJs.npmResolutionManager.toString() }
}
@get:Input
val packageJsonCustomFields: Map<String, Any?>
get() = PackageJson(fakePackageJsonValue, fakePackageJsonValue)
@@ -51,11 +56,13 @@ constructor(
private val realExternalDependencies: Collection<NpmDependency>
get() = compilationResolution.externalNpmDependencies
private val publicPackageJsonTaskName = npmProject.publicPackageJsonTaskName
@get:OutputFile
var packageJsonFile: File by property {
project.buildDir
.resolve("tmp")
.resolve(npmProject.publicPackageJsonTaskName)
.resolve(publicPackageJsonTaskName)
.resolve(PACKAGE_JSON)
}
@@ -11,6 +11,7 @@ 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.utils.disableTaskOnConfigurationCacheBuild
import java.io.File
open class KotlinNpmInstallTask : DefaultTask() {
@@ -27,17 +28,24 @@ open class KotlinNpmInstallTask : DefaultTask() {
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val resolutionManager get() = nodeJs.npmResolutionManager
init {
// TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { resolutionManager.toString() }
}
@Input
val args: MutableList<String> = mutableListOf()
@Suppress("unused")
@get:InputFiles
val packageJsonFiles: Collection<File>
get() = resolutionManager.packageJsonFiles
val packageJsonFiles: Collection<File> by lazy {
resolutionManager.packageJsonFiles
}
@get:InputFiles
val preparedFiles: Collection<File>
get() = nodeJs.packageManager.preparedFiles(project)
val preparedFiles: Collection<File> by lazy {
nodeJs.packageManager.preparedFiles(project)
}
// avoid using node_modules as output directory, as it is significantly slows down build
@get:OutputFile
@@ -33,11 +33,12 @@ open class KotlinPackageJsonTask : DefaultTask() {
get() = compilationResolver.packageJsonProducer
@get:Input
val packageJsonCustomFields: Map<String, Any?>
get() = PackageJson(fakePackageJsonValue, fakePackageJsonValue)
val packageJsonCustomFields: Map<String, Any?> by lazy {
PackageJson(fakePackageJsonValue, fakePackageJsonValue)
.apply {
compilation.packageJsonHandlers.forEach { it() }
}.customFields
}
private fun findDependentTasks(): Collection<Any> =
producer.internalDependencies.map { dependentResolver ->
@@ -47,18 +48,21 @@ open class KotlinPackageJsonTask : DefaultTask() {
}
@get:Input
internal val toolsNpmDependencies: List<String>
get() = nodeJs.taskRequirements
internal val toolsNpmDependencies: List<String> by lazy {
nodeJs.taskRequirements
.getCompilationNpmRequirements(compilation)
.map { it.uniqueRepresentation() }
}
@get:Nested
internal val producerInputs: KotlinCompilationNpmResolver.PackageJsonProducerInputs
get() = producer.inputs
internal val producerInputs: KotlinCompilationNpmResolver.PackageJsonProducerInputs by lazy {
producer.inputs
}
@get:OutputFile
val packageJson: File
get() = compilationResolver.npmProject.prePackageJsonFile
val packageJson: File by lazy {
compilationResolver.npmProject.prePackageJsonFile
}
@TaskAction
fun resolve() {
@@ -10,6 +10,7 @@ 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.npm.NpmProject
import org.jetbrains.kotlin.gradle.utils.disableTaskOnConfigurationCacheBuild
import java.io.File
open class RootPackageJsonTask : DefaultTask() {
@@ -24,6 +25,11 @@ open class RootPackageJsonTask : DefaultTask() {
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val resolutionManager get() = nodeJs.npmResolutionManager
init {
// TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { resolutionManager.toString() }
}
@get:OutputFile
val rootPackageJson: File
get() = nodeJs.rootPackageDir.resolve(NpmProject.PACKAGE_JSON)
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode
import org.jetbrains.kotlin.gradle.testing.internal.reportsDir
import org.jetbrains.kotlin.gradle.utils.disableTaskOnConfigurationCacheBuild
import org.jetbrains.kotlin.gradle.utils.injected
import org.jetbrains.kotlin.gradle.utils.newFileProperty
import org.jetbrains.kotlin.gradle.utils.property
@@ -41,6 +42,11 @@ constructor(
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val versions = nodeJs.versions
init {
// TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { nodeJs.npmResolutionManager.toString() }
}
@get:Inject
open val fileResolver: FileResolver
get() = injected
@@ -50,11 +56,13 @@ constructor(
get() = injected
@Suppress("unused")
val compilationId: String
@Input get() = compilation.let {
@get:Input
val compilationId: String by lazy {
compilation.let {
val target = it.target
target.project.path + "@" + target.name + ":" + it.compilationName
}
}
@Input
var mode: Mode = Mode.DEVELOPMENT
@@ -82,11 +90,15 @@ constructor(
internal var resolveFromModulesFirst: Boolean = false
@Suppress("unused")
val runtimeClasspath: FileCollection
@InputFiles get() = compilation.compileDependencyFiles
@get:InputFiles
val runtimeClasspath: FileCollection by lazy {
compilation.compileDependencyFiles
}
open val configFile: File
@OutputFile get() = compilation.npmProject.dir.resolve("webpack.config.js")
@get:OutputFile
open val configFile: File by lazy {
compilation.npmProject.dir.resolve("webpack.config.js")
}
@Input
var saveEvaluatedConfigFile: Boolean = true
@@ -134,12 +146,12 @@ constructor(
open val reportDir: File
@Internal get() = reportDirProvider.get()
open val reportDirProvider: Provider<File>
@OutputDirectory get() = entryProperty
.map { it.asFile.nameWithoutExtension }
.map {
project.reportsDir.resolve("webpack").resolve(it)
}
@OutputDirectory
open val reportDirProvider: Provider<File> = entryProperty
.map { it.asFile.nameWithoutExtension }
.map {
project.reportsDir.resolve("webpack").resolve(it)
}
open val evaluatedConfigFile: File
@Internal get() = evaluatedConfigFileProvider.get()
@@ -58,11 +58,3 @@ internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Compar
fun isGradleVersionAtLeast(major: Int, minor: Int) =
ParsedGradleVersion.parse(GradleVersion.current().version)
?.let { it >= ParsedGradleVersion(major, minor) } ?: false
fun isConfigurationCacheAvailable(gradle: Gradle) =
try {
val startParameters = gradle.startParameter
startParameters.javaClass.getMethod("isConfigurationCache").invoke(startParameters) as? Boolean
} catch (_: Exception) {
null
} ?: false
@@ -0,0 +1,33 @@
/*
* 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.utils
import org.gradle.api.Task
import org.gradle.api.invocation.Gradle
internal fun isConfigurationCacheAvailable(gradle: Gradle) =
try {
val startParameters = gradle.startParameter
startParameters.javaClass.getMethod("isConfigurationCache").invoke(startParameters) as? Boolean
} catch (_: Exception) {
null
} ?: false
internal fun Task.disableTaskOnConfigurationCacheBuild(transientFieldAccessor: () -> Unit) {
if (isConfigurationCacheAvailable(project.gradle)) {
onlyIf {
logger.warn("Configuration cache is not yet fully supported: use it at your own risk.")
try {
// transientFieldAccessor() will throw an exception after loading task from configuration cache
transientFieldAccessor()
true
} catch (e: Exception) {
logger.warn("Task cannot be executed because of corrupted state after loading from configuration cache.")
false
}
}
}
}