Importing projects with granular metadata is partially supported
(except k-lib data format of metadata libraries and multiple target platforms per source set)
This commit is contained in:
@@ -9,18 +9,17 @@ import org.jetbrains.plugins.gradle.model.ExternalDependency
|
||||
import org.jetbrains.plugins.gradle.model.ModelFactory
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
typealias KotlinDependency = ExternalDependency
|
||||
|
||||
fun KotlinDependency.deepCopy(cache: MutableMap<Any, Any>): KotlinDependency {
|
||||
val cachedValue = cache[this] as? KotlinDependency
|
||||
if (cachedValue != null) {
|
||||
return cachedValue
|
||||
return if (cachedValue != null) {
|
||||
cachedValue
|
||||
} else {
|
||||
val result = ModelFactory.createCopy(this)
|
||||
cache[this] = result
|
||||
return result
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +40,8 @@ interface KotlinSourceSet : KotlinModule {
|
||||
const val COMMON_MAIN_SOURCE_SET_NAME = "commonMain"
|
||||
const val COMMON_TEST_SOURCE_SET_NAME = "commonTest"
|
||||
|
||||
// Note. This method could not be deleted due to usage in KotlinAndroidGradleMPPModuleDataService from IDEA Core
|
||||
@Suppress("unused")
|
||||
fun commonName(forTests: Boolean) = if (forTests) COMMON_TEST_SOURCE_SET_NAME else COMMON_MAIN_SOURCE_SET_NAME
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,22 +127,25 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
dependencyResolver: DependencyResolver,
|
||||
configurationNameAccessor: String,
|
||||
scope: String,
|
||||
project: Project
|
||||
project: Project,
|
||||
metadataDependencyTransformationBuilder: MetadataDependencyTransformationBuilder
|
||||
): Collection<KotlinDependency> {
|
||||
val dependencyHolderClass = dependencyHolder.javaClass
|
||||
val getConfigurationName = dependencyHolderClass.getMethodOrNull(configurationNameAccessor) ?: return emptyList()
|
||||
val configurationName = getConfigurationName(dependencyHolder) as? String ?: return emptyList()
|
||||
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
|
||||
@Suppress("UnstableApiUsage")
|
||||
if (!configuration.isCanBeResolved) return emptyList()
|
||||
|
||||
val dependencyAdjuster = DependencyAdjuster(configuration, scope, project)
|
||||
val dependencyAdjuster =
|
||||
DependencyAdjuster(configuration, scope, project, metadataDependencyTransformationBuilder.getTransformations(configurationName))
|
||||
|
||||
val resolvedDependencies = dependencyResolver
|
||||
.resolveDependencies(configuration)
|
||||
.apply {
|
||||
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
|
||||
}
|
||||
.flatMap(dependencyAdjuster::adjustDependency)
|
||||
.flatMap { dependencyAdjuster.adjustDependency(it) }
|
||||
val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet<File>()) {
|
||||
(it as? FileCollectionDependency)?.files?.singleOrNull()
|
||||
}
|
||||
@@ -178,13 +181,13 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
val platform = KotlinPlatform.byId(platformId) ?: return null
|
||||
val disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String
|
||||
val getPreset = targetClass.getMethodOrNull("getPreset")
|
||||
var targetPresetName: String?
|
||||
try {
|
||||
val targetPresetName: String?
|
||||
targetPresetName = try {
|
||||
val targetPreset = getPreset?.invoke(gradleTarget)
|
||||
val getPresetName = targetPreset?.javaClass?.getMethodOrNull("getName")
|
||||
targetPresetName = getPresetName?.invoke(targetPreset) as? String
|
||||
getPresetName?.invoke(targetPreset) as? String
|
||||
} catch (e: Throwable) {
|
||||
targetPresetName = "${e::class.java.name}:${e.message}"
|
||||
"${e::class.java.name}:${e.message}"
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val gradleCompilations =
|
||||
@@ -243,33 +246,86 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
project: Project
|
||||
): Set<KotlinDependency> {
|
||||
return LinkedHashSet<KotlinDependency>().apply {
|
||||
val transformationBuilder = MetadataDependencyTransformationBuilder(gradleCompilation)
|
||||
this += buildDependencies(
|
||||
gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project
|
||||
gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project, transformationBuilder
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project
|
||||
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project, transformationBuilder
|
||||
)
|
||||
this += sourceSetMap[compilationFullName(gradleCompilation.name, classifier)]?.dependencies ?: emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private class MetadataDependencyTransformationBuilder(val sourceSet: Any) {
|
||||
val transformationsMethod = sourceSet.javaClass.getMethodOrNull("getDependenciesTransformation", String::class.java)
|
||||
|
||||
class KotlinMetadataDependencyTransformation(
|
||||
val groupId: String?,
|
||||
val moduleName: String,
|
||||
val projectPath: String?,
|
||||
val allVisibleSourceSets: Set<String>,
|
||||
val useFilesForSourceSets: Map<String, Iterable<File>>
|
||||
) {
|
||||
constructor(
|
||||
transformation: Any,
|
||||
group: Method,
|
||||
module: Method,
|
||||
projectPath: Method,
|
||||
visibleSourceSets: Method,
|
||||
useFilesForSourceSets: Method
|
||||
) : this(
|
||||
group(transformation) as String?,
|
||||
module(transformation) as String,
|
||||
projectPath(transformation) as String?,
|
||||
visibleSourceSets(transformation) as Set<String>,
|
||||
useFilesForSourceSets(transformation) as Map<String, Iterable<File>>
|
||||
)
|
||||
}
|
||||
|
||||
fun getTransformations(configurationName: String): Collection<KotlinMetadataDependencyTransformation> {
|
||||
val transformations = transformationsMethod?.invoke(sourceSet, configurationName) as? Iterable<Any> ?: return emptyList()
|
||||
val transformationClass = transformations.firstOrNull()?.javaClass
|
||||
?: return emptyList()
|
||||
|
||||
val getGroupId = transformationClass.getMethodOrNull("getGroupId") ?: return emptyList()
|
||||
val getModuleName = transformationClass.getMethodOrNull("getModuleName") ?: return emptyList()
|
||||
val getProjectPath = transformationClass.getMethodOrNull("getProjectPath") ?: return emptyList()
|
||||
val getAllVisibleSourceSets = transformationClass.getMethodOrNull("getAllVisibleSourceSets") ?: return emptyList()
|
||||
val getUseFilesForSourceSets = transformationClass.getMethodOrNull("getUseFilesForSourceSets") ?: return emptyList()
|
||||
|
||||
return transformations.map { transformation ->
|
||||
KotlinMetadataDependencyTransformation(
|
||||
transformation,
|
||||
getGroupId,
|
||||
getModuleName,
|
||||
getProjectPath,
|
||||
getAllVisibleSourceSets,
|
||||
getUseFilesForSourceSets
|
||||
)
|
||||
}.filter { it.allVisibleSourceSets.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun buildSourceSetDependencies(
|
||||
gradleSourceSet: Named,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
): Set<KotlinDependency> {
|
||||
return LinkedHashSet<KotlinDependency>().apply {
|
||||
val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project
|
||||
gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project, transformationBuilder
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project
|
||||
gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project, transformationBuilder
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project
|
||||
gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project, transformationBuilder
|
||||
)
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project
|
||||
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -300,6 +356,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
return (getCompileClasspath(compileKotlinTask) as? Collection<File>)?.map { it.path } ?: emptyList()
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
private fun buildCompilationOutput(
|
||||
gradleCompilation: Named,
|
||||
compileKotlinTask: Task
|
||||
@@ -359,13 +416,19 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
private class DependencyAdjuster(
|
||||
private val configuration: Configuration,
|
||||
private val scope: String,
|
||||
private val project: Project
|
||||
private val project: Project,
|
||||
transformations: Collection<MetadataDependencyTransformationBuilder.KotlinMetadataDependencyTransformation>
|
||||
) {
|
||||
private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
|
||||
|
||||
private val projectDependencyTransformation =
|
||||
transformations.filter { it.projectPath != null }.associateBy { it.projectPath }
|
||||
//TODO
|
||||
|
||||
val dependenciesByProjectPath by lazy {
|
||||
configuration
|
||||
.resolvedConfiguration
|
||||
@@ -384,17 +447,61 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
return DefaultExternalProjectDependency(dependency).apply {
|
||||
this.configurationName = newConfigurationName
|
||||
|
||||
val nestedDependencies = this.dependencies.flatMap(::adjustDependency)
|
||||
val nestedDependencies = this.dependencies.flatMap { adjustDependency(it) }
|
||||
this.dependencies.clear()
|
||||
this.dependencies.addAll(nestedDependencies)
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustDependency(dependency: ExternalDependency): List<ExternalDependency> {
|
||||
private val libraryDependencyTransformation =
|
||||
transformations.filter { it.projectPath == null }.associateBy { it.groupId to it.moduleName }
|
||||
|
||||
private fun adjustLibraryDependency(dependency: ExternalDependency, parentScope: String? = null): List<ExternalDependency> =
|
||||
when (dependency) {
|
||||
is ExternalLibraryDependency -> {
|
||||
val replaceFiles = libraryDependencyTransformation[dependency.id.group to dependency.id.name]?.useFilesForSourceSets
|
||||
when {
|
||||
replaceFiles != null -> replaceFiles.flatMap { (sourceSetName, replaceFiles) ->
|
||||
replaceFiles.map { replaceFile ->
|
||||
DefaultExternalLibraryDependency(dependency).apply {
|
||||
// Transitive dependencies don't have their scope set properly; TODO investigate may be IJ bug?
|
||||
scope = dependency.scope ?: parentScope
|
||||
|
||||
classifier = sourceSetName
|
||||
file = replaceFile
|
||||
|
||||
val adjustedDependencies =
|
||||
dependency.dependencies.flatMap { adjustDependency(it, dependency.scope ?: parentScope) }
|
||||
|
||||
dependencies.clear()
|
||||
dependencies.addAll(adjustedDependencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
else ->
|
||||
listOf(
|
||||
// Do nothing but set the correct scope for this dependency if needed and adjust recursively:
|
||||
DefaultExternalLibraryDependency(dependency).apply {
|
||||
scope = dependency.scope ?: parentScope
|
||||
|
||||
val adjustedDependencies =
|
||||
dependency.dependencies.flatMap { adjustDependency(it, dependency.scope ?: parentScope) }
|
||||
|
||||
dependencies.clear()
|
||||
dependencies.addAll(adjustedDependencies)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> listOf(dependency)
|
||||
}
|
||||
|
||||
fun adjustDependency(dependency: ExternalDependency, parentScope: String? = null): List<ExternalDependency> {
|
||||
return adjustmentMap.getOrPut(dependency) {
|
||||
if (dependency !is ExternalProjectDependency
|
||||
|| dependency.configurationName != Dependency.DEFAULT_CONFIGURATION
|
||||
) return@getOrPut listOf(dependency)
|
||||
if (dependency !is ExternalProjectDependency)
|
||||
return@getOrPut adjustLibraryDependency(dependency, parentScope)
|
||||
if (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
|
||||
@@ -420,8 +527,13 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
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)
|
||||
val commonDependencies = if (dependencyProject.path in projectDependencyTransformation) {
|
||||
val visibleSourceSets = projectDependencyTransformation.getValue(dependencyProject.path).allVisibleSourceSets
|
||||
visibleSourceSets.map { sourceSetName -> wrapDependency(dependency, sourceSetName) }
|
||||
} else {
|
||||
listOf(wrapDependency(dependency, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME))
|
||||
}
|
||||
return if (platformDependency != null) listOf(platformDependency) + commonDependencies else commonDependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,9 +558,3 @@ private fun Project.getChildProjectByPath(path: String): Project? {
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user