diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformKaptProjectImportingTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformKaptProjectImportingTest.kt new file mode 100644 index 00000000000..7e3cb448373 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformKaptProjectImportingTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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 com.intellij.openapi.roots.* +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.idea.codeInsight.gradle.MultiplePluginVersionGradleImportingTestCase +import org.jetbrains.kotlin.idea.util.sourceRoots +import org.junit.Test +import java.io.File + +class NewMultiplatformKaptProjectImportingTest : MultiplePluginVersionGradleImportingTestCase() { + override fun isApplicableTest(): Boolean { + val isOldGradlePlugin = gradleKotlinPluginVersion != MINIMAL_SUPPORTED_VERSION + && StringUtil.compareVersionNumbers(gradleKotlinPluginVersion, "1.3.40") < 0 + + return !isOldGradlePlugin && !gradleVersion.startsWith("3.") + } + + @Test + fun testKaptPaths() { + configureByFiles() + importProject() + + checkProjectStructure( + project, + projectPath, + exhaustiveModuleList = true, + exhaustiveSourceSourceRootList = false, + exhaustiveDependencyList = false + ) { + module("project") + + module("project_main") + module("project_test") + + module("project_commonMain") + module("project_commonTest") { + moduleDependency("project_commonMain", DependencyScope.TEST) + } + + module("project_jvmMain") { + moduleDependency("project_commonMain", DependencyScope.COMPILE) + val basePath = File(projectPath).parentFile.path.replace(File.separatorChar, '/') + val actualSourceRoots = module.sourceRoots.map { it.path.replace(basePath, "") }.sorted() + + val expectedSourceRoots = listOf( + "/project/build/generated/source/kapt/main", + "/project/build/generated/source/kaptKotlin/main", + "/project/src/jvmMain/java", + "/project/src/jvmMain/kotlin", + "/project/src/jvmMain/resources" + ) + + assertEquals(expectedSourceRoots, actualSourceRoots) + } + + module("project_jvmTest") { + moduleDependency("project_commonMain", DependencyScope.TEST) + moduleDependency("project_commonTest", DependencyScope.TEST) + moduleDependency("project_jvmMain", DependencyScope.TEST) + } + } + } + + override fun importProject() { + val isCreateEmptyContentRootDirectories = currentExternalProjectSettings.isCreateEmptyContentRootDirectories + currentExternalProjectSettings.isCreateEmptyContentRootDirectories = true + try { + super.importProject() + } finally { + currentExternalProjectSettings.isCreateEmptyContentRootDirectories = isCreateEmptyContentRootDirectories + } + } + + override fun testDataDirName(): String { + return "newMultiplatformImport" + } +} \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt index ce1513ad727..3cb5067eb4b 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt @@ -175,7 +175,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { ): 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 @@ -189,9 +188,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { } catch (e: Throwable) { "${e::class.java.name}:${e.message}" } - @Suppress("UNCHECKED_CAST") - val gradleCompilations = - (getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + + val gradleCompilations = getCompilations(gradleTarget) ?: return null val compilations = gradleCompilations.mapNotNull { buildCompilation(it, disambiguationClassifier, sourceSetMap, dependencyResolver, project) } @@ -227,10 +225,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { @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 compileKotlinTask = getCompileKotlinTaskName(project, gradleCompilation) ?: return null val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null val arguments = buildCompilationArguments(compileKotlinTask) ?: return null val dependencyClasspath = buildDependencyClasspath(compileKotlinTask) @@ -547,6 +542,20 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { @Suppress("UNCHECKED_CAST") return (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() } + + fun getCompilations(target: Named): Collection? { + val getCompilationsMethod = target.javaClass.getMethodOrNull("getCompilations") ?: return null + @Suppress("UNCHECKED_CAST") + return (getCompilationsMethod.invoke(target) as? NamedDomainObjectContainer)?.asMap?.values ?: emptyList() + } + + fun getCompileKotlinTaskName(project: Project, compilation: Named): Task? { + val compilationClass = compilation.javaClass + val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null + @Suppress("UNCHECKED_CAST") + val compileKotlinTaskName = (getCompileKotlinTaskName(compilation) as? String) ?: return null + return project.tasks.findByName(compileKotlinTaskName) ?: return null + } } } diff --git a/idea/testData/gradle/newMultiplatformImport/kaptPaths/build.gradle b/idea/testData/gradle/newMultiplatformImport/kaptPaths/build.gradle new file mode 100644 index 00000000000..9336153a1ee --- /dev/null +++ b/idea/testData/gradle/newMultiplatformImport/kaptPaths/build.gradle @@ -0,0 +1,25 @@ +buildscript { + repositories { + mavenCentral() + maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } + } + + dependencies { + apply from: "include.gradle" + def kotlinVersion = gradleKotlinPluginVersion('1.3.40-eap-67') + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } +} + +apply plugin: 'kotlin-multiplatform' +apply plugin: 'kotlin-kapt' + + +repositories { + mavenCentral() + maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } +} + +kotlin { + jvm('jvm') { withJava() } +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt b/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt index 7e23bd99756..918378c9d1c 100644 --- a/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt +++ b/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt @@ -20,10 +20,15 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.ProjectKeys import com.intellij.openapi.externalSystem.model.project.* +import org.gradle.api.Named import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.tooling.model.idea.IdeaModule import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.Companion.getCompilations +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.Companion.getCompileKotlinTaskName +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.Companion.getTargets import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension @@ -140,8 +145,12 @@ class KaptModelBuilderService : AbstractKotlinGradleModelBuilder() { val sourceSets = mutableListOf() if (kaptIsEnabled) { - project.getAllTasks(false)[project]?.forEach { compileTask -> - if (compileTask.javaClass.name !in kotlinCompileJvmTaskClasses) return@forEach + val targets = project.getTargets() + + fun handleCompileTask(moduleName: String, compileTask: Task) { + if (compileTask.javaClass.name !in kotlinCompileJvmTaskClasses) { + return + } val sourceSetName = compileTask.getSourceSetName() val isTest = sourceSetName.toLowerCase().endsWith("test") @@ -150,13 +159,41 @@ class KaptModelBuilderService : AbstractKotlinGradleModelBuilder() { val kaptGeneratedClassesDir = getKaptDirectory("getKaptGeneratedClassesDir", project, sourceSetName) val kaptGeneratedKotlinSourcesDir = getKaptDirectory("getKaptGeneratedKotlinSourcesDir", project, sourceSetName) sourceSets += KaptSourceSetModelImpl( - sourceSetName, isTest, kaptGeneratedSourcesDir, kaptGeneratedClassesDir, kaptGeneratedKotlinSourcesDir) + moduleName, isTest, kaptGeneratedSourcesDir, kaptGeneratedClassesDir, kaptGeneratedKotlinSourcesDir + ) + } + + if (targets != null && targets.isNotEmpty()) { + for (target in targets) { + if (!isWithJavaEnabled(target)) { + continue + } + + val compilations = getCompilations(target) ?: continue + for (compilation in compilations) { + val compileTask = getCompileKotlinTaskName(project, compilation) ?: continue + val moduleName = target.name + compilation.name.capitalize() + handleCompileTask(moduleName, compileTask) + } + } + } else { + project.getAllTasks(false)[project]?.forEach { compileTask -> + val sourceSetName = compileTask.getSourceSetName() + handleCompileTask(sourceSetName, compileTask) + } } } return KaptGradleModelImpl(kaptIsEnabled, project.buildDir, sourceSets) } + private fun isWithJavaEnabled(target: Named): Boolean { + val getWithJavaEnabledMethod = target.javaClass.methods + .firstOrNull { it.name == "getWithJavaEnabled" && it.parameterCount == 0 } ?: return false + + return getWithJavaEnabledMethod.invoke(target) == true + } + private fun getKaptDirectory(funName: String, project: Project, sourceSetName: String): String { val kotlinKaptPlugin = project.plugins.findPlugin("kotlin-kapt") ?: return ""