MPP: Fix adjustment of transitive project dependencies

#KT-27632 Fixed
This commit is contained in:
Alexey Sedunov
2018-10-19 16:06:10 +03:00
parent fd4c07f0e6
commit af5d4b3156
6 changed files with 503 additions and 244 deletions
@@ -354,7 +354,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
sourceSetMap,
artifactsMap,
dataNode,
preprocessDependencies(compilation.dependencies, ideProject),
preprocessDependencies(compilation.dependencies),
ideProject
)
for (sourceSet in compilation.sourceSets) {
@@ -432,7 +432,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
sourceSetMap,
artifactsMap,
fromDataNode,
preprocessDependencies(mergedDependencies, ideProject),
preprocessDependencies(mergedDependencies),
ideProject
)
}
@@ -445,49 +445,12 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
else -> null
}
private fun preprocessDependencies(
dependencies: Set<KotlinDependency>,
ideProject: DataNode<ProjectData>
): List<ExternalDependency> {
private fun preprocessDependencies(dependencies: Set<KotlinDependency>): List<ExternalDependency> {
return dependencies
.groupBy { it.id }
.mapValues { it.value.firstOrNull { it.scope == "COMPILE" } ?: it.value.lastOrNull() }
.values
.filterNotNull()
.adjustDependencies(ideProject)
}
private fun List<ExternalDependency>.adjustDependencies(ideProject: DataNode<ProjectData>): List<ExternalDependency> {
val result = LinkedHashSet(this)
for (dependency in this) {
if (dependency !is ExternalProjectDependency) continue
val projectPath = dependency.projectPath
val classifier = dependency.classifier ?: continue
val targetModuleNode = ExternalSystemApiUtil.findFirstRecursively(ideProject) {
(it.data as? ModuleData)?.id == projectPath
} ?: continue
val candidateDataNodes = ExternalSystemApiUtil.getChildren(targetModuleNode, GradleSourceSetData.KEY)
if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
val compilationName = candidateDataNodes
.mapNotNull { sourceSetNode ->
sourceSetNode.kotlinSourceSet?.kotlinModule as? KotlinCompilation
}
.firstOrNull {
it.name == KotlinCompilation.MAIN_COMPILATION_NAME && it.target.disambiguationClassifier == classifier
}
?.fullName() ?: continue
result += DefaultExternalProjectDependency(dependency).also {
it.configurationName = compilationName
}
}
result += DefaultExternalProjectDependency(dependency).also {
it.configurationName = KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
}
result -= dependency
}
return result.toList()
}
private fun addDependency(fromModule: DataNode<*>, toModule: DataNode<*>) {
@@ -602,6 +602,174 @@ class NewMultiplatformProjectImportingTest : GradleImportingTestCase() {
}
}
@Test
fun testNestedDependencies() {
createProjectSubFile(
"settings.gradle",
"include 'aaa', 'bbb', 'ccc'"
)
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' }
}
}
""".trimIndent()
)
createProjectSubFile(
"aaa/build.gradle",
"""
apply plugin: 'kotlin-multiplatform'
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
api project(':bbb')
}
}
}
targets {
fromPreset(presets.jvm, 'jvm')
}
}
""".trimIndent()
)
createProjectSubFile(
"bbb/build.gradle",
"""
apply plugin: 'kotlin-multiplatform'
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
api project(':ccc')
}
}
}
targets {
fromPreset(presets.jvm, 'jvm')
}
}
""".trimIndent()
)
createProjectSubFile(
"ccc/build.gradle",
"""
apply plugin: 'kotlin-multiplatform'
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
}
}
}
targets {
fromPreset(presets.jvm, 'jvm')
}
}
""".trimIndent()
)
importProject()
checkProjectStructure(exhaustiveSourceSourceRootList = false) {
module("project")
module("aaa")
module("aaa_commonMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("bbb_commonMain", DependencyScope.COMPILE)
moduleDependency("ccc_commonMain", DependencyScope.COMPILE)
}
module("aaa_commonTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("aaa_commonMain", DependencyScope.TEST)
moduleDependency("bbb_commonMain", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
}
module("aaa_jvmMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("aaa_commonMain", DependencyScope.COMPILE)
moduleDependency("bbb_commonMain", DependencyScope.COMPILE)
moduleDependency("bbb_jvmMain", DependencyScope.COMPILE)
moduleDependency("ccc_commonMain", DependencyScope.COMPILE)
moduleDependency("ccc_jvmMain", DependencyScope.COMPILE)
}
module("aaa_jvmTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("aaa_commonMain", DependencyScope.TEST)
moduleDependency("aaa_commonTest", DependencyScope.TEST)
moduleDependency("aaa_jvmMain", DependencyScope.TEST)
moduleDependency("bbb_commonMain", DependencyScope.TEST)
moduleDependency("bbb_jvmMain", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
moduleDependency("ccc_jvmMain", DependencyScope.TEST)
}
module("bbb")
module("bbb_commonMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("ccc_commonMain", DependencyScope.COMPILE)
}
module("bbb_commonTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("bbb_commonMain", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
}
module("bbb_jvmMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("bbb_commonMain", DependencyScope.COMPILE)
moduleDependency("ccc_commonMain", DependencyScope.COMPILE)
moduleDependency("ccc_jvmMain", DependencyScope.COMPILE)
}
module("bbb_jvmTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("bbb_commonMain", DependencyScope.TEST)
moduleDependency("bbb_commonTest", DependencyScope.TEST)
moduleDependency("bbb_jvmMain", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
moduleDependency("ccc_jvmMain", DependencyScope.TEST)
}
module("ccc")
module("ccc_commonMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
}
module("ccc_commonTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
}
module("ccc_jvmMain") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("ccc_commonMain", DependencyScope.COMPILE)
}
module("ccc_jvmTest") {
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("ccc_commonMain", DependencyScope.TEST)
moduleDependency("ccc_commonTest", DependencyScope.TEST)
moduleDependency("ccc_jvmMain", DependencyScope.TEST)
}
}
}
private fun checkProjectStructure(
exhaustiveModuleList: Boolean = true,
exhaustiveSourceSourceRootList: Boolean = true,
@@ -9,6 +9,7 @@ import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
@@ -62,14 +63,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
.forEach { logger.warn(it) }
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
@@ -139,45 +132,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val dependenciesByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
val resolvedDependencies = dependencyResolver
.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map { dependency ->
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@map dependency
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@map dependency
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@map dependency
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@map dependency
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@map dependency
DefaultExternalProjectDependency(dependency).apply {
this.classifier = classifier
}
}
.flatMap(dependencyAdjuster::adjustDependency)
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
@@ -199,13 +162,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return project.getTargets()?.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
@@ -389,7 +345,83 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
}
private class DependencyAdjuster(
private val configuration: Configuration,
private val scope: String,
private val project: Project
) {
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
val dependenciesByProjectPath by lazy {
configuration
.resolvedConfiguration
.lenientConfiguration
.allModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
}
private fun wrapDependency(dependency: ExternalProjectDependency, newConfigurationName: String): ExternalProjectDependency {
return DefaultExternalProjectDependency(dependency).apply {
this.configurationName = newConfigurationName
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
this.dependencies.clear()
this.dependencies.addAll(nestedDependencies)
}
}
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
return adjustmentMap.getOrPut(dependency) {
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@getOrPut listOf(dependency)
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@getOrPut listOf(dependency)
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@getOrPut listOf(dependency)
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@getOrPut listOf(dependency)
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@getOrPut listOf(dependency)
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@getOrPut listOf(dependency)
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@getOrPut listOf(dependency)
val platformDependency = if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
wrapDependency(dependency, compilationFullName(KotlinCompilation.MAIN_COMPILATION_NAME, classifier))
} else null
val commonDependency = wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
return if (platformDependency != null) listOf(platformDependency, commonDependency) else listOf(commonDependency)
}
}
}
companion object {
private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
}
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
@@ -9,6 +9,7 @@ import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
@@ -61,14 +62,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
.forEach { logger.warn(it) }
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
@@ -138,45 +131,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val dependenciesByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
val resolvedDependencies = dependencyResolver
.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map { dependency ->
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@map dependency
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@map dependency
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@map dependency
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@map dependency
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@map dependency
DefaultExternalProjectDependency(dependency).apply {
this.classifier = classifier
}
}
.flatMap(dependencyAdjuster::adjustDependency)
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
@@ -198,13 +161,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return project.getTargets()?.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
@@ -383,7 +339,83 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
}
private class DependencyAdjuster(
private val configuration: Configuration,
private val scope: String,
private val project: Project
) {
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
val dependenciesByProjectPath by lazy {
configuration
.resolvedConfiguration
.lenientConfiguration
.allModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
}
private fun wrapDependency(dependency: ExternalProjectDependency, newConfigurationName: String): ExternalProjectDependency {
return DefaultExternalProjectDependency(dependency).apply {
this.configurationName = newConfigurationName
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
this.dependencies.clear()
this.dependencies.addAll(nestedDependencies)
}
}
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
return adjustmentMap.getOrPut(dependency) {
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@getOrPut listOf(dependency)
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@getOrPut listOf(dependency)
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@getOrPut listOf(dependency)
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@getOrPut listOf(dependency)
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@getOrPut listOf(dependency)
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@getOrPut listOf(dependency)
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@getOrPut listOf(dependency)
val platformDependency = if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
wrapDependency(dependency, compilationFullName(KotlinCompilation.MAIN_COMPILATION_NAME, classifier))
} else null
val commonDependency = wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
return if (platformDependency != null) listOf(platformDependency, commonDependency) else listOf(commonDependency)
}
}
}
companion object {
private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
}
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
@@ -9,6 +9,7 @@ import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
@@ -62,14 +63,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
.forEach { logger.warn(it) }
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
@@ -139,45 +132,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val dependenciesByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
val resolvedDependencies = dependencyResolver
.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map { dependency ->
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@map dependency
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@map dependency
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@map dependency
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@map dependency
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@map dependency
DefaultExternalProjectDependency(dependency).apply {
this.classifier = classifier
}
}
.flatMap(dependencyAdjuster::adjustDependency)
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
@@ -199,13 +162,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return project.getTargets()?.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
@@ -389,7 +345,83 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
}
private class DependencyAdjuster(
private val configuration: Configuration,
private val scope: String,
private val project: Project
) {
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
val dependenciesByProjectPath by lazy {
configuration
.resolvedConfiguration
.lenientConfiguration
.allModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
}
private fun wrapDependency(dependency: ExternalProjectDependency, newConfigurationName: String): ExternalProjectDependency {
return DefaultExternalProjectDependency(dependency).apply {
this.configurationName = newConfigurationName
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
this.dependencies.clear()
this.dependencies.addAll(nestedDependencies)
}
}
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
return adjustmentMap.getOrPut(dependency) {
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@getOrPut listOf(dependency)
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@getOrPut listOf(dependency)
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@getOrPut listOf(dependency)
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@getOrPut listOf(dependency)
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@getOrPut listOf(dependency)
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@getOrPut listOf(dependency)
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@getOrPut listOf(dependency)
val platformDependency = if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
wrapDependency(dependency, compilationFullName(KotlinCompilation.MAIN_COMPILATION_NAME, classifier))
} else null
val commonDependency = wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
return if (platformDependency != null) listOf(platformDependency, commonDependency) else listOf(commonDependency)
}
}
}
companion object {
private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
}
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
@@ -9,6 +9,7 @@ import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
@@ -61,14 +62,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
.forEach { logger.warn(it) }
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
@@ -138,45 +131,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val dependenciesByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
val resolvedDependencies = dependencyResolver.resolveDependencies(configuration)
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
val resolvedDependencies = dependencyResolver
.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map { dependency ->
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@map dependency
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@map dependency
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@map dependency
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@map dependency
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@map dependency
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@map dependency
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@map dependency
DefaultExternalProjectDependency(dependency).apply {
this.classifier = classifier
}
}
.flatMap(dependencyAdjuster::adjustDependency)
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
(it as? FileCollectionDependency)?.files?.singleOrNull()
}
@@ -198,13 +161,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return project.getTargets()?.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
@@ -383,7 +339,83 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
}
private class DependencyAdjuster(
private val configuration: Configuration,
private val scope: String,
private val project: Project
) {
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
val dependenciesByProjectPath by lazy {
configuration
.resolvedConfiguration
.lenientConfiguration
.allModuleDependencies
.mapNotNull { dependency ->
val artifact = dependency.moduleArtifacts.firstOrNull {
it.id.componentIdentifier is ProjectComponentIdentifier
} ?: return@mapNotNull null
dependency to artifact
}
.groupBy { (it.second.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
}
private fun wrapDependency(dependency: ExternalProjectDependency, newConfigurationName: String): ExternalProjectDependency {
return DefaultExternalProjectDependency(dependency).apply {
this.configurationName = newConfigurationName
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
this.dependencies.clear()
this.dependencies.addAll(nestedDependencies)
}
}
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
return adjustmentMap.getOrPut(dependency) {
if (dependency !is ExternalProjectDependency
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
) return@getOrPut listOf(dependency)
val artifacts = dependenciesByProjectPath[dependency.projectPath] ?: return@getOrPut listOf(dependency)
val artifactConfiguration = artifacts.mapTo(LinkedHashSet()) {
it.first.configuration
}.singleOrNull() ?: return@getOrPut listOf(dependency)
val taskGetterName = when (scope) {
"COMPILE" -> "getApiElementsConfigurationName"
"RUNTIME" -> "getRuntimeElementsConfigurationName"
else -> return@getOrPut listOf(dependency)
}
val targets = project.rootProject.getChildProjectByPath(dependency.projectPath)?.getTargets() ?: return@getOrPut listOf(dependency)
val gradleTarget = targets.firstOrNull {
val getter = it.javaClass.getMethodOrNull(taskGetterName) ?: return@firstOrNull false
getter(it) == artifactConfiguration
} ?: return@getOrPut listOf(dependency)
val classifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")?.invoke(gradleTarget) as? String
?: return@getOrPut listOf(dependency)
val platformDependency = if (classifier != KotlinTarget.METADATA_TARGET_NAME) {
wrapDependency(dependency, compilationFullName(KotlinCompilation.MAIN_COMPILATION_NAME, classifier))
} else null
val commonDependency = wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
return if (platformDependency != null) listOf(platformDependency, commonDependency) else listOf(commonDependency)
}
}
}
companion object {
private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
}
}
private fun Project.getChildProjectByPath(path: String): Project? {
var project = this
for (name in path.split(":").asSequence().drop(1)) {
project = project.childProjects[name] ?: return null
}
return project
}
private fun Project.getTargets(): Collection<Named>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList()
}