[Gradle, JS] Fix composite build to not depend package json task on full aggregated configuration

^KT-57604 fixed
This commit is contained in:
Ilya Goncharov
2023-03-28 13:37:23 +00:00
committed by Space Team
parent 1f0d56e157
commit 4fabe8b29a
15 changed files with 228 additions and 19 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.YA
import org.jetbrains.kotlin.gradle.tasks.USING_JS_INCREMENTAL_COMPILATION_MESSAGE
import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.testbase.TestVersions.Gradle.G_7_6
import org.jetbrains.kotlin.gradle.util.jsCompilerType
import org.jetbrains.kotlin.gradle.util.normalizePath
import org.junit.jupiter.api.DisplayName
@@ -81,6 +82,7 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
@DisplayName("js composite build works")
@GradleTest
@GradleTestVersions(minVersion = G_7_6)
fun testJsCompositeBuild(gradleVersion: GradleVersion) {
project("js-composite-build", gradleVersion) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
@@ -470,6 +472,18 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
assertEquals(fingerprints[0], fingerprints[1], "fingerprints must be stable")
}
}
@DisplayName("Klib runtime dependency on module which already depends on dependent")
@GradleTest
fun testKlibRuntimeDependency(gradleVersion: GradleVersion) {
project("kotlin-js-ir-runtime-dependency", gradleVersion) {
build("assemble") {
assertTasksExecuted(":lib:otherKlib")
assertTasksExecuted(":lib:compileOtherKotlinJs")
assertTasksExecuted(":app:compileProductionExecutableKotlinJs")
}
}
}
}
@JsGradlePluginTests
@@ -0,0 +1,19 @@
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
import org.jetbrains.kotlin.gradle.targets.js.ir.JsIrBinary
plugins {
kotlin("js")
}
dependencies {
implementation(project(":lib"))
testImplementation(kotlin("test-js"))
}
kotlin {
js {
browser {
}
binaries.executable()
}
}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2019 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 com.example
fun main() {
println("Sheldon: ${sheldon()}")
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2023 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.
*/
import kotlin.test.Test
import kotlin.test.assertEquals
class MainTest {
@Test
fun testHello() {
assertEquals(true, true)
}
}
@@ -0,0 +1,13 @@
plugins {
kotlin("js").apply(false)
}
group = "com.example"
version = "1.0"
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
@@ -0,0 +1,32 @@
plugins {
kotlin("js")
}
kotlin {
js {
val otherCompilation = compilations.create("other")
tasks.register<Zip>("otherKlib") {
from(otherCompilation.output.allOutputs)
archiveExtension.set("klib")
}
useCommonJs()
browser {
}
}
sourceSets {
val main by getting {
kotlin.exclude("**/other/**")
dependencies {
runtimeOnly(files(tasks.named("otherKlib")))
}
}
val other by getting {
kotlin.srcDirs("src/main/kotlin/other")
dependencies {
implementation(project(path = project.path))
}
}
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2019 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 com.example
fun answer(): Int {
return 42
}
fun sheldon(): Int {
return 73
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2019 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 com.example.other
@JsExport
fun foo(): Int {
return 3
}
@@ -0,0 +1,4 @@
rootProject.name = "kotlin-js-ir-runtime-dependency"
include("lib")
include("app")
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.api.Named
import org.gradle.api.attributes.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import java.io.Serializable
// For Gradle attributes
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationFriendPathsResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsIrCompilationSourceSetsContainerFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsKotlinCompilationDependencyConfigurationsFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
@@ -20,6 +21,11 @@ class KotlinJsIrCompilationFactory internal constructor(
private val compilationImplFactory: KotlinCompilationImplFactory = KotlinCompilationImplFactory(
compilerOptionsFactory = KotlinJsCompilerOptionsFactory,
compilationFriendPathsResolver = DefaultKotlinCompilationFriendPathsResolver(
friendArtifactResolver = DefaultKotlinCompilationFriendPathsResolver.FriendArtifactResolver { _ ->
target.project.files()
}
),
compilationSourceSetsContainerFactory = JsIrCompilationSourceSetsContainerFactory,
compilationDependencyConfigurationsFactory = JsKotlinCompilationDependencyConfigurationsFactory
)
@@ -34,6 +34,9 @@ internal class GradleNodeModuleBuilder(
srcFiles.forEach { srcFile ->
when {
isKotlinJsRuntimeFile(srcFile) -> files.add(srcFile)
srcFile.name == NpmProject.PACKAGE_JSON -> {
srcPackageJsonFile = srcFile
}
srcFile.isCompatibleArchive -> {
archiveOperations.zipTree(srcFile).forEach { innerFile ->
when {
@@ -8,13 +8,13 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolver
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements
import org.jetbrains.kotlin.gradle.targets.js.npm.*
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution
import java.io.Serializable
import java.io.File
class KotlinCompilationNpmResolution(
var internalDependencies: Collection<InternalDependency>,
var internalCompositeDependencies: Collection<CompositeDependency>,
var externalGradleDependencies: Collection<FileExternalGradleDependency>,
var externalNpmDependencies: Collection<NpmDependencyDeclaration>,
var fileCollectionDependencies: Collection<FileCollectionExternalGradleDependency>,
@@ -171,13 +171,4 @@ class KotlinCompilationNpmResolution(
}
return direct + unique
}
internal fun CompositeDependency.getPackages(): List<File> {
val packages = includedBuildDir.resolve(projectPackagesDir.relativeTo(rootDir))
return packages
.list()
?.map { packages.resolve(it) }
?.map { it.resolve(PACKAGE_JSON) }
?: emptyList()
}
}
@@ -10,6 +10,7 @@ import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage
import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
@@ -78,6 +79,9 @@ class KotlinCompilationNpmResolver(
it.npmProjectName.set(npmProject.name)
it.npmProjectMain.set(npmProject.main)
}.also { packageJsonTask ->
project.dependencies.attributesSchema {
it.attribute(publicPackageJsonAttribute)
}
if (compilation.isMain()) {
project.tasks
.withType(Zip::class.java)
@@ -85,7 +89,15 @@ class KotlinCompilationNpmResolver(
.configure {
it.dependsOn(packageJsonTask)
}
val publicPackageJsonConfiguration = createPublicPackageJsonConfiguration()
target.project.artifacts.add(publicPackageJsonConfiguration.name, packageJsonTask.map { it.packageJsonFile }) {
it.builtBy(packageJsonTask)
}
}
}
}
@@ -119,6 +131,7 @@ class KotlinCompilationNpmResolver(
all.usesPlatformOf(target)
all.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(target))
all.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
all.attributes.attribute(publicPackageJsonAttribute, PUBLIC_PACKAGE_JSON_ATTR_VALUE)
all.isVisible = false
all.isCanBeConsumed = false
all.isCanBeResolved = true
@@ -142,8 +155,23 @@ class KotlinCompilationNpmResolver(
return all
}
private fun createPublicPackageJsonConfiguration(): Configuration {
val all = project.configurations.create(compilation.disambiguateName("publicPackageJsonConfiguration"))
all.usesPlatformOf(target)
all.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(target))
all.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
all.attributes.attribute(publicPackageJsonAttribute, PUBLIC_PACKAGE_JSON_ATTR_VALUE)
all.isVisible = false
all.isCanBeConsumed = true
all.isCanBeResolved = false
return all
}
inner class ConfigurationVisitor {
private val internalDependencies = mutableSetOf<InternalDependency>()
private val internalCompositeDependencies = mutableSetOf<CompositeDependency>()
private val externalGradleDependencies = mutableSetOf<ExternalGradleDependency>()
private val externalNpmDependencies = mutableSetOf<NpmDependencyDeclaration>()
private val fileCollectionDependencies = mutableSetOf<FileCollectionExternalGradleDependency>()
@@ -215,6 +243,10 @@ class KotlinCompilationNpmResolver(
val artifactId = artifact.id
val componentIdentifier = artifactId.componentIdentifier
if (artifactId `is` CompositeProjectComponentArtifactMetadata) {
visitCompositeProjectDependency(dependency, componentIdentifier as ProjectComponentIdentifier)
}
if (componentIdentifier is ProjectComponentIdentifier && !(artifactId `is` CompositeProjectComponentArtifactMetadata)) {
visitProjectDependency(componentIdentifier)
return
@@ -223,6 +255,28 @@ class KotlinCompilationNpmResolver(
externalGradleDependencies.add(ExternalGradleDependency(dependency, artifact))
}
private fun visitCompositeProjectDependency(
dependency: ResolvedDependency,
componentIdentifier: ProjectComponentIdentifier
) {
check(target is KotlinJsIrTarget) {
"""
Composite builds for Kotlin/JS are supported only for IR compiler.
Use kotlin.js.compiler=ir in gradle.properties or
js(IR) {
...
}
""".trimIndent()
}
(componentIdentifier as DefaultProjectComponentIdentifier).let { identifier ->
val includedBuild = project.gradle.includedBuild(identifier.identityPath.topRealPath().name!!)
internalCompositeDependencies.add(
CompositeDependency(dependency.moduleName, dependency.moduleVersion, includedBuild.projectDir, includedBuild)
)
}
}
private fun visitProjectDependency(
componentIdentifier: ProjectComponentIdentifier
) {
@@ -243,6 +297,7 @@ class KotlinCompilationNpmResolver(
fun toPackageJsonProducer() = KotlinCompilationNpmResolution(
internalDependencies,
internalCompositeDependencies,
externalGradleDependencies.map {
FileExternalGradleDependency(
it.dependency.moduleName,
@@ -264,4 +319,13 @@ class KotlinCompilationNpmResolver(
rootResolver.tasksRequirements
)
}
}
companion object {
val publicPackageJsonAttribute = Attribute.of(
"org.jetbrains.kotlin.js.public.package.json",
String::class.java
)
const val PUBLIC_PACKAGE_JSON_ATTR_VALUE = "public-package-json"
}
}
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.npm.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.work.NormalizeLineEndings
@@ -16,10 +16,10 @@ import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNpmResolutionManager
import org.jetbrains.kotlin.gradle.targets.js.npm.*
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.PackageJsonProducerInputs
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.*
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata
import org.jetbrains.kotlin.gradle.utils.`is`
import java.io.File
abstract class KotlinPackageJsonTask :
@@ -41,6 +41,9 @@ abstract class KotlinPackageJsonTask :
private fun findDependentTasks(): Collection<Any> =
compilationResolver.compilationNpmResolution.internalDependencies.map { dependency ->
nodeJs.resolver[dependency.projectPath][dependency.compilationName].npmProject.packageJsonTaskPath
} + compilationResolver.compilationNpmResolution.internalCompositeDependencies.map { dependency ->
dependency.includedBuild?.task(":$PACKAGE_JSON_UMBRELLA_TASK_NAME") ?: error("includedBuild instance is not available")
dependency.includedBuild.task(":${RootPackageJsonTask.NAME}")
}
// -----
@@ -75,8 +78,21 @@ abstract class KotlinPackageJsonTask :
@get:NormalizeLineEndings
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
internal val aggregatedConfiguration: FileCollection by lazy {
compilationResolver.aggregatedConfiguration
internal val compositeFiles: Set<File> by lazy {
val map = compilationResolver.aggregatedConfiguration
.incoming
.artifactView { artifactView ->
artifactView.componentFilter { componentIdentifier ->
componentIdentifier is ProjectComponentIdentifier
}
}
.artifacts
.filter {
it.id `is` CompositeProjectComponentArtifactMetadata
}
.map { it.file }
.toSet()
map
}
// nested inputs are processed in configuration phase