mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin, Gradle] Add support for Kotlin generated source in Kotlin/JVM projects
As ContentRootData is being loaded by main IDEA import pipeline, our KotlinGradleProjectResolverExtension only finds 'SOURCE_GENERATED' or 'TEST_GENERATED' that should mark as generated and changes their type. ^KTIJ-35910 GitOrigin-RevId: 1ff808738b0d575e72ca0b25ff7b97151ab7fb82
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d14ab3efc9
commit
d7f2bc3603
+3
-3
@@ -766,14 +766,14 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
|
||||
assertAllModulesConfigured()
|
||||
}
|
||||
|
||||
// kotlin-2js plugin
|
||||
@Test
|
||||
fun testStableModuleNameWhileUsingGradleJS() {
|
||||
configureByFiles()
|
||||
importProject()
|
||||
|
||||
checkStableModuleName("project.main", "project", JsPlatforms.defaultJsPlatform, isProduction = true)
|
||||
// Note "_test" suffix: this is current behavior of K2JS Compiler
|
||||
checkStableModuleName("project.test", "project_test", JsPlatforms.defaultJsPlatform, isProduction = false)
|
||||
checkStableModuleName("project.main", "project.main", JsPlatforms.defaultJsPlatform, isProduction = true)
|
||||
checkStableModuleName("project.test", "project.test", JsPlatforms.defaultJsPlatform, isProduction = false)
|
||||
|
||||
assertAllModulesConfigured()
|
||||
}
|
||||
|
||||
+62
-18
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.gradleTooling.KotlinGradleModel
|
||||
import org.jetbrains.kotlin.idea.gradleTooling.KotlinGradleModelBuilder
|
||||
import org.jetbrains.kotlin.idea.gradleTooling.KotlinTaskPropertiesBySourceSet
|
||||
import org.jetbrains.kotlin.idea.projectModel.KotlinTarget
|
||||
import org.jetbrains.kotlin.idea.projectModel.KotlinTaskProperties
|
||||
import org.jetbrains.kotlin.idea.statistics.KotlinIDEGradleActionsFUSCollector
|
||||
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
|
||||
import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty
|
||||
@@ -358,28 +359,71 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
|
||||
gradleModel.kotlinTaskProperties.filter { (k, _) -> gradleSourceSetNode.data.id == "$moduleNamePrefix:$k" }
|
||||
.toList().singleOrNull()
|
||||
gradleSourceSetNode.children.forEach { dataNode ->
|
||||
val data = dataNode.data as? ContentRootData
|
||||
if (data != null) {
|
||||
/**
|
||||
* Code snippet for setting in content root properties
|
||||
* if (propertiesForSourceSet?.second?.pureKotlinSourceFolders?.contains(File(data.rootPath)) == true) {
|
||||
* @Suppress("UNCHECKED_CAST")
|
||||
* (dataNode as DataNode<ContentRootData>).isPureKotlinSourceFolder = true
|
||||
* }
|
||||
*/
|
||||
val packagePrefix = propertiesForSourceSet?.second?.packagePrefix
|
||||
if (packagePrefix != null) {
|
||||
ExternalSystemSourceType.values().filter { !(it.isResource || it.isGenerated) }.forEach { type ->
|
||||
val paths = data.getPaths(type)
|
||||
val newPaths = paths.map { ContentRootData.SourceRoot(it.path, packagePrefix) }
|
||||
paths.clear()
|
||||
paths.addAll(newPaths)
|
||||
}
|
||||
}
|
||||
dataNode.markGeneratedSourceRoots(gradleModel.generatedSourcesRoots)
|
||||
dataNode.updateSourceRootPackagePrefix(propertiesForSourceSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DataNode<*>.markGeneratedSourceRoots(
|
||||
generatedSources: List<String>
|
||||
) {
|
||||
visitData { contentRootData ->
|
||||
if (contentRootData == null ||
|
||||
generatedSources.isEmpty() ||
|
||||
contentRootData as? ContentRootData == null
|
||||
) return@visitData contentRootData
|
||||
|
||||
// it is already marked as generated or contains other types in which we are not interested in
|
||||
if (contentRootData.getPaths(ExternalSystemSourceType.SOURCE).isEmpty() &&
|
||||
contentRootData.getPaths(ExternalSystemSourceType.TEST).isEmpty()
|
||||
) return@visitData contentRootData
|
||||
|
||||
val newDataNode = ContentRootData(contentRootData.owner, contentRootData.rootPath)
|
||||
ExternalSystemSourceType.entries.forEach { type ->
|
||||
contentRootData.getPaths(type).forEach { sourceRoot ->
|
||||
when (type) {
|
||||
ExternalSystemSourceType.SOURCE if sourceRoot.path in generatedSources -> {
|
||||
newDataNode.storePath(ExternalSystemSourceType.SOURCE_GENERATED, sourceRoot.path, sourceRoot.packagePrefix)
|
||||
}
|
||||
ExternalSystemSourceType.TEST if sourceRoot.path in generatedSources -> {
|
||||
newDataNode.storePath(ExternalSystemSourceType.TEST_GENERATED, sourceRoot.path, sourceRoot.packagePrefix)
|
||||
}
|
||||
else -> {
|
||||
newDataNode.storePath(type, sourceRoot.path, sourceRoot.packagePrefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return@visitData newDataNode
|
||||
}
|
||||
}
|
||||
|
||||
private fun DataNode<*>.updateSourceRootPackagePrefix(
|
||||
propertiesForSourceSet: Pair<String, KotlinTaskProperties>?,
|
||||
) {
|
||||
visitData { contentRootData ->
|
||||
if (contentRootData == null ||
|
||||
propertiesForSourceSet == null ||
|
||||
contentRootData as? ContentRootData == null
|
||||
) return@visitData contentRootData
|
||||
|
||||
val packagePrefix = propertiesForSourceSet.second.packagePrefix ?: return@visitData contentRootData
|
||||
|
||||
val newDataNode = ContentRootData(contentRootData.owner, contentRootData.rootPath)
|
||||
ExternalSystemSourceType.entries
|
||||
.forEach { type ->
|
||||
contentRootData.getPaths(type).forEach { path ->
|
||||
if (type.isResource || type.isGenerated) {
|
||||
newDataNode.storePath(type, path.path, path.packagePrefix)
|
||||
} else {
|
||||
newDataNode.storePath(type, path.path, packagePrefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
newDataNode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ContentRootData
|
||||
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
|
||||
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import org.jetbrains.kotlin.idea.codeInsight.gradle.KotlinGradleImportingTestCase
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import org.junit.Test
|
||||
|
||||
class KotlinGeneratedSourcesImportTest : KotlinGradleImportingTestCase() {
|
||||
|
||||
@Test
|
||||
@TargetVersions("7.6+")
|
||||
fun testGeneratedInMainSourceSet() {
|
||||
configureByFiles()
|
||||
importProject()
|
||||
|
||||
val generatedRoot = getContentRootFor("main", ExternalSystemSourceType.SOURCE_GENERATED)
|
||||
assertSize(1, generatedRoot)
|
||||
assertTrue(generatedRoot.first().path.endsWith("src/main/kotlinGen"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@TargetVersions("7.6+")
|
||||
fun testGeneratedInTestSourceSet() {
|
||||
configureByFiles()
|
||||
importProject()
|
||||
|
||||
val generatedRoot = getContentRootFor("test", ExternalSystemSourceType.TEST_GENERATED)
|
||||
assertSize(1, generatedRoot)
|
||||
assertTrue(generatedRoot.first().path.endsWith("src/test/kotlinGen"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@TargetVersions("7.6+")
|
||||
fun testGeneratedWithIdeaPlugin() {
|
||||
configureByFiles()
|
||||
importProject()
|
||||
|
||||
val generatedRoot = getContentRootFor("main", ExternalSystemSourceType.SOURCE_GENERATED)
|
||||
assertSize(1, generatedRoot)
|
||||
assertTrue(generatedRoot.first().path.endsWith("src/main/kotlinGen"))
|
||||
}
|
||||
|
||||
override fun testDataDirName(): String = "kotlinGeneratedSourcesImportTest"
|
||||
|
||||
private fun getContentRootFor(
|
||||
sourceSetName: String,
|
||||
type: ExternalSystemSourceType,
|
||||
): Collection<ContentRootData.SourceRoot> {
|
||||
val gradleProjectData = ProjectDataManager.getInstance().getExternalProjectsData(myProject, GradleConstants.SYSTEM_ID).first()
|
||||
val externalProjectData = gradleProjectData!!.externalProjectStructure!!
|
||||
val modulesNodes = ExternalSystemApiUtil.findAll(externalProjectData, ProjectKeys.MODULE)
|
||||
|
||||
// main module
|
||||
val mainModule = modulesNodes.first()
|
||||
val gradleSourceSets = ExternalSystemApiUtil.findAll(mainModule, GradleSourceSetData.KEY)
|
||||
val gradleSourceSet = gradleSourceSets.single { it.data.id == "${mainModule.data.id}:${sourceSetName}" }
|
||||
val contentRootData = gradleSourceSet.children.single { it.data is ContentRootData }.data as ContentRootData
|
||||
|
||||
return contentRootData.getPaths(type)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -400,7 +400,7 @@ abstract class KotlinGradleImportingTestCase : GradleImportingTestCase(),
|
||||
|
||||
const val LATEST_STABLE_GRADLE_PLUGIN_VERSION = "2.0.0"
|
||||
|
||||
val SUPPORTED_GRADLE_VERSIONS = arrayOf("6.8.3", "7.6")//, "9.0.0", "9.1.0") // To support them in KTIJ-36754
|
||||
val SUPPORTED_GRADLE_VERSIONS = arrayOf("6.8.3", "7.6.5")//, "9.0.0", "9.1.0") // To support them in KTIJ-36754
|
||||
|
||||
// https://kotlinlang.org/docs/gradle-configure-project.html#targeting-the-jvm
|
||||
val GRADLE_TO_KGP_VERSION = mapOf(
|
||||
|
||||
+9
-3
@@ -28,10 +28,10 @@ import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider.GradleModel
|
||||
import org.jetbrains.plugins.gradle.tooling.Message
|
||||
import org.jetbrains.plugins.gradle.tooling.ModelBuilderContext
|
||||
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.util.Locale
|
||||
import kotlin.io.path.absolutePathString
|
||||
|
||||
typealias AdditionalVisibleSourceSetsBySourceSet = Map</* Source Set Name */ String, /* Visible Source Set Names */ Set<String>>
|
||||
|
||||
@@ -48,6 +48,7 @@ interface KotlinGradleModel : Serializable {
|
||||
val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet
|
||||
val gradleUserHome: String
|
||||
val kotlinGradlePluginVersion: KotlinGradlePluginVersion?
|
||||
val generatedSourcesRoots: List<String>
|
||||
}
|
||||
|
||||
data class KotlinGradleModelImpl(
|
||||
@@ -60,7 +61,8 @@ data class KotlinGradleModelImpl(
|
||||
override val kotlinTarget: String? = null,
|
||||
override val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet,
|
||||
override val gradleUserHome: String,
|
||||
override val kotlinGradlePluginVersion: KotlinGradlePluginVersion?
|
||||
override val kotlinGradlePluginVersion: KotlinGradlePluginVersion?,
|
||||
override val generatedSourcesRoots: List<String>,
|
||||
) : KotlinGradleModel
|
||||
|
||||
abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService {
|
||||
@@ -235,6 +237,7 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder(), ModelBuilde
|
||||
val compilerArgumentsBySourceSet = LinkedHashMap<String, List<String>>()
|
||||
val additionalVisibleSourceSets = LinkedHashMap<String, Set<String>>()
|
||||
val extraProperties = HashMap<String, KotlinTaskProperties>()
|
||||
val generatedSources = mutableSetOf<String>()
|
||||
|
||||
val kotlinCompileTasks = target?.let { it.compilations ?: emptyList() }
|
||||
?.mapNotNull { compilation -> compilation.getCompileKotlinTaskName(project) }
|
||||
@@ -251,6 +254,8 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder(), ModelBuilde
|
||||
extraProperties.acknowledgeTask(compileTask, null)
|
||||
}
|
||||
|
||||
generatedSources.addAll(project.getKotlinSourceSetGeneratedSourceRoots().map { it.absolutePathString() })
|
||||
|
||||
val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value
|
||||
|
||||
if (builderContext != null) {
|
||||
@@ -267,7 +272,8 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder(), ModelBuilde
|
||||
kotlinTarget = platform ?: kotlinPluginId,
|
||||
kotlinTaskProperties = extraProperties,
|
||||
gradleUserHome = project.gradle.gradleUserHomeDir.absolutePath,
|
||||
kotlinGradlePluginVersion = project.kotlinGradlePluginVersion()
|
||||
kotlinGradlePluginVersion = project.kotlinGradlePluginVersion(),
|
||||
generatedSourcesRoots = generatedSources.toList(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.gradleTooling
|
||||
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.idea.gradleTooling.reflect.KotlinSourceSetReflection
|
||||
import java.nio.file.Path
|
||||
|
||||
internal fun Project.getKotlinSourceSetGeneratedSourceRoots(): List<Path> {
|
||||
val kotlinExtension = project.extensions.findByName("kotlin") ?: return emptyList()
|
||||
val kotlinExtensionClass = kotlinExtension.javaClass
|
||||
val getSourceSets = kotlinExtensionClass.getMethodOrNull("getSourceSets") ?: return emptyList()
|
||||
val sourceSets = getSourceSets.invoke(kotlinExtension) as NamedDomainObjectCollection<*>
|
||||
val generatedSourceRoots = sourceSets.map { it as Named }.fold(mutableListOf<Path>()) { acc, named ->
|
||||
named.getKotlinSourceSetGeneratedSourceRoots()?.let { acc.addAll(it) }
|
||||
acc
|
||||
}
|
||||
return generatedSourceRoots.toList()
|
||||
}
|
||||
|
||||
private fun Named.getKotlinSourceSetGeneratedSourceRoots(): List<Path>? {
|
||||
return KotlinSourceSetReflection(this).generatedSources?.srcDirs?.map {
|
||||
@Suppress("IO_FILE_USAGE") // Gradle API
|
||||
it.toPath()
|
||||
}
|
||||
}
|
||||
+5
@@ -15,6 +15,7 @@ interface KotlinSourceSetReflection {
|
||||
val languageSettings: KotlinLanguageSettingsReflection?
|
||||
val kotlin: SourceDirectorySet?
|
||||
val resources: SourceDirectorySet?
|
||||
val generatedSources: SourceDirectorySet?
|
||||
val dependsOn: Set<KotlinSourceSetReflection>
|
||||
val additionalVisibleSourceSets: Set<KotlinSourceSetReflection>
|
||||
val androidSourceSetInfo: KotlinAndroidSourceSetInfoReflection?
|
||||
@@ -36,6 +37,10 @@ private class KotlinSourceSetReflectionImpl(override val instance: Named) : Kotl
|
||||
instance.callReflectiveGetter("getResources", logger)
|
||||
}
|
||||
|
||||
override val generatedSources: SourceDirectorySet? by lazy {
|
||||
instance.callReflectiveGetter("getGeneratedKotlin", logger)
|
||||
}
|
||||
|
||||
override val dependsOn: Set<KotlinSourceSetReflection> by lazy {
|
||||
instance.callReflectiveGetter<Iterable<Named>>("getDependsOn", logger)?.map {
|
||||
KotlinSourceSetReflection(it)
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '2.3.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// Task to create the directory so it exists during import
|
||||
def generatorTask = project.tasks.register("generator") {
|
||||
def outputDirectory = project.layout.projectDirectory.dir("src/main/kotlinGen")
|
||||
outputs.dir(outputDirectory)
|
||||
doLast {
|
||||
outputDirectory.file("generatedCommon.kt").asFile.writeText(
|
||||
//language=kotlin
|
||||
"""
|
||||
fun printHello() {
|
||||
println("hello")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.sourceSets.main.generatedKotlin.srcDir(generatorTask)
|
||||
+1
@@ -0,0 +1 @@
|
||||
rootProject.name = 'project'
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '2.3.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// Task to create the directory so it exists during import
|
||||
def generatorTask = project.tasks.register("generator") {
|
||||
def outputDirectory = project.layout.projectDirectory.dir("src/test/kotlinGen")
|
||||
outputs.dir(outputDirectory)
|
||||
doLast {
|
||||
outputDirectory.file("generatedCommon.kt").asFile.writeText(
|
||||
//language=kotlin
|
||||
"""
|
||||
fun printHello() {
|
||||
println("hello")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.sourceSets.test.generatedKotlin.srcDir(generatorTask)
|
||||
+1
@@ -0,0 +1 @@
|
||||
rootProject.name = 'project'
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '2.3.0'
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// Task to create the directory so it exists during import
|
||||
def generatorTask = project.tasks.register("generator") {
|
||||
def outputDirectory = project.layout.projectDirectory.dir("src/main/kotlinGen")
|
||||
outputs.dir(outputDirectory)
|
||||
doLast {
|
||||
outputDirectory.file("generatedCommon.kt").asFile.writeText(
|
||||
//language=kotlin
|
||||
"""
|
||||
fun printHello() {
|
||||
println("hello")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.sourceSets.main.generatedKotlin.srcDir(generatorTask)
|
||||
|
||||
idea {
|
||||
module {
|
||||
generatedSourceDirs += file("src/main/kotlinGen")
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
rootProject.name = 'project'
|
||||
Reference in New Issue
Block a user