diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformModuleBuilder.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformModuleBuilder.kt index 88eb416bef5..e058b1800ed 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformModuleBuilder.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformModuleBuilder.kt @@ -21,7 +21,7 @@ import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.module.Module -import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.ui.configuration.ModulesProvider import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile @@ -31,40 +31,35 @@ import org.jetbrains.kotlin.idea.util.rootManager import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl +import javax.swing.Icon class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { + var commonModuleName: String? = null var jvmModuleName: String? = null var jsModuleName: String? = null override fun getBuilderId() = "kotlin.gradle.multiplatform" - override fun getNodeIcon() = KotlinIcons.MPP + override fun getNodeIcon(): Icon = KotlinIcons.MPP override fun getPresentableName() = "Kotlin (Multiplatform - Experimental)" override fun getDescription() = - "Multiplatform projects allow reusing the same code between multiple platforms supported by Kotlin. Such projects are built with Gradle." + "Multiplatform projects allow reusing the same code between multiple platforms supported by Kotlin. Such projects are built with Gradle." override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array { super.createWizardSteps(wizardContext, modulesProvider) // initializes GradleModuleBuilder.myWizardContext - return arrayOf(KotlinGradleMultiplatformWizardStep(this, wizardContext), - ExternalModuleSettingsStep( - wizardContext, this, GradleProjectSettingsControl(externalProjectSettings)) + return arrayOf( + KotlinGradleMultiplatformWizardStep(this, wizardContext), + ExternalModuleSettingsStep(wizardContext, this, GradleProjectSettingsControl(externalProjectSettings)) ) } - override fun setupRootModel(modifiableRootModel: ModifiableRootModel) { - super.setupRootModel(modifiableRootModel) - val module = modifiableRootModel.module - val buildScriptData = getBuildScriptData(module) ?: return - val sdk = modifiableRootModel.sdk - GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, sdk) - } - override fun setupModule(module: Module) { super.setupModule(module) val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return + setupCommonModule(module, contentRoot) setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider()) setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider()) @@ -72,24 +67,42 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { settingsGradle?.let { module.project.executeCommand("Update settings.gradle") { val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand - doc.insertString(doc.textLength, "include '$jvmModuleName', '$jsModuleName'") + val commonPrefix = commonModuleName?.let { "'$it', " } ?: "" + doc.insertString(doc.textLength, "include $commonPrefix'$jvmModuleName', '$jsModuleName'") FileDocumentManager.getInstance().saveDocument(doc) } } } - private fun setupPlatformModule(module: Module, - contentRoot: VirtualFile, - platformModuleName: String?, - supportProvider: GradleKotlinFrameworkSupportProvider) { - if (platformModuleName.isNullOrEmpty()) return - - val sdk = module.rootManager.sdk - val moduleDir = contentRoot.createChildDirectory(this, platformModuleName!!) + private fun setupChildModule( + rootModule: Module, + contentRoot: VirtualFile, + childModuleName: String?, + extendScript: (BuildScriptDataBuilder, Sdk?) -> Unit = { _, _ -> } + ) { + if (childModuleName.isNullOrEmpty()) return + val moduleDir = contentRoot.createChildDirectory(this, childModuleName!!) val buildGradle = moduleDir.createChildData(null, "build.gradle") val buildScriptData = BuildScriptDataBuilder(buildGradle) - supportProvider.addSupport(buildScriptData, sdk) - buildScriptData.addDependencyNotation("expectedBy project(\":\")") + extendScript(buildScriptData, rootModule.rootManager.sdk) VfsUtil.saveText(buildGradle, buildScriptData.buildConfigurationPart() + buildScriptData.buildMainPart()) } + + private fun setupCommonModule( + rootModule: Module, + contentRoot: VirtualFile + ) = setupChildModule(rootModule, contentRoot, commonModuleName) { builder, sdk -> + GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, sdk) + } + + private fun setupPlatformModule( + rootModule: Module, + contentRoot: VirtualFile, + platformModuleName: String?, + supportProvider: GradleKotlinFrameworkSupportProvider + ) = setupChildModule(rootModule, contentRoot, platformModuleName) { builder, sdk -> + supportProvider.addSupport(builder, sdk) + val dependency = commonModuleName ?: "" + builder.addDependencyNotation("expectedBy project(\":$dependency\")") + } } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformWizardStep.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformWizardStep.kt index 67c2288936a..223be2fd9b7 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformWizardStep.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleMultiplatformWizardStep.kt @@ -37,8 +37,10 @@ class KotlinGradleMultiplatformWizardStep( private val wizardContext: WizardContext ) : ModuleWizardStep() { - private val mainModuleNameComponent: LabeledComponent = - LabeledComponent.create(JTextField(), "Main module name:", BorderLayout.WEST) + private val rootModuleNameComponent: LabeledComponent = + LabeledComponent.create(JTextField(), "Root module name:", BorderLayout.WEST) + private val commonModuleNameComponent: LabeledComponent = + LabeledComponent.create(JTextField(), "Common module name:", BorderLayout.WEST) private val jvmModuleNameComponent: LabeledComponent = LabeledComponent.create(JTextField(), "JVM module name:", BorderLayout.WEST) private val jsModuleNameComponent: LabeledComponent = @@ -50,13 +52,14 @@ class KotlinGradleMultiplatformWizardStep( init { panel = object : JPanel(GridBagLayout()), PanelWithAnchor { - private var anchor: JComponent? = mainModuleNameComponent.anchor + private var anchor: JComponent? = rootModuleNameComponent.anchor override fun getAnchor(): JComponent? = anchor override fun setAnchor(anchor: JComponent?) { this.anchor = anchor - mainModuleNameComponent.anchor = anchor + rootModuleNameComponent.anchor = anchor + commonModuleNameComponent.anchor = anchor jvmModuleNameComponent.anchor = anchor jsModuleNameComponent.anchor = anchor } @@ -64,12 +67,12 @@ class KotlinGradleMultiplatformWizardStep( val baseDir = wizardContext.projectFileDirectory val projectName = wizardContext.projectName val initialProjectName = projectName ?: ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", "") - mainModuleNameComponent.component.text = initialProjectName - mainModuleNameComponent.component.select(0, initialProjectName.length) + rootModuleNameComponent.component.text = initialProjectName + rootModuleNameComponent.component.select(0, initialProjectName.length) updateDerivedModuleNames() - mainModuleNameComponent.component.document.addDocumentListener(object : DocumentAdapter() { + rootModuleNameComponent.component.document.addDocumentListener(object : DocumentAdapter() { override fun textChanged(e: DocumentEvent?) { if (syncEditing) { updateDerivedModuleNames() @@ -84,18 +87,27 @@ class KotlinGradleMultiplatformWizardStep( } } } + commonModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener) jvmModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener) jsModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener) panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) panel.add( - mainModuleNameComponent, + rootModuleNameComponent, GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, JBUI.insets(10, 0, 0, 0), 0, 0 ) ) + panel.add( + commonModuleNameComponent, + GridBagConstraints( + 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, + GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, + JBUI.emptyInsets(), 0, 0 + ) + ) panel.add( jvmModuleNameComponent, GridBagConstraints( @@ -126,8 +138,9 @@ class KotlinGradleMultiplatformWizardStep( private fun updateDerivedModuleNames() { inSyncUpdate = true try { - jvmModuleNameComponent.component.text = "$mainModuleName-jvm" - jsModuleNameComponent.component.text = "$mainModuleName-js" + commonModuleNameComponent.component.text = "$rootModuleName-common" + jvmModuleNameComponent.component.text = "$rootModuleName-jvm" + jsModuleNameComponent.component.text = "$rootModuleName-js" } finally { inSyncUpdate = false @@ -136,30 +149,38 @@ class KotlinGradleMultiplatformWizardStep( override fun updateDataModel() { wizardContext.projectBuilder = builder - wizardContext.projectName = mainModuleName + wizardContext.projectName = rootModuleName - builder.projectId = ProjectId("", mainModuleName, "") + builder.projectId = ProjectId("", rootModuleName, "") + builder.commonModuleName = commonModuleName builder.jvmModuleName = jvmModuleName builder.jsModuleName = jsModuleName } override fun getComponent() = panel - private val mainModuleName: String - get() = mainModuleNameComponent.component.text + private val rootModuleName: String + get() = rootModuleNameComponent.component.text + private val commonModuleName: String + get() = commonModuleNameComponent.component.text private val jvmModuleName: String get() = jvmModuleNameComponent.component.text private val jsModuleName: String get() = jsModuleNameComponent.component.text override fun validate(): Boolean { - if (mainModuleName.isEmpty()) { - throw ConfigurationException("Please specify the main module name") + if (rootModuleName.isEmpty()) { + throw ConfigurationException("Please specify the root module name") } - if (jvmModuleName.isNotEmpty() && (jvmModuleName == mainModuleName || jvmModuleName == jsModuleName)) { + if (commonModuleName.isNotEmpty() + && (commonModuleName == rootModuleName || commonModuleName == jvmModuleName || commonModuleName == jsModuleName) + ) { + throw ConfigurationException("The common module name should be distinct") + } + if (jvmModuleName.isNotEmpty() && (jvmModuleName == rootModuleName || jvmModuleName == jsModuleName)) { throw ConfigurationException("The JVM module name should be distinct") } - if (jsModuleName.isNotEmpty() && jsModuleName == mainModuleName) { + if (jsModuleName.isNotEmpty() && jsModuleName == rootModuleName) { throw ConfigurationException("The IS module name should be distinct") } return true