Modules: Support production-on-test dependency
This feature is supported in IDEA project configuration and can be used in Maven/Gradle-based projects #KT-20112 Fixed
This commit is contained in:
+27
-10
@@ -21,6 +21,7 @@ import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.projectRoots.Sdk
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
import com.intellij.openapi.roots.*
|
import com.intellij.openapi.roots.*
|
||||||
|
import com.intellij.openapi.roots.impl.ModuleOrderEntryImpl
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
import com.intellij.openapi.util.ModificationTracker
|
import com.intellij.openapi.util.ModificationTracker
|
||||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|||||||
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
|
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
|
||||||
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||||
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
|
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
|
||||||
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
||||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||||
@@ -58,8 +60,8 @@ interface IdeaModuleInfo : ModuleInfo {
|
|||||||
override fun dependencies(): List<IdeaModuleInfo>
|
override fun dependencies(): List<IdeaModuleInfo>
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, productionOnly: Boolean): List<IdeaModuleInfo> {
|
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, forProduction: Boolean): List<IdeaModuleInfo> {
|
||||||
fun Module.toInfos() = correspondingModuleInfos().filter { !productionOnly || it is ModuleProductionSourceInfo }
|
fun Module.toInfos() = correspondingModuleInfos().filter { !forProduction || it is ModuleProductionSourceInfo }
|
||||||
|
|
||||||
if (!orderEntry.isValid) return emptyList()
|
if (!orderEntry.isValid) return emptyList()
|
||||||
|
|
||||||
@@ -68,7 +70,13 @@ private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, pro
|
|||||||
orderEntry.getOwnerModule().toInfos()
|
orderEntry.getOwnerModule().toInfos()
|
||||||
}
|
}
|
||||||
is ModuleOrderEntry -> {
|
is ModuleOrderEntry -> {
|
||||||
orderEntry.module?.toInfos().orEmpty()
|
val module = orderEntry.module ?: return emptyList()
|
||||||
|
if (forProduction && orderEntry is ModuleOrderEntryImpl && orderEntry.isProductionOnTestDependency) {
|
||||||
|
listOfNotNull(module.testSourceInfo())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
module.toInfos()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is LibraryOrderEntry -> {
|
is LibraryOrderEntry -> {
|
||||||
val library = orderEntry.library ?: return listOf()
|
val library = orderEntry.library ?: return listOf()
|
||||||
@@ -88,16 +96,25 @@ fun <T> Module.cached(provider: CachedValueProvider<T>): T {
|
|||||||
return CachedValuesManager.getManager(project).getCachedValue(this, provider)
|
return CachedValuesManager.getManager(project).getCachedValue(this, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ideaModelDependencies(module: Module, productionOnly: Boolean): List<IdeaModuleInfo> {
|
private fun OrderEntry.acceptAsDependency(forProduction: Boolean): Boolean {
|
||||||
|
return this !is ExportableOrderEntry
|
||||||
|
|| !forProduction
|
||||||
|
// this is needed for Maven/Gradle projects with "production-on-test" dependency
|
||||||
|
|| this is ModuleOrderEntryImpl && isProductionOnTestDependency
|
||||||
|
|| scope.isForProductionCompile
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ideaModelDependencies(module: Module, forProduction: Boolean): List<IdeaModuleInfo> {
|
||||||
//NOTE: lib dependencies can be processed several times during recursive traversal
|
//NOTE: lib dependencies can be processed several times during recursive traversal
|
||||||
val result = LinkedHashSet<IdeaModuleInfo>()
|
val result = LinkedHashSet<IdeaModuleInfo>()
|
||||||
val dependencyEnumerator = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly()
|
val dependencyEnumerator = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly()
|
||||||
if (productionOnly) {
|
if (forProduction && !(KotlinPluginUtil.isMavenModule(module) || KotlinPluginUtil.isGradleModule(module))) {
|
||||||
dependencyEnumerator.productionOnly()
|
dependencyEnumerator.productionOnly()
|
||||||
}
|
}
|
||||||
dependencyEnumerator.forEach {
|
dependencyEnumerator.forEach { orderEntry ->
|
||||||
orderEntry ->
|
if (orderEntry.acceptAsDependency(forProduction)) {
|
||||||
result.addAll(orderEntryToModuleInfo(module.project, orderEntry!!, productionOnly))
|
result.addAll(orderEntryToModuleInfo(module.project, orderEntry!!, forProduction))
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
return result.toList()
|
return result.toList()
|
||||||
@@ -125,7 +142,7 @@ data class ModuleProductionSourceInfo internal constructor(override val module:
|
|||||||
|
|
||||||
override fun dependencies() = module.cached(CachedValueProvider {
|
override fun dependencies() = module.cached(CachedValueProvider {
|
||||||
CachedValueProvider.Result(
|
CachedValueProvider.Result(
|
||||||
ideaModelDependencies(module, productionOnly = true),
|
ideaModelDependencies(module, forProduction = true),
|
||||||
ProjectRootModificationTracker.getInstance(module.project))
|
ProjectRootModificationTracker.getInstance(module.project))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -140,7 +157,7 @@ data class ModuleTestSourceInfo internal constructor(override val module: Module
|
|||||||
|
|
||||||
override fun dependencies() = module.cached(CachedValueProvider {
|
override fun dependencies() = module.cached(CachedValueProvider {
|
||||||
CachedValueProvider.Result(
|
CachedValueProvider.Result(
|
||||||
ideaModelDependencies(module, productionOnly = false),
|
ideaModelDependencies(module, forProduction = false),
|
||||||
ProjectRootModificationTracker.getInstance(module.project))
|
ProjectRootModificationTracker.getInstance(module.project))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,16 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable
|
|||||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||||
import com.intellij.openapi.roots.ModuleRootManager
|
import com.intellij.openapi.roots.ModuleRootManager
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||||
|
import junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -1787,6 +1791,217 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testProductionOnTestDependency() {
|
||||||
|
createProjectSubDirs(
|
||||||
|
"module-with-java/src/main/java",
|
||||||
|
"module-with-java/src/test/java",
|
||||||
|
"module-with-kotlin/src/main/kotlin",
|
||||||
|
"module-with-kotlin/src/test/kotlin"
|
||||||
|
)
|
||||||
|
|
||||||
|
val dummyFile = createProjectSubFile(
|
||||||
|
"module-with-kotlin/src/main/kotlin/foo/dummy.kt",
|
||||||
|
"""
|
||||||
|
package foo
|
||||||
|
|
||||||
|
fun dummy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val pomA = createModulePom(
|
||||||
|
"module-with-java",
|
||||||
|
"""
|
||||||
|
<parent>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>mvnktest</artifactId>
|
||||||
|
<version>0.0.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>module-with-java</artifactId>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>test-jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val pomB = createModulePom(
|
||||||
|
"module-with-kotlin",
|
||||||
|
"""
|
||||||
|
<parent>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>mvnktest</artifactId>
|
||||||
|
<version>0.0.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>module-with-kotlin</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<kotlin.version>1.1.4</kotlin.version>
|
||||||
|
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
||||||
|
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib</artifactId>
|
||||||
|
<version>${"$"}{kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-runtime</artifactId>
|
||||||
|
<version>${"$"}{kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-reflect</artifactId>
|
||||||
|
<version>${"$"}{kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>module-with-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>module-with-java</artifactId>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<version>${"$"}{kotlin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
|
<goals> <goal>compile</goal> </goals>
|
||||||
|
<configuration>
|
||||||
|
<sourceDirs>
|
||||||
|
<sourceDir>${"$"}{project.basedir}/src/main/kotlin</sourceDir>
|
||||||
|
<sourceDir>${"$"}{project.basedir}/src/main/java</sourceDir>
|
||||||
|
</sourceDirs>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>test-compile</id>
|
||||||
|
<goals> <goal>test-compile</goal> </goals>
|
||||||
|
<configuration>
|
||||||
|
<sourceDirs>
|
||||||
|
<sourceDir>${"$"}{project.basedir}/src/test/kotlin</sourceDir>
|
||||||
|
<sourceDir>${"$"}{project.basedir}/src/test/java</sourceDir>
|
||||||
|
</sourceDirs>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<!-- Replacing default-compile as it is treated specially by maven -->
|
||||||
|
<execution>
|
||||||
|
<id>default-compile</id>
|
||||||
|
<phase>none</phase>
|
||||||
|
</execution>
|
||||||
|
<!-- Replacing default-testCompile as it is treated specially by maven -->
|
||||||
|
<execution>
|
||||||
|
<id>default-testCompile</id>
|
||||||
|
<phase>none</phase>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>java-compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals> <goal>compile</goal> </goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>java-test-compile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals> <goal>testCompile</goal> </goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val pomMain = createModulePom(
|
||||||
|
"",
|
||||||
|
"""
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>mvnktest</artifactId>
|
||||||
|
<version>0.0.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<kotlin.version>1.1.4</kotlin.version>
|
||||||
|
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
||||||
|
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>module-with-java</module>
|
||||||
|
<module>module-with-kotlin</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>module-with-kotlin</artifactId>
|
||||||
|
<version>${"$"}{project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>module-with-java</artifactId>
|
||||||
|
<version>${"$"}{project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>test-group</groupId>
|
||||||
|
<artifactId>module-with-java</artifactId>
|
||||||
|
<version>${"$"}{project.version}</version>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
importProjects(pomMain, pomA, pomB)
|
||||||
|
|
||||||
|
assertModules("module-with-kotlin", "module-with-java", "mvnktest")
|
||||||
|
|
||||||
|
val dependencies = (dummyFile.toPsiFile(myProject) as KtFile).analyzeAndGetResult().moduleDescriptor.allDependencyModules
|
||||||
|
TestCase.assertTrue(dependencies.any { it.name.asString() == "<production sources for module module-with-java>" })
|
||||||
|
TestCase.assertTrue(dependencies.any { it.name.asString() == "<test sources for module module-with-java>" })
|
||||||
|
}
|
||||||
|
|
||||||
private fun assertImporterStatePresent() {
|
private fun assertImporterStatePresent() {
|
||||||
assertNotNull("Kotlin importer component is not present", myTestFixture.module.getComponent(KotlinImporterComponent::class.java))
|
assertNotNull("Kotlin importer component is not present", myTestFixture.module.getComponent(KotlinImporterComponent::class.java))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user