From f514424324cea549586df8e76142309ffac7f9a7 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Sat, 8 Sep 2018 11:23:42 +0300 Subject: [PATCH] Revert non-convention KotlinNativeCompile task in import 173 / AS31 'Property' is not supported for these IDEA / AS versions --- .../src/KotlinMPPGradleModelBuilder.kt.173 | 310 ++++++++++++++++++ .../src/KotlinMPPGradleModelBuilder.kt.as31 | 310 ++++++++++++++++++ 2 files changed, 620 insertions(+) create mode 100644 idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.173 create mode 100644 idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.as31 diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.173 b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.173 new file mode 100644 index 00000000000..45cd6e292e2 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.173 @@ -0,0 +1,310 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +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.Dependency +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.file.FileCollection +import org.gradle.api.file.SourceDirectorySet +import org.jetbrains.plugins.gradle.model.AbstractExternalDependency +import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency +import org.jetbrains.plugins.gradle.model.ExternalDependency +import org.jetbrains.plugins.gradle.model.ExternalProjectDependency +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl +import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder +import java.io.File +import java.lang.Exception + +class KotlinMPPGradleModelBuilder : ModelBuilderService { + override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder { + return ErrorMessageBuilder + .create(project, e, "Gradle import errors") + .withDescription("Unable to build Kotlin project configuration") + } + + override fun canBuild(modelName: String?): Boolean { + return modelName == KotlinMPPGradleModel::class.java.name + } + + override fun buildAll(modelName: String, project: Project): Any? { + val dependencyResolver = DependencyResolverImpl( + project, + false, + false, + true, + SourceSetCachedFinder(project) + ) + val sourceSets = buildSourceSets(dependencyResolver, project) ?: return null + val sourceSetMap = sourceSets.map { it.name to it }.toMap() + val targets = buildTargets(sourceSetMap, dependencyResolver, project) ?: return null + computeSourceSetsDeferredInfo(sourceSets, targets) + val coroutinesState = getCoroutinesState(project) + return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState)) + } + + 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 + } + + private fun buildSourceSets(dependencyResolver: DependencyResolver, project: Project): Collection? { + val kotlinExt = project.extensions.findByName("kotlin") ?: return null + val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null + @Suppress("UNCHECKED_CAST") + val sourceSets = + (getSourceSets(kotlinExt) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + return sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project) } + } + + private fun buildSourceSet( + gradleSourceSet: Named, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinSourceSetImpl? { + 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() + val dependencies = buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project) + @Suppress("UNCHECKED_CAST") + val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet() + return KotlinSourceSetImpl(gradleSourceSet.name, languageSettings, sourceDirs, resourceDirs, dependencies, dependsOnSourceSets) + } + + 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 + @Suppress("UNCHECKED_CAST") + return KotlinLanguageSettingsImpl( + getLanguageVersion(gradleLanguageSettings) as? String, + getApiVersion(gradleLanguageSettings) as? String, + getProgressiveMode(gradleLanguageSettings) as? Boolean ?: false, + getEnabledLanguageFeatures(gradleLanguageSettings) as? Set ?: emptySet() + ) + } + + private fun buildDependencies( + dependencyHolder: Any, + dependencyResolver: DependencyResolver, + configurationNameAccessor: String, + scope: String, + project: Project + ): Collection { + 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 artifactsByProjectPath = configuration + .resolvedConfiguration + .lenientConfiguration + .firstLevelModuleDependencies + .flatMap { it.moduleArtifacts } + .filter { it.id.componentIdentifier is ProjectComponentIdentifier } + .groupBy { (it.id.componentIdentifier as ProjectComponentIdentifier).projectPath } + return dependencyResolver.resolveDependencies(configuration) + .apply { + forEach { (it as? AbstractExternalDependency)?.scope = scope } + } + .map { + if (it !is ExternalProjectDependency || it.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map it + val artifacts = artifactsByProjectPath[it.projectPath] ?: return@map it + val classifier = artifacts.mapTo(LinkedHashSet()) { it.classifier }.singleOrNull() ?: return@map it + DefaultExternalProjectDependency(it).apply { + this.classifier = classifier + } + } + } + + private fun buildTargets( + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): Collection? { + val kotlinExt = project.extensions.findByName("kotlin") ?: return null + val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null + @Suppress("UNCHECKED_CAST") + val targets = (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + return targets.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) } + } + + private fun buildTarget( + gradleTarget: Named, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinTarget? { + val targetClass = gradleTarget.javaClass + val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null + val getCompilations = targetClass.getMethodOrNull("getCompilations") ?: 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 disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String + @Suppress("UNCHECKED_CAST") + val gradleCompilations = + (getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + val compilations = gradleCompilations.mapNotNull { + buildCompilation(it, disambiguationClassifier, sourceSetMap, dependencyResolver, project) + } + val jar = buildTargetJar(gradleTarget, project) + val target = KotlinTargetImpl(gradleTarget.name, disambiguationClassifier, platform, compilations, jar) + compilations.forEach { it.target = target } + return target + } + + 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( + gradleCompilation: Named, + classifier: String?, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinCompilationImpl? { + val compilationClass = gradleCompilation.javaClass + val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null + @Suppress("UNCHECKED_CAST") + val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection) ?: return null + val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { sourceSetMap[it.name] } + val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null + @Suppress("UNCHECKED_CAST") + val compileKotlinTaskName = (getCompileKotlinTaskName(gradleCompilation) as? String) ?: return null + val compileKotlinTask = project.tasks.findByName(compileKotlinTaskName) ?: return null + val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null + val arguments = buildCompilationArguments(compileKotlinTask) ?: return null + val dependencyClasspath = buildDependencyClasspath(compileKotlinTask) + val dependencies = buildCompilationDependencies(gradleCompilation, classifier, sourceSetMap, dependencyResolver, project) + return KotlinCompilationImpl(gradleCompilation.name, kotlinSourceSets, dependencies, output, arguments, dependencyClasspath) + } + + private fun buildCompilationDependencies( + gradleCompilation: Named, + classifier: String?, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): Set { + return LinkedHashSet().apply { + this += buildDependencies( + gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project + ) + this += sourceSetMap[compilationFullName(gradleCompilation.name, classifier)]?.dependencies ?: emptySet() + } + } + + private fun buildSourceSetDependencies( + gradleSourceSet: Named, + dependencyResolver: DependencyResolver, + project: Project + ): Set { + return LinkedHashSet().apply { + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project + ) + } + } + + private fun buildCompilationArguments(compileKotlinTask: Task): KotlinCompilationArguments? { + val compileTaskClass = compileKotlinTask.javaClass + val getCurrentArguments = compileTaskClass.getMethodOrNull("getSerializedCompilerArguments") ?: return null + val getDefaultArguments = compileTaskClass.getMethodOrNull("getDefaultSerializedCompilerArguments") ?: return null + @Suppress("UNCHECKED_CAST") + val currentArguments = getCurrentArguments(compileKotlinTask) as? List ?: return null + @Suppress("UNCHECKED_CAST") + val defaultArguments = getDefaultArguments(compileKotlinTask) as? List ?: return null + return KotlinCompilationArgumentsImpl(defaultArguments, currentArguments) + } + + private fun buildDependencyClasspath(compileKotlinTask: Task): List { + val abstractKotlinCompileClass = + compileKotlinTask.javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS) + val getCompileClasspath = + abstractKotlinCompileClass.getDeclaredMethodOrNull("getCompileClasspath") ?: return emptyList() + @Suppress("UNCHECKED_CAST") + return (getCompileClasspath(compileKotlinTask) as? Collection)?.map { it.path } ?: 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") ?: return null + val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null + val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null + val destinationDir = getDestinationDir(compileKotlinTask) as? File + return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir) + } + + private fun computeSourceSetsDeferredInfo( + sourceSets: Collection, + targets: Collection + ) { + val sourceSetToCompilations = LinkedHashMap>() + for (target in targets) { + for (compilation in target.compilations) { + for (sourceSet in compilation.sourceSets) { + sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation + } + } + } + for (sourceSet in sourceSets) { + val compilations = sourceSetToCompilations[sourceSet] + if (compilations != null) { + sourceSet.platform = compilations.map { it.platform }.distinct().singleOrNull() ?: KotlinPlatform.COMMON + sourceSet.isTestModule = compilations.all { it.isTestModule } + } else { + // TODO: change me after design about it + sourceSet.isTestModule = "Test" in sourceSet.name + } + } + } +} \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.as31 b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.as31 new file mode 100644 index 00000000000..45cd6e292e2 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.as31 @@ -0,0 +1,310 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle + +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.Dependency +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.file.FileCollection +import org.gradle.api.file.SourceDirectorySet +import org.jetbrains.plugins.gradle.model.AbstractExternalDependency +import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency +import org.jetbrains.plugins.gradle.model.ExternalDependency +import org.jetbrains.plugins.gradle.model.ExternalProjectDependency +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl +import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder +import java.io.File +import java.lang.Exception + +class KotlinMPPGradleModelBuilder : ModelBuilderService { + override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder { + return ErrorMessageBuilder + .create(project, e, "Gradle import errors") + .withDescription("Unable to build Kotlin project configuration") + } + + override fun canBuild(modelName: String?): Boolean { + return modelName == KotlinMPPGradleModel::class.java.name + } + + override fun buildAll(modelName: String, project: Project): Any? { + val dependencyResolver = DependencyResolverImpl( + project, + false, + false, + true, + SourceSetCachedFinder(project) + ) + val sourceSets = buildSourceSets(dependencyResolver, project) ?: return null + val sourceSetMap = sourceSets.map { it.name to it }.toMap() + val targets = buildTargets(sourceSetMap, dependencyResolver, project) ?: return null + computeSourceSetsDeferredInfo(sourceSets, targets) + val coroutinesState = getCoroutinesState(project) + return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState)) + } + + 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 + } + + private fun buildSourceSets(dependencyResolver: DependencyResolver, project: Project): Collection? { + val kotlinExt = project.extensions.findByName("kotlin") ?: return null + val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null + @Suppress("UNCHECKED_CAST") + val sourceSets = + (getSourceSets(kotlinExt) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + return sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project) } + } + + private fun buildSourceSet( + gradleSourceSet: Named, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinSourceSetImpl? { + 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() + val dependencies = buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project) + @Suppress("UNCHECKED_CAST") + val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet() + return KotlinSourceSetImpl(gradleSourceSet.name, languageSettings, sourceDirs, resourceDirs, dependencies, dependsOnSourceSets) + } + + 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 + @Suppress("UNCHECKED_CAST") + return KotlinLanguageSettingsImpl( + getLanguageVersion(gradleLanguageSettings) as? String, + getApiVersion(gradleLanguageSettings) as? String, + getProgressiveMode(gradleLanguageSettings) as? Boolean ?: false, + getEnabledLanguageFeatures(gradleLanguageSettings) as? Set ?: emptySet() + ) + } + + private fun buildDependencies( + dependencyHolder: Any, + dependencyResolver: DependencyResolver, + configurationNameAccessor: String, + scope: String, + project: Project + ): Collection { + 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 artifactsByProjectPath = configuration + .resolvedConfiguration + .lenientConfiguration + .firstLevelModuleDependencies + .flatMap { it.moduleArtifacts } + .filter { it.id.componentIdentifier is ProjectComponentIdentifier } + .groupBy { (it.id.componentIdentifier as ProjectComponentIdentifier).projectPath } + return dependencyResolver.resolveDependencies(configuration) + .apply { + forEach { (it as? AbstractExternalDependency)?.scope = scope } + } + .map { + if (it !is ExternalProjectDependency || it.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map it + val artifacts = artifactsByProjectPath[it.projectPath] ?: return@map it + val classifier = artifacts.mapTo(LinkedHashSet()) { it.classifier }.singleOrNull() ?: return@map it + DefaultExternalProjectDependency(it).apply { + this.classifier = classifier + } + } + } + + private fun buildTargets( + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): Collection? { + val kotlinExt = project.extensions.findByName("kotlin") ?: return null + val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null + @Suppress("UNCHECKED_CAST") + val targets = (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + return targets.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) } + } + + private fun buildTarget( + gradleTarget: Named, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinTarget? { + val targetClass = gradleTarget.javaClass + val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null + val getCompilations = targetClass.getMethodOrNull("getCompilations") ?: 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 disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String + @Suppress("UNCHECKED_CAST") + val gradleCompilations = + (getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + val compilations = gradleCompilations.mapNotNull { + buildCompilation(it, disambiguationClassifier, sourceSetMap, dependencyResolver, project) + } + val jar = buildTargetJar(gradleTarget, project) + val target = KotlinTargetImpl(gradleTarget.name, disambiguationClassifier, platform, compilations, jar) + compilations.forEach { it.target = target } + return target + } + + 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( + gradleCompilation: Named, + classifier: String?, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): KotlinCompilationImpl? { + val compilationClass = gradleCompilation.javaClass + val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null + @Suppress("UNCHECKED_CAST") + val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection) ?: return null + val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { sourceSetMap[it.name] } + val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null + @Suppress("UNCHECKED_CAST") + val compileKotlinTaskName = (getCompileKotlinTaskName(gradleCompilation) as? String) ?: return null + val compileKotlinTask = project.tasks.findByName(compileKotlinTaskName) ?: return null + val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null + val arguments = buildCompilationArguments(compileKotlinTask) ?: return null + val dependencyClasspath = buildDependencyClasspath(compileKotlinTask) + val dependencies = buildCompilationDependencies(gradleCompilation, classifier, sourceSetMap, dependencyResolver, project) + return KotlinCompilationImpl(gradleCompilation.name, kotlinSourceSets, dependencies, output, arguments, dependencyClasspath) + } + + private fun buildCompilationDependencies( + gradleCompilation: Named, + classifier: String?, + sourceSetMap: Map, + dependencyResolver: DependencyResolver, + project: Project + ): Set { + return LinkedHashSet().apply { + this += buildDependencies( + gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project + ) + this += sourceSetMap[compilationFullName(gradleCompilation.name, classifier)]?.dependencies ?: emptySet() + } + } + + private fun buildSourceSetDependencies( + gradleSourceSet: Named, + dependencyResolver: DependencyResolver, + project: Project + ): Set { + return LinkedHashSet().apply { + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getApiMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getImplementationMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getCompileOnlyMetadataConfigurationName", "COMPILE", project + ) + this += buildDependencies( + gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project + ) + } + } + + private fun buildCompilationArguments(compileKotlinTask: Task): KotlinCompilationArguments? { + val compileTaskClass = compileKotlinTask.javaClass + val getCurrentArguments = compileTaskClass.getMethodOrNull("getSerializedCompilerArguments") ?: return null + val getDefaultArguments = compileTaskClass.getMethodOrNull("getDefaultSerializedCompilerArguments") ?: return null + @Suppress("UNCHECKED_CAST") + val currentArguments = getCurrentArguments(compileKotlinTask) as? List ?: return null + @Suppress("UNCHECKED_CAST") + val defaultArguments = getDefaultArguments(compileKotlinTask) as? List ?: return null + return KotlinCompilationArgumentsImpl(defaultArguments, currentArguments) + } + + private fun buildDependencyClasspath(compileKotlinTask: Task): List { + val abstractKotlinCompileClass = + compileKotlinTask.javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS) + val getCompileClasspath = + abstractKotlinCompileClass.getDeclaredMethodOrNull("getCompileClasspath") ?: return emptyList() + @Suppress("UNCHECKED_CAST") + return (getCompileClasspath(compileKotlinTask) as? Collection)?.map { it.path } ?: 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") ?: return null + val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null + val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null + val destinationDir = getDestinationDir(compileKotlinTask) as? File + return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir) + } + + private fun computeSourceSetsDeferredInfo( + sourceSets: Collection, + targets: Collection + ) { + val sourceSetToCompilations = LinkedHashMap>() + for (target in targets) { + for (compilation in target.compilations) { + for (sourceSet in compilation.sourceSets) { + sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation + } + } + } + for (sourceSet in sourceSets) { + val compilations = sourceSetToCompilations[sourceSet] + if (compilations != null) { + sourceSet.platform = compilations.map { it.platform }.distinct().singleOrNull() ?: KotlinPlatform.COMMON + sourceSet.isTestModule = compilations.all { it.isTestModule } + } else { + // TODO: change me after design about it + sourceSet.isTestModule = "Test" in sourceSet.name + } + } + } +} \ No newline at end of file