diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUtil.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinPluginUtil.java similarity index 100% rename from idea/src/org/jetbrains/kotlin/idea/KotlinPluginUtil.java rename to idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinPluginUtil.java diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt index bffa8c2a4a4..d046bc971ce 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.module.impl.scopes.LibraryScopeBase import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk 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.libraries.Library 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.caches.resolve.LibraryModuleInfo import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.framework.getLibraryPlatform import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker import org.jetbrains.kotlin.idea.project.TargetPlatformDetector @@ -58,8 +60,8 @@ interface IdeaModuleInfo : ModuleInfo { override fun dependencies(): List } -private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, productionOnly: Boolean): List { - fun Module.toInfos() = correspondingModuleInfos().filter { !productionOnly || it is ModuleProductionSourceInfo } +private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, forProduction: Boolean): List { + fun Module.toInfos() = correspondingModuleInfos().filter { !forProduction || it is ModuleProductionSourceInfo } if (!orderEntry.isValid) return emptyList() @@ -68,7 +70,13 @@ private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, pro orderEntry.getOwnerModule().toInfos() } 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 -> { val library = orderEntry.library ?: return listOf() @@ -88,16 +96,25 @@ fun Module.cached(provider: CachedValueProvider): T { return CachedValuesManager.getManager(project).getCachedValue(this, provider) } -private fun ideaModelDependencies(module: Module, productionOnly: Boolean): List { +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 { //NOTE: lib dependencies can be processed several times during recursive traversal val result = LinkedHashSet() val dependencyEnumerator = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly() - if (productionOnly) { + if (forProduction && !(KotlinPluginUtil.isMavenModule(module) || KotlinPluginUtil.isGradleModule(module))) { dependencyEnumerator.productionOnly() } - dependencyEnumerator.forEach { - orderEntry -> - result.addAll(orderEntryToModuleInfo(module.project, orderEntry!!, productionOnly)) + dependencyEnumerator.forEach { orderEntry -> + if (orderEntry.acceptAsDependency(forProduction)) { + result.addAll(orderEntryToModuleInfo(module.project, orderEntry!!, forProduction)) + } true } return result.toList() @@ -125,7 +142,7 @@ data class ModuleProductionSourceInfo internal constructor(override val module: override fun dependencies() = module.cached(CachedValueProvider { CachedValueProvider.Result( - ideaModelDependencies(module, productionOnly = true), + ideaModelDependencies(module, forProduction = true), ProjectRootModificationTracker.getInstance(module.project)) }) } @@ -140,7 +157,7 @@ data class ModuleTestSourceInfo internal constructor(override val module: Module override fun dependencies() = module.cached(CachedValueProvider { CachedValueProvider.Result( - ideaModelDependencies(module, productionOnly = false), + ideaModelDependencies(module, forProduction = false), ProjectRootModificationTracker.getInstance(module.project)) }) diff --git a/idea/idea-maven/test/org/jetbrains/kotlin/idea/maven/KotlinMavenImporterTest.kt b/idea/idea-maven/test/org/jetbrains/kotlin/idea/maven/KotlinMavenImporterTest.kt index 438f2d3ce15..ceaf3753f8c 100644 --- a/idea/idea-maven/test/org/jetbrains/kotlin/idea/maven/KotlinMavenImporterTest.kt +++ b/idea/idea-maven/test/org/jetbrains/kotlin/idea/maven/KotlinMavenImporterTest.kt @@ -23,12 +23,16 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.ModuleRootManager 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.K2JVMCompilerArguments 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.framework.CommonLibraryKind 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 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", + """ + + test-group + mvnktest + 0.0.0.0-SNAPSHOT + + + module-with-java + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + test-jar + + + + + + + """.trimIndent() + ) + + val pomB = createModulePom( + "module-with-kotlin", + """ + + test-group + mvnktest + 0.0.0.0-SNAPSHOT + + + module-with-kotlin + + + 1.1.4 + 1.8 + true + + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${"$"}{kotlin.version} + + + org.jetbrains.kotlin + kotlin-runtime + ${"$"}{kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${"$"}{kotlin.version} + + + + test-group + module-with-java + + + + test-group + module-with-java + test-jar + compile + + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${"$"}{kotlin.version} + + + compile + compile + + + ${"$"}{project.basedir}/src/main/kotlin + ${"$"}{project.basedir}/src/main/java + + + + + test-compile + test-compile + + + ${"$"}{project.basedir}/src/test/kotlin + ${"$"}{project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + compile + + + java-test-compile + test-compile + testCompile + + + + + + """.trimIndent() + ) + + val pomMain = createModulePom( + "", + """ + test-group + mvnktest + 0.0.0.0-SNAPSHOT + + pom + + + 1.1.4 + 1.8 + true + + + + module-with-java + module-with-kotlin + + + + + + test-group + module-with-kotlin + ${"$"}{project.version} + + + test-group + module-with-java + ${"$"}{project.version} + + + test-group + module-with-java + ${"$"}{project.version} + test-jar + test + + + + """.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() == "" }) + TestCase.assertTrue(dependencies.any { it.name.asString() == "" }) + } + private fun assertImporterStatePresent() { assertNotNull("Kotlin importer component is not present", myTestFixture.module.getComponent(KotlinImporterComponent::class.java)) }