MPP: Support multiple 'expectedBy' dependencies for single module

#KT-22865 Fixed
This commit is contained in:
Alexey Sedunov
2018-02-20 19:26:24 +03:00
parent e3a69d118f
commit d9aa1796cd
22 changed files with 197 additions and 108 deletions
@@ -239,7 +239,7 @@ interface ModuleInfo {
val name: Name
val displayedName: String get() = name.asString()
fun dependencies(): List<ModuleInfo>
val expectedBy: ModuleInfo? get() = null
val expectedBy: List<ModuleInfo> get() = emptyList()
val platform: TargetPlatform? get() = null
fun modulesWhoseInternalsAreVisible(): Collection<ModuleInfo> = listOf()
val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
@@ -310,8 +310,8 @@ class LazyModuleDependencies<M : ModuleInfo>(
override val allDependencies: List<ModuleDescriptorImpl> get() = dependencies()
override val expectedByDependency by storageManager.createNullableLazyValue {
module.expectedBy?.let { resolverForProject.descriptorForModule(it as M) }
override val expectedByDependencies by storageManager.createLazyValue {
module.expectedBy.map { resolverForProject.descriptorForModule(it as M) }
}
override val modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compati
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.ifEmpty
import java.io.File
object ExpectedActualDeclarationChecker : DeclarationChecker {
@@ -114,8 +115,16 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
// However, in compiler context platform & common modules are joined into one module,
// so there is yet no "common module" in this situation.
// So yet we are using own module in compiler context and common module in IDE context.
val commonOrOwnModule = descriptor.module.expectedByModule ?: descriptor.module
val compatibility = ExpectedActualResolver.findExpectedForActual(descriptor, commonOrOwnModule) ?: return
val commonOrOwnModules = descriptor.module.expectedByModules.ifEmpty { listOf(descriptor.module) }
val compatibility = commonOrOwnModules
.asSequence()
.fold(LinkedHashMap<Compatibility, List<MemberDescriptor>>()) { resultMap, commonModule ->
val currentMap = ExpectedActualResolver.findExpectedForActual(descriptor, commonModule)
if (currentMap != null) {
resultMap.putAll(currentMap)
}
resultMap
}
val hasActualModifier = descriptor.isActual && reportOn.hasActualModifier()
if (!hasActualModifier) {
@@ -40,7 +40,7 @@ interface ModuleDescriptor : DeclarationDescriptor {
*/
val allDependencyModules: List<ModuleDescriptor>
val expectedByModule: ModuleDescriptor?
val expectedByModules: List<ModuleDescriptor>
fun <T> getCapability(capability: Capability<T>): T?
@@ -66,8 +66,8 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
override val allDependencyModules: List<ModuleDescriptor>
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.allDependencies.filter { it != this }
override val expectedByModule: ModuleDescriptor?
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.expectedByDependency
override val expectedByModules: List<ModuleDescriptor>
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.expectedByDependencies
override fun getPackage(fqName: FqName): PackageViewDescriptor {
assertValid()
@@ -111,7 +111,7 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
}
fun setDependencies(descriptors: List<ModuleDescriptorImpl>, friends: Set<ModuleDescriptorImpl>) {
setDependencies(ModuleDependenciesImpl(descriptors, friends, null))
setDependencies(ModuleDependenciesImpl(descriptors, friends, emptyList()))
}
override fun shouldSeeInternalsOf(targetModule: ModuleDescriptor): Boolean {
@@ -150,11 +150,11 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
interface ModuleDependencies {
val allDependencies: List<ModuleDescriptorImpl>
val modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>
val expectedByDependency: ModuleDescriptorImpl?
val expectedByDependencies: List<ModuleDescriptorImpl>
}
class ModuleDependenciesImpl(
override val allDependencies: List<ModuleDescriptorImpl>,
override val modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>,
override val expectedByDependency: ModuleDescriptorImpl?
override val expectedByDependencies: List<ModuleDescriptorImpl>
) : ModuleDependencies
@@ -85,10 +85,10 @@ public class ErrorUtils {
return emptyList();
}
@Nullable
@NotNull
@Override
public ModuleDescriptor getExpectedByModule() {
return null;
public List<ModuleDescriptor> getExpectedByModules() {
return emptyList();
}
@Override
@@ -117,24 +117,22 @@ private fun ideaModelDependencies(module: Module, forProduction: Boolean): List<
return result.toList()
}
fun Module.findImplementedModuleName(modelsProvider: IdeModifiableModelsProvider): String? {
fun Module.findImplementedModuleNames(modelsProvider: IdeModifiableModelsProvider): List<String> {
val facetModel = modelsProvider.getModifiableFacetModel(this)
val facet = facetModel.findFacet(
KotlinFacetType.TYPE_ID,
FacetTypeRegistry.getInstance().findFacetType(ID)!!.defaultFacetName
)
return facet?.configuration?.settings?.implementedModuleName
return facet?.configuration?.settings?.implementedModuleNames ?: emptyList()
}
fun Module.findImplementedModule(modelsProvider: IdeModifiableModelsProvider): Module? {
val implementedModuleName = findImplementedModuleName(modelsProvider)
return implementedModuleName?.let { modelsProvider.findIdeModule(it) }
}
fun Module.findImplementedModules(modelsProvider: IdeModifiableModelsProvider) =
findImplementedModuleNames(modelsProvider).mapNotNull { modelsProvider.findIdeModule(it) }
interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
val module: Module
override val expectedBy: ModuleSourceInfo?
override val expectedBy: List<ModuleSourceInfo>
override val displayedName get() = module.name
@@ -149,11 +147,11 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
}
sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) : ModuleSourceInfo {
override val expectedBy: ModuleSourceInfo?
override val expectedBy: List<ModuleSourceInfo>
get() {
val modelsProvider = IdeModifiableModelsProviderImpl(module.project)
val expectedByModule = module.findImplementedModule(modelsProvider)
return if (forProduction) expectedByModule?.productionSourceInfo() else expectedByModule?.testSourceInfo()
val expectedByModules = module.findImplementedModules(modelsProvider)
return expectedByModules.mapNotNull { if (forProduction) it.productionSourceInfo() else it.testSourceInfo() }
}
override fun dependencies(): List<IdeaModuleInfo> = module.cached(createCachedValueProvider {
@@ -49,8 +49,8 @@ var DataNode<ModuleData>.coroutines
by CopyableDataNodeUserDataProperty(Key.create<String>("KOTLIN_COROUTINES"))
var DataNode<ModuleData>.platformPluginId
by CopyableDataNodeUserDataProperty(Key.create<String>("PLATFORM_PLUGIN_ID"))
var DataNode<out ModuleData>.implementedModuleName
by CopyableDataNodeUserDataProperty(Key.create<String>("IMPLEMENTED_MODULE_NAME"))
var DataNode<out ModuleData>.implementedModuleNames
by NotNullableCopyableDataNodeUserDataProperty(Key.create<List<String>>("IMPLEMENTED_MODULE_NAME"), emptyList())
class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() {
val isAndroidProjectKey = Key.findKeyByName("IS_ANDROID_PROJECT_KEY")
@@ -134,23 +134,23 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
gradleIdeaProject.modules.firstOrNull { it.gradleProject.path == moduleNodeForGradleModel?.data?.id }
}
else gradleModule
val implementsModuleId = resolverCtx.getExtraProject(ideaModule, KotlinGradleModel::class.java)?.implements
val implementsModuleIds = resolverCtx.getExtraProject(ideaModule, KotlinGradleModel::class.java)?.implements
?: emptyList()
val targetModule = implementsModuleId?.let {
for (implementsModuleId in implementsModuleIds) {
val compositePrefix = if (resolverCtx.models.ideaProject != gradleModule.project
&& it.startsWith(":")) {
&& implementsModuleId.startsWith(":")) {
gradleModule.project.name
} else {
""
}
findModuleById(ideProject, compositePrefix + it)
}
if (targetModule != null) {
val targetModule = findModuleById(ideProject, compositePrefix + implementsModuleId) ?: continue
if (useModulePerSourceSet()) {
val targetSourceSetsByName = ExternalSystemApiUtil
.findAll(targetModule, GradleSourceSetData.KEY)
.associateBy { it.sourceSetName }
.findAll(targetModule, GradleSourceSetData.KEY)
.associateBy { it.sourceSetName }
val targetMainSourceSet = targetSourceSetsByName["main"] ?: targetModule
val targetSourceSet = targetSourceSetsByName[currentModuleNode.sourceSetName]
if (targetSourceSet != null) {
@@ -193,17 +193,15 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
ideProject: DataNode<ProjectData>,
gradleModel: KotlinGradleModel
) {
val implementedModule = gradleModel.implements?.let { findModuleById(ideProject, it) } ?: return
val implementedModules = gradleModel.implements.mapNotNull { findModuleById(ideProject, it) }
if (resolverCtx.isResolveModulePerSourceSet) {
val dependentSourceSets = dependentModule.getSourceSetsMap()
val implementedSourceSets = implementedModule.getSourceSetsMap()
val implementedSourceSetMaps = implementedModules.map { it.getSourceSetsMap() }
for ((sourceSetName, dependentSourceSet) in dependentSourceSets) {
val implementedSourceSet = implementedSourceSets[sourceSetName] ?: continue
dependentSourceSet.implementedModuleName = implementedSourceSet.data.internalName
dependentSourceSet.implementedModuleNames = implementedSourceSetMaps.mapNotNull { it[sourceSetName]?.data?.internalName }
}
}
else {
dependentModule.implementedModuleName = implementedModule.data.internalName
} else {
dependentModule.implementedModuleNames = implementedModules.map { it.data.internalName }
}
}
@@ -214,7 +214,7 @@ private fun configureFacetByGradleModule(
}
with(kotlinFacet.configuration.settings) {
implementedModuleName = (sourceSetNode ?: moduleNode).implementedModuleName
implementedModuleNames = (sourceSetNode ?: moduleNode).implementedModuleNames
productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main")
testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test")
}
@@ -175,5 +175,5 @@ class MultiplatformGradleOrderEnumeratorHandler(val factory: MultiplatformGradle
private fun Module.isMultiplatformModule(): Boolean {
val settings = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(this)
return settings.targetPlatformKind is TargetPlatformKind.Common || settings.implementedModuleName != null
return settings.targetPlatformKind is TargetPlatformKind.Common || settings.implementedModuleNames.isNotEmpty()
}
@@ -17,14 +17,12 @@
package org.jetbrains.kotlin.gradle
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.PathUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.codeInsight.gradle.GradleImportingTestCase
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Test
import java.io.File
class MultiplatformProjectImportingTest : GradleImportingTestCase() {
@Test
@@ -74,9 +72,9 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() {
@Test
fun testPlatformToCommonExpectedByDependency() {
createProjectSubFile("settings.gradle", "include ':common', ':jvm', ':js'")
createProjectSubFile("settings.gradle", "include ':common1', ':common2', ':jvm', ':js'")
val kotlinVersion = "1.2.0-beta-74"
val kotlinVersion = "1.2.40-dev-610"
createProjectSubFile("build.gradle", """
buildscript {
@@ -90,7 +88,11 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() {
}
}
project('common') {
project('common1') {
apply plugin: 'kotlin-platform-common'
}
project('common2') {
apply plugin: 'kotlin-platform-common'
}
@@ -98,7 +100,8 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() {
apply plugin: 'kotlin-platform-jvm'
dependencies {
expectedBy project(':common')
expectedBy project(':common1')
expectedBy project(':common2')
}
}
@@ -106,16 +109,20 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() {
apply plugin: 'kotlin-platform-js'
dependencies {
expectedBy project(':common')
expectedBy project(':common1')
}
}
""")
importProject()
assertModuleModuleDepScope("jvm_main", "common_main", DependencyScope.COMPILE)
assertModuleModuleDepScope("jvm_test", "common_test", DependencyScope.COMPILE)
assertModuleModuleDepScope("js_main", "common_main", DependencyScope.COMPILE)
assertModuleModuleDepScope("js_test", "common_test", DependencyScope.COMPILE)
assertModuleModuleDepScope("jvm_main", "common1_main", DependencyScope.COMPILE)
assertModuleModuleDepScope("jvm_main", "common2_main", DependencyScope.COMPILE)
assertModuleModuleDepScope("jvm_test", "common1_test", DependencyScope.COMPILE)
assertModuleModuleDepScope("jvm_test", "common2_test", DependencyScope.COMPILE)
assertModuleModuleDepScope("js_main", "common1_main", DependencyScope.COMPILE)
assertModuleModuleDepScope("js_test", "common1_test", DependencyScope.COMPILE)
assertNoDepForModule("js_main", "common2_main")
assertNoDepForModule("js_test", "common2_test")
}
@Test
@@ -128,6 +128,10 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
expectedScopes, actualScopes);
}
protected void assertNoDepForModule(String moduleName, String depName) {
assertEmpty("No dependency '" + depName + "' was expected", collectModuleDeps(moduleName, depName, ModuleOrderEntry.class));
}
@NotNull
private List<ModuleOrderEntry> getModuleModuleDeps(@NotNull String moduleName, @NotNull String depName) {
return getModuleDep(moduleName, depName, ModuleOrderEntry.class);
@@ -139,6 +143,14 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
@NotNull
private <T> List<T> getModuleDep(@NotNull String moduleName, @NotNull String depName, @NotNull Class<T> clazz) {
List<T> deps = collectModuleDeps(moduleName, depName, clazz);
assertTrue("Dependency '" + depName + "' for module '" + moduleName + "' not found among: " + collectModuleDepsNames(moduleName, clazz),
!deps.isEmpty());
return deps;
}
@NotNull
private <T> List<T> collectModuleDeps(@NotNull String moduleName, @NotNull String depName, @NotNull Class<T> clazz) {
List<T> deps = ContainerUtil.newArrayList();
for (OrderEntry e : getRootManager(moduleName).getOrderEntries()) {
@@ -146,8 +158,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
deps.add((T)e);
}
}
assertTrue("Dependency '" + depName + "' for module '" + moduleName + "' not found among: " + collectModuleDepsNames(moduleName, clazz),
!deps.isEmpty());
return deps;
}
@@ -1481,10 +1481,10 @@ compileTestKotlin {
importProject()
Assert.assertEquals("MultiTest_main", facetSettings("MultiTest-jvm_main").implementedModuleName)
Assert.assertEquals("MultiTest_test", facetSettings("MultiTest-jvm_test").implementedModuleName)
Assert.assertEquals("MultiTest_main", facetSettings("MultiTest-js_main").implementedModuleName)
Assert.assertEquals("MultiTest_test", facetSettings("MultiTest-js_test").implementedModuleName)
Assert.assertEquals(listOf("MultiTest_main"), facetSettings("MultiTest-jvm_main").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_test"), facetSettings("MultiTest-jvm_test").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_main"), facetSettings("MultiTest-js_main").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_test"), facetSettings("MultiTest-js_test").implementedModuleNames)
}
@Test
@@ -1630,10 +1630,10 @@ compileTestKotlin {
importProject()
Assert.assertEquals("MultiTest_myMain", facetSettings("MultiTest-jvm_myMain").implementedModuleName)
Assert.assertEquals("MultiTest_myTest", facetSettings("MultiTest-jvm_myTest").implementedModuleName)
Assert.assertEquals("MultiTest_myMain", facetSettings("MultiTest-js_myMain").implementedModuleName)
Assert.assertEquals("MultiTest_myTest", facetSettings("MultiTest-js_myTest").implementedModuleName)
Assert.assertEquals(listOf("MultiTest_myMain"), facetSettings("MultiTest-jvm_myMain").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_myTest"), facetSettings("MultiTest-jvm_myTest").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_myMain"), facetSettings("MultiTest-js_myMain").implementedModuleNames)
Assert.assertEquals(listOf("MultiTest_myTest"), facetSettings("MultiTest-js_myTest").implementedModuleNames)
}
@Test
@@ -192,7 +192,7 @@ class KotlinFacetSettings {
}
}
var implementedModuleName: String? = null
var implementedModuleNames: List<String> = emptyList()
var productionOutputPath: String? = null
var testOutputPath: String? = null
@@ -99,7 +99,12 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
val platformName = element.getAttributeValue("platform")
val platformKind = TargetPlatformKind.ALL_PLATFORMS.firstOrNull { it.description == platformName } ?: TargetPlatformKind.DEFAULT_PLATFORM
element.getChild("implements")?.let {
implementedModuleName = (it.content.firstOrNull() as? Text)?.textTrim
val items = it.getChildren("implement")
implementedModuleNames = if (items.isNotEmpty()) {
items.mapNotNull { (it.content.firstOrNull() as? Text)?.textTrim }
} else {
listOfNotNull((it.content.firstOrNull() as? Text)?.textTrim)
}
}
element.getChild("compilerSettings")?.let {
compilerSettings = CompilerSettings()
@@ -237,8 +242,17 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
if (!useProjectSettings) {
element.setAttribute("useProjectSettings", useProjectSettings.toString())
}
implementedModuleName?.let {
element.addContent(Element("implements").apply { addContent(it) })
if (implementedModuleNames.isNotEmpty()) {
element.addContent(
Element("implements").apply {
val singleModule = implementedModuleNames.singleOrNull()
if (singleModule != null) {
addContent(singleModule)
} else {
implementedModuleNames.map { addContent(Element("implement").apply { addContent(it) }) }
}
}
)
}
productionOutputPath?.let {
if (it != (compilerArguments as? K2JSCompilerArguments)?.outputFile) {
@@ -301,13 +301,13 @@ class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_
private fun setImplementedModuleName(kotlinFacet: KotlinFacet, mavenProject: MavenProject, module: Module) {
if (kotlinFacet.configuration.settings.targetPlatformKind == TargetPlatformKind.Common) {
kotlinFacet.configuration.settings.implementedModuleName = null
kotlinFacet.configuration.settings.implementedModuleNames = emptyList()
} else {
val manager = MavenProjectsManager.getInstance(module.project)
val mavenDependencies = mavenProject.dependencies.mapNotNull { manager?.findProject(it) }
val implemented = mavenDependencies.singleOrNull { detectPlatformByExecutions(it) == TargetPlatformKind.Common }
val implemented = mavenDependencies.filter { detectPlatformByExecutions(it) == TargetPlatformKind.Common }
kotlinFacet.configuration.settings.implementedModuleName = implemented?.let { manager.findModule(it)?.name ?: it.displayName }
kotlinFacet.configuration.settings.implementedModuleNames = implemented.map { manager.findModule(it)?.name ?: it.displayName }
}
}
}
@@ -1973,8 +1973,8 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
"""
)
val commonModule = createModulePom(
"my-common-module",
val commonModule1 = createModulePom(
"my-common-module1",
"""
<parent>
@@ -1984,7 +1984,50 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
</parent>
<groupId>test</groupId>
<artifactId>my-common-module</artifactId>
<artifactId>my-common-module1</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-common</artifactId>
<version>$kotlinVersion</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>meta</id>
<phase>compile</phase>
<goals>
<goal>metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
"""
)
val commonModule2 = createModulePom(
"my-common-module2",
"""
<parent>
<groupId>test</groupId>
<artifactId>project</artifactId>
<version>1.0.0</version>
</parent>
<groupId>test</groupId>
<artifactId>my-common-module2</artifactId>
<version>1.0.0</version>
<dependencies>
@@ -2038,7 +2081,12 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>my-common-module</artifactId>
<artifactId>my-common-module1</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>my-common-module2</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
@@ -2086,7 +2134,7 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>my-common-module</artifactId>
<artifactId>my-common-module1</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
@@ -2112,23 +2160,27 @@ class KotlinMavenImporterTest : MavenImportingTestCase() {
"""
)
importProjects(mainPom, commonModule, jvmModule, jsModule)
importProjects(mainPom, commonModule1, commonModule2, jvmModule, jsModule)
assertModules("project", "my-common-module", "my-jvm-module", "my-js-module")
assertModules("project", "my-common-module1", "my-common-module2", "my-jvm-module", "my-js-module")
assertImporterStatePresent()
with(facetSettings("my-common-module")) {
with(facetSettings("my-common-module1")) {
Assert.assertEquals(TargetPlatformKind.Common.description, targetPlatformKind!!.description)
}
with(facetSettings("my-common-module2")) {
Assert.assertEquals(TargetPlatformKind.Common.description, targetPlatformKind!!.description)
}
with(facetSettings("my-jvm-module")) {
Assert.assertEquals(TargetPlatformKind.Jvm(JvmTarget.JVM_1_6).description, targetPlatformKind!!.description)
Assert.assertEquals("my-common-module", implementedModuleName)
Assert.assertEquals(listOf("my-common-module1", "my-common-module2"), implementedModuleNames)
}
with(facetSettings("my-js-module")) {
Assert.assertEquals(TargetPlatformKind.JavaScript.description, targetPlatformKind!!.description)
Assert.assertEquals("my-common-module", implementedModuleName)
Assert.assertEquals(listOf("my-common-module1"), implementedModuleNames)
}
}
@@ -20,7 +20,6 @@ import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import java.io.File
@@ -48,7 +47,7 @@ interface KotlinGradleModel : Serializable {
val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val coroutines: String?
val platformPluginId: String?
val implements: String?
val implements: List<String>
}
class KotlinGradleModelImpl(
@@ -56,7 +55,7 @@ class KotlinGradleModelImpl(
override val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet,
override val coroutines: String?,
override val platformPluginId: String?,
override val implements: String?
override val implements: List<String>
) : KotlinGradleModel
abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService {
@@ -89,11 +88,11 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() {
override fun canBuild(modelName: String?): Boolean = modelName == KotlinGradleModel::class.java.name
private fun getImplements(project: Project): Project? {
private fun getImplementedProjects(project: Project): List<Project> {
return listOf("expectedBy", "implement")
.flatMap { project.configurations.findByName(it)?.dependencies ?: emptySet<Dependency>() }
.firstIsInstanceOrNull<ProjectDependency>()
?.dependencyProject
.filterIsInstance<ProjectDependency>()
.mapNotNull { it.dependencyProject }
}
// see GradleProjectResolverUtil.getModuleId() in IDEA codebase
@@ -164,14 +163,14 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() {
}
val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value
val implementedProject = getImplements(project)
val implementedProjects = getImplementedProjects(project)
return KotlinGradleModelImpl(
kotlinPluginId != null || platformPluginId != null,
compilerArgumentsBySourceSet,
getCoroutines(project),
platform,
implementedProject?.pathOrName()
implementedProjects.map { it.pathOrName() }
)
}
}
@@ -123,11 +123,8 @@ val mavenLibraryIdToPlatform: Map<String, TargetPlatformKind<*>> by lazy {
.toMap()
}
internal fun Module.findImplementingModules(modelsProvider: IdeModifiableModelsProvider): List<Module> {
return modelsProvider.modules.filter { module ->
module.findImplementedModuleName(modelsProvider) == name
}
}
internal fun Module.findImplementingModules(modelsProvider: IdeModifiableModelsProvider) =
modelsProvider.modules.filter { name in it.findImplementedModuleNames(modelsProvider) }
val Module.implementingModules: List<Module>
get() = cached(CachedValueProvider {
@@ -167,12 +164,11 @@ val ModuleDescriptor.implementingDescriptors: List<ModuleDescriptor>
})
}
val ModuleDescriptor.implementedDescriptor: ModuleDescriptor?
val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
get() {
val moduleSourceInfo = getCapability(ModuleInfo.Capability) as? ModuleSourceInfo ?: return null
val moduleSourceInfo = getCapability(ModuleInfo.Capability) as? ModuleSourceInfo ?: return emptyList()
val implementedModuleInfo = moduleSourceInfo.expectedBy
return implementedModuleInfo?.let {
return moduleSourceInfo.expectedBy.mapNotNull {
KotlinCacheService.getInstance(moduleSourceInfo.module.project)
.getResolutionFacadeByModuleInfo(it, it.platform)?.moduleDescriptor
}
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.facet.implementedDescriptor
import org.jetbrains.kotlin.idea.facet.implementedDescriptors
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -44,8 +44,8 @@ fun getExpectedDeclarationTooltip(declaration: KtDeclaration?): String? {
val descriptor = declaration?.toDescriptor() as? MemberDescriptor ?: return null
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
val commonModuleDescriptor = platformModuleDescriptor.implementedDescriptor ?: return null
if (!commonModuleDescriptor.hasDeclarationOf(descriptor)) return null
val commonModuleDescriptors = platformModuleDescriptor.implementedDescriptors ?: return null
if (!commonModuleDescriptors.any { it.hasDeclarationOf(descriptor) }) return null
return "Has declaration in common module"
}
@@ -54,7 +54,10 @@ fun navigateToExpectedDeclaration(declaration: KtDeclaration?) {
declaration?.expectedDeclarationIfAny()?.navigate(false)
}
internal fun MemberDescriptor.expectedDescriptor() = module.implementedDescriptor?.declarationOf(this)
internal fun MemberDescriptor.expectedDescriptors() = module.implementedDescriptors.mapNotNull { it.declarationOf(this) }
// TODO: Sort out the cases with multiple expected descriptors
internal fun MemberDescriptor.expectedDescriptor() = expectedDescriptors().firstOrNull()
internal fun KtDeclaration.expectedDeclarationIfAny(): KtDeclaration? {
val expectedDescriptor = (toDescriptor() as? MemberDescriptor)?.expectedDescriptor() ?: return null
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightMethod
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.facet.implementedDescriptor
import org.jetbrains.kotlin.idea.facet.implementedDescriptors
import org.jetbrains.kotlin.idea.facet.implementingDescriptors
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
@@ -379,8 +379,7 @@ private fun collectExpectedMarkers(declaration: KtNamedDeclaration,
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
val commonModuleDescriptor = platformModuleDescriptor.implementedDescriptor ?: return
if (!commonModuleDescriptor.hasDeclarationOf(descriptor)) return
if (!platformModuleDescriptor.implementedDescriptors.any { it.hasDeclarationOf(descriptor) }) return
val anchor = declaration.expectOrActualAnchor
@@ -48,6 +48,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.stream.StreamSupport;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
public class ConfigureKotlinTest extends AbstractConfigureKotlinTest {
public void testNewLibrary_copyJar() {
doTestOneJavaModule(KotlinWithLibraryConfigurator.FileState.COPY);
@@ -269,8 +272,8 @@ public class ConfigureKotlinTest extends AbstractConfigureKotlinTest {
Module module2 = moduleManager.findModuleByName("module2");
assert module2 != null;
assertEquals(KotlinFacet.Companion.get(module1).getConfiguration().getSettings().getImplementedModuleName(), null);
assertEquals(KotlinFacet.Companion.get(module2).getConfiguration().getSettings().getImplementedModuleName(), "module1");
assertEquals(KotlinFacet.Companion.get(module1).getConfiguration().getSettings().getImplementedModuleNames(), emptyList());
assertEquals(KotlinFacet.Companion.get(module2).getConfiguration().getSettings().getImplementedModuleNames(), singletonList("module1"));
}
public void testJava9WithModuleInfo() {
@@ -121,7 +121,7 @@ fun Module.createFacet(
platformKind
)
if (implementedModuleName != null) {
this.implementedModuleName = implementedModuleName
this.implementedModuleNames = listOf(implementedModuleName)
}
}
modelsProvider.commit()