summaryrefslogtreecommitdiff
path: root/plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt')
-rw-r--r--plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt845
1 files changed, 33 insertions, 812 deletions
diff --git a/plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt b/plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt
index 6e90878e57ed..0d6eab73aa6c 100644
--- a/plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt
+++ b/plugins/kotlin/gradle/gradle-tooling/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinMPPGradleModelBuilder.kt
@@ -5,40 +5,25 @@ package org.jetbrains.kotlin.idea.gradleTooling
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
-import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logging
-import org.gradle.api.provider.Property
-import org.gradle.api.provider.Provider
-import org.gradle.api.tasks.Exec
-import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.idea.gradleTooling.GradleImportProperties.*
import org.jetbrains.kotlin.idea.gradleTooling.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
-import org.jetbrains.kotlin.idea.gradleTooling.arguments.CACHE_MAPPER_BRANCHING
-import org.jetbrains.kotlin.idea.gradleTooling.arguments.buildCachedArgsInfo
-import org.jetbrains.kotlin.idea.gradleTooling.arguments.buildSerializedArgsInfo
-import org.jetbrains.kotlin.idea.projectModel.*
+import org.jetbrains.kotlin.idea.gradleTooling.arguments.CompilerArgumentsCacheMapperImpl
+import org.jetbrains.kotlin.idea.gradleTooling.builders.KotlinSourceSetProtoBuilder
+import org.jetbrains.kotlin.idea.gradleTooling.builders.KotlinTargetBuilder
+import org.jetbrains.kotlin.idea.gradleTooling.reflect.KotlinTargetReflection
+import org.jetbrains.kotlin.idea.projectModel.KotlinPlatform
import org.jetbrains.kotlin.idea.projectModel.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME
import org.jetbrains.kotlin.idea.projectModel.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME
-import org.jetbrains.plugins.gradle.DefaultExternalDependencyId
-import org.jetbrains.plugins.gradle.model.*
+import org.jetbrains.kotlin.idea.projectModel.KotlinTarget
+import org.jetbrains.plugins.gradle.tooling.AbstractModelBuilderService
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderContext
-import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
-import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
-import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder
-import org.jetbrains.plugins.gradle.tooling.util.resolve.DependencyResolverImpl
-import java.io.File
-import java.lang.reflect.Method
-import kotlin.collections.ArrayList
-import kotlin.collections.HashMap
-import kotlin.collections.LinkedHashSet
-
-class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
+
+private val MPP_BUILDER_LOGGER = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
+
+class KotlinMPPGradleModelBuilder : AbstractModelBuilderService() {
+
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
return ErrorMessageBuilder
.create(project, e, "Gradle import errors")
@@ -49,10 +34,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
return modelName == KotlinMPPGradleModel::class.java.name
}
- override fun buildAll(modelName: String, project: Project): KotlinMPPGradleModel? {
- return buildAll(project, null)
- }
-
override fun buildAll(modelName: String, project: Project, builderContext: ModelBuilderContext): KotlinMPPGradleModel? {
return buildAll(project, builderContext)
}
@@ -60,17 +41,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
private fun buildAll(project: Project, builderContext: ModelBuilderContext?): KotlinMPPGradleModel? {
try {
val projectTargets = project.getTargets() ?: return null
- val masterCompilerArgumentsCacheMapper = builderContext?.getData(CACHE_MAPPER_BRANCHING) ?: return null
- val detachableCompilerArgumentsCacheMapper = masterCompilerArgumentsCacheMapper.branchOffDetachable()
+ val modelBuilderContext = builderContext ?: return null
+ val argsMapper = CompilerArgumentsCacheMapperImpl()
- val dependencyResolver = DependencyResolverImpl(project, false, true, SourceSetCachedFinder(project))
- val dependencyMapper = KotlinDependencyMapper()
- val importingContext = MultiplatformModelImportingContextImpl(project, detachableCompilerArgumentsCacheMapper)
+ val importingContext = MultiplatformModelImportingContextImpl(project, argsMapper, modelBuilderContext)
- val sourceSets = buildSourceSets(importingContext, dependencyResolver, dependencyMapper) ?: return null
+ val sourceSets = buildSourceSets(importingContext) ?: return null
importingContext.initializeSourceSets(sourceSets)
- val targets = buildTargets(importingContext, projectTargets, dependencyResolver, dependencyMapper)
+ val targets = buildTargets(importingContext, projectTargets)
importingContext.initializeTargets(targets)
importingContext.initializeCompilations(targets.flatMap { it.compilations })
@@ -87,8 +66,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
isNativeDependencyPropagationEnabled = importingContext.getProperty(ENABLE_NATIVE_DEPENDENCY_PROPAGATION)
),
kotlinNativeHome = kotlinNativeHome,
- dependencyMap = dependencyMapper.toDependencyMap(),
- partialCacheAware = detachableCompilerArgumentsCacheMapper.detachCacheAware()
+ dependencyMap = importingContext.dependencyMapper.toDependencyMap(),
+ cacheAware = argsMapper
).apply {
kotlinImportingDiagnostics += collectDiagnostics(importingContext)
}
@@ -107,24 +86,18 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
val (orphanSourceSets, nonOrphanSourceSets) = importingContext.sourceSets.partition { importingContext.isOrphanSourceSet(it) }
orphanSourceSets.forEach {
- logger.warn("[sync warning] Source set \"${it.name}\" is not compiled with any compilation. This source set is not imported in the IDE.")
+ MPP_BUILDER_LOGGER.warn("[sync warning] Source set \"${it.name}\" is not compiled with any compilation. This source set is not imported in the IDE.")
}
return nonOrphanSourceSets.associateBy { it.name }
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
- val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
- val experimentalExt = getExperimental(kotlinExt) ?: return null
- val getCoroutines = experimentalExt.javaClass.getMethodOrNull("getCoroutines") ?: return null
- return getCoroutines(experimentalExt) as? String
+ val experimentalExt = kotlinExt["getExperimental"] ?: return null
+ return experimentalExt["getCoroutines"] as? String
}
- private fun buildSourceSets(
- importingContext: MultiplatformModelImportingContext,
- dependencyResolver: DependencyResolver,
- dependencyMapper: KotlinDependencyMapper
- ): Map<String, KotlinSourceSetImpl>? {
+ private fun buildSourceSets(importingContext: MultiplatformModelImportingContext): Map<String, KotlinSourceSetImpl>? {
val kotlinExt = importingContext.project.extensions.findByName("kotlin") ?: return null
val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null
@@ -132,9 +105,10 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
val sourceSets =
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
val androidDeps = buildAndroidDeps(importingContext, kotlinExt.javaClass.classLoader)
+ val sourceSetProtoBuilder = KotlinSourceSetProtoBuilder(androidDeps, importingContext.project)
val allSourceSetsProtosByNames = sourceSets.mapNotNull {
- buildSourceSet(it, dependencyResolver, importingContext.project, dependencyMapper, androidDeps)
+ sourceSetProtoBuilder.buildComponent(it, importingContext)
}.associateBy { it.name }
// Some performance optimisation: do not build metadata dependencies if source set is not common
@@ -159,635 +133,19 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
@Suppress("UNCHECKED_CAST")
return getAndroidSourceSetDependencies?.let { it(resolver, importingContext.project) } as Map<String, List<Any>>?
} catch (e: Exception) {
- logger.info("Unexpected exception", e)
+ MPP_BUILDER_LOGGER.info("Unexpected exception", e)
}
}
return null
}
- private fun buildSourceSet(
- gradleSourceSet: Named,
- dependencyResolver: DependencyResolver,
- project: Project,
- dependencyMapper: KotlinDependencyMapper,
- androidDeps: Map<String, List<Any>>?
- ): KotlinSourceSetProto? {
- val sourceSetClass = gradleSourceSet.javaClass
- val getLanguageSettings = sourceSetClass.getMethodOrNull("getLanguageSettings") ?: return null
- val getSourceDirSet = sourceSetClass.getMethodOrNull("getKotlin") ?: return null
- val getResourceDirSet = sourceSetClass.getMethodOrNull("getResources") ?: return null
- val getDependsOn = sourceSetClass.getMethodOrNull("getDependsOn") ?: return null
- val languageSettings = getLanguageSettings(gradleSourceSet)?.let { buildLanguageSettings(it) } ?: return null
- val sourceDirs = (getSourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
- val resourceDirs = (getResourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
-
- @Suppress("UNCHECKED_CAST")
- val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
-
-
- val sourceSetDependenciesBuilder: () -> Array<KotlinDependencyId> = {
- buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project, androidDeps)
- .map { dependencyMapper.getId(it) }
- .distinct()
- .toTypedArray()
- }
-
- val intransitiveSourceSetDependenciesBuilder: () -> Array<KotlinDependencyId> = {
- buildIntransitiveSourceSetDependencies(gradleSourceSet, dependencyResolver, project)
- .map { dependencyMapper.getId(it) }
- .distinct()
- .toTypedArray()
- }
-
- return KotlinSourceSetProto(
- name = gradleSourceSet.name,
- languageSettings = languageSettings,
- sourceDirs = sourceDirs,
- resourceDirs = resourceDirs,
- regularDependencies = sourceSetDependenciesBuilder,
- intransitiveDependencies = intransitiveSourceSetDependenciesBuilder,
- dependsOnSourceSets = dependsOnSourceSets,
- additionalVisibleSourceSets = getAdditionalVisibleSourceSets(project, gradleSourceSet)
- )
- }
-
- private fun buildLanguageSettings(gradleLanguageSettings: Any): KotlinLanguageSettings? {
- val languageSettingsClass = gradleLanguageSettings.javaClass
- val getLanguageVersion = languageSettingsClass.getMethodOrNull("getLanguageVersion") ?: return null
- val getApiVersion = languageSettingsClass.getMethodOrNull("getApiVersion") ?: return null
- val getProgressiveMode = languageSettingsClass.getMethodOrNull("getProgressiveMode") ?: return null
- val getEnabledLanguageFeatures = languageSettingsClass.getMethodOrNull("getEnabledLanguageFeatures") ?: return null
- val getOptInAnnotationsInUse = languageSettingsClass.getMethodOrNull("getOptInAnnotationsInUse")
- val getCompilerPluginArguments = languageSettingsClass.getMethodOrNull("getCompilerPluginArguments")
- val getCompilerPluginClasspath = languageSettingsClass.getMethodOrNull("getCompilerPluginClasspath")
- val getFreeCompilerArgs = languageSettingsClass.getMethodOrNull("getFreeCompilerArgs")
- @Suppress("UNCHECKED_CAST")
- return KotlinLanguageSettingsImpl(
- getLanguageVersion(gradleLanguageSettings) as? String,
- getApiVersion(gradleLanguageSettings) as? String,
- getProgressiveMode(gradleLanguageSettings) as? Boolean ?: false,
- getEnabledLanguageFeatures(gradleLanguageSettings) as? Set<String> ?: emptySet(),
- getOptInAnnotationsInUse?.invoke(gradleLanguageSettings) as? Set<String> ?: emptySet(),
- (getCompilerPluginArguments?.invoke(gradleLanguageSettings) as? List<String> ?: emptyList()).toTypedArray(),
- (getCompilerPluginClasspath?.invoke(gradleLanguageSettings) as? FileCollection)?.files ?: emptySet(),
- (getFreeCompilerArgs?.invoke(gradleLanguageSettings) as? List<String>).orEmpty().toTypedArray()
- )
- }
-
- private fun buildDependencies(
- dependencyHolder: Any,
- dependencyResolver: DependencyResolver,
- configurationNameAccessor: String,
- scope: String,
- 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()
- if (!configuration.isCanBeResolved) return emptyList()
-
- val dependencyAdjuster =
- DependencyAdjuster(configuration, scope, project, metadataDependencyTransformationBuilder.getTransformations(configurationName))
-
- val resolvedDependencies = dependencyResolver
- .resolveDependencies(configuration)
- .apply {
- forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
- forEach<ExternalDependency?> {
- if (it is DefaultExternalProjectDependency && it.projectDependencyArtifacts !is ArrayList) {
- it.projectDependencyArtifacts = ArrayList(it.projectDependencyArtifacts)
- }
- }
- }
- .flatMap { dependencyAdjuster.adjustDependency(it) }
- val singleDependencyFiles = resolvedDependencies.mapNotNullTo(LinkedHashSet()) {
- (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(
importingContext: MultiplatformModelImportingContext,
- projectTargets: Collection<Named>,
- dependencyResolver: DependencyResolver,
- dependencyMapper: KotlinDependencyMapper
+ projectTargets: Collection<Named>
): Collection<KotlinTarget> {
- return projectTargets.mapNotNull { buildTarget(importingContext, it, dependencyResolver, dependencyMapper) }
- }
-
- private fun buildArtifact(
- executableName: String,
- linkTask: Task,
- runConfiguration: KonanRunConfigurationModel,
- exportDependencies: Array<KotlinDependencyId>
- ): KonanArtifactModel? {
- val outputKind = linkTask["getOutputKind"]["name"] as? String ?: return null
- val konanTargetName = linkTask["getTarget"] as? String ?: error("No arch target found")
- val outputFile = (linkTask["getOutputFile"] as? Provider<*>)?.orNull as? File ?: return null
- val compilation = if (linkTask["getCompilation"] is Provider<*>)
- (linkTask["getCompilation"] as Provider<*>).get()
- else
- linkTask["getCompilation"]
- val compilationTarget = compilation["getTarget"]
- val compilationTargetName = compilationTarget["getName"] as? String ?: return null
- val isTests = linkTask["getProcessTests"] as? Boolean ?: return null
-
- @Suppress("UNCHECKED_CAST")
- val freeCompilerArgs = (linkTask["getKotlinOptions"]["getFreeCompilerArgs"] as? List<String>).orEmpty().toTypedArray()
-
- return KonanArtifactModelImpl(
- compilationTargetName,
- executableName,
- outputKind,
- konanTargetName,
- outputFile,
- linkTask.path,
- runConfiguration,
- isTests,
- freeCompilerArgs,
- exportDependencies
- )
- }
-
- private fun konanArtifacts(
- target: Named, dependencyResolver: DependencyResolver, project: Project, dependencyMapper: KotlinDependencyMapper
- ): List<KonanArtifactModel> {
- val result = ArrayList<KonanArtifactModel>()
-
- val binaries = target["getBinaries"] as? Collection<*> ?: return result
- binaries.forEach { binary ->
- val executableName = binary["getBaseName"] as? String ?: ""
- val linkTask = binary["getLinkTask"] as? Task ?: return@forEach
- val runConfiguration = KonanRunConfigurationModelImpl(binary["getRunTask"] as? Exec)
- val exportDependencies = binary?.let {
- val transformationBuilder = MetadataDependencyTransformationBuilder(it)
- buildDependencies(it, dependencyResolver, "getExportConfigurationName", "COMPILE", project, transformationBuilder)
- }
-
- buildArtifact(
- executableName,
- linkTask,
- runConfiguration,
- exportDependencies?.map { dependencyMapper.getId(it) }?.distinct()?.toTypedArray() ?: emptyArray()
- )?.let { result.add(it) }
- }
-
- return result
- }
-
- private fun buildTarget(
- importingContext: MultiplatformModelImportingContext,
- gradleTarget: Named,
- dependencyResolver: DependencyResolver,
- dependencyMapper: KotlinDependencyMapper
- ): KotlinTarget? {
- val targetClass = gradleTarget.javaClass
-
- /* Loading class safely to still support Kotlin Gradle Plugin 1.3.30 */
- val metadataTargetClass = targetClass.classLoader.loadClassOrNull("org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget")
- if (metadataTargetClass?.isInstance(gradleTarget) == true) return null
-
- val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null
- val getDisambiguationClassifier = targetClass.getMethodOrNull("getDisambiguationClassifier") ?: return null
- val platformId = (getPlatformType.invoke(gradleTarget) as? Named)?.name ?: return null
- val platform = KotlinPlatform.byId(platformId) ?: return null
- val useDisambiguationClassifier =
- targetClass.getMethodOrNull("getUseDisambiguationClassifierAsSourceSetNamePrefix")?.invoke(gradleTarget) as? Boolean ?: true
- val disambiguationClassifier = if (useDisambiguationClassifier)
- getDisambiguationClassifier(gradleTarget) as? String
- else {
- targetClass.getMethodOrNull("getOverrideDisambiguationClassifierOnIdeImport")?.invoke(gradleTarget) as? String
- }
- val getPreset = targetClass.getMethodOrNull("getPreset")
- val targetPresetName: String? = try {
- val targetPreset = getPreset?.invoke(gradleTarget)
- val getPresetName = targetPreset?.javaClass?.getMethodOrNull("getName")
- getPresetName?.invoke(targetPreset) as? String
- } catch (e: Throwable) {
- "${e::class.java.name}:${e.message}"
- }
-
- val gradleCompilations = gradleTarget.compilations ?: return null
- val compilations = gradleCompilations.mapNotNull {
- buildCompilation(importingContext, it, platform, disambiguationClassifier, dependencyResolver, dependencyMapper)
- }
- val jar = buildTargetJar(gradleTarget, importingContext.project)
- val testRunTasks = buildTestRunTasks(importingContext.project, gradleTarget)
- val nativeMainRunTasks =
- if (platform == KotlinPlatform.NATIVE) buildNativeMainRunTasks(gradleTarget)
- else emptyList()
- val artifacts = konanArtifacts(gradleTarget, dependencyResolver, importingContext.project, dependencyMapper)
- val target = KotlinTargetImpl(
- gradleTarget.name,
- targetPresetName,
- disambiguationClassifier,
- platform,
- compilations,
- testRunTasks,
- nativeMainRunTasks,
- jar,
- artifacts
- )
- compilations.forEach {
- it.disambiguationClassifier = target.disambiguationClassifier
- it.platform = target.platform
- }
- return target
- }
-
- private fun buildNativeMainRunTasks(gradleTarget: Named): Collection<KotlinNativeMainRunTask> {
- val executableBinaries = (gradleTarget::class.java.getMethodOrNull("getBinaries")?.invoke(gradleTarget) as? Collection<Any>)
- ?.filter { it.javaClass.name == "org.jetbrains.kotlin.gradle.plugin.mpp.Executable" } ?: return emptyList()
- return executableBinaries.mapNotNull { binary ->
- val runTaskName = binary::class.java.getMethod("getRunTaskName").invoke(binary) as String? ?: return@mapNotNull null
- val entryPoint = binary::class.java.getMethod("getEntryPoint").invoke(binary) as String? ?: return@mapNotNull null
- val debuggable = binary::class.java.getMethod("getDebuggable").invoke(binary) as Boolean
-
- val compilationName = binary.javaClass.getMethodOrNull("getCompilation")?.invoke(binary)?.let {
- it.javaClass.getMethodOrNull("getCompilationName")?.invoke(it)?.toString()
- } ?: KotlinCompilation.MAIN_COMPILATION_NAME
-
- KotlinNativeMainRunTaskImpl(
- runTaskName,
- compilationName,
- entryPoint,
- debuggable
- )
- }
- }
-
- private fun Named.testTaskClass(className: String) = try {
- // This is a workaround that makes assumptions about the tasks naming logic
- // and is therefore an unstable and temporary solution until test runs API is implemented:
- @Suppress("UNCHECKED_CAST")
- this.javaClass.classLoader.loadClass(className) as Class<out Task>
- } catch (_: ClassNotFoundException) {
- null
- }
-
- private fun buildTestRunTasks(project: Project, gradleTarget: Named): Collection<KotlinTestRunTask> {
- val getTestRunsMethod = gradleTarget.javaClass.getMethodOrNull("getTestRuns")
- if (getTestRunsMethod != null) {
- val testRuns = getTestRunsMethod.invoke(gradleTarget) as? Iterable<Any>
- if (testRuns != null) {
- val testReports =
- testRuns.mapNotNull { (it.javaClass.getMethodOrNull("getExecutionTask")?.invoke(it) as? TaskProvider<Task>)?.get() }
- val testTasks = testReports.flatMap {
- ((it.javaClass.getMethodOrNull("getTestTasks")?.invoke(it) as? Collection<Any>)?.mapNotNull {
- //TODO(auskov): getTestTasks should return collection of TaskProviders without mixing with Tasks
- when (it) {
- is Provider<*> -> it.get() as? Task
- is Task -> it
- else -> null
- }
- }) ?: listOf(it)
- }
- return testTasks.filter { it.enabled }.map {
- val name = it.name
- val compilation = it.javaClass.getMethodOrNull("getCompilation")?.invoke(it)
- val compilationName = compilation?.javaClass?.getMethodOrNull("getCompilationName")?.invoke(compilation)?.toString()
- ?: KotlinCompilation.TEST_COMPILATION_NAME
- KotlinTestRunTaskImpl(name, compilationName)
- }.toList()
- }
- return emptyList()
- }
-
- // Otherwise, find the Kotlin/JVM test task with names matching the target name or
- // aggregate Android JVM tasks (like testDebugUnitTest).
- val kotlinTestTaskClass = gradleTarget.testTaskClass("org.jetbrains.kotlin.gradle.tasks.KotlinTest") ?: return emptyList()
-
- val targetDisambiguationClassifier = run {
- val getDisambiguationClassifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier")
- ?: return emptyList()
-
- getDisambiguationClassifier(gradleTarget) as String?
- }
-
- // The 'targetName' of a test task matches the target disambiguation classifier, potentially with suffix, e.g. jsBrowser
- val getTargetName = kotlinTestTaskClass.getDeclaredMethodOrNull("getTargetName") ?: return emptyList()
-
- val jvmTestTaskClass =
- gradleTarget.testTaskClass("org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest") ?: return emptyList()
- val getJvmTargetName = jvmTestTaskClass.getDeclaredMethodOrNull("getTargetName") ?: return emptyList()
-
- if (targetDisambiguationClassifier == "android") {
- val androidUnitTestClass = gradleTarget.testTaskClass("com.android.build.gradle.tasks.factory.AndroidUnitTest")
- ?: return emptyList()
-
- return project.tasks.filter { androidUnitTestClass.isInstance(it) }.mapNotNull { task -> task.name }
- .map { KotlinTestRunTaskImpl(it, KotlinCompilation.TEST_COMPILATION_NAME) }
+ return projectTargets.mapNotNull {
+ KotlinTargetBuilder.buildComponent(KotlinTargetReflection(it), importingContext)
}
-
- return project.tasks.filter { kotlinTestTaskClass.isInstance(it) || jvmTestTaskClass.isInstance(it) }.mapNotNull { task ->
- val testTaskDisambiguationClassifier =
- (if (kotlinTestTaskClass.isInstance(task)) getTargetName(task) else getJvmTargetName(task)) as String?
- task.name.takeIf {
- targetDisambiguationClassifier.isNullOrEmpty() ||
- testTaskDisambiguationClassifier != null &&
- testTaskDisambiguationClassifier.startsWith(targetDisambiguationClassifier.orEmpty())
- }
- }.map { KotlinTestRunTaskImpl(it, KotlinCompilation.TEST_COMPILATION_NAME) }
- }
-
- private fun buildTargetJar(gradleTarget: Named, project: Project): KotlinTargetJar? {
- val targetClass = gradleTarget.javaClass
- val getArtifactsTaskName = targetClass.getMethodOrNull("getArtifactsTaskName") ?: return null
- val artifactsTaskName = getArtifactsTaskName(gradleTarget) as? String ?: return null
- val jarTask = project.tasks.findByName(artifactsTaskName) ?: return null
- val jarTaskClass = jarTask.javaClass
- val getArchivePath = jarTaskClass.getMethodOrNull("getArchivePath")
- val archiveFile = getArchivePath?.invoke(jarTask) as? File?
- return KotlinTargetJarImpl(archiveFile)
- }
-
- private fun buildCompilation(
- importingContext: MultiplatformModelImportingContext,
- gradleCompilation: Named,
- platform: KotlinPlatform,
- classifier: String?,
- dependencyResolver: DependencyResolver,
- dependencyMapper: KotlinDependencyMapper
- ): KotlinCompilationImpl? {
- val compilationClass = gradleCompilation.javaClass
- val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null
-
- @Suppress("UNCHECKED_CAST")
- val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection<Named>) ?: return null
- val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { importingContext.sourceSetByName(it.name) }
- val compileKotlinTask = gradleCompilation.getCompileKotlinTaskName(importingContext.project) ?: return null
- val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null
- val dependencies =
- buildCompilationDependencies(importingContext, gradleCompilation, classifier, dependencyResolver, dependencyMapper)
- val kotlinTaskProperties = getKotlinTaskProperties(compileKotlinTask, classifier)
-
- // Get konanTarget (for native compilations only).
- val konanTarget = compilationClass.getMethodOrNull("getKonanTarget")?.let { getKonanTarget ->
- val konanTarget = getKonanTarget.invoke(gradleCompilation)
- konanTarget.javaClass.getMethodOrNull("getName")?.let {
- it.invoke(konanTarget) as? String
- }
- }
- val nativeExtensions = konanTarget?.let(::KotlinNativeCompilationExtensionsImpl)
-
- val allSourceSets = kotlinSourceSets
- .flatMap { sourceSet -> importingContext.resolveAllDependsOnSourceSets(sourceSet) }
- .union(kotlinSourceSets)
-
- val cachedArgsInfo = if (compileKotlinTask.isCompilerArgumentAware)
- buildCachedArgsInfo(compileKotlinTask, importingContext.compilerArgumentsCacheMapper)
- else
- buildSerializedArgsInfo(compileKotlinTask, importingContext.compilerArgumentsCacheMapper, logger)
-
- @Suppress("DEPRECATION_ERROR")
- return KotlinCompilationImpl(
- name = gradleCompilation.name,
- allSourceSets = allSourceSets,
- declaredSourceSets = if (platform == KotlinPlatform.ANDROID) allSourceSets else kotlinSourceSets,
- dependencies = dependencies.map { dependencyMapper.getId(it) }.distinct().toTypedArray(),
- output = output,
- arguments = KotlinCompilationArgumentsImpl(emptyArray(), emptyArray()),
- dependencyClasspath = emptyArray(),
- cachedArgsInfo = cachedArgsInfo,
- kotlinTaskProperties = kotlinTaskProperties,
- nativeExtensions = nativeExtensions
- )
- }
-
-
- /**
- * Returns only those dependencies with RUNTIME scope which are not present with compile scope
- */
- private fun Collection<KotlinDependency>.onlyNewDependencies(compileDependencies: Collection<KotlinDependency>): List<KotlinDependency> {
- val compileDependencyArtefacts =
- compileDependencies.flatMap { (it as? ExternalProjectDependency)?.projectDependencyArtifacts ?: emptyList() }
- return this.filter {
- if (it is ExternalProjectDependency)
- !(compileDependencyArtefacts.containsAll(it.projectDependencyArtifacts))
- else
- true
- }
- }
-
- private fun buildCompilationDependencies(
- importingContext: MultiplatformModelImportingContext,
- gradleCompilation: Named,
- classifier: String?,
- dependencyResolver: DependencyResolver,
- dependencyMapper: KotlinDependencyMapper
- ): Set<KotlinDependency> {
- return LinkedHashSet<KotlinDependency>().apply {
- val transformationBuilder = MetadataDependencyTransformationBuilder(gradleCompilation)
- this += buildDependencies(
- gradleCompilation,
- dependencyResolver,
- "getCompileDependencyConfigurationName",
- "COMPILE",
- importingContext.project,
- transformationBuilder
- )
- this += buildDependencies(
- gradleCompilation,
- dependencyResolver,
- "getRuntimeDependencyConfigurationName",
- "RUNTIME",
- importingContext.project,
- transformationBuilder
- ).onlyNewDependencies(this)
-
- val sourceSet = importingContext.sourceSetByName(compilationFullName(gradleCompilation.name, classifier))
- this += sourceSet?.dependencies?.mapNotNull { dependencyMapper.getDependency(it) } ?: 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,
- androidDeps: Map<String, List<Any>>?
- ): List<KotlinDependency> {
- return ArrayList<KotlinDependency>().apply {
- buildAndroidSourceSetDependencies(androidDeps, gradleSourceSet).let { androidDepsForSourceSet ->
- if (androidDepsForSourceSet.isNotEmpty()) {
- this += androidDepsForSourceSet
- return@apply
- }
- }
-
- if (project.skipBuildingMetadataDependenciesForSourceSet(gradleSourceSet)) return@apply
-
- val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
- this += buildDependencies(
- gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project, transformationBuilder
- )
- this += buildDependencies(
- gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project, transformationBuilder
- )
- this += buildDependencies(
- gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project, transformationBuilder
- )
- this += buildDependencies(
- gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
- ).onlyNewDependencies(this)
- }
- }
-
- private fun buildIntransitiveSourceSetDependencies(
- gradleSourceSet: Named,
- dependencyResolver: DependencyResolver,
- project: Project
- ): List<KotlinDependency> {
- val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
- return buildDependencies(
- gradleSourceSet, dependencyResolver, "getIntransitiveMetadataConfigurationName", "COMPILE", project, transformationBuilder
- ).toList()
- }
-
- /**
- * Building Metadata Dependencies for android-specific source-sets might fail
- * due to debug & release variants available for the same gradle resolution request.
- * Luckily, there is no need of building Metadata Dependencies for such case.
- *
- * @see KTIJ-20097
- */
- private fun Project.skipBuildingMetadataDependenciesForSourceSet(gradleSourceSet: Named): Boolean {
- val isHMPPEnabled = getProperty(IS_HMPP_ENABLED)
- if (!isHMPPEnabled) return false
-
- val sourceSetPlatforms = mutableSetOf<KotlinPlatform>()
- val targets = getTargets().orEmpty()
-
- for (target in targets) {
- val platform = target
- .get("getPlatformType")
- ?.get("getName")
- ?.let { it as? String }
- ?.let { KotlinPlatform.byId(it) }
- ?: continue
-
- for (compilation in target.compilations.orEmpty()) {
- val allSourceSets = compilation
- .get("getAllKotlinSourceSets")
- ?.let { it as? Collection<*> }
- ?.filterIsInstance<Named>()
- ?.toSet()
- ?: emptySet()
-
- if (gradleSourceSet in allSourceSets) {
- sourceSetPlatforms.add(platform)
- }
- }
- }
-
- return sourceSetPlatforms.singleOrNull() == KotlinPlatform.ANDROID
- }
-
- private fun buildAndroidSourceSetDependencies(
- androidDeps: Map<String, List<Any>>?,
- gradleSourceSet: Named
- ): Collection<KotlinDependency> {
- return androidDeps?.get(gradleSourceSet.name)?.mapNotNull { it ->
- @Suppress("UNCHECKED_CAST")
- val collection = it["getCollection"] as Set<File>?
- if (collection == null) {
- DefaultExternalLibraryDependency().apply {
- (id as? DefaultExternalDependencyId)?.apply {
- name = it["getName"] as String?
- group = it["getGroup"] as String?
- version = it["getVersion"] as String?
- }
- file = it["getJar"] as File? ?: return@mapNotNull null.also {
- logger.warn("[sync warning] ${gradleSourceSet.name}: $id does not resolve to a jar")
- }
- source = it["getSource"] as File?
- }
- } else {
- DefaultFileCollectionDependency(collection)
- }
- } ?: emptyList()
- }
-
- private fun buildCompilationOutput(
- gradleCompilation: Named,
- compileKotlinTask: Task
- ): KotlinCompilationOutput? {
- val compilationClass = gradleCompilation.javaClass
- val getOutput = compilationClass.getMethodOrNull("getOutput") ?: return null
- val gradleOutput = getOutput(gradleCompilation) ?: return null
- val gradleOutputClass = gradleOutput.javaClass
- val getClassesDirs = gradleOutputClass.getMethodOrNull("getClassesDirs") ?: return null
- val getResourcesDir = gradleOutputClass.getMethodOrNull("getResourcesDir") ?: return null
- val compileKotlinTaskClass = compileKotlinTask.javaClass
- val getDestinationDir = compileKotlinTaskClass.getMethodOrNull("getDestinationDir")
- val getOutputFile = compileKotlinTaskClass.getMethodOrNull("getOutputFile")
- val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null
- val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null
- @Suppress("UNCHECKED_CAST") val destinationDir =
- getDestinationDir?.invoke(compileKotlinTask) as? File
- //TODO: Hack for KotlinNativeCompile
- ?: (getOutputFile?.invoke(compileKotlinTask) as? Property<File>)?.orNull?.parentFile
- ?: return null
- return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir)
}
private fun computeSourceSetsDeferredInfo(importingContext: MultiplatformModelImportingContext) {
@@ -795,16 +153,16 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
if (!importingContext.getProperty(IS_HMPP_ENABLED)) {
val name = sourceSet.name
if (name == COMMON_MAIN_SOURCE_SET_NAME) {
- sourceSet.isTestModule = false
+ sourceSet.isTestComponent = false
continue
}
if (name == COMMON_TEST_SOURCE_SET_NAME) {
- sourceSet.isTestModule = true
+ sourceSet.isTestComponent = true
continue
}
}
- sourceSet.isTestModule = importingContext.compilationsBySourceSet(sourceSet)?.all { it.isTestModule } ?: false
+ sourceSet.isTestComponent = importingContext.compilationsBySourceSet(sourceSet)?.all { it.isTestComponent } ?: false
importingContext.computeSourceSetPlatforms(sourceSet)
}
@@ -861,133 +219,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
}
}
- private class DependencyAdjuster(
- private val configuration: Configuration,
- private val scope: String,
- private val project: Project,
- transformations: Collection<MetadataDependencyTransformationBuilder.KotlinMetadataDependencyTransformation>
- ) {
- private val adjustmentMap = HashMap<ExternalDependency, List<ExternalDependency>>()
-
- private val EXTRA_DEFAULT_CONFIGURATION_NAMES = listOf("metadataApiElements")
-
- private val projectDependencyTransformation =
- transformations.filter { it.projectPath != null }.associateBy { it.projectPath }
-
- 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(it) }
- this.dependencies.clear()
- this.dependencies.addAll(nestedDependencies)
- }
- }
-
- 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)
- return@getOrPut adjustLibraryDependency(dependency, parentScope)
- if (dependency.configurationName != Dependency.DEFAULT_CONFIGURATION &&
- !EXTRA_DEFAULT_CONFIGURATION_NAMES.contains(dependency.configurationName)
- )
- 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 dependencyProject =
- if (project.rootProject.path == dependency.projectPath)
- project.rootProject
- else
- project.rootProject.getChildProjectByPath(dependency.projectPath)
-
- val targets = dependencyProject?.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 commonDependencies = if (dependencyProject.path in projectDependencyTransformation) {
- val visibleSourceSets = projectDependencyTransformation.getValue(dependencyProject.path).allVisibleSourceSets
- visibleSourceSets.map { sourceSetName -> wrapDependency(dependency, sourceSetName) }
- } else {
- listOf(wrapDependency(dependency, COMMON_MAIN_SOURCE_SET_NAME))
- }
- return if (platformDependency != null) listOf(platformDependency) + commonDependencies else commonDependencies
- }
- }
- }
-
companion object {
- private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java)
- private const val COMPILER_ARGUMENT_AWARE_CLASS = "org.jetbrains.kotlin.gradle.internal.CompilerArgumentAware"
private val DEFAULT_IMPORTING_CHECKERS = listOf(
OrphanSourceSetImportingChecker
@@ -999,16 +231,5 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService.Ex {
it.check(this@collectDiagnostics, this, importingContext)
}
}
-
- private val Task.isCompilerArgumentAware: Boolean
- get() = javaClass.classLoader.loadClassOrNull(COMPILER_ARGUMENT_AWARE_CLASS)?.isAssignableFrom(javaClass) ?: false
- }
-}
-
-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
-}
+} \ No newline at end of file