MPP: Drop duplicating library produced by file collection dependency

#KT-26675 Fixed
This commit is contained in:
Alexey Sedunov
2018-09-11 16:28:32 +03:00
parent 5ac64bcc1d
commit 469b11e0aa
6 changed files with 182 additions and 55 deletions
@@ -311,8 +311,68 @@ class NewMultiplatformProjectImportingTest : GradleImportingTestCase() {
} }
} }
private fun checkProjectStructure(body: ProjectInfo.() -> Unit = {}) { @Test
checkProjectStructure(myProject, projectPath, body) fun testFileCollectionDependency() {
createProjectSubFile(
"build.gradle",
"""
buildscript {
repositories {
mavenLocal()
jcenter()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
}
apply plugin: 'kotlin-multiplatform'
kotlin {
sourceSets {
jvmMain {
dependencies {
implementation files("a", "b")
}
}
}
targets {
fromPreset(presets.jvmWithJava, 'jvm')
}
}
""".trimIndent()
)
importProject()
checkProjectStructure(
exhaustiveModuleList = false,
exhaustiveSourceSourceRootList = false
) {
module("project_jvmMain") {
libraryDependencyByUrl("file://$projectPath/a", DependencyScope.COMPILE)
libraryDependencyByUrl("file://$projectPath/b", DependencyScope.COMPILE)
moduleDependency("project_commonMain", DependencyScope.COMPILE)
moduleDependency("project_main", DependencyScope.COMPILE)
}
}
}
private fun checkProjectStructure(
exhaustiveModuleList: Boolean = true,
exhaustiveSourceSourceRootList: Boolean = true,
body: ProjectInfo.() -> Unit = {}
) {
checkProjectStructure(myProject, projectPath, exhaustiveModuleList, exhaustiveSourceSourceRootList, body)
} }
override fun importProject() { override fun importProject() {
@@ -33,8 +33,13 @@ class MessageCollector {
} }
} }
class ProjectInfo(project: Project, private val projectPath: String) { class ProjectInfo(
private val messageCollector = MessageCollector() project: Project,
internal val projectPath: String,
internal val exhaustiveModuleList: Boolean,
internal val exhaustiveSourceSourceRootList: Boolean
) {
internal val messageCollector = MessageCollector()
private val moduleManager = ModuleManager.getInstance(project) private val moduleManager = ModuleManager.getInstance(project)
private val expectedModuleNames = HashSet<String>() private val expectedModuleNames = HashSet<String>()
private var allModulesAsserter: (ModuleInfo.() -> Unit)? = null private var allModulesAsserter: (ModuleInfo.() -> Unit)? = null
@@ -50,7 +55,7 @@ class ProjectInfo(project: Project, private val projectPath: String) {
messageCollector.report("No module found: '$name'") messageCollector.report("No module found: '$name'")
return return
} }
val moduleInfo = ModuleInfo(module, messageCollector, projectPath) val moduleInfo = ModuleInfo(module, this)
allModulesAsserter?.let { moduleInfo.it() } allModulesAsserter?.let { moduleInfo.it() }
moduleInfo.run(body) moduleInfo.run(body)
expectedModuleNames += name expectedModuleNames += name
@@ -59,10 +64,12 @@ class ProjectInfo(project: Project, private val projectPath: String) {
fun run(body: ProjectInfo.() -> Unit = {}) { fun run(body: ProjectInfo.() -> Unit = {}) {
body() body()
val actualNames = moduleManager.modules.map { it.name }.sorted() if (exhaustiveModuleList) {
val expectedNames = expectedModuleNames.sorted() val actualNames = moduleManager.modules.map { it.name }.sorted()
if (actualNames != expectedNames) { val expectedNames = expectedModuleNames.sorted()
messageCollector.report("Expected module list $expectedNames doesn't match the actual one: $actualNames") if (actualNames != expectedNames) {
messageCollector.report("Expected module list $expectedNames doesn't match the actual one: $actualNames")
}
} }
messageCollector.check() messageCollector.check()
@@ -71,8 +78,7 @@ class ProjectInfo(project: Project, private val projectPath: String) {
class ModuleInfo( class ModuleInfo(
val module: Module, val module: Module,
private val messageCollector: MessageCollector, val projectInfo: ProjectInfo
private val projectPath: String
) { ) {
private val rootModel = module.rootManager private val rootModel = module.rootManager
private val expectedDependencyNames = HashSet<String>() private val expectedDependencyNames = HashSet<String>()
@@ -82,7 +88,7 @@ class ModuleInfo(
.flatMap { it.sourceFolders.asSequence() } .flatMap { it.sourceFolders.asSequence() }
.mapNotNull { .mapNotNull {
val path = it.file?.path ?: return@mapNotNull null val path = it.file?.path ?: return@mapNotNull null
FileUtil.getRelativePath(projectPath, path, '/')!! to it FileUtil.getRelativePath(projectInfo.projectPath, path, '/')!! to it
} }
.toMap() .toMap()
} }
@@ -90,21 +96,21 @@ class ModuleInfo(
fun languageVersion(version: String) { fun languageVersion(version: String) {
val actualVersion = module.languageVersionSettings.languageVersion.versionString val actualVersion = module.languageVersionSettings.languageVersion.versionString
if (actualVersion != version) { if (actualVersion != version) {
messageCollector.report("Module '${module.name}': expected language version '$version' but found '$actualVersion'") projectInfo.messageCollector.report("Module '${module.name}': expected language version '$version' but found '$actualVersion'")
} }
} }
fun apiVersion(version: String) { fun apiVersion(version: String) {
val actualVersion = module.languageVersionSettings.apiVersion.versionString val actualVersion = module.languageVersionSettings.apiVersion.versionString
if (actualVersion != version) { if (actualVersion != version) {
messageCollector.report("Module '${module.name}': expected API version '$version' but found '$actualVersion'") projectInfo.messageCollector.report("Module '${module.name}': expected API version '$version' but found '$actualVersion'")
} }
} }
fun platform(platform: IdePlatform<*, *>) { fun platform(platform: IdePlatform<*, *>) {
val actualPlatform = module.platform val actualPlatform = module.platform
if (actualPlatform != platform) { if (actualPlatform != platform) {
messageCollector.report( projectInfo.messageCollector.report(
"Module '${module.name}': expected platform '${platform.description}' but found '${actualPlatform?.description}'" "Module '${module.name}': expected platform '${platform.description}' but found '${actualPlatform?.description}'"
) )
} }
@@ -113,7 +119,7 @@ class ModuleInfo(
fun additionalArguments(arguments: String?) { fun additionalArguments(arguments: String?) {
val actualArguments = KotlinFacet.get(module)?.configuration?.settings?.compilerSettings?.additionalArguments val actualArguments = KotlinFacet.get(module)?.configuration?.settings?.compilerSettings?.additionalArguments
if (actualArguments != arguments) { if (actualArguments != arguments) {
messageCollector.report( projectInfo.messageCollector.report(
"Module '${module.name}': expected additional arguments '$arguments' but found '$actualArguments'" "Module '${module.name}': expected additional arguments '$arguments' but found '$actualArguments'"
) )
} }
@@ -121,8 +127,19 @@ class ModuleInfo(
fun libraryDependency(libraryName: String, scope: DependencyScope) { fun libraryDependency(libraryName: String, scope: DependencyScope) {
val libraryEntry = rootModel.orderEntries.filterIsInstance<LibraryOrderEntry>().singleOrNull { it.libraryName == libraryName } val libraryEntry = rootModel.orderEntries.filterIsInstance<LibraryOrderEntry>().singleOrNull { it.libraryName == libraryName }
checkLibrary(libraryEntry, libraryName, scope)
}
fun libraryDependencyByUrl(classesUrl: String, scope: DependencyScope) {
val libraryEntry = rootModel.orderEntries.filterIsInstance<LibraryOrderEntry>().singleOrNull { entry ->
entry.library?.getUrls(OrderRootType.CLASSES)?.any { it == classesUrl } ?: false
}
checkLibrary(libraryEntry, classesUrl, scope)
}
private fun checkLibrary(libraryEntry: LibraryOrderEntry?, id: String, scope: DependencyScope) {
if (libraryEntry == null) { if (libraryEntry == null) {
messageCollector.report("Module '${module.name}': No library dependency found: '$libraryName'") projectInfo.messageCollector.report("Module '${module.name}': No library dependency found: '$id'")
return return
} }
checkDependencyScope(libraryEntry, scope) checkDependencyScope(libraryEntry, scope)
@@ -132,7 +149,7 @@ class ModuleInfo(
fun moduleDependency(moduleName: String, scope: DependencyScope) { fun moduleDependency(moduleName: String, scope: DependencyScope) {
val moduleEntry = rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>().singleOrNull { it.moduleName == moduleName } val moduleEntry = rootModel.orderEntries.filterIsInstance<ModuleOrderEntry>().singleOrNull { it.moduleName == moduleName }
if (moduleEntry == null) { if (moduleEntry == null) {
messageCollector.report("Module '${module.name}': No module dependency found: '$moduleName'") projectInfo.messageCollector.report("Module '${module.name}': No module dependency found: '$moduleName'")
return return
} }
checkDependencyScope(moduleEntry, scope) checkDependencyScope(moduleEntry, scope)
@@ -142,13 +159,13 @@ class ModuleInfo(
fun sourceFolder(pathInProject: String, rootType: JpsModuleSourceRootType<*>) { fun sourceFolder(pathInProject: String, rootType: JpsModuleSourceRootType<*>) {
val sourceFolder = sourceFolderByPath[pathInProject] val sourceFolder = sourceFolderByPath[pathInProject]
if (sourceFolder == null) { if (sourceFolder == null) {
messageCollector.report("Module '${module.name}': No source folder found: '$pathInProject'") projectInfo.messageCollector.report("Module '${module.name}': No source folder found: '$pathInProject'")
return return
} }
expectedSourceRoots += pathInProject expectedSourceRoots += pathInProject
val actualRootType = sourceFolder.rootType val actualRootType = sourceFolder.rootType
if (actualRootType != rootType) { if (actualRootType != rootType) {
messageCollector.report( projectInfo.messageCollector.report(
"Module '${module.name}', source root '$pathInProject': Expected root type $rootType doesn't match the actual one: $actualRootType" "Module '${module.name}', source root '$pathInProject': Expected root type $rootType doesn't match the actual one: $actualRootType"
) )
return return
@@ -158,7 +175,7 @@ class ModuleInfo(
fun inheritProjectOutput() { fun inheritProjectOutput() {
val isInherited = CompilerModuleExtension.getInstance(module)?.isCompilerOutputPathInherited ?: true val isInherited = CompilerModuleExtension.getInstance(module)?.isCompilerOutputPathInherited ?: true
if (!isInherited) { if (!isInherited) {
messageCollector.report("Module '${module.name}': project output is not inherited") projectInfo.messageCollector.report("Module '${module.name}': project output is not inherited")
} }
} }
@@ -167,7 +184,7 @@ class ModuleInfo(
val url = if (isProduction) compilerModuleExtension?.compilerOutputUrl else compilerModuleExtension?.compilerOutputUrlForTests val url = if (isProduction) compilerModuleExtension?.compilerOutputUrl else compilerModuleExtension?.compilerOutputUrlForTests
val actualPathInProject = url?.let { val actualPathInProject = url?.let {
FileUtil.getRelativePath( FileUtil.getRelativePath(
projectPath, projectInfo.projectPath,
JpsPathUtil.urlToPath( JpsPathUtil.urlToPath(
it it
), ),
@@ -175,7 +192,7 @@ class ModuleInfo(
) )
} }
if (actualPathInProject != pathInProject) { if (actualPathInProject != pathInProject) {
messageCollector.report( projectInfo.messageCollector.report(
"Module '${module.name}': Expected output path $pathInProject doesn't match the actual one: $actualPathInProject" "Module '${module.name}': Expected output path $pathInProject doesn't match the actual one: $actualPathInProject"
) )
return return
@@ -192,24 +209,38 @@ class ModuleInfo(
.sorted() .sorted()
val expectedDependencyNames = expectedDependencyNames.sorted() val expectedDependencyNames = expectedDependencyNames.sorted()
if (actualDependencyNames != expectedDependencyNames) { if (actualDependencyNames != expectedDependencyNames) {
messageCollector.report("Module '${module.name}': Expected dependency list $expectedDependencyNames doesn't match the actual one: $actualDependencyNames") projectInfo.messageCollector.report(
"Module '${module.name}': Expected dependency list $expectedDependencyNames doesn't match the actual one: $actualDependencyNames"
)
} }
val actualSourceRoots = sourceFolderByPath.keys.sorted() if (projectInfo.exhaustiveSourceSourceRootList) {
val expectedSourceRoots = expectedSourceRoots.sorted() val actualSourceRoots = sourceFolderByPath.keys.sorted()
if (actualSourceRoots != expectedSourceRoots) { val expectedSourceRoots = expectedSourceRoots.sorted()
messageCollector.report("Module '${module.name}': Expected source root list $expectedSourceRoots doesn't match the actual one: $actualSourceRoots") if (actualSourceRoots != expectedSourceRoots) {
projectInfo.messageCollector.report(
"Module '${module.name}': Expected source root list $expectedSourceRoots doesn't match the actual one: $actualSourceRoots"
)
}
} }
} }
private fun checkDependencyScope(library: ExportableOrderEntry, scope: DependencyScope) { private fun checkDependencyScope(library: ExportableOrderEntry, scope: DependencyScope) {
val actualScope = library.scope val actualScope = library.scope
if (actualScope != scope) { if (actualScope != scope) {
messageCollector.report("Module '${module.name}': Dependency '${library.presentableName}': expected scope '$scope' but found '$actualScope'") projectInfo.messageCollector.report(
"Module '${module.name}': Dependency '${library.presentableName}': expected scope '$scope' but found '$actualScope'"
)
} }
} }
} }
fun checkProjectStructure(project: Project, projectPath: String, body: ProjectInfo.() -> Unit = {}) { fun checkProjectStructure(
ProjectInfo(project, projectPath).run(body) project: Project,
projectPath: String,
exhaustiveModuleList: Boolean,
exhaustiveSourceSourceRootList: Boolean,
body: ProjectInfo.() -> Unit = {}
) {
ProjectInfo(project, projectPath, exhaustiveModuleList, exhaustiveSourceSourceRootList).run(body)
} }
@@ -14,10 +14,7 @@ import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
@@ -135,13 +132,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
dependency to artifact dependency to artifact
} }
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath } .groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration) val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
.apply { .apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope } forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
} }
.map { dependency -> .map { dependency ->
if (dependency !is ExternalProjectDependency if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map dependency || dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) { val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration it.first.configuration
@@ -162,6 +160,17 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
this.classifier = classifier this.classifier = classifier
} }
} }
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
// Workaround for duplicated dependencies specified as a file collection (KT-26675)
// Drop this code when the issue is fixed in the platform
return resolvedDependencies.filter { dependency ->
if (dependency !is FileCollectionDependency) return@filter true
val files = dependency.files
if (files.size <= 1) return@filter true
(files.any { it !in singleDependencyFiles })
}
} }
private fun buildTargets( private fun buildTargets(
@@ -13,10 +13,7 @@ import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
@@ -134,13 +131,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
dependency to artifact dependency to artifact
} }
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath } .groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration) val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
.apply { .apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope } forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
} }
.map { dependency -> .map { dependency ->
if (dependency !is ExternalProjectDependency if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map dependency || dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) { val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration it.first.configuration
@@ -161,6 +159,17 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
this.classifier = classifier this.classifier = classifier
} }
} }
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
// Workaround for duplicated dependencies specified as a file collection (KT-26675)
// Drop this code when the issue is fixed in the platform
return resolvedDependencies.filter { dependency ->
if (dependency !is FileCollectionDependency) return@filter true
val files = dependency.files
if (files.size <= 1) return@filter true
(files.any { it !in singleDependencyFiles })
}
} }
private fun buildTargets( private fun buildTargets(
@@ -14,10 +14,7 @@ import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
@@ -135,13 +132,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
dependency to artifact dependency to artifact
} }
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath } .groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration) val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
.apply { .apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope } forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
} }
.map { dependency -> .map { dependency ->
if (dependency !is ExternalProjectDependency if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map dependency || dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) { val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration it.first.configuration
@@ -162,6 +160,17 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
this.classifier = classifier this.classifier = classifier
} }
} }
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
// Workaround for duplicated dependencies specified as a file collection (KT-26675)
// Drop this code when the issue is fixed in the platform
return resolvedDependencies.filter { dependency ->
if (dependency !is FileCollectionDependency) return@filter true
val files = dependency.files
if (files.size <= 1) return@filter true
(files.any { it !in singleDependencyFiles })
}
} }
private fun buildTargets( private fun buildTargets(
@@ -13,10 +13,7 @@ import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
@@ -134,13 +131,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
dependency to artifact dependency to artifact
} }
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath } .groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration) val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
.apply { .apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope } forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
} }
.map { dependency -> .map { dependency ->
if (dependency !is ExternalProjectDependency if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map dependency || dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) { val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration it.first.configuration
@@ -161,6 +159,17 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
this.classifier = classifier this.classifier = classifier
} }
} }
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
// Workaround for duplicated dependencies specified as a file collection (KT-26675)
// Drop this code when the issue is fixed in the platform
return resolvedDependencies.filter { dependency ->
if (dependency !is FileCollectionDependency) return@filter true
val files = dependency.files
if (files.size <= 1) return@filter true
(files.any { it !in singleDependencyFiles })
}
} }
private fun buildTargets( private fun buildTargets(