From 5b99cd6d753121d7f05ff6d3deddad1249a43306 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 13 Feb 2024 11:19:43 +0100 Subject: [PATCH] #1 --- .../js/npm/resolver/GradleDependencies.kt | 13 +- .../KotlinCompilationNpmResolution.kt | 370 ++++++++++++++++- .../resolver/KotlinCompilationNpmResolver.kt | 375 +++++++++--------- .../js/npm/tasks/KotlinPackageJsonTask.kt | 147 +++++-- 4 files changed, 675 insertions(+), 230 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt index 04f597f998e98..e64af60f54b18 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt @@ -5,19 +5,24 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolver -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.initialization.IncludedBuild +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Optional import java.io.File import java.io.Serializable data class ExternalGradleDependency( - val dependency: ResolvedDependency, - val artifact: ResolvedArtifact + val component: ModuleComponentIdentifier, + val artifact: File ) : Serializable data class FileCollectionExternalGradleDependency( + @InputFiles val files: Collection, + @Input + @Optional val dependencyVersion: String? ) : Serializable diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt index 9f206842721d5..540b6e70d2db2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt @@ -6,21 +6,41 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolver import org.gradle.api.Action +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.FileCollectionDependency +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.artifacts.component.ComponentIdentifier +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier import org.gradle.api.logging.Logger import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Provider +import org.gradle.api.tasks.bundling.Zip +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.isMain +import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation +import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget 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.resolved.KotlinRootNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata import org.jetbrains.kotlin.gradle.utils.getFile +import org.jetbrains.kotlin.gradle.utils.`is` +import org.jetbrains.kotlin.gradle.utils.topRealPath +import java.io.File import java.io.Serializable class KotlinCompilationNpmResolution( - var internalDependencies: Collection, - var internalCompositeDependencies: Collection, - var externalGradleDependencies: Collection, - var externalNpmDependencies: Collection, - var fileCollectionDependencies: Collection, +// var internalDependencies: Collection, +// var internalCompositeDependencies: Collection, +// var externalGradleDependencies: Collection, +// var externalNpmDependencies: Collection, +// var fileCollectionDependencies: Collection, val projectPath: String, val compilationDisambiguatedName: String, val npmProjectName: String, @@ -28,13 +48,13 @@ class KotlinCompilationNpmResolution( val tasksRequirements: TasksRequirements, ) : Serializable { - val inputs: PackageJsonProducerInputs - get() = PackageJsonProducerInputs( - internalDependencies.map { it.projectName }, - externalGradleDependencies.map { it.file }, - externalNpmDependencies.map { it.uniqueRepresentation() }, - fileCollectionDependencies.flatMap { it.files } - ) +// val inputs: PackageJsonProducerInputs +// get() = PackageJsonProducerInputs( +// internalDependencies.map { it.projectName }, +// externalGradleDependencies.map { it.file }, +// externalNpmDependencies.map { it.uniqueRepresentation() }, +// fileCollectionDependencies.flatMap { it.files } +// ) private var closed = false internal var resolution: PreparedKotlinCompilationNpmResolution? = null @@ -43,12 +63,18 @@ class KotlinCompilationNpmResolution( fun prepareWithDependencies( npmResolutionManager: KotlinNpmResolutionManager, logger: Logger, + resolvedConfiguration: Pair>, + npmDeps: Set, + fileDeps: Set ): PreparedKotlinCompilationNpmResolution { check(resolution == null) { "$this already resolved" } return createPreparedResolution( npmResolutionManager, - logger + logger, + resolvedConfiguration, + npmDeps, + fileDeps, ).also { resolution = it } @@ -58,11 +84,15 @@ class KotlinCompilationNpmResolution( fun getResolutionOrPrepare( npmResolutionManager: KotlinNpmResolutionManager, logger: Logger, + resolvedConfiguration: Pair>? = null, ): PreparedKotlinCompilationNpmResolution { return resolution ?: prepareWithDependencies( npmResolutionManager, - logger + logger, + resolvedConfiguration!!, + null!!, + null!! ) } @@ -79,10 +109,16 @@ class KotlinCompilationNpmResolution( fun createPreparedResolution( npmResolutionManager: KotlinNpmResolutionManager, logger: Logger, + resolvedConfiguration: Pair>, + npmDeps: Set, + fileDeps: Set, ): PreparedKotlinCompilationNpmResolution { val rootResolver = npmResolutionManager.parameters.resolution.get() - val internalNpmDependencies = internalDependencies + val visitor = ConfigurationVisitor(rootResolver) + visitor.visit(resolvedConfiguration.first to resolvedConfiguration.second) + + val internalNpmDependencies = visitor.internalDependencies .map { val compilationNpmResolution: KotlinCompilationNpmResolution = rootResolver[it.projectPath][it.compilationName] compilationNpmResolution.getResolutionOrPrepare( @@ -91,9 +127,9 @@ class KotlinCompilationNpmResolution( ) } .flatMap { it.externalNpmDependencies } - val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull { - npmResolutionManager.parameters.gradleNodeModulesProvider.get().get(it.dependencyName, it.dependencyVersion, it.file) - } + fileCollectionDependencies.flatMap { dependency -> + val importedExternalGradleDependencies = visitor.externalGradleDependencies.mapNotNull { + npmResolutionManager.parameters.gradleNodeModulesProvider.get().get(it.component.module, it.component.version, it.artifact) + } + fileDeps.flatMap { dependency -> dependency.files // Gradle can hash with FileHasher only files and only existed files .filter { it.isFile } @@ -113,7 +149,7 @@ class KotlinCompilationNpmResolution( .getCompilationNpmRequirements(projectPath, compilationDisambiguatedName) val otherNpmDependencies = toolsNpmDependencies + transitiveNpmDependencies - val allNpmDependencies = disambiguateDependencies(externalNpmDependencies, otherNpmDependencies, logger) + val allNpmDependencies = disambiguateDependencies(npmDeps, otherNpmDependencies, logger) return PreparedKotlinCompilationNpmResolution( npmResolutionManager.packagesDir.map { it.dir(npmProjectName) }, @@ -168,4 +204,300 @@ class KotlinCompilationNpmResolution( } return direct + unique } + + inner class ConfigurationVisitor(val rootResolution: KotlinRootNpmResolution) { + val internalDependencies = mutableSetOf() + val internalCompositeDependencies = mutableSetOf() + val externalGradleDependencies = mutableSetOf() + val externalNpmDependencies = mutableSetOf() + val fileCollectionDependencies = mutableSetOf() + + private val visitedDependencies = mutableSetOf() + + fun visit(configuration: Pair>) { + configuration.first.dependencies.forEach { result -> + if (result is ResolvedDependencyResult) { + val owner = result.resolvedVariant.externalVariant.orElse(result.resolvedVariant).owner + visitDependency(owner, configuration.second.getValue(owner)) + } else { + println("WTF ${result}") + } + } +// configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach { +// visitDependency(it) +// } +// +// configuration.allDependencies.forEach { dependency -> +// when (dependency) { +// is NpmDependency -> externalNpmDependencies.add(dependency) +// is FileCollectionDependency -> fileCollectionDependencies.add( +// FileCollectionExternalGradleDependency( +// dependency.files.files, +// dependency.version +// ) +// ) +// } +// } + +// TODO: rewrite when we get general way to have inter compilation dependencies +// if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { +// val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsCompilation +// internalDependencies.add( +// InternalDependency( +// projectResolver.project.path, +// main.disambiguatedName, +// projectResolver[main].npmProject.name +// ) +// ) +// } + + val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty() + +// if (compilation.isMain() && hasPublicNpmDependencies) { +// project.tasks +// .withType(Zip::class.java) +// .named(npmProject.target.artifactsTaskName) +// .configure { task -> +// task.from(publicPackageJsonTaskHolder) +// } +// } + } + + private fun visitDependency(dependency: ComponentIdentifier, second: File) { + if (dependency in visitedDependencies) return + visitedDependencies.add(dependency) + visitArtifact(dependency, second) +// visitArtifacts(dependency, dependency.) + +// dependency.children.forEach { +// visitDependency(it) +// } + } + +// private fun visitArtifacts( +// dependency: ResolvedDependency, +// artifacts: MutableSet +// ) { +// artifacts.forEach { visitArtifact(dependency, it) } +// } + + private fun visitArtifact( + dependency: ComponentIdentifier, + artifact: File + ) { +// val artifactId = artifact.id +// val componentIdentifier = dependency.id +// +// if (artifactId `is` CompositeProjectComponentArtifactMetadata) { +// visitCompositeProjectDependency(dependency, componentIdentifier as ProjectComponentIdentifier) +// return +// } + + if (dependency is ProjectComponentIdentifier) { + visitProjectDependency(dependency) + return + } + + if (dependency is ModuleComponentIdentifier) { + 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 + ) { + val dependentProject = rootResolution[componentIdentifier.projectPath] + + val dependentCompilation = dependentProject.npmProjects.single { it.compilationDisambiguatedName.contains("main", ignoreCase = true) } + + internalDependencies.add( + InternalDependency( + dependentCompilation.projectPath, + dependentCompilation.compilationDisambiguatedName, + dependentCompilation.npmProjectName + ) + ) + } + +// fun toPackageJsonProducer() = PackageJsonProducer( +// internalDependencies, +// internalCompositeDependencies, +// externalGradleDependencies.map { +// it.component to it.artifact +// }, +// externalNpmDependencies.map { it.toDeclaration() }, +// fileCollectionDependencies, +// projectPath +// ) + + + + + // ================ +// private val internalDependencies = mutableSetOf() +// private val internalCompositeDependencies = mutableSetOf() +// private val externalGradleDependencies = mutableSetOf() +// private val externalNpmDependencies = mutableSetOf() +// private val fileCollectionDependencies = mutableSetOf() +// +// private val visitedDependencies = mutableSetOf() + +// fun visit(configuration: Configuration) { +// configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach { +// visitDependency(it) +// } +// +// configuration.allDependencies.forEach { dependency -> +// when (dependency) { +// is NpmDependency -> externalNpmDependencies.add(dependency.toDeclaration()) +// is FileCollectionDependency -> fileCollectionDependencies.add( +// FileCollectionExternalGradleDependency( +// dependency.files.files, +// dependency.version +// ) +// ) +// } +// } +// +// //TODO: rewrite when we get general way to have inter compilation dependencies +// if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { +// val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsIrCompilation +// internalDependencies.add( +// InternalDependency( +// projectResolver.projectPath, +// main.disambiguatedName, +// projectResolver[main].npmProject.name +// ) +// ) +// } +// +// val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty() +// +// if (compilation.isMain() && hasPublicNpmDependencies) { +// project.tasks +// .withType(Zip::class.java) +// .named(npmProject.target.artifactsTaskName) +// .configure { task -> +// task.from(publicPackageJsonTaskHolder) +// } +// } +// } + +// private fun visitDependency(dependency: ResolvedDependency) { +// if (dependency in visitedDependencies) return +// visitedDependencies.add(dependency) +// visitArtifacts(dependency, dependency.moduleArtifacts) +// +// dependency.children.forEach { +// visitDependency(it) +// } +// } +// +// private fun visitArtifacts( +// dependency: ResolvedDependency, +// artifacts: MutableSet, +// ) { +// artifacts.forEach { visitArtifact(dependency, it) } +// } +// +// private fun visitArtifact( +// dependency: ResolvedDependency, +// artifact: ResolvedArtifact, +// ) { +// 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 +// } +// +// 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, +// ) { +// val dependentProject = project.findProject(componentIdentifier.projectPath) +// ?: error("Cannot find project ${componentIdentifier.projectPath}") +// +// rootResolver.findDependentResolver(project, dependentProject) +// ?.forEach { dependentResolver -> +// internalDependencies.add( +// InternalDependency( +// dependentResolver.projectPath, +// dependentResolver.compilationDisambiguatedName, +// dependentResolver.npmProject.name +// ) +// ) +// } +// } +// +// fun toPackageJsonProducer() = KotlinCompilationNpmResolution( +// internalDependencies, +// internalCompositeDependencies, +// externalGradleDependencies.map { +// FileExternalGradleDependency( +// it.dependency.moduleName, +// it.dependency.moduleVersion, +// it.artifact.file +// ) +// }, +// externalNpmDependencies, +// fileCollectionDependencies, +// projectPath, +// compilationDisambiguatedName, +// npmProject.name, +// npmVersion, +// rootResolver.tasksRequirements +// ) + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt index 2863fc1fcb3de..9660b1f6ad57b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt @@ -109,18 +109,37 @@ class KotlinCompilationNpmResolver( override fun toString(): String = "KotlinCompilationNpmResolver(${npmProject.name})" - val aggregatedConfiguration: Configuration = run { - createAggregatedConfiguration() - } +// val aggregatedConfiguration: Configuration = run { +// createAggregatedConfiguration() +// } private var _compilationNpmResolution: KotlinCompilationNpmResolution? = null val compilationNpmResolution: KotlinCompilationNpmResolution get() { return _compilationNpmResolution ?: run { - val visitor = ConfigurationVisitor() - visitor.visit(aggregatedConfiguration) - visitor.toPackageJsonProducer() +// val visitor = ConfigurationVisitor() +// visitor.visit(aggregatedConfiguration) +// visitor.toPackageJsonProducer() + + KotlinCompilationNpmResolution( +// internalDependencies, +// internalCompositeDependencies, +// externalGradleDependencies.map { +// FileExternalGradleDependency( +// it.dependency.moduleName, +// it.dependency.moduleVersion, +// it.artifact.file +// ) +// }, +// externalNpmDependencies, +// fileCollectionDependencies, + projectPath, + compilationDisambiguatedName, + npmProject.name, + npmVersion, + rootResolver.tasksRequirements + ) }.also { _compilationNpmResolution = it } @@ -131,33 +150,33 @@ class KotlinCompilationNpmResolver( return _compilationNpmResolution } - private fun createAggregatedConfiguration(): Configuration { - val all = project.configurations.createResolvable(compilation.npmAggregatedConfigurationName) - - all.usesPlatformOf(target) - all.attributes.setAttribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(target)) - all.attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY)) - all.attributes.setAttribute(publicPackageJsonAttribute, PUBLIC_PACKAGE_JSON_ATTR_VALUE) - all.isVisible = false - all.description = "NPM configuration for $compilation." - - KotlinDependencyScope.values().forEach { scope -> - val compilationConfiguration = project.compilationDependencyConfigurationByScope( - compilation, - scope - ) - all.extendsFrom(compilationConfiguration) - compilation.allKotlinSourceSets.forEach { sourceSet -> - val sourceSetConfiguration = project.configurations.sourceSetDependencyConfigurationByScope(sourceSet, scope) - all.extendsFrom(sourceSetConfiguration) - } - } - - // We don't have `kotlin-js-test-runner` in NPM yet - all.dependencies.add(rootResolver.versions.kotlinJsTestRunner.createDependency(project)) - - return all - } +// private fun createAggregatedConfiguration(): Configuration { +// val all = project.configurations.createResolvable(compilation.npmAggregatedConfigurationName) +// +// all.usesPlatformOf(target) +// all.attributes.setAttribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(target)) +// all.attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY)) +// all.attributes.setAttribute(publicPackageJsonAttribute, PUBLIC_PACKAGE_JSON_ATTR_VALUE) +// all.isVisible = false +// all.description = "NPM configuration for $compilation." +// +// KotlinDependencyScope.values().forEach { scope -> +// val compilationConfiguration = project.compilationDependencyConfigurationByScope( +// compilation, +// scope +// ) +// all.extendsFrom(compilationConfiguration) +// compilation.allKotlinSourceSets.forEach { sourceSet -> +// val sourceSetConfiguration = project.configurations.sourceSetDependencyConfigurationByScope(sourceSet, scope) +// all.extendsFrom(sourceSetConfiguration) +// } +// } +// +// // We don't have `kotlin-js-test-runner` in NPM yet +// all.dependencies.add(rootResolver.versions.kotlinJsTestRunner.createDependency(project)) +// +// return all +// } private fun createPublicPackageJsonConfiguration(): Configuration { val all = project.configurations.createConsumable(compilation.publicPackageJsonConfigurationName) @@ -171,151 +190,151 @@ class KotlinCompilationNpmResolver( return all } - inner class ConfigurationVisitor { - private val internalDependencies = mutableSetOf() - private val internalCompositeDependencies = mutableSetOf() - private val externalGradleDependencies = mutableSetOf() - private val externalNpmDependencies = mutableSetOf() - private val fileCollectionDependencies = mutableSetOf() - - private val visitedDependencies = mutableSetOf() - - fun visit(configuration: Configuration) { - configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach { - visitDependency(it) - } - - configuration.allDependencies.forEach { dependency -> - when (dependency) { - is NpmDependency -> externalNpmDependencies.add(dependency.toDeclaration()) - is FileCollectionDependency -> fileCollectionDependencies.add( - FileCollectionExternalGradleDependency( - dependency.files.files, - dependency.version - ) - ) - } - } - - //TODO: rewrite when we get general way to have inter compilation dependencies - if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { - val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsIrCompilation - internalDependencies.add( - InternalDependency( - projectResolver.projectPath, - main.disambiguatedName, - projectResolver[main].npmProject.name - ) - ) - } - - val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty() - - if (compilation.isMain() && hasPublicNpmDependencies) { - project.tasks - .withType(Zip::class.java) - .named(npmProject.target.artifactsTaskName) - .configure { task -> - task.from(publicPackageJsonTaskHolder) - } - } - } - - private fun visitDependency(dependency: ResolvedDependency) { - if (dependency in visitedDependencies) return - visitedDependencies.add(dependency) - visitArtifacts(dependency, dependency.moduleArtifacts) - - dependency.children.forEach { - visitDependency(it) - } - } - - private fun visitArtifacts( - dependency: ResolvedDependency, - artifacts: MutableSet, - ) { - artifacts.forEach { visitArtifact(dependency, it) } - } - - private fun visitArtifact( - dependency: ResolvedDependency, - artifact: ResolvedArtifact, - ) { - 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 - } - - 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, - ) { - val dependentProject = project.findProject(componentIdentifier.projectPath) - ?: error("Cannot find project ${componentIdentifier.projectPath}") - - rootResolver.findDependentResolver(project, dependentProject) - ?.forEach { dependentResolver -> - internalDependencies.add( - InternalDependency( - dependentResolver.projectPath, - dependentResolver.compilationDisambiguatedName, - dependentResolver.npmProject.name - ) - ) - } - } - - fun toPackageJsonProducer() = KotlinCompilationNpmResolution( - internalDependencies, - internalCompositeDependencies, - externalGradleDependencies.map { - FileExternalGradleDependency( - it.dependency.moduleName, - it.dependency.moduleVersion, - it.artifact.file - ) - }, - externalNpmDependencies, - fileCollectionDependencies, - projectPath, - compilationDisambiguatedName, - npmProject.name, - npmVersion, - rootResolver.tasksRequirements - ) - } +// inner class ConfigurationVisitor { +// private val internalDependencies = mutableSetOf() +// private val internalCompositeDependencies = mutableSetOf() +// private val externalGradleDependencies = mutableSetOf() +// private val externalNpmDependencies = mutableSetOf() +// private val fileCollectionDependencies = mutableSetOf() +// +// private val visitedDependencies = mutableSetOf() +// +// fun visit(configuration: Configuration) { +// configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach { +// visitDependency(it) +// } +// +// configuration.allDependencies.forEach { dependency -> +// when (dependency) { +// is NpmDependency -> externalNpmDependencies.add(dependency.toDeclaration()) +// is FileCollectionDependency -> fileCollectionDependencies.add( +// FileCollectionExternalGradleDependency( +// dependency.files.files, +// dependency.version +// ) +// ) +// } +// } +// +// //TODO: rewrite when we get general way to have inter compilation dependencies +// if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { +// val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsIrCompilation +// internalDependencies.add( +// InternalDependency( +// projectResolver.projectPath, +// main.disambiguatedName, +// projectResolver[main].npmProject.name +// ) +// ) +// } +// +// val hasPublicNpmDependencies = externalNpmDependencies.isNotEmpty() +// +// if (compilation.isMain() && hasPublicNpmDependencies) { +// project.tasks +// .withType(Zip::class.java) +// .named(npmProject.target.artifactsTaskName) +// .configure { task -> +// task.from(publicPackageJsonTaskHolder) +// } +// } +// } +// +// private fun visitDependency(dependency: ResolvedDependency) { +// if (dependency in visitedDependencies) return +// visitedDependencies.add(dependency) +// visitArtifacts(dependency, dependency.moduleArtifacts) +// +// dependency.children.forEach { +// visitDependency(it) +// } +// } +// +// private fun visitArtifacts( +// dependency: ResolvedDependency, +// artifacts: MutableSet, +// ) { +// artifacts.forEach { visitArtifact(dependency, it) } +// } +// +// private fun visitArtifact( +// dependency: ResolvedDependency, +// artifact: ResolvedArtifact, +// ) { +// 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 +// } +// +// 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, +// ) { +// val dependentProject = project.findProject(componentIdentifier.projectPath) +// ?: error("Cannot find project ${componentIdentifier.projectPath}") +// +// rootResolver.findDependentResolver(project, dependentProject) +// ?.forEach { dependentResolver -> +// internalDependencies.add( +// InternalDependency( +// dependentResolver.projectPath, +// dependentResolver.compilationDisambiguatedName, +// dependentResolver.npmProject.name +// ) +// ) +// } +// } +// +// fun toPackageJsonProducer() = KotlinCompilationNpmResolution( +// internalDependencies, +// internalCompositeDependencies, +// externalGradleDependencies.map { +// FileExternalGradleDependency( +// it.dependency.moduleName, +// it.dependency.moduleVersion, +// it.artifact.file +// ) +// }, +// externalNpmDependencies, +// fileCollectionDependencies, +// projectPath, +// compilationDisambiguatedName, +// npmProject.name, +// npmVersion, +// rootResolver.tasksRequirements +// ) +// } companion object { val publicPackageJsonAttribute = Attribute.of( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt index 7672060eb396d..ed06d7c986ff8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt @@ -7,13 +7,28 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.tasks import org.gradle.api.Action import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.FileCollectionDependency +import org.gradle.api.artifacts.component.ComponentArtifactIdentifier import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.Usage import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.* import org.gradle.work.DisableCachingByDefault import org.gradle.work.NormalizeLineEndings +import org.jetbrains.kotlin.gradle.plugin.categoryByName +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages +import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName +import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope +import org.jetbrains.kotlin.gradle.plugin.sources.compilationDependencyConfigurationByScope +import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope +import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin @@ -44,13 +59,13 @@ abstract class KotlinPackageJsonTask : private val compilationResolver: KotlinCompilationNpmResolver get() = rootResolver[projectPath][compilationDisambiguatedName.get()] - private fun findDependentTasks(): Collection = - 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}") - } +// private fun findDependentTasks(): Collection = +// 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}") +// } // ----- @@ -87,40 +102,42 @@ abstract class KotlinPackageJsonTask : @get:NormalizeLineEndings @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - internal val compositeFiles: Set 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 - } + internal abstract val compositeFiles: SetProperty + + @get:Input + internal abstract val components: Property + + @get:Input + internal abstract val map: MapProperty + + @get:Nested + internal abstract val npmDeps: SetProperty + + @get:Nested + internal abstract val fileDeps: SetProperty // nested inputs are processed in configuration phase // so npmResolutionManager must not be used - @get:Nested - internal val producerInputs: PackageJsonProducerInputs by lazy { - compilationResolver.compilationNpmResolution.inputs - } +// @get:Nested +// internal val producerInputs: PackageJsonProducerInputs by lazy { +// compilationResolver.compilationNpmResolution.inputs +// } @get:OutputFile abstract val packageJson: Property @TaskAction fun resolve() { + val resolvedConfiguration = components.get() to map.get().map { (key, value) -> key.componentIdentifier to value }.toMap() + val resolution = npmResolutionManager.get().resolution.get()[projectPath][compilationDisambiguatedName.get()] val preparedResolution = resolution .prepareWithDependencies( npmResolutionManager = npmResolutionManager.get(), - logger = logger + logger = logger, + resolvedConfiguration = resolvedConfiguration, + npmDeps = npmDeps.get(), + fileDeps = fileDeps.get(), ) resolution.createPackageJson(preparedResolution, packageJsonMain, packageJsonHandlers) @@ -136,6 +153,36 @@ abstract class KotlinPackageJsonTask : val npmCachesSetupTask = nodeJsTaskProviders.npmCachesSetupTaskProvider val packageJsonTaskName = npmProject.packageJsonTaskName val packageJsonUmbrella = nodeJsTaskProviders.packageJsonUmbrellaTaskProvider + + fun createAggregatedConfiguration(): Configuration { + val all = project.configurations.create(compilation.disambiguateName("npm")) + + all.usesPlatformOf(target) + all.attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(target)) + all.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY)) + all.isVisible = false + all.isCanBeConsumed = false + all.isCanBeResolved = true + all.description = "NPM configuration for $compilation." + + KotlinDependencyScope.values().forEach { scope -> + val compilationConfiguration = project.compilationDependencyConfigurationByScope( + compilation, + scope + ) + all.extendsFrom(compilationConfiguration) + compilation.allKotlinSourceSets.forEach { sourceSet -> + val sourceSetConfiguration = project.configurations.sourceSetDependencyConfigurationByScope(sourceSet, scope) + all.extendsFrom(sourceSetConfiguration) + } + } + + // We don't have `kotlin-js-test-runner` in NPM yet + all.dependencies.add(nodeJsTaskProviders.versions.kotlinJsTestRunner.createDependency(project)) + + return all + } + val npmResolutionManager = project.kotlinNpmResolutionManager val gradleNodeModules = GradleNodeModulesCache.registerIfAbsent(project, null, null) val packageJsonTask = project.registerTask(packageJsonTaskName) { task -> @@ -144,6 +191,48 @@ abstract class KotlinPackageJsonTask : task.description = "Create package.json file for $compilation" task.group = NodeJsRootPlugin.TASKS_GROUP_NAME + val all = createAggregatedConfiguration() + val createAggregatedConfiguration = all.incoming.resolutionResult.rootComponent to all.incoming.artifacts.resolvedArtifacts.map { + it.map { it.id to it.file }.toMap() + } + + val externalNpmDependencies = mutableSetOf() + val fileCollectionDependencies = mutableSetOf() + + all.allDependencies.forEach { dependency -> + when (dependency) { + is NpmDependency -> externalNpmDependencies.add(dependency.toDeclaration()) + is FileCollectionDependency -> fileCollectionDependencies.add( + FileCollectionExternalGradleDependency( + dependency.files.files, + dependency.version + ) + ) + } + } + task.components.set(createAggregatedConfiguration.first) + task.map.set(createAggregatedConfiguration.second) +// task.resolvedConfiguration = createAggregatedConfiguration + + task.npmDeps.set(externalNpmDependencies) + task.fileDeps.set(fileCollectionDependencies) + + task.compositeFiles.set( + all + .incoming + .artifactView { artifactView -> + artifactView.componentFilter { componentIdentifier -> + componentIdentifier is ProjectComponentIdentifier + } + } + .artifacts + .filter { + it.id `is` CompositeProjectComponentArtifactMetadata + } + .map { it.file } + .toSet() + ) + task.npmResolutionManager.value(npmResolutionManager) .disallowChanges() @@ -159,7 +248,7 @@ abstract class KotlinPackageJsonTask : it.npmResolutionManager.get().isConfiguringState() } - task.dependsOn(target.project.provider { task.findDependentTasks() }) +// task.dependsOn(target.project.provider { task.findDependentTasks() }) task.dependsOn(npmCachesSetupTask) }