[Gradle, JS] Add error state to know when operation was failed
This commit is contained in:
+31
@@ -1564,6 +1564,37 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DisplayName("smoothly fail npm install")
|
||||||
|
@GradleTest
|
||||||
|
fun testFailNpmInstall(gradleVersion: GradleVersion) {
|
||||||
|
project("kotlin-js-browser-project", gradleVersion) {
|
||||||
|
buildGradleKts.modify { originalScript ->
|
||||||
|
buildString {
|
||||||
|
append(originalScript)
|
||||||
|
append(
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
|
||||||
|
| val nodejs = the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>()
|
||||||
|
| tasks.named<org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask>("kotlinNpmInstall") {
|
||||||
|
| doFirst {
|
||||||
|
| nodejs.npmResolutionManager.state =
|
||||||
|
| org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager.ResolutionState.Error(GradleException("someSpecialException"))
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildAndFail("build") {
|
||||||
|
assertTasksFailed(":kotlinNpmInstall")
|
||||||
|
assertOutputContains("someSpecialException")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsGradlePluginTests
|
@JsGradlePluginTests
|
||||||
|
|||||||
+4
-2
@@ -10,6 +10,7 @@ import org.gradle.api.tasks.*
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
|
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
|
||||||
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.KotlinNpmResolutionManager
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
|
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.npm.npmProject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -53,7 +54,8 @@ abstract class DukatTask(
|
|||||||
get() = resolutionManager.requireInstalled(
|
get() = resolutionManager.requireInstalled(
|
||||||
services,
|
services,
|
||||||
logger
|
logger
|
||||||
)[projectPath][compilationName]
|
)?.get(projectPath)?.get(compilationName)
|
||||||
|
?: throw (resolutionManager.state as KotlinNpmResolutionManager.ResolutionState.Error).wrappedException
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
val dts: List<DtsResolver.Dts>
|
val dts: List<DtsResolver.Dts>
|
||||||
@@ -97,7 +99,7 @@ abstract class DukatTask(
|
|||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
open fun run() {
|
open fun run() {
|
||||||
resolutionManager.checkRequiredDependencies(this, services, logger, projectPath)
|
resolutionManager.checkRequiredDependencies(this)
|
||||||
|
|
||||||
destinationDir.deleteRecursively()
|
destinationDir.deleteRecursively()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -137,7 +137,7 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat
|
|||||||
|
|
||||||
val versions = NpmVersions()
|
val versions = NpmVersions()
|
||||||
|
|
||||||
internal val npmResolutionManager = KotlinNpmResolutionManager(this)
|
val npmResolutionManager = KotlinNpmResolutionManager(this)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTENSION_NAME: String = "kotlinNodeJs"
|
const val EXTENSION_NAME: String = "kotlinNodeJs"
|
||||||
|
|||||||
+33
-16
@@ -86,7 +86,7 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
(nodeJsSettings ?: unavailableValueError("nodeJsSettings")).rootProject.isInIdeaSync
|
(nodeJsSettings ?: unavailableValueError("nodeJsSettings")).rootProject.isInIdeaSync
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val resolver = KotlinRootNpmResolver(nodeJsSettings, forceFullResolve)
|
val resolver = KotlinRootNpmResolver(nodeJsSettings, forceFullResolve)
|
||||||
|
|
||||||
internal abstract class KotlinNpmResolutionManagerStateHolder : BuildService<BuildServiceParameters.None> {
|
internal abstract class KotlinNpmResolutionManagerStateHolder : BuildService<BuildServiceParameters.None> {
|
||||||
@Volatile
|
@Volatile
|
||||||
@@ -101,13 +101,13 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
|
|
||||||
private val stateHolder get() = stateHolderProvider.get()
|
private val stateHolder get() = stateHolderProvider.get()
|
||||||
|
|
||||||
private var state: ResolutionState
|
var state: ResolutionState
|
||||||
get() = stateHolder.state ?: ResolutionState.Configuring(resolver)
|
get() = stateHolder.state ?: ResolutionState.Configuring(resolver)
|
||||||
set(value) {
|
set(value) {
|
||||||
stateHolder.state = value
|
stateHolder.state = value
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ResolutionState {
|
sealed class ResolutionState {
|
||||||
abstract val npmProjects: List<NpmProject>
|
abstract val npmProjects: List<NpmProject>
|
||||||
|
|
||||||
class Configuring(val resolver: KotlinRootNpmResolver) : ResolutionState() {
|
class Configuring(val resolver: KotlinRootNpmResolver) : ResolutionState() {
|
||||||
@@ -120,11 +120,16 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
get() = npmProjectsByProjectResolutions(preparedInstallation.projectResolutions)
|
get() = npmProjectsByProjectResolutions(preparedInstallation.projectResolutions)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Installed(val resolved: KotlinRootNpmResolution) : ResolutionState() {
|
class Installed internal constructor(internal val resolved: KotlinRootNpmResolution) : ResolutionState() {
|
||||||
override val npmProjects: List<NpmProject>
|
override val npmProjects: List<NpmProject>
|
||||||
get() = npmProjectsByProjectResolutions(resolved.projects)
|
get() = npmProjectsByProjectResolutions(resolved.projects)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Error(val wrappedException: Throwable) : ResolutionState() {
|
||||||
|
override val npmProjects: List<NpmProject>
|
||||||
|
get() = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun npmProjectsByProjectResolutions(
|
fun npmProjectsByProjectResolutions(
|
||||||
resolutions: Map<String, KotlinProjectNpmResolution>
|
resolutions: Map<String, KotlinProjectNpmResolution>
|
||||||
@@ -156,23 +161,30 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
args: List<String> = emptyList(),
|
args: List<String> = emptyList(),
|
||||||
services: ServiceRegistry,
|
services: ServiceRegistry,
|
||||||
logger: Logger
|
logger: Logger
|
||||||
): KotlinRootNpmResolution {
|
): KotlinRootNpmResolution? {
|
||||||
synchronized(stateHolder) {
|
synchronized(stateHolder) {
|
||||||
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 ?: false
|
if (state is ResolutionState.Error) {
|
||||||
val forceUpToDate = installUpToDate && !forceFullResolve
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val installation = prepareIfNeeded(requireUpToDateReason = reason, logger = logger)
|
return try {
|
||||||
val resolution = installation
|
val installUpToDate = nodeJsSettings?.npmInstallTaskProvider?.get()?.state?.upToDate ?: false
|
||||||
.install(forceUpToDate, args, services, logger)
|
val forceUpToDate = installUpToDate && !forceFullResolve
|
||||||
state = ResolutionState.Installed(resolution)
|
|
||||||
|
|
||||||
installation.closePlugins(resolution)
|
val installation = prepareIfNeeded(requireUpToDateReason = reason, logger = logger)
|
||||||
|
val resolution = installation
|
||||||
return resolution
|
.install(forceUpToDate, args, services, logger)
|
||||||
|
state = ResolutionState.Installed(resolution)
|
||||||
|
installation.closePlugins(resolution)
|
||||||
|
resolution
|
||||||
|
} catch (e: Exception) {
|
||||||
|
state = ResolutionState.Error(e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,10 +227,12 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ResolutionState.Installed -> error("Project already installed")
|
is ResolutionState.Installed -> error("Project already installed")
|
||||||
|
is ResolutionState.Error -> throw state1.wrappedException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ResolutionState.Installed -> error("Project already installed")
|
is ResolutionState.Installed -> error("Project already installed")
|
||||||
|
is ResolutionState.Error -> throw state0.wrappedException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +244,7 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
|
|
||||||
val resolvedProject =
|
val resolvedProject =
|
||||||
if (forceFullResolve) {
|
if (forceFullResolve) {
|
||||||
installIfNeeded(reason = null, services = services, logger = logger)[projectPath]
|
installIfNeeded(reason = null, services = services, logger = logger)?.get(projectPath) ?: return null
|
||||||
} 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
|
||||||
@@ -243,13 +257,16 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo
|
|||||||
//error("Cannot use NpmDependency before :kotlinNpmInstall task execution")
|
//error("Cannot use NpmDependency before :kotlinNpmInstall task execution")
|
||||||
}
|
}
|
||||||
is ResolutionState.Installed -> state0.resolved[projectPath]
|
is ResolutionState.Installed -> state0.resolved[projectPath]
|
||||||
|
is ResolutionState.Error -> {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolvedProject.npmProjectsByNpmDependency[npmDependency] ?: error("NPM project resolved without $this")
|
return resolvedProject.npmProjectsByNpmDependency[npmDependency] ?: error("NPM project resolved without $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T> checkRequiredDependencies(task: T, services: ServiceRegistry, logger: Logger, projectPath: String)
|
internal fun <T> checkRequiredDependencies(task: T)
|
||||||
where T : RequiresNpmDependencies,
|
where T : RequiresNpmDependencies,
|
||||||
T : Task {
|
T : Task {
|
||||||
val targetRequired = resolver.taskRequirements.byTask[task.path]?.toSet() ?: setOf()
|
val targetRequired = resolver.taskRequirements.byTask[task.path]?.toSet() ?: setOf()
|
||||||
|
|||||||
+11
-7
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.gradle.utils.unavailableValueError
|
|||||||
* Regularly tasks share the same instance of this class, but with configuration cache each task that holds a reference to the instance will
|
* Regularly tasks share the same instance of this class, but with configuration cache each task that holds a reference to the instance will
|
||||||
* create an own copy. We use build services as a single storage for the heavy state of this class.
|
* create an own copy. We use build services as a single storage for the heavy state of this class.
|
||||||
*/
|
*/
|
||||||
internal class KotlinRootNpmResolver internal constructor(
|
class KotlinRootNpmResolver internal constructor(
|
||||||
@Transient
|
@Transient
|
||||||
val nodeJs: NodeJsRootExtension?,
|
val nodeJs: NodeJsRootExtension?,
|
||||||
val forceFullResolve: Boolean
|
val forceFullResolve: Boolean
|
||||||
@@ -62,7 +62,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
@Volatile
|
@Volatile
|
||||||
private var state_: RootResolverState? = RootResolverState.CONFIGURING
|
private var state_: RootResolverState? = RootResolverState.CONFIGURING
|
||||||
|
|
||||||
private var state
|
var state
|
||||||
get() = state_ ?: resolverStateHolder.get().state
|
get() = state_ ?: resolverStateHolder.get().state
|
||||||
set(value) {
|
set(value) {
|
||||||
if (state_ != null) {
|
if (state_ != null) {
|
||||||
@@ -82,7 +82,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
it.parameters.rootProjectDir.set(rootProject_.projectDir)
|
it.parameters.rootProjectDir.set(rootProject_.projectDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
val gradleNodeModules: GradleNodeModulesCache
|
internal val gradleNodeModules: GradleNodeModulesCache
|
||||||
get() = gradleNodeModulesProvider.get().also {
|
get() = gradleNodeModulesProvider.get().also {
|
||||||
it.archiveOperations = archiveOperations
|
it.archiveOperations = archiveOperations
|
||||||
it.fs = fs
|
it.fs = fs
|
||||||
@@ -95,7 +95,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
it.parameters.rootProjectDir.set(rootProject_.projectDir)
|
it.parameters.rootProjectDir.set(rootProject_.projectDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
val compositeNodeModules: CompositeNodeModulesCache
|
internal val compositeNodeModules: CompositeNodeModulesCache
|
||||||
get() = compositeNodeModulesProvider.get()
|
get() = compositeNodeModulesProvider.get()
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
@@ -157,7 +157,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
return projResolvers
|
return projResolvers
|
||||||
}
|
}
|
||||||
|
|
||||||
val plugins
|
internal val plugins
|
||||||
get() = plugins_ ?: resolverStateHolder.get().parameters.plugins.get()
|
get() = plugins_ ?: resolverStateHolder.get().parameters.plugins.get()
|
||||||
|
|
||||||
private val projectResolvers
|
private val projectResolvers
|
||||||
@@ -190,7 +190,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(projectPath: String) = projectResolvers[projectPath] ?: error("$projectPath is not configured for JS usage")
|
internal operator fun get(projectPath: String) = projectResolvers[projectPath] ?: error("$projectPath is not configured for JS usage")
|
||||||
|
|
||||||
val compilations: Collection<KotlinJsCompilation>
|
val compilations: Collection<KotlinJsCompilation>
|
||||||
get() = projectResolvers.values.flatMap { it.compilationResolvers.map { it.compilation } }
|
get() = projectResolvers.values.flatMap { it.compilationResolvers.map { it.compilation } }
|
||||||
@@ -198,7 +198,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
internal fun getPackageJsonHandlers(projectPath: String, compilationDisambiguatedName: String): List<PackageJson.() -> Unit> =
|
internal fun getPackageJsonHandlers(projectPath: String, compilationDisambiguatedName: String): List<PackageJson.() -> Unit> =
|
||||||
resolverStateHolder.get().parameters.packageJsonHandlers.get()["$projectPath:$compilationDisambiguatedName"] ?: emptyList()
|
resolverStateHolder.get().parameters.packageJsonHandlers.get()["$projectPath:$compilationDisambiguatedName"] ?: emptyList()
|
||||||
|
|
||||||
fun findDependentResolver(src: Project, target: Project): List<KotlinCompilationNpmResolver>? {
|
internal fun findDependentResolver(src: Project, target: Project): List<KotlinCompilationNpmResolver>? {
|
||||||
// todo: proper finding using KotlinTargetComponent.findUsageContext
|
// todo: proper finding using KotlinTargetComponent.findUsageContext
|
||||||
val targetResolver = this[target.path]
|
val targetResolver = this[target.path]
|
||||||
val mainCompilations = targetResolver.compilationResolvers.filter { it.compilation.isMain() }
|
val mainCompilations = targetResolver.compilationResolvers.filter { it.compilation.isMain() }
|
||||||
@@ -224,6 +224,7 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
containsIrJs = true
|
containsIrJs = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
check(!containsLegacyJs) { errorMessage }
|
check(!containsLegacyJs) { errorMessage }
|
||||||
containsLegacyJs = true
|
containsLegacyJs = true
|
||||||
@@ -279,6 +280,9 @@ internal class KotlinRootNpmResolver internal constructor(
|
|||||||
logger: Logger
|
logger: Logger
|
||||||
): KotlinRootNpmResolution {
|
): KotlinRootNpmResolution {
|
||||||
synchronized(projectResolvers) {
|
synchronized(projectResolvers) {
|
||||||
|
if (state == RootResolverState.INSTALLED) {
|
||||||
|
return KotlinRootNpmResolution(rootProject, projectResolutions)
|
||||||
|
}
|
||||||
check(state == RootResolverState.PROJECTS_CLOSED) {
|
check(state == RootResolverState.PROJECTS_CLOSED) {
|
||||||
"Projects must be closed"
|
"Projects must be closed"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -9,6 +9,7 @@ import org.gradle.api.DefaultTask
|
|||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
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.nodejs.NodeJsRootPlugin
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asNpmEnvironment
|
import org.jetbrains.kotlin.gradle.targets.js.npm.asNpmEnvironment
|
||||||
import org.jetbrains.kotlin.gradle.utils.unavailableValueError
|
import org.jetbrains.kotlin.gradle.utils.unavailableValueError
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -70,7 +71,7 @@ open class KotlinNpmInstallTask : DefaultTask() {
|
|||||||
args = args,
|
args = args,
|
||||||
services = services,
|
services = services,
|
||||||
logger = logger
|
logger = logger
|
||||||
)
|
) ?: throw (resolutionManager.state as KotlinNpmResolutionManager.ResolutionState.Error).wrappedException
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
+1
-1
@@ -157,7 +157,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun executeTests() {
|
override fun executeTests() {
|
||||||
npmResolutionManager.checkRequiredDependencies(task = this, services = services, logger = logger, projectPath = projectPath)
|
npmResolutionManager.checkRequiredDependencies(task = this)
|
||||||
super.executeTests()
|
super.executeTests()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -297,7 +297,7 @@ constructor(
|
|||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun doExecute() {
|
fun doExecute() {
|
||||||
resolutionManager.checkRequiredDependencies(task = this, services = services, logger = logger, projectPath = projectPath)
|
resolutionManager.checkRequiredDependencies(task = this)
|
||||||
|
|
||||||
val runner = createRunner()
|
val runner = createRunner()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user