MPP: Attach all classes/resources directories to the runtime classpath (KT-30710)

This commit is contained in:
Yan Zhulanow
2019-06-10 21:54:05 +09:00
parent bc0f10449a
commit 12861595f5
5 changed files with 150 additions and 0 deletions
@@ -9,9 +9,11 @@ import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData
import com.intellij.openapi.externalSystem.model.project.AbstractNamedData
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Key
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.KotlinModule
import org.jetbrains.kotlin.gradle.KotlinPlatform
@@ -53,4 +55,10 @@ class KotlinTargetData(name: String) : AbstractNamedData(GradleConstants.SYSTEM_
companion object {
val KEY = ExternalKey.create(KotlinTargetData::class.java, ProjectKeys.MODULE.processingWeight + 1)
}
}
class KotlinOutputPathsData(val paths: MultiMap<ExternalSystemSourceType, String>) : AbstractExternalEntityData(GradleConstants.SYSTEM_ID) {
companion object {
val KEY = ExternalKey.create(KotlinOutputPathsData::class.java, ProjectKeys.CONTENT_ROOT.processingWeight + 1)
}
}
@@ -110,6 +110,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
recordOutputDir(it, it, compilation.resourceType, moduleData, moduleOutputsMap, gradleOutputMap)
}
}
dataNode.createChild(KotlinOutputPathsData.KEY, KotlinOutputPathsData(gradleOutputMap.copy()))
}
if (outputDirs.any { FileUtil.isAncestor(ideaOutDir, File(it), false) }) {
excludeOutDir(ideModule, ideaOutDir)
@@ -5,15 +5,97 @@
package org.jetbrains.kotlin.idea.gradle.execution
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil.getExternalProjectInfo
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModuleRootModel
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.VfsUtilCore
import org.jetbrains.kotlin.idea.caches.project.isMPPModule
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.configuration.KotlinOutputPathsData
import org.jetbrains.plugins.gradle.execution.GradleOrderEnumeratorHandler
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil
import org.jetbrains.plugins.gradle.util.GradleConstants
open class KotlinGradleOrderEnumerationHandler(module: Module) : GradleOrderEnumeratorHandler(module) {
private companion object {
private val PRODUCTION_SOURCE_TYPES = listOf(
ExternalSystemSourceType.SOURCE,
ExternalSystemSourceType.RESOURCE,
ExternalSystemSourceType.SOURCE_GENERATED
)
private val TEST_SOURCE_TYPES = listOf(
ExternalSystemSourceType.TEST,
ExternalSystemSourceType.TEST_RESOURCE,
ExternalSystemSourceType.TEST_GENERATED
)
}
override fun shouldIncludeTestsFromDependentModulesToTestClasspath(): Boolean {
return true
}
override fun addCustomModuleRoots(
type: OrderRootType,
rootModel: ModuleRootModel,
result: MutableCollection<String>,
includeProduction: Boolean,
includeTests: Boolean
): Boolean {
if (super.addCustomModuleRoots(type, rootModel, result, includeProduction, includeTests)) {
// The code below works as a fallback when the default implementation fails
return true
}
val module = rootModel.module.takeIf { it.isNewMPPModule } ?: return false
val projectPath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return false
val externalProjectInfo = getExternalProjectInfo(module.project, GradleConstants.SYSTEM_ID, projectPath) ?: return false
val moduleData = GradleProjectResolverUtil.findModule(externalProjectInfo.externalProjectStructure, projectPath) ?: return false
val sourceSetData = ExternalSystemApiUtil.getChildren(moduleData, GradleSourceSetData.KEY)
.filter { it.data.internalName == module.name }
if (sourceSetData.isEmpty()) {
return false
}
val existingRoots = result.toMutableSet()
var hasNewRoots = false
fun appendPaths(paths: Collection<String>) {
for (path in paths) {
val url = VfsUtilCore.pathToUrl(path)
if (existingRoots.add(url)) {
result.add(url)
if (!hasNewRoots) {
hasNewRoots = true
}
}
}
}
fun appendPaths(outputPaths: KotlinOutputPathsData, configurations: List<ExternalSystemSourceType>) {
configurations.forEach { appendPaths(outputPaths.paths[it]) }
}
for (data in sourceSetData) {
for (outputPaths in ExternalSystemApiUtil.findAll(data, KotlinOutputPathsData.KEY)) {
if (includeProduction) {
appendPaths(outputPaths.data, PRODUCTION_SOURCE_TYPES)
}
if (includeTests) {
appendPaths(outputPaths.data, TEST_SOURCE_TYPES)
}
}
}
return hasNewRoots
}
open class Factory : GradleOrderEnumeratorHandler.FactoryImpl() {
override fun isApplicable(module: Module): Boolean {
return module.isMPPModule
@@ -5,7 +5,9 @@
package org.jetbrains.kotlin.gradle
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.ModulesOrderEnumerator
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.idea.codeInsight.gradle.MultiplePluginVersionGradleImportingTestCase
import org.jetbrains.kotlin.idea.util.sourceRoots
@@ -66,6 +68,37 @@ class NewMultiplatformKaptProjectImportingTest : MultiplePluginVersionGradleImpo
}
}
@Test
fun testRuntimeClasspath() {
configureByFiles()
val projectPath = this.projectPath
val expectedRoots = listOf(
"build/tmp/kapt3/classes/main",
"build/classes/java/main",
"build/classes/kotlin/jvm/main",
"build/processedResources/jvm/main"
).map {
File(projectPath, it).apply { mkdirs() }
}
importProject()
val jvmMainModule = ModuleManager.getInstance(project).modules.first { it.name == "project_jvmMain" }
val enumerator = ModulesOrderEnumerator(listOf(jvmMainModule))
val roots = enumerator.classesRoots
fun isRootPresent(file: File) = roots.any { it.path == file.path }
val missingRoots = expectedRoots.filter { !isRootPresent(it) }
assert(missingRoots.isEmpty()) {
"Missing roots found: " + missingRoots.joinToString()
}
}
override fun importProject() {
val isCreateEmptyContentRootDirectories = currentExternalProjectSettings.isCreateEmptyContentRootDirectories
currentExternalProjectSettings.isCreateEmptyContentRootDirectories = true
@@ -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() }
}