Kapt: Fix source root importing in MPP projects

This commit is contained in:
Yan Zhulanow
2019-06-07 16:58:32 +09:00
parent 9eab9f386d
commit bc0f10449a
4 changed files with 164 additions and 11 deletions
@@ -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"
}
}
@@ -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<Named>)?.asMap?.values ?: emptyList<Named>()
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<Named>) ?: 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<Named>)?.asMap?.values ?: emptyList()
}
fun getCompilations(target: Named): Collection<Named>? {
val getCompilationsMethod = target.javaClass.getMethodOrNull("getCompilations") ?: return null
@Suppress("UNCHECKED_CAST")
return (getCompilationsMethod.invoke(target) as? NamedDomainObjectContainer<Named>)?.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
}
}
}
@@ -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() }
}
@@ -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<KaptSourceSetModel>()
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 ""