From 1650e69980c21586c5fba1f141d8f568bf40a9aa Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 11 May 2018 18:52:55 +0300 Subject: [PATCH] Configuration: Use 'plugins' block in generated/modified Gradle scripts #KT-20665 Fixed --- .../KotlinAndroidGradleModuleConfigurator.kt | 9 +- .../GradleBuildScriptManipulator.kt | 36 +- .../GradleKotlinFrameworkSupportProvider.kt | 102 ++- .../GroovyBuildScriptManipulator.kt | 264 ++++-- .../KotlinBuildScriptManipulator.kt | 220 +++-- ...DslGradleKotlinFrameworkSupportProvider.kt | 84 +- .../KotlinGradleModuleConfigurator.kt | 3 + .../KotlinGradleMultiplatformModuleBuilder.kt | 34 +- .../KotlinJsGradleModuleConfigurator.kt | 19 + .../KotlinWithGradleConfigurator.kt | 74 +- .../configuration/gradleModuleBuilderUtils.kt | 88 ++ .../gradle/GradleConfiguratorTest.kt | 770 ++++++++++++++++-- 12 files changed, 1392 insertions(+), 311 deletions(-) create mode 100644 idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/gradleModuleBuilderUtils.kt diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleModuleConfigurator.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleModuleConfigurator.kt index 2019310b9aa..6cab0250f09 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleModuleConfigurator.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleModuleConfigurator.kt @@ -32,17 +32,20 @@ class KotlinAndroidGradleModuleConfigurator internal constructor() : KotlinWithG override val kotlinPluginName: String = KOTLIN_ANDROID + override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String = + if (forKotlinDsl) "kotlin(\"android\")" else "id 'org.jetbrains.kotlin.android' " + override fun addElementsToFile(file: PsiFile, isTopLevelProjectFile: Boolean, version: String): Boolean { - val manipulator = getManipulator(file) + val manipulator = getManipulator(file, false) val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk } val jvmTarget = getJvmTarget(sdk, version) - return if (isTopLevelProjectFile) { - manipulator.configureProjectBuildScript(version) + manipulator.configureProjectBuildScript(kotlinPluginName, version) } else { manipulator.configureModuleBuildScript( kotlinPluginName, + getKotlinPluginExpression(file.isKtDsl()), getStdlibArtifactName(sdk, version), version, jvmTarget diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt index 5d0be4698e8..d7492a5702e 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt @@ -16,21 +16,31 @@ package org.jetbrains.kotlin.idea.configuration +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.ExternalLibraryDescriptor import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.idea.util.module +import org.jetbrains.plugins.gradle.settings.GradleSettings -interface GradleBuildScriptManipulator { - fun isConfigured(kotlinPluginName: String): Boolean +interface GradleBuildScriptManipulator { + val scriptFile: Psi + val preferNewSyntax: Boolean + + fun isConfiguredWithOldSyntax(kotlinPluginName: String): Boolean + fun isConfigured(kotlinPluginExpression: String): Boolean fun configureModuleBuildScript( kotlinPluginName: String, + kotlinPluginExpression: String, stdlibArtifactName: String, version: String, jvmTarget: String? ): Boolean - fun configureProjectBuildScript(version: String): Boolean + fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? @@ -45,4 +55,24 @@ interface GradleBuildScriptManipulator { ) fun getKotlinStdlibVersion(): String? + + // For settings.gradle/settings.gradle.kts + + fun addMavenCentralPluginRepository() + fun addPluginRepository(repository: RepositoryDescription) + + fun addResolutionStrategy(pluginId: String) +} + +val MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX = GradleVersion.version("4.4") + +fun GradleBuildScriptManipulator<*>.useNewSyntax(kotlinPluginName: String): Boolean { + if (!preferNewSyntax) return false + + val module = scriptFile.module ?: return false + val path = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return false + val gradleVersion = GradleSettings.getInstance(module.project).getLinkedProjectSettings(path)?.resolveGradleVersion() ?: return false + if (gradleVersion < MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX) return false + + return !isConfiguredWithOldSyntax(kotlinPluginName) } \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt index 78f200c137c..d3dcf236715 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt @@ -24,6 +24,7 @@ import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.ModifiableModelsProvider import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.versions.* import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder @@ -46,7 +47,7 @@ abstract class GradleKotlinFrameworkSupportProvider( } override fun createConfigurable(model: FrameworkSupportModel): FrameworkSupportInModuleConfigurable { - val configurable = super.createConfigurable(model) + val configurable = KotlinGradleFrameworkSupportInModuleConfigurable(model, this) return object : FrameworkSupportInModuleConfigurable() { override fun addSupport(module: Module, rootModel: ModifiableRootModel, modifiableModelsProvider: ModifiableModelsProvider) { configurable.addSupport(module, rootModel, modifiableModelsProvider) @@ -62,50 +63,75 @@ abstract class GradleKotlinFrameworkSupportProvider( modifiableModelsProvider: ModifiableModelsProvider, buildScriptData: BuildScriptDataBuilder ) { - addSupport(buildScriptData, rootModel.sdk) + addSupport(buildScriptData, module, rootModel.sdk, true) } - open fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) { + open fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) { var kotlinVersion = bundledRuntimeVersion() val additionalRepository = getRepositoryForVersion(kotlinVersion) if (isSnapshot(bundledRuntimeVersion())) { kotlinVersion = LAST_SNAPSHOT_VERSION } - if (additionalRepository != null) { - val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ') - buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository) + val useNewSyntax = buildScriptData.gradleVersion >= MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX + if (useNewSyntax) { + if (additionalRepository != null) { + val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ') + updateSettingsScript(module) { + with(KotlinWithGradleConfigurator.getManipulator(it)) { + addPluginRepository(additionalRepository) + addMavenCentralPluginRepository() + } + } + buildScriptData.addRepositoriesDefinition("mavenCentral()") + buildScriptData.addRepositoriesDefinition(oneLineRepository) + } - buildScriptData.addRepositoriesDefinition("mavenCentral()") - buildScriptData.addRepositoriesDefinition(oneLineRepository) + buildScriptData.addPluginDefinitionInPluginsGroup( + getPluginExpression() + if (specifyPluginVersionIfNeeded) " version '$kotlinVersion'" else "" + ) + } else { + if (additionalRepository != null) { + val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ') + buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository) + + buildScriptData.addRepositoriesDefinition("mavenCentral()") + buildScriptData.addRepositoriesDefinition(oneLineRepository) + } + + buildScriptData + .addPluginDefinition(KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(getPluginId())) + + .addBuildscriptRepositoriesDefinition("mavenCentral()") + .addRepositoriesDefinition("mavenCentral()") + + .addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'") } - buildScriptData - .addPluginDefinition(KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(getPluginId())) - - .addBuildscriptRepositoriesDefinition("mavenCentral()") - .addRepositoriesDefinition("mavenCentral()") - - .addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'") - for (dependency in getDependencies(sdk)) { - buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile")) + buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile", !useNewSyntax)) } for (dependency in getTestDependencies()) { buildScriptData.addDependencyNotation( if (":" in dependency) "testCompile \"$dependency\"" else - KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "testCompile") + KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "testCompile", !useNewSyntax) ) } - buildScriptData.addBuildscriptDependencyNotation(KotlinWithGradleConfigurator.CLASSPATH) + + if (useNewSyntax) { + updateSettingsScript(module) { updateSettingsScript(it, specifyPluginVersionIfNeeded) } + } } + protected open fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { } + protected abstract fun getDependencies(sdk: Sdk?): List protected open fun getTestDependencies(): List = listOf() protected abstract fun getPluginId(): String + protected abstract fun getPluginExpression(): String protected abstract fun getDescription(): String } @@ -116,11 +142,12 @@ open class GradleKotlinJavaFrameworkSupportProvider( ) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.SMALL_LOGO) { override fun getPluginId() = KotlinGradleModuleConfigurator.KOTLIN + override fun getPluginExpression() = "id 'org.jetbrains.kotlin.jvm'" override fun getDependencies(sdk: Sdk?) = listOf(getStdlibArtifactId(sdk, bundledRuntimeVersion())) - override fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) { - super.addSupport(buildScriptData, sdk) + override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) { + super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded) val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion()) if (jvmTarget != null) { val description = jvmTarget.description @@ -138,21 +165,35 @@ open class GradleKotlinJSFrameworkSupportProvider( ) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.JS) { override fun getPluginId() = KotlinJsGradleModuleConfigurator.KOTLIN_JS + override fun getPluginExpression() = "id 'kotlin2js'" override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_JS_STDLIB_ID) override fun getTestDependencies() = listOf(MAVEN_JS_TEST_ID) + override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { + if (specifyPluginVersionIfNeeded) { + KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin2js") + } + } + override fun getDescription() = "A Kotlin library or application targeting JavaScript" } open class GradleKotlinMPPCommonFrameworkSupportProvider : GradleKotlinFrameworkSupportProvider("KOTLIN_MPP_COMMON", "Kotlin (Multiplatform Common - Experimental)", KotlinIcons.MPP) { override fun getPluginId() = "kotlin-platform-common" + override fun getPluginExpression() = "id 'kotlin-platform-common'" override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_COMMON_STDLIB_ID) override fun getTestDependencies() = listOf(MAVEN_COMMON_TEST_ID, MAVEN_COMMON_TEST_ANNOTATIONS_ID) + override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { + if (specifyPluginVersionIfNeeded) { + KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-common") + } + } + override fun getDescription() = "Shared code for a Kotlin multiplatform project (targeting JVM and JS)" } @@ -160,11 +201,13 @@ class GradleKotlinMPPJavaFrameworkSupportProvider : GradleKotlinJavaFrameworkSupportProvider("KOTLIN_MPP_JVM", "Kotlin (Multiplatform JVM - Experimental)") { override fun getPluginId() = "kotlin-platform-jvm" + override fun getPluginExpression() = "id 'kotlin-platform-jvm'" + override fun getDescription() = "JVM-specific code for a Kotlin multiplatform project" override fun getTestDependencies() = listOf(MAVEN_TEST_ID, MAVEN_TEST_JUNIT_ID, "junit:junit:4.12") - override fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) { - super.addSupport(buildScriptData, sdk) + override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) { + super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded) val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion()) if (jvmTarget != null) { val description = jvmTarget.description @@ -172,11 +215,24 @@ class GradleKotlinMPPJavaFrameworkSupportProvider } } + override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { + if (specifyPluginVersionIfNeeded) { + KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-jvm") + } + } } class GradleKotlinMPPJSFrameworkSupportProvider : GradleKotlinJSFrameworkSupportProvider("KOTLIN_MPP_JS", "Kotlin (Multiplatform JS - Experimental)") { override fun getPluginId() = "kotlin-platform-js" + override fun getPluginExpression() = "id 'kotlin-platform-js'" + + override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { + if (specifyPluginVersionIfNeeded) { + KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-js") + } + } + override fun getDescription() = "JavaScript-specific code for a Kotlin multiplatform project" } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt index 83d2e4cc225..df038488fe1 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt @@ -18,71 +18,109 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.plugins.groovy.lang.psi.GroovyFile import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner +import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement -class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : GradleBuildScriptManipulator { - override fun isConfigured(kotlinPluginName: String): Boolean { - val fileText = runReadAction { groovyScript.text } +class GroovyBuildScriptManipulator( + override val scriptFile: GroovyFile, + override val preferNewSyntax: Boolean +) : GradleBuildScriptManipulator { + override fun isConfiguredWithOldSyntax(kotlinPluginName: String): Boolean { + val fileText = runReadAction { scriptFile.text } return containsDirective(fileText, getApplyPluginDirective(kotlinPluginName)) && fileText.contains("org.jetbrains.kotlin") && fileText.contains("kotlin-stdlib") } + override fun isConfigured(kotlinPluginExpression: String): Boolean { + val fileText = runReadAction { scriptFile.text } + val pluginsBlockText = runReadAction { scriptFile.getBlockByName("plugins")?.text ?: "" } + return (containsDirective(pluginsBlockText, kotlinPluginExpression)) && + fileText.contains("org.jetbrains.kotlin") && + fileText.contains("kotlin-stdlib") + } + override fun configureModuleBuildScript( kotlinPluginName: String, + kotlinPluginExpression: String, stdlibArtifactName: String, version: String, jvmTarget: String? ): Boolean { - val oldText = groovyScript.text - val applyPluginDirective = getGroovyApplyPluginDirective(kotlinPluginName) - if (!containsDirective(groovyScript.text, applyPluginDirective)) { - val apply = GroovyPsiElementFactory.getInstance(groovyScript.project).createExpressionFromText(applyPluginDirective) - val applyStatement = getApplyStatement(groovyScript) - if (applyStatement != null) { - groovyScript.addAfter(apply, applyStatement) - } else { - val anchorBlock = groovyScript.getBlockByName("plugins") ?: groovyScript.getBlockByName("buildscript") - if (anchorBlock != null) { - groovyScript.addAfter(apply, anchorBlock.parent) - } else { - groovyScript.addAfter(apply, groovyScript.statements.lastOrNull() ?: groovyScript.firstChild) + val oldText = scriptFile.text + + val useNewSyntax = useNewSyntax(kotlinPluginName) + if (useNewSyntax) { + scriptFile + .getPluginsBlock() + .addLastExpressionInBlockIfNeeded("$kotlinPluginExpression version '$version'") + scriptFile.getRepositoriesBlock().apply { + val repository = getRepositoryForVersion(version) + if (repository != null) { + scriptFile.module?.getBuildScriptSettingsPsiFile()?.let { + with(KotlinWithGradleConfigurator.getManipulator(it)) { + addPluginRepository(repository) + addMavenCentralPluginRepository() + } + } } + addMavenCentralIfMissing() + } + } + else { + val applyPluginDirective = getGroovyApplyPluginDirective(kotlinPluginName) + if (!containsDirective(scriptFile.text, applyPluginDirective)) { + val apply = GroovyPsiElementFactory.getInstance(scriptFile.project).createExpressionFromText(applyPluginDirective) + val applyStatement = getApplyStatement(scriptFile) + if (applyStatement != null) { + scriptFile.addAfter(apply, applyStatement) + } else { + val anchorBlock = scriptFile.getBlockByName("plugins") ?: scriptFile.getBlockByName("buildscript") + if (anchorBlock != null) { + scriptFile.addAfter(apply, anchorBlock.parent) + } else { + scriptFile.addAfter(apply, scriptFile.statements.lastOrNull() ?: scriptFile.firstChild) + } + } + } + + scriptFile.getRepositoriesBlock().apply { + addRepository(version) + addMavenCentralIfMissing() } } - groovyScript.getRepositoriesBlock().apply { - addRepository(version) - addMavenCentralIfMissing() - } - - groovyScript.getDependenciesBlock().apply { - addExpressionInBlockIfNeeded(getGroovyDependencySnippet(stdlibArtifactName), false) + scriptFile.getDependenciesBlock().apply { + addExpressionOrStatementInBlockIfNeeded(getGroovyDependencySnippet(stdlibArtifactName, !useNewSyntax), false, false) } if (jvmTarget != null) { - changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = false) - changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = true) + changeKotlinTaskParameter(scriptFile, "jvmTarget", jvmTarget, forTests = false) + changeKotlinTaskParameter(scriptFile, "jvmTarget", jvmTarget, forTests = true) } - return groovyScript.text != oldText + return scriptFile.text != oldText } - override fun configureProjectBuildScript(version: String): Boolean { - val oldText = groovyScript.text - groovyScript.apply { + override fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean { + if (useNewSyntax(kotlinPluginName)) return false + + val oldText = scriptFile.text + scriptFile.apply { getBuildScriptBlock().apply { addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version)) } @@ -97,12 +135,12 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl } } - return oldText != groovyScript.text + return oldText != scriptFile.text } override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? { val snippet = "coroutines \"$coroutineOption\"" - val kotlinBlock = groovyScript.getBlockOrCreate("kotlin") + val kotlinBlock = scriptFile.getBlockOrCreate("kotlin") kotlinBlock.getBlockOrCreate("experimental").apply { addOrReplaceExpression(snippet) { stmt -> (stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines" @@ -113,10 +151,10 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl } override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? = - changeKotlinTaskParameter(groovyScript, "languageVersion", version, forTests) + changeKotlinTaskParameter(scriptFile, "languageVersion", version, forTests) override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? = - changeKotlinTaskParameter(groovyScript, "apiVersion", version, forTests) + changeKotlinTaskParameter(scriptFile, "apiVersion", version, forTests) override fun addKotlinLibraryToModuleBuildScript( scope: DependencyScope, @@ -131,20 +169,20 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl libraryDescriptor.maxVersion ) - groovyScript.getDependenciesBlock().apply { + scriptFile.getDependenciesBlock().apply { addLastExpressionInBlockIfNeeded(dependencyString) } } override fun getKotlinStdlibVersion(): String? { val versionProperty = "\$kotlin_version" - groovyScript.getBlockByName("buildScript")?.let { + scriptFile.getBlockByName("buildScript")?.let { if (it.text.contains("ext.kotlin_version = ")) { return versionProperty } } - val dependencies = groovyScript.getBlockByName("dependencies")?.statements + val dependencies = scriptFile.getBlockByName("dependencies")?.statements val stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:" dependencies?.forEach { dependency -> val dependencyText = dependency.text @@ -158,6 +196,35 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl return null } + private fun addPluginRepositoryExpression(expression: String) { + scriptFile + .getBlockOrPrepend("pluginManagement") + .getBlockOrCreate("repositories") + .addLastExpressionInBlockIfNeeded(expression) + } + + override fun addMavenCentralPluginRepository() { + addPluginRepositoryExpression("mavenCentral()") + } + + override fun addPluginRepository(repository: RepositoryDescription) { + addPluginRepositoryExpression(repository.toGroovyRepositorySnippet()) + } + + override fun addResolutionStrategy(pluginId: String) { + scriptFile + .getBlockOrPrepend("pluginManagement") + .getBlockOrCreate("resolutionStrategy") + .getBlockOrCreate("eachPlugin") + .addLastStatementInBlockIfNeeded( + """ + if (requested.id.id == "$pluginId") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + """.trimIndent() + ) + } + private fun changeKotlinTaskParameter( gradleFile: GroovyFile, parameterName: String, @@ -182,7 +249,10 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl return kotlinBlock.parent } - private fun getGroovyDependencySnippet(artifactName: String) = "compile \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\"" + private fun getGroovyDependencySnippet( + artifactName: String, + withVersion: Boolean + ) = "compile \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\"" private fun getApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'" @@ -195,13 +265,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl private fun getApplyStatement(file: GroovyFile): GrApplicationStatement? = file.getChildrenOfType().find { it.invokedExpression.text == "apply" } - private fun PsiElement.getBlockByName(name: String): GrClosableBlock? { - return getChildrenOfType() - .filter { it.closureArguments.isNotEmpty() } - .find { it.invokedExpression.text == name } - ?.let { it.closureArguments[0] } - } - private fun GrClosableBlock.addRepository(version: String): Boolean { val repository = getRepositoryForVersion(version) val snippet = when { @@ -212,13 +275,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl return addLastExpressionInBlockIfNeeded(snippet) } - private fun GrClosableBlock.addMavenCentralIfMissing(): Boolean = - if (!isRepositoryConfigured(text)) addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL) else false - - private fun GrStatementOwner.getRepositoriesBlock() = getBlockOrCreate("repositories") - - private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies") - private fun GrStatementOwner.getBuildScriptBlock() = getBlockOrCreate("buildscript") { newBlock -> val pluginsBlock = getBlockByName("plugins") ?: return@getBlockOrCreate false addBefore(newBlock, pluginsBlock.parent) @@ -231,21 +287,12 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl private fun GrStatementOwner.getBuildScriptDependenciesBlock(): GrClosableBlock = getBuildScriptBlock().getBlockOrCreate("dependencies") - private fun GrStatementOwner.getBlockOrCreate( - name: String, - customInsert: GrStatementOwner.(newBlock: PsiElement) -> Boolean = { false } - ): GrClosableBlock { - var block = getBlockByName(name) - if (block == null) { - val factory = GroovyPsiElementFactory.getInstance(project) - val newBlock = factory.createExpressionFromText("$name{\n}\n") - if (!customInsert(newBlock)) { - addAfter(newBlock, statements.lastOrNull() ?: firstChild) - } - block = getBlockByName(name)!! - } - return block - } + private fun GrClosableBlock.addMavenCentralIfMissing(): Boolean = + if (!isRepositoryConfigured(text)) addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL) else false + + private fun GrStatementOwner.getRepositoriesBlock() = getBlockOrCreate("repositories") + + private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies") private fun GrClosableBlock.addOrReplaceExpression(snippet: String, predicate: (GrStatement) -> Boolean) { statements.firstOrNull(predicate)?.let { stmt -> @@ -255,29 +302,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl addLastExpressionInBlockIfNeeded(snippet) } - private fun GrClosableBlock.addLastExpressionInBlockIfNeeded(expressionText: String): Boolean = - addExpressionInBlockIfNeeded(expressionText, false) - - private fun GrClosableBlock.addFirstExpressionInBlockIfNeeded(expressionText: String): Boolean = - addExpressionInBlockIfNeeded(expressionText, true) - - private fun GrClosableBlock.addExpressionInBlockIfNeeded(expressionText: String, isFirst: Boolean): Boolean { - if (text.contains(expressionText)) return false - val newStatement = GroovyPsiElementFactory.getInstance(project).createExpressionFromText(expressionText) - CodeStyleManager.getInstance(project).reformat(newStatement) - if (!isFirst && statements.isNotEmpty()) { - val lastStatement = statements[statements.size - 1] - if (lastStatement != null) { - addAfter(newStatement, lastStatement) - } - } else { - if (firstChild != null) { - addAfter(newStatement, firstChild) - } - } - return true - } - private fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'" private fun GrStatement.replaceWithStatementFromText(snippet: String): GrStatement { @@ -291,5 +315,65 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl private val VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE) private val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin" private val CLASSPATH = "classpath \"$KOTLIN_GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\"" + + private fun PsiElement.getBlockByName(name: String): GrClosableBlock? { + return getChildrenOfType() + .filter { it.closureArguments.isNotEmpty() } + .find { it.invokedExpression.text == name } + ?.let { it.closureArguments[0] } + } + + fun GrStatementOwner.getBlockOrCreate( + name: String, + customInsert: GrStatementOwner.(newBlock: PsiElement) -> Boolean = { false } + ): GrClosableBlock { + var block = getBlockByName(name) + if (block == null) { + val factory = GroovyPsiElementFactory.getInstance(project) + val newBlock = factory.createExpressionFromText("$name{\n}\n") + if (!customInsert(newBlock)) { + addAfter(newBlock, statements.lastOrNull() ?: firstChild) + } + block = getBlockByName(name)!! + } + return block + } + + fun GrStatementOwner.getBlockOrPrepend(name: String) = getBlockOrCreate(name) { newBlock -> + addAfter(newBlock, null) + true + } + + fun GrStatementOwner.getPluginsBlock() = getBlockOrCreate("plugins") { newBlock -> + addAfter(newBlock, getBlockByName("buildscript")) + true + } + + fun GrClosableBlock.addLastExpressionInBlockIfNeeded(expressionText: String): Boolean = + addExpressionOrStatementInBlockIfNeeded(expressionText, false, false) + + fun GrClosableBlock.addLastStatementInBlockIfNeeded(expressionText: String): Boolean = + addExpressionOrStatementInBlockIfNeeded(expressionText, true, false) + + private fun GrClosableBlock.addFirstExpressionInBlockIfNeeded(expressionText: String): Boolean = + addExpressionOrStatementInBlockIfNeeded(expressionText, false, true) + + private fun GrClosableBlock.addExpressionOrStatementInBlockIfNeeded(text: String, isStatement: Boolean, isFirst: Boolean): Boolean { + if (statements.any { StringUtil.equalsIgnoreWhitespaces(it.text, text) }) return false + val psiFactory = GroovyPsiElementFactory.getInstance(project) + val newStatement = if (isStatement) psiFactory.createStatementFromText(text) else psiFactory.createExpressionFromText(text) + CodeStyleManager.getInstance(project).reformat(newStatement) + if (!isFirst && statements.isNotEmpty()) { + val lastStatement = statements[statements.size - 1] + if (lastStatement != null) { + addAfter(newStatement, lastStatement) + } + } else { + if (firstChild != null) { + addAfter(newStatement, firstChild) + } + } + return true + } } } \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt index 14ee9079e47..f5791b603dd 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt @@ -18,24 +18,35 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.utils.addToStdlib.cast -class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBuildScriptManipulator { - override fun isConfigured(kotlinPluginName: String): Boolean = - runReadAction { - kotlinScript.containsApplyKotlinPlugin(kotlinPluginName) && kotlinScript.containsCompileStdLib() - } +class KotlinBuildScriptManipulator( + override val scriptFile: KtFile, + override val preferNewSyntax: Boolean +) : GradleBuildScriptManipulator { + override fun isConfiguredWithOldSyntax(kotlinPluginName: String) = runReadAction { + scriptFile.containsApplyKotlinPlugin(kotlinPluginName) && scriptFile.containsCompileStdLib() + } - override fun configureProjectBuildScript(version: String): Boolean { - val originalText = kotlinScript.text - kotlinScript.getBuildScriptBlock()?.apply { + override fun isConfigured(kotlinPluginExpression: String): Boolean = runReadAction { + scriptFile.containsKotlinPluginInPluginsGroup(kotlinPluginExpression) && scriptFile.containsCompileStdLib() + } + + override fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean { + if (useNewSyntax(kotlinPluginName)) return false + + val originalText = scriptFile.text + scriptFile.getBuildScriptBlock()?.apply { addDeclarationIfMissing("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true).also { addExpressionAfterIfMissing("$GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$version\"", it) } @@ -48,50 +59,70 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui getDependenciesBlock()?.addPluginToClassPathIfMissing() } - return originalText != kotlinScript.text + return originalText != scriptFile.text } override fun configureModuleBuildScript( kotlinPluginName: String, + kotlinPluginExpression: String, stdlibArtifactName: String, version: String, jvmTarget: String? ): Boolean { - val originalText = kotlinScript.text - kotlinScript.apply { - script?.blockExpression?.addDeclarationIfMissing("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true) - getApplyBlock()?.createPluginIfMissing(kotlinPluginName) - getDependenciesBlock()?.addCompileStdlibIfMissing(stdlibArtifactName) - getRepositoriesBlock()?.apply { - addRepositoryIfMissing(version) - addMavenCentralIfMissing() + val originalText = scriptFile.text + val useNewSyntax = useNewSyntax(kotlinPluginName) + scriptFile.apply { + if (useNewSyntax) { + createPluginInPluginsGroupIfMissing(kotlinPluginExpression, version) + getDependenciesBlock()?.addNoVersionCompileStdlibIfMissing(stdlibArtifactName) + getRepositoriesBlock()?.apply { + val repository = getRepositoryForVersion(version) + if (repository != null) { + scriptFile.module?.getBuildScriptSettingsPsiFile()?.let { + with(KotlinWithGradleConfigurator.getManipulator(it)) { + addPluginRepository(repository) + addMavenCentralPluginRepository() + } + } + } + addMavenCentralIfMissing() + } + } else { + script?.blockExpression?.addDeclarationIfMissing("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true) + getApplyBlock()?.createPluginIfMissing(kotlinPluginName) + getDependenciesBlock()?.addCompileStdlibIfMissing(stdlibArtifactName) + getRepositoriesBlock()?.apply { + addRepositoryIfMissing(version) + addMavenCentralIfMissing() + } } - jvmTarget?.let { changeKotlinTaskParameter("jvmTarget", it, forTests = false) changeKotlinTaskParameter("jvmTarget", it, forTests = true) } } - return originalText != kotlinScript.text + return originalText != scriptFile.text } override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? = - kotlinScript.changeCoroutineConfiguration(coroutineOption) + scriptFile.changeCoroutineConfiguration(coroutineOption) override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? = - kotlinScript.changeKotlinTaskParameter("languageVersion", version, forTests) + scriptFile.changeKotlinTaskParameter("languageVersion", version, forTests) override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? = - kotlinScript.changeKotlinTaskParameter("apiVersion", version, forTests) + scriptFile.changeKotlinTaskParameter("apiVersion", version, forTests) override fun addKotlinLibraryToModuleBuildScript( scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor, isAndroidModule: Boolean ) { - val kotlinLibraryVersion = libraryDescriptor.maxVersion.takeIf { it != GSK_KOTLIN_VERSION_PROPERTY_NAME } - kotlinScript.getDependenciesBlock()?.apply { + val kotlinLibraryVersion = libraryDescriptor.maxVersion.let { + if (it == GSK_KOTLIN_VERSION_PROPERTY_NAME) "\$$it" else it + } + scriptFile.getDependenciesBlock()?.apply { addExpressionIfMissing( getCompileDependencySnippet( libraryDescriptor.libraryGroupId, @@ -103,10 +134,46 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui } } - override fun getKotlinStdlibVersion(): String? = kotlinScript.getKotlinStdlibVersion() + override fun getKotlinStdlibVersion(): String? = scriptFile.getKotlinStdlibVersion() private fun KtBlockExpression.addCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? = - findStdLibDependency() ?: addExpressionIfMissing(getCompileDependencySnippet(KOTLIN_GROUP_ID, stdlibArtifactName)) as? KtCallExpression + findStdLibDependency() + ?: addExpressionIfMissing( + getCompileDependencySnippet( + KOTLIN_GROUP_ID, + stdlibArtifactName, + version = "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME" + ) + ) as? KtCallExpression + + private fun addPluginRepositoryExpression(expression: String) { + scriptFile.getPluginManagementBlock()?.findOrCreateBlock("repositories")?.addExpressionIfMissing(expression) + } + + override fun addMavenCentralPluginRepository() { + addPluginRepositoryExpression("mavenCentral()") + } + + override fun addPluginRepository(repository: RepositoryDescription) { + addPluginRepositoryExpression(repository.toKotlinRepositorySnippet()) + } + + override fun addResolutionStrategy(pluginId: String) { + scriptFile + .getPluginManagementBlock() + ?.findOrCreateBlock("resolutionStrategy") + ?.findOrCreateBlock("eachPlugin") + ?.addExpressionIfMissing( + """ + if (requested.id.id == "$pluginId") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + """.trimIndent() + ) + } + + private fun KtBlockExpression.addNoVersionCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? = + findStdLibDependency() ?: addExpressionIfMissing("compile(${getKotlinModuleDependencySnippet(stdlibArtifactName, null)})") as? KtCallExpression private fun KtFile.containsCompileStdLib(): Boolean = findScriptInitializer("dependencies")?.getBlock()?.findStdLibDependency() != null @@ -114,12 +181,39 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui private fun KtFile.containsApplyKotlinPlugin(pluginName: String): Boolean = findScriptInitializer("apply")?.getBlock()?.findPlugin(pluginName) != null + private fun KtFile.containsKotlinPluginInPluginsGroup(pluginName: String): Boolean = + findScriptInitializer("plugins")?.getBlock()?.findPluginInPluginsGroup(pluginName) != null + private fun KtBlockExpression.findPlugin(pluginName: String): KtCallExpression? { return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find { it.calleeExpression?.text == "plugin" && it.valueArguments.firstOrNull()?.text == "\"$pluginName\"" } } + private fun KtBlockExpression.findPluginInPluginsGroup(pluginName: String): KtCallExpression? { + return PsiTreeUtil.getChildrenOfAnyType( + this, + KtCallExpression::class.java, + KtBinaryExpression::class.java, + KtDotQualifiedExpression::class.java + ).mapNotNull { + when (it) { + is KtCallExpression -> it + is KtBinaryExpression -> { + if (it.operationReference.text == "version") it.left as? KtCallExpression else null + } + is KtDotQualifiedExpression -> { + if ((it.selectorExpression as? KtCallExpression)?.calleeExpression?.text == "version") { + it.receiverExpression as? KtCallExpression + } else null + } + else -> null + } + }.find { + "${it.calleeExpression?.text?.trim() ?: ""}(${it.valueArguments.firstOrNull()?.text ?: ""})" == pluginName + } + } + private fun KtFile.findScriptInitializer(startsWith: String): KtScriptInitializer? = PsiTreeUtil.findChildrenOfType(this, KtScriptInitializer::class.java).find { it.text.startsWith(startsWith) } @@ -140,7 +234,7 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui return findScriptInitializer("dependencies")?.getBlock()?.let { val expression = it.findStdLibDependency()?.valueArguments?.firstOrNull()?.getArgumentExpression() when (expression) { - is KtCallExpression -> expression.valueArguments[1].text.trim('\"') + is KtCallExpression -> expression.valueArguments.getOrNull(1)?.text?.trim('\"') is KtStringTemplateExpression -> expression.text?.trim('\"')?.substringAfterLast(":")?.removePrefix("$") else -> null } @@ -150,23 +244,39 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui private fun KtBlockExpression.findStdLibDependency(): KtCallExpression? { return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find { val calleeText = it.calleeExpression?.text - calleeText in PRODUCTION_DEPENDENCY_STATEMENTS && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() ?: false) + calleeText in PRODUCTION_DEPENDENCY_STATEMENTS && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() + ?: false) } } private fun KtExpression.isKotlinStdLib(): Boolean = when (this) { - is KtCallExpression -> calleeExpression?.text == "kotlinModule" && - valueArguments.firstOrNull()?.getArgumentExpression()?.text?.startsWith("\"stdlib") ?: false + is KtCallExpression -> { + val calleeText = calleeExpression?.text + (calleeText == "kotlinModule" || calleeText == "kotlin") && + valueArguments.firstOrNull()?.getArgumentExpression()?.text?.startsWith("\"stdlib") ?: false + } is KtStringTemplateExpression -> text.startsWith("\"$STDLIB_ARTIFACT_PREFIX") else -> false } + private fun KtFile.getPluginManagementBlock(): KtBlockExpression? = + findScriptInitializer("pluginManagement")?.getBlock() ?: addTopLevelBlock("pluginManagement", true) + private fun KtFile.getRepositoriesBlock(): KtBlockExpression? = findScriptInitializer("repositories")?.getBlock() ?: addTopLevelBlock("repositories") private fun KtFile.getDependenciesBlock(): KtBlockExpression? = findScriptInitializer("dependencies")?.getBlock() ?: addTopLevelBlock("dependencies") + private fun KtFile.getPluginsBlock(): KtBlockExpression? = + findScriptInitializer("plugins")?.getBlock() ?: addTopLevelBlock("plugins", true) + + private fun KtFile.createPluginInPluginsGroupIfMissing(pluginName: String, version: String): KtCallExpression? = + getPluginsBlock()?.let { + it.findPluginInPluginsGroup(pluginName) + ?: it.addExpressionIfMissing("$pluginName version \"$version\"") as? KtCallExpression + } + private fun KtFile.createApplyBlock(): KtBlockExpression? { val apply = psiFactory.createScriptInitializer("apply {\n}") val plugins = findScriptInitializer("plugins") @@ -212,6 +322,15 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui } } + private fun KtBlockExpression.getRepositorySnippet(version: String): String? { + val repository = getRepositoryForVersion(version) + return when { + repository != null -> repository.toKotlinRepositorySnippet() + !isRepositoryConfigured(text) -> MAVEN_CENTRAL + else -> null + } + } + private fun KtFile.getBuildScriptBlock(): KtBlockExpression? = findScriptInitializer("buildscript")?.getBlock() ?: addTopLevelBlock("buildscript", true) @@ -222,24 +341,24 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui findBlock("dependencies") ?: addBlock("dependencies") private fun KtBlockExpression.addRepositoryIfMissing(version: String): KtCallExpression? { - val repository = getRepositoryForVersion(version) - val snippet = when { - repository != null -> repository.toKotlinRepositorySnippet() - !isRepositoryConfigured(text) -> MAVEN_CENTRAL - else -> return null - } - + val snippet = getRepositorySnippet(version) ?: return null return addExpressionIfMissing(snippet) as? KtCallExpression } private fun KtBlockExpression.addMavenCentralIfMissing(): KtCallExpression? = if (!isRepositoryConfigured(text)) addExpressionIfMissing(MAVEN_CENTRAL) as? KtCallExpression else null + private fun KtBlockExpression.findOrCreateBlock(name: String, first: Boolean = false) = findBlock(name) ?: addBlock(name, first) + private fun KtBlockExpression.addPluginToClassPathIfMissing(): KtCallExpression? = addExpressionIfMissing(getKotlinGradlePluginClassPathSnippet()) as? KtCallExpression - private fun KtBlockExpression.addBlock(name: String): KtBlockExpression? { - return add(psiFactory.createExpression("$name {\n}")) + fun getKotlinGradlePluginClassPathSnippet(): String = + "classpath(${getKotlinModuleDependencySnippet("gradle-plugin", "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME")})" + + private fun KtBlockExpression.addBlock(name: String, first: Boolean = false): KtBlockExpression? { + return psiFactory.createExpression("$name {\n}") + .let { if (first) addAfter(it, null) else add(it) } ?.apply { addNewLinesIfNeeded() } ?.cast() ?.getBlock() @@ -277,11 +396,11 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui private fun KtFile.addImportIfMissing(path: String): KtImportDirective = importDirectives.find { it.importPath?.pathStr == path } ?: importList?.add( - psiFactory.createImportDirective( - ImportPath.fromString( - path + psiFactory.createImportDirective( + ImportPath.fromString( + path + ) ) - ) ) as KtImportDirective private fun KtBlockExpression.addExpressionAfterIfMissing(text: String, after: PsiElement): KtExpression = addStatementIfMissing(text) { @@ -307,7 +426,7 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui text: String, crossinline factory: (String) -> PsiElement ): T { - statements.find { it.text == text }?.let { + statements.find { StringUtil.equalsIgnoreWhitespaces(it.text, text) }?.let { return it as T } @@ -324,10 +443,17 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui private val STDLIB_ARTIFACT_PREFIX = "org.jetbrains.kotlin:kotlin-stdlib" val GSK_KOTLIN_VERSION_PROPERTY_NAME = "kotlin_version" - fun getKotlinGradlePluginClassPathSnippet(): String = "classpath(${getKotlinModuleDependencySnippet("gradle-plugin")})" + fun getKotlinGradlePluginClassPathSnippet(): String = + "classpath(${getKotlinModuleDependencySnippet("gradle-plugin", "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME")})" - fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String = - "kotlinModule(\"${artifactId.removePrefix("kotlin-")}\", ${version?.let { "\"$it\"" } ?: GSK_KOTLIN_VERSION_PROPERTY_NAME})" + fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String { + val moduleName = artifactId.removePrefix("kotlin-") + return when (version) { + null -> "kotlin(\"$moduleName\")" + "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME" -> "kotlinModule(\"$moduleName\", $GSK_KOTLIN_VERSION_PROPERTY_NAME)" + else -> "kotlinModule(\"$moduleName\", ${"\"$version\""})" + } + } fun getCompileDependencySnippet( groupId: String, diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinDslGradleKotlinFrameworkSupportProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinDslGradleKotlinFrameworkSupportProvider.kt index 0f2b7216e9b..8d80f5d4901 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinDslGradleKotlinFrameworkSupportProvider.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinDslGradleKotlinFrameworkSupportProvider.kt @@ -18,14 +18,15 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.framework.FrameworkTypeEx import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider +import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel import com.intellij.openapi.externalSystem.model.project.ProjectId import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ModifiableModelsProvider import com.intellij.openapi.roots.ModifiableRootModel import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.GSK_KOTLIN_VERSION_PROPERTY_NAME -import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getCompileDependencySnippet import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getKotlinGradlePluginClassPathSnippet +import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getKotlinModuleDependencySnippet import org.jetbrains.kotlin.idea.versions.* import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder import org.jetbrains.plugins.gradle.frameworkSupport.KotlinDslGradleFrameworkSupportProvider @@ -42,6 +43,8 @@ abstract class KotlinDslGradleKotlinFrameworkSupportProvider( override fun createProvider(): FrameworkSupportInModuleProvider = this@KotlinDslGradleKotlinFrameworkSupportProvider } + override fun createConfigurable(model: FrameworkSupportModel) = KotlinGradleFrameworkSupportInModuleConfigurable(model, this) + override fun addSupport( projectId: ProjectId, module: Module, @@ -55,38 +58,59 @@ abstract class KotlinDslGradleKotlinFrameworkSupportProvider( kotlinVersion = LAST_SNAPSHOT_VERSION } - if (additionalRepository != null) { - val repository = additionalRepository.toKotlinRepositorySnippet() - buildScriptData.addBuildscriptRepositoriesDefinition(repository) - buildScriptData.addRepositoriesDefinition("mavenCentral()") - buildScriptData.addRepositoriesDefinition(repository) - } + val useNewSyntax = buildScriptData.gradleVersion >= MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX + if (useNewSyntax) { + if (additionalRepository != null) { + val repository = additionalRepository.toKotlinRepositorySnippet() + updateSettingsScript(module) { + with(KotlinWithGradleConfigurator.getManipulator(it)) { + addPluginRepository(additionalRepository) + addMavenCentralPluginRepository() + } + } + buildScriptData.addRepositoriesDefinition("mavenCentral()") + buildScriptData.addRepositoriesDefinition(repository) + } - buildScriptData - .addPropertyDefinition("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra") - .addPluginDefinition(getPluginDefinition()) - .addBuildscriptRepositoriesDefinition("mavenCentral()") - .addRepositoriesDefinition("mavenCentral()") - // TODO: in gradle > 4.1 this could be single declaration e.g. 'val kotlin_version: String by extra { "1.1.11" }' - .addBuildscriptPropertyDefinition("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra\n $GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$kotlinVersion\"") - .addDependencyNotation(getRuntimeLibrary(rootModel)) - .addBuildscriptDependencyNotation(getKotlinGradlePluginClassPathSnippet()) + buildScriptData + .addPluginDefinitionInPluginsGroup(getPluginDefinition() + " version \"$kotlinVersion\"") + .addDependencyNotation(getRuntimeLibrary(rootModel, null)) + } else { + if (additionalRepository != null) { + val repository = additionalRepository.toKotlinRepositorySnippet() + buildScriptData.addBuildscriptRepositoriesDefinition(repository) + buildScriptData.addRepositoriesDefinition("mavenCentral()") + buildScriptData.addRepositoriesDefinition(repository) + } + + buildScriptData + .addPropertyDefinition("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra") + .addPluginDefinition(getOldSyntaxPluginDefinition()) + .addBuildscriptRepositoriesDefinition("mavenCentral()") + .addRepositoriesDefinition("mavenCentral()") + // TODO: in gradle > 4.1 this could be single declaration e.g. 'val kotlin_version: String by extra { "1.1.11" }' + .addBuildscriptPropertyDefinition("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra\n $GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$kotlinVersion\"") + .addDependencyNotation(getRuntimeLibrary(rootModel, "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME")) + .addBuildscriptDependencyNotation(getKotlinGradlePluginClassPathSnippet()) + } } private fun RepositoryDescription.toKotlinRepositorySnippet() = "maven { setUrl(\"$url\") }" - protected abstract fun getRuntimeLibrary(rootModel: ModifiableRootModel): String + protected abstract fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?): String + protected abstract fun getOldSyntaxPluginDefinition(): String protected abstract fun getPluginDefinition(): String } class KotlinDslGradleKotlinJavaFrameworkSupportProvider : KotlinDslGradleKotlinFrameworkSupportProvider("KOTLIN", "Kotlin (Java)", KotlinIcons.SMALL_LOGO) { - override fun getPluginDefinition() = "plugin(\"${KotlinGradleModuleConfigurator.KOTLIN}\")" + override fun getOldSyntaxPluginDefinition() = "plugin(\"${KotlinGradleModuleConfigurator.KOTLIN}\")" + override fun getPluginDefinition() = "kotlin(\"jvm\")" - override fun getRuntimeLibrary(rootModel: ModifiableRootModel) = - getCompileDependencySnippet(KOTLIN_GROUP_ID, getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion())) + override fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?) = + "compile(${getKotlinModuleDependencySnippet(getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion()), version)})" override fun addSupport( projectId: ProjectId, @@ -108,8 +132,22 @@ class KotlinDslGradleKotlinJavaFrameworkSupportProvider : class KotlinDslGradleKotlinJSFrameworkSupportProvider : KotlinDslGradleKotlinFrameworkSupportProvider("KOTLIN_JS", "Kotlin (JavaScript)", KotlinIcons.JS) { - override fun getPluginDefinition(): String = "plugin(\"${KotlinJsGradleModuleConfigurator.KOTLIN_JS}\")" + override fun getOldSyntaxPluginDefinition(): String = "plugin(\"${KotlinJsGradleModuleConfigurator.KOTLIN_JS}\")" + override fun getPluginDefinition(): String = "id(\"kotlin2js\")" - override fun getRuntimeLibrary(rootModel: ModifiableRootModel) = - getCompileDependencySnippet(KOTLIN_GROUP_ID, MAVEN_JS_STDLIB_ID.removePrefix("kotlin-")) + override fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?) = + "compile(${getKotlinModuleDependencySnippet(MAVEN_JS_STDLIB_ID.removePrefix("kotlin-"), version)})" + + override fun addSupport( + projectId: ProjectId, + module: Module, + rootModel: ModifiableRootModel, + modifiableModelsProvider: ModifiableModelsProvider, + buildScriptData: BuildScriptDataBuilder + ) { + super.addSupport(projectId, module, rootModel, modifiableModelsProvider, buildScriptData) + updateSettingsScript(module) { + KotlinWithGradleConfigurator.getManipulator(it).addResolutionStrategy("kotlin2js") + } + } } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleModuleConfigurator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleModuleConfigurator.kt index 2b740f6c467..cb0c37cc261 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleModuleConfigurator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleModuleConfigurator.kt @@ -37,6 +37,9 @@ class KotlinGradleModuleConfigurator : KotlinWithGradleConfigurator() { override val kotlinPluginName: String get() = KOTLIN + override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String = + if (forKotlinDsl) "kotlin(\"jvm\")" else "id 'org.jetbrains.kotlin.jvm'" + override fun getJvmTarget(sdk: Sdk?, version: String) = getDefaultJvmTarget(sdk, version)?.description override fun configureModule( 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 af9d9b08144..2ef1daf1f58 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 @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.ide.util.projectWizard.ModuleWizardStep 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.projectRoots.Sdk import com.intellij.openapi.roots.ModifiableRootModel @@ -27,11 +26,11 @@ import com.intellij.openapi.roots.ui.configuration.ModulesProvider import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.idea.KotlinIcons -import org.jetbrains.kotlin.idea.util.application.executeCommand 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 org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory import javax.swing.Icon class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { @@ -65,28 +64,31 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { val module = modifiableRootModel.module val buildScriptData = getBuildScriptData(module) ?: return val sdk = modifiableRootModel.sdk - GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, sdk) + GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, module, sdk, true) } } override fun setupModule(module: Module) { - super.setupModule(module) + try { + module.gradleModuleBuilder = this - val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return - setupCommonModule(module, contentRoot) - setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider(), jdk) - setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider()) + super.setupModule(module) - val settingsGradle = contentRoot.findChild("settings.gradle") - settingsGradle?.let { - module.project.executeCommand("Update settings.gradle") { - val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand + val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return + setupCommonModule(module, contentRoot) + setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider(), jdk) + setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider()) + + updateSettingsScript(module) { val includedModules = listOfNotNull(commonModuleName, jvmModuleName, jsModuleName).filter { it.isNotEmpty() } if (includedModules.isNotEmpty()) { - doc.insertString(doc.textLength, includedModules.joinToString(prefix = "include ") { "'$it'" }) + val includeCall = GroovyPsiElementFactory.getInstance(module.project) + .createExpressionFromText(includedModules.joinToString(prefix = "include ") { "'$it'" }) + it.add(includeCall) } - FileDocumentManager.getInstance().saveDocument(doc) } + } finally { + flushSettingsGradleCopy(module) } } @@ -110,7 +112,7 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { rootModule: Module, contentRoot: VirtualFile ) = setupChildModule(rootModule, contentRoot, commonModuleName) { builder, sdk -> - GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, sdk) + GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, rootModule, sdk, true) } private fun setupPlatformModule( @@ -120,7 +122,7 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { supportProvider: GradleKotlinFrameworkSupportProvider, sdk: Sdk? = null ) = setupChildModule(rootModule, contentRoot, platformModuleName, sdk) { builder, finalSdk -> - supportProvider.addSupport(builder, finalSdk) + supportProvider.addSupport(builder, rootModule, finalSdk, !commonModuleName.isNullOrEmpty()) val dependency = commonModuleName ?: "" builder.addDependencyNotation("expectedBy project(\":$dependency\")") } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinJsGradleModuleConfigurator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinJsGradleModuleConfigurator.kt index 3cf00761442..76875203986 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinJsGradleModuleConfigurator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinJsGradleModuleConfigurator.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.projectRoots.Sdk +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.versions.MAVEN_JS_STDLIB_ID import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.resolve.TargetPlatform @@ -26,9 +28,26 @@ class KotlinJsGradleModuleConfigurator : KotlinWithGradleConfigurator() { override val presentableText: String = "JavaScript with Gradle" override val targetPlatform: TargetPlatform = JsPlatform override val kotlinPluginName: String = KOTLIN_JS + override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String = + if (forKotlinDsl) "id(\"kotlin2js\")" else "id 'kotlin2js'" override fun getMinimumSupportedVersion() = "1.1.0" override fun getStdlibArtifactName(sdk: Sdk?, version: String): String = MAVEN_JS_STDLIB_ID + override fun addElementsToFile(file: PsiFile, isTopLevelProjectFile: Boolean, version: String): Boolean { + if (getManipulator(file).useNewSyntax(kotlinPluginName)) { + val settingsPsiFile = if (isTopLevelProjectFile) { + file.module?.getTopLevelBuildScriptSettingsPsiFile() + } else { + file.module?.getBuildScriptSettingsPsiFile() + } + if (settingsPsiFile != null) { + getManipulator(settingsPsiFile).addResolutionStrategy("kotlin2js") + } + } + + return super.addElementsToFile(file, isTopLevelProjectFile, version) + } + companion object { val KOTLIN_JS = "kotlin2js" } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt index 1300244102e..6232d7932c3 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt @@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.WritingAccessProvider import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager +import com.intellij.util.PathUtil import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.CoroutineSupport import org.jetbrains.kotlin.config.LanguageFeature @@ -82,7 +83,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { protected open fun getMinimumSupportedVersion() = "1.0.0" - private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName) + protected fun PsiFile.isKtDsl() = this is KtFile + + private fun isFileConfigured(buildScript: PsiFile): Boolean = with(getManipulator(buildScript)) { + isConfiguredWithOldSyntax(kotlinPluginName) || isConfigured(getKotlinPluginExpression(buildScript.isKtDsl())) + } @JvmSuppressWildcards override fun configure(project: Project, excludeModules: Collection) { @@ -146,10 +151,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk } val jvmTarget = getJvmTarget(sdk, version) return getManipulator(file).configureModuleBuildScript( - kotlinPluginName, - getStdlibArtifactName(sdk, version), - version, - jvmTarget + kotlinPluginName, + getKotlinPluginExpression(file.isKtDsl()), + getStdlibArtifactName(sdk, version), + version, + jvmTarget ) } @@ -158,6 +164,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null protected abstract val kotlinPluginName: String + protected abstract fun getKotlinPluginExpression(forKotlinDsl: Boolean): String protected open fun addElementsToFile( file: PsiFile, @@ -165,7 +172,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { version: String ): Boolean { if (!isTopLevelProjectFile) { - var wasModified = configureProjectFile(file, version) + var wasModified = getManipulator(file).configureProjectBuildScript(kotlinPluginName, version) wasModified = wasModified or configureModuleBuildScript(file, version) return wasModified } @@ -251,21 +258,20 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { } companion object { - fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) { - is KtFile -> KotlinBuildScriptManipulator(file) - is GroovyFile -> GroovyBuildScriptManipulator(file) + fun getManipulator(file: PsiFile, preferNewSyntax: Boolean = true): GradleBuildScriptManipulator<*> = when (file) { + is KtFile -> KotlinBuildScriptManipulator(file, preferNewSyntax) + is GroovyFile -> GroovyBuildScriptManipulator(file, preferNewSyntax) else -> error("Unknown build script file type!") } val GROUP_ID = "org.jetbrains.kotlin" val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin" - val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\"" - private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts" + private val KOTLIN_SETTINGS_SCRIPT_NAME = "settings.gradle.kts" - fun getGroovyDependencySnippet(artifactName: String, scope: String) = - "$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\"" + fun getGroovyDependencySnippet(artifactName: String, scope: String, withVersion: Boolean) = + "$scope \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\"" fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'" @@ -320,28 +326,37 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { } } - fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version) - private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null) - private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project) + private fun Module.getBuildScriptPsiFile() = + getBuildScriptFile(GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(project) - private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) } + fun Module.getBuildScriptSettingsPsiFile() = + getBuildScriptSettingsFile(GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project) - private fun Module.getBuildScriptFile(): File? { + private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { + findBuildGradleFile(it, GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(this) + } + + fun Module.getTopLevelBuildScriptSettingsPsiFile() = + ExternalSystemApiUtil.getExternalRootProjectPath(this)?.let { externalProjectPath -> + findBuildGradleFile(externalProjectPath, GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project) + } + + private fun Module.getBuildScriptFile(vararg fileNames: String): File? { val moduleDir = File(moduleFilePath).parent - findBuildGradleFile(moduleDir)?.let { + findBuildGradleFile(moduleDir, *fileNames)?.let { return it } ModuleRootManager.getInstance(this).contentRoots.forEach { root -> - findBuildGradleFile(root.path)?.let { + findBuildGradleFile(root.path, *fileNames)?.let { return it } } ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath -> - findBuildGradleFile(externalProjectPath)?.let { + findBuildGradleFile(externalProjectPath, *fileNames)?.let { return it } } @@ -349,9 +364,20 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { return null } - private fun findBuildGradleFile(path: String): File? = - File(path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME).takeIf { it.exists() } - ?: File(path + "/" + KOTLIN_BUILD_SCRIPT_NAME).takeIf { it.exists() } + private fun Module.getBuildScriptSettingsFile(vararg fileNames: String): File? { + ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath -> + return generateSequence(externalProjectPath) { + PathUtil.getParentPath(it).let { if (it.isBlank()) null else it } + }.mapNotNull { + findBuildGradleFile(it, *fileNames) + }.firstOrNull() + } + + return null + } + + private fun findBuildGradleFile(path: String, vararg fileNames: String): File? = + fileNames.asSequence().map { File(path + "/" + it) }.firstOrNull { it.exists() } private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let { PsiManager.getInstance(project).findFile(it) diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/gradleModuleBuilderUtils.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/gradleModuleBuilderUtils.kt new file mode 100644 index 00000000000..e1d2b0cafbe --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/gradleModuleBuilderUtils.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.configuration + +import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable +import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel +import com.intellij.openapi.externalSystem.model.project.ProjectId +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ModifiableModelsProvider +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.openapi.util.Key +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiFile +import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.idea.core.copied +import org.jetbrains.kotlin.idea.refactoring.toPsiFile +import org.jetbrains.kotlin.psi.UserDataProperty +import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider +import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder +import org.jetbrains.plugins.gradle.util.GradleConstants +import java.io.File + +internal var Module.gradleModuleBuilder: GradleModuleBuilder? by UserDataProperty(Key.create("GRADLE_MODULE_BUILDER")) +private var Module.editableSettingsPsiFileCopy: PsiFile? by UserDataProperty(Key.create("EDITABLE_SETTINGS_PSI_FILE_COPY")) + +internal fun findSettingsGradleFile(module: Module): VirtualFile? { + val contentEntryPath = module.gradleModuleBuilder?.contentEntryPath ?: return null + if (contentEntryPath.isEmpty()) return null + val contentRootDir = File(contentEntryPath) + val modelContentRootDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(contentRootDir) ?: return null + return modelContentRootDir.findChild(GradleConstants.SETTINGS_FILE_NAME) + ?: module.project.baseDir.findChild(GradleConstants.SETTINGS_FILE_NAME) +} + +// Circumvent write actions and modify the file directly +// TODO: Get rid of this hack when IDEA API allows manipulation of settings script similarly to the main script itself +internal fun updateSettingsScript(module: Module, updater: (PsiFile) -> Unit) { + val storedCopy = module.editableSettingsPsiFileCopy + val settingsPsiCopy = storedCopy ?: findSettingsGradleFile(module)?.toPsiFile(module.project)?.copied() ?: return + if (storedCopy == null) { + module.editableSettingsPsiFileCopy = settingsPsiCopy + } + updater(settingsPsiCopy) +} + +internal fun flushSettingsGradleCopy(module: Module) { + try { + val settingsFile = findSettingsGradleFile(module) + val settingsPsiCopy = module.editableSettingsPsiFileCopy + if (settingsPsiCopy != null && settingsFile != null) { + CodeStyleManager.getInstance(module.project).reformat(settingsPsiCopy) + VfsUtil.saveText(settingsFile, settingsPsiCopy.text) + } + } finally { + module.gradleModuleBuilder = null + module.editableSettingsPsiFileCopy = null + } +} + +class KotlinGradleFrameworkSupportInModuleConfigurable( + private val model: FrameworkSupportModel, + private val supportProvider: GradleFrameworkSupportProvider +) : FrameworkSupportInModuleConfigurable() { + override fun createComponent() = supportProvider.createComponent() + + override fun addSupport( + module: Module, + rootModel: ModifiableRootModel, + modifiableModelsProvider: ModifiableModelsProvider + ) { + val buildScriptData = GradleModuleBuilder.getBuildScriptData(module) + if (buildScriptData != null) { + val builder = model.moduleBuilder + val projectId = (builder as? GradleModuleBuilder)?.projectId ?: ProjectId(null, module.name, null) + try { + module.gradleModuleBuilder = builder as? GradleModuleBuilder + supportProvider.addSupport(projectId, module, rootModel, modifiableModelsProvider, buildScriptData) + } finally { + flushSettingsGradleCopy(module) + } + } + } +} \ No newline at end of file diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt index 4123c7251c4..1669caf530d 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt @@ -11,11 +11,13 @@ import com.intellij.openapi.fileEditor.impl.LoadTextUtil import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.idea.configuration.* import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.test.testFramework.runInEdtAndWait import org.jetbrains.kotlin.test.testFramework.runWriteAction +import org.junit.Ignore import org.junit.Test class GradleConfiguratorTest : GradleImportingTestCase() { @@ -24,7 +26,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testProjectWithModule() { createProjectSubFile("settings.gradle", "include ':app'") createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -65,7 +67,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testConfigure10() { createProjectSubFile("settings.gradle", "include ':app'") val file = createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -87,7 +89,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { FileDocumentManager.getInstance().saveAllDocuments() val content = LoadTextUtil.loadText(file).toString() assertEquals( - """ + """ buildscript { ext.kotlin_version = '1.0.6' repositories { @@ -181,21 +183,625 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } } + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJvmWithBuildGradle() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle", + """ + plugins { + id 'java' + } + + group 'testgroup' + version '1.0-SNAPSHOT' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.2.40' + } + + group 'testgroup' + version '1.0-SNAPSHOT' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + } + compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } + } + compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJvmWithBuildGradleKts() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle.kts", + """ + plugins { + java + } + + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + + dependencies { + testCompile("junit", "junit", "4.12") + } + + configure { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + + plugins { + java + kotlin("jvm") version "1.2.40" + } + + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + + dependencies { + testCompile("junit", "junit", "4.12") + compile(kotlin("stdlib-jdk8")) + } + + configure { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + val compileKotlin: KotlinCompile by tasks + compileKotlin.kotlinOptions { + jvmTarget = "1.8" + } + val compileTestKotlin: KotlinCompile by tasks + compileTestKotlin.kotlinOptions { + jvmTarget = "1.8" + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJvmEAPWithBuildGradle() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle", + """ + plugins { + id 'java' + } + + group 'testgroup' + version '1.0-SNAPSHOT' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40-eap-62", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.2.40-eap-62' + } + + group 'testgroup' + version '1.0-SNAPSHOT' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + } + compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } + } + compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + repositories { + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap' + } + mavenCentral() + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJvmEAPWithBuildGradleKts() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle.kts", + """ + plugins { + java + } + + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + + dependencies { + testCompile("junit", "junit", "4.12") + } + + configure { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40-eap-62", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + + plugins { + java + kotlin("jvm") version "1.2.40-eap-62" + } + + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + + dependencies { + testCompile("junit", "junit", "4.12") + compile(kotlin("stdlib-jdk8")) + } + + configure { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + val compileKotlin: KotlinCompile by tasks + compileKotlin.kotlinOptions { + jvmTarget = "1.8" + } + val compileTestKotlin: KotlinCompile by tasks + compileTestKotlin.kotlinOptions { + jvmTarget = "1.8" + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + repositories { + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap' + } + mavenCentral() + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJsWithBuildGradle() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle", + """ + group 'testgroup' + version '1.0-SNAPSHOT' + + repositories { + mavenCentral() + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findJsGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id 'kotlin2js' version '1.2.40' + } + group 'testgroup' + version '1.0-SNAPSHOT' + + repositories { + mavenCentral() + } + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js" + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + resolutionStrategy { + eachPlugin { + if (requested.id.id == "kotlin2js") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + } + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJsWithBuildGradleKts() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle.kts", + """ + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findJsGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id("kotlin2js") version "1.2.40" + } + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + dependencies { + compile(kotlin("stdlib-js")) + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + resolutionStrategy { + eachPlugin { + if (requested.id.id == "kotlin2js") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + } + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJsEAPWithBuildGradle() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle", + """ + group 'testgroup' + version '1.0-SNAPSHOT' + + repositories { + mavenCentral() + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findJsGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40-eap-62", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id 'kotlin2js' version '1.2.40-eap-62' + } + group 'testgroup' + version '1.0-SNAPSHOT' + + repositories { + mavenCentral() + } + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js" + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + resolutionStrategy { + eachPlugin { + if (requested.id.id == "kotlin2js") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + } + } + repositories { + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap' + } + mavenCentral() + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + @Test + @Ignore // Enable in Gradle 4.4+ + fun testConfigureJsEAPWithBuildGradleKts() { + val settingsFile = createProjectSubFile("settings.gradle", "include ':app'") + val buildFile = createProjectSubFile( + "app/build.gradle.kts", + """ + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + """.trimIndent() + ) + + importProject() + + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(myProject).findModuleByName("app")!! + val configurator = findJsGradleModuleConfigurator() + val collector = createConfigureKotlinNotificationCollector(myProject) + configurator.configureWithVersion(myProject, listOf(module), "1.2.40-eap-62", collector) + + FileDocumentManager.getInstance().saveAllDocuments() + assertFileContent( + buildFile, + """ + plugins { + id("kotlin2js") version "1.2.40-eap-62" + } + group = "testgroup" + version = "1.0-SNAPSHOT" + + repositories { + mavenCentral() + } + dependencies { + compile(kotlin("stdlib-js")) + } + """.trimIndent() + ) + assertFileContent( + settingsFile, + """ + pluginManagement { + resolutionStrategy { + eachPlugin { + if (requested.id.id == "kotlin2js") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}") + } + } + } + repositories { + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap' + } + mavenCentral() + } + } + include ':app' + """.trimIndent() + ) + } + } + } + + private fun assertFileContent(file: VirtualFile, expected: String) { + assertEquals(expected, LoadTextUtil.loadText(file).toString()) + } + private fun findGradleModuleConfigurator() = Extensions.findExtension( - KotlinProjectConfigurator.EP_NAME, - KotlinGradleModuleConfigurator::class.java + KotlinProjectConfigurator.EP_NAME, + KotlinGradleModuleConfigurator::class.java ) private fun findJsGradleModuleConfigurator() = Extensions.findExtension( - KotlinProjectConfigurator.EP_NAME, - KotlinJsGradleModuleConfigurator::class.java + KotlinProjectConfigurator.EP_NAME, + KotlinJsGradleModuleConfigurator::class.java ) @Test fun testConfigureGSK() { createProjectSubFile("settings.gradle", "include ':app'") val file = createProjectSubFile( - "app/build.gradle.kts", """ + "app/build.gradle.kts", """ buildscript { repositories { jcenter() @@ -217,7 +823,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { FileDocumentManager.getInstance().saveAllDocuments() val content = LoadTextUtil.loadText(file).toString() assertEquals( - """ + """ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val kotlin_version: String by extra @@ -259,7 +865,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testListNonConfiguredModules() { createProjectSubFile("settings.gradle", "include ':app'") createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -295,7 +901,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testListNonConfiguredModules_Configured() { createProjectSubFile("settings.gradle", "include ':app'") createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -328,7 +934,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testListNonConfiguredModules_ConfiguredWithImplementation() { createProjectSubFile("settings.gradle", "include ':app'") createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -361,7 +967,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { fun testListNonConfiguredModules_ConfiguredOnlyTest() { createProjectSubFile("settings.gradle", "include ':app'") createProjectSubFile( - "app/build.gradle", """ + "app/build.gradle", """ buildscript { repositories { jcenter() @@ -393,8 +999,8 @@ class GradleConfiguratorTest : GradleImportingTestCase() { @Test fun testAddNonKotlinLibraryGSK() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """ + "build.gradle.kts", + """ |dependencies { | testCompile("junit:junit:4.12") | compile(kotlinModule("stdlib-jre8")) @@ -407,33 +1013,33 @@ class GradleConfiguratorTest : GradleImportingTestCase() { runInEdtAndWait { myTestFixture.project.executeWriteCommand("") { KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.COMPILE, - object : ExternalLibraryDescriptor("org.a.b", "lib", "1.0.0", "1.0.0") { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.COMPILE, + object : ExternalLibraryDescriptor("org.a.b", "lib", "1.0.0", "1.0.0") { + override fun getLibraryClassesRoots() = emptyList() + }) } FileDocumentManager.getInstance().saveAllDocuments() } assertEquals( - """ + """ |dependencies { | testCompile("junit:junit:4.12") | compile(kotlinModule("stdlib-jre8")) | compile("org.a.b:lib:1.0.0") |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddLibraryGSK_WithKotlinVersion() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """ + "build.gradle.kts", + """ |val kotlin_version: String by extra |dependencies { | testCompile("junit:junit:4.12") @@ -448,18 +1054,18 @@ class GradleConfiguratorTest : GradleImportingTestCase() { myTestFixture.project.executeWriteCommand("") { val stdLibVersion = KotlinWithGradleConfigurator.getKotlinStdlibVersion(myTestFixture.module) KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.COMPILE, - object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", stdLibVersion, stdLibVersion) { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.COMPILE, + object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", stdLibVersion, stdLibVersion) { + override fun getLibraryClassesRoots() = emptyList() + }) } FileDocumentManager.getInstance().saveAllDocuments() } assertEquals( - """ + """ |val kotlin_version: String by extra |dependencies { | testCompile("junit:junit:4.12") @@ -467,15 +1073,15 @@ class GradleConfiguratorTest : GradleImportingTestCase() { | compile(kotlinModule("reflect", kotlin_version)) |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddTestLibraryGSK() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """ + "build.gradle.kts", + """ |dependencies { | compile(kotlinModule("stdlib-jre8")) |} @@ -487,40 +1093,40 @@ class GradleConfiguratorTest : GradleImportingTestCase() { runInEdtAndWait { myTestFixture.project.executeWriteCommand("") { KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.TEST, - object : ExternalLibraryDescriptor("junit", "junit", "4.12", "4.12") { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.TEST, + object : ExternalLibraryDescriptor("junit", "junit", "4.12", "4.12") { + override fun getLibraryClassesRoots() = emptyList() + }) KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.TEST, - object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-test", "1.1.2", "1.1.2") { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.TEST, + object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-test", "1.1.2", "1.1.2") { + override fun getLibraryClassesRoots() = emptyList() + }) } FileDocumentManager.getInstance().saveAllDocuments() } assertEquals( - """ + """ |dependencies { | compile(kotlinModule("stdlib-jre8")) | testCompile("junit:junit:4.12") | testCompile(kotlinModule("test", "1.1.2")) |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddLibraryGSK() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """ + "build.gradle.kts", + """ |dependencies { | testCompile("junit:junit:4.12") | compile(kotlinModule("stdlib-jre8")) @@ -533,32 +1139,32 @@ class GradleConfiguratorTest : GradleImportingTestCase() { runInEdtAndWait { myTestFixture.project.executeWriteCommand("") { KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.COMPILE, - object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.COMPILE, + object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") { + override fun getLibraryClassesRoots() = emptyList() + }) } FileDocumentManager.getInstance().saveAllDocuments() } assertEquals( - """ + """ |dependencies { | testCompile("junit:junit:4.12") | compile(kotlinModule("stdlib-jre8")) | compile(kotlinModule("reflect", "1.0.0")) |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddCoroutinesSupport() { val buildScript = createProjectSubFile( - "build.gradle", """ + "build.gradle", """ buildscript { repositories { jcenter() @@ -588,7 +1194,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """ + """ buildscript { repositories { jcenter() @@ -610,7 +1216,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } } """.trimIndent(), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @@ -629,19 +1235,19 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """import org.jetbrains.kotlin.gradle.dsl.Coroutines + """import org.jetbrains.kotlin.gradle.dsl.Coroutines | |kotlin { | experimental.coroutines = Coroutines.ENABLE |}""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testChangeCoroutinesSupport() { val buildScript = createProjectSubFile( - "build.gradle", """ + "build.gradle", """ buildscript { repositories { jcenter() @@ -676,7 +1282,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """ + """ buildscript { repositories { jcenter() @@ -704,8 +1310,8 @@ class GradleConfiguratorTest : GradleImportingTestCase() { @Test fun testChangeCoroutinesSupportGSK() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """import org.jetbrains.kotlin.gradle.dsl.Coroutines + "build.gradle.kts", + """import org.jetbrains.kotlin.gradle.dsl.Coroutines | |kotlin { | experimental.coroutines = Coroutines.DISABLE @@ -724,20 +1330,20 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """import org.jetbrains.kotlin.gradle.dsl.Coroutines + """import org.jetbrains.kotlin.gradle.dsl.Coroutines | |kotlin { | experimental.coroutines = Coroutines.ENABLE |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddLanguageVersion() { val buildScript = createProjectSubFile( - "build.gradle", """ + "build.gradle", """ buildscript { repositories { jcenter() @@ -767,7 +1373,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """ + """ buildscript { repositories { jcenter() @@ -807,20 +1413,20 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + """import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | |val compileKotlin: KotlinCompile by tasks |compileKotlin.kotlinOptions { | languageVersion = "1.1" |}""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testChangeLanguageVersion() { val buildScript = createProjectSubFile( - "build.gradle", """ + "build.gradle", """ buildscript { repositories { jcenter() @@ -855,7 +1461,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """ + """ buildscript { repositories { jcenter() @@ -883,8 +1489,8 @@ class GradleConfiguratorTest : GradleImportingTestCase() { @Test fun testChangeLanguageVersionGSK() { val buildScript = createProjectSubFile( - "build.gradle.kts", - """val compileKotlin: KotlinCompile by tasks + "build.gradle.kts", + """val compileKotlin: KotlinCompile by tasks |compileKotlin.kotlinOptions { | languageVersion = "1.0" |} @@ -902,19 +1508,19 @@ class GradleConfiguratorTest : GradleImportingTestCase() { } assertEquals( - """val compileKotlin: KotlinCompile by tasks + """val compileKotlin: KotlinCompile by tasks |compileKotlin.kotlinOptions { | languageVersion = "1.1" |} |""".trimMargin("|"), - LoadTextUtil.loadText(buildScript).toString() + LoadTextUtil.loadText(buildScript).toString() ) } @Test fun testAddLibrary() { val buildScript = createProjectSubFile( - "build.gradle", """ + "build.gradle", """ buildscript { repositories { jcenter() @@ -938,18 +1544,18 @@ class GradleConfiguratorTest : GradleImportingTestCase() { runInEdtAndWait { myTestFixture.project.executeWriteCommand("") { KotlinWithGradleConfigurator.addKotlinLibraryToModule( - myTestFixture.module, - DependencyScope.COMPILE, - object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") { - override fun getLibraryClassesRoots() = emptyList() - }) + myTestFixture.module, + DependencyScope.COMPILE, + object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") { + override fun getLibraryClassesRoots() = emptyList() + }) } FileDocumentManager.getInstance().saveAllDocuments() } assertEquals( - """ + """ buildscript { repositories { jcenter()