KotlinWithGradleConfigurator: support addKotlinLibraryToModuleBuildScript for multiplatform

#KT-27270 Fixed
This commit is contained in:
Dmitry Gridin
2019-07-17 18:01:32 +03:00
parent c88d4f43d3
commit 645bbc93cb
26 changed files with 507 additions and 35 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.psi.PsiElement
@@ -53,6 +54,7 @@ interface GradleBuildScriptManipulator<out Psi : PsiFile> {
fun changeApiVersion(version: String, forTests: Boolean): PsiElement?
fun addKotlinLibraryToModuleBuildScript(
targetModule: Module,
scope: DependencyScope,
libraryDescriptor: ExternalLibraryDescriptor
)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.openapi.util.text.StringUtil
@@ -139,7 +140,7 @@ class GroovyBuildScriptManipulator(
override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? {
val snippet = "coroutines \"$coroutineOption\""
val kotlinBlock = scriptFile.getBlockOrCreate("kotlin")
val kotlinBlock = scriptFile.getKotlinBlock()
kotlinBlock.getBlockOrCreate("experimental").apply {
addOrReplaceExpression(snippet) { stmt ->
(stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines"
@@ -156,8 +157,8 @@ class GroovyBuildScriptManipulator(
): PsiElement? {
if (usesNewMultiplatform()) {
state.assertApplicableInMultiplatform()
val kotlinBlock = scriptFile.getBlockOrCreate("kotlin")
val sourceSetsBlock = kotlinBlock.getBlockOrCreate("sourceSets")
val kotlinBlock = scriptFile.getKotlinBlock()
val sourceSetsBlock = kotlinBlock.getSourceSetsBlock()
val allBlock = sourceSetsBlock.getBlockOrCreate("all")
allBlock.addLastExpressionInBlockIfNeeded("languageSettings.enableLanguageFeature(\"${feature.name}\")")
return allBlock.statements.lastOrNull()
@@ -184,6 +185,7 @@ class GroovyBuildScriptManipulator(
changeKotlinTaskParameter(scriptFile, "apiVersion", version, forTests)
override fun addKotlinLibraryToModuleBuildScript(
targetModule: Module,
scope: DependencyScope,
libraryDescriptor: ExternalLibraryDescriptor
) {
@@ -195,8 +197,17 @@ class GroovyBuildScriptManipulator(
libraryDescriptor.maxVersion
)
scriptFile.getDependenciesBlock().apply {
addLastExpressionInBlockIfNeeded(dependencyString)
if (usesNewMultiplatform()) {
scriptFile
.getKotlinBlock()
.getSourceSetsBlock()
.getBlockOrCreate(targetModule.name.takeLastWhile { it != '.' })
.getDependenciesBlock()
.addLastExpressionInBlockIfNeeded(dependencyString)
} else {
scriptFile.getDependenciesBlock().apply {
addLastExpressionInBlockIfNeeded(dependencyString)
}
}
}
@@ -282,7 +293,7 @@ class GroovyBuildScriptManipulator(
replaceIt: GrStatement.(Boolean) -> GrStatement
): PsiElement? {
if (usesNewMultiplatform()) {
val kotlinBlock = gradleFile.getBlockOrCreate("kotlin")
val kotlinBlock = gradleFile.getKotlinBlock()
val kotlinTargets = kotlinBlock.getBlockOrCreate("targets")
val targetNames = mutableListOf<String>()
@@ -378,10 +389,10 @@ class GroovyBuildScriptManipulator(
}
private fun GrStatementOwner.getBuildScriptRepositoriesBlock(): GrClosableBlock =
getBuildScriptBlock().getBlockOrCreate("repositories")
getBuildScriptBlock().getRepositoriesBlock()
private fun GrStatementOwner.getBuildScriptDependenciesBlock(): GrClosableBlock =
getBuildScriptBlock().getBlockOrCreate("dependencies")
getBuildScriptBlock().getDependenciesBlock()
private fun GrClosableBlock.addMavenCentralIfMissing(): Boolean =
if (!isRepositoryConfigured(text)) addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL) else false
@@ -390,6 +401,10 @@ class GroovyBuildScriptManipulator(
private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies")
private fun GrStatementOwner.getKotlinBlock(): GrClosableBlock = getBlockOrCreate("kotlin")
private fun GrStatementOwner.getSourceSetsBlock(): GrClosableBlock = getBlockOrCreate("sourceSets")
private fun GrClosableBlock.addOrReplaceExpression(snippet: String, predicate: (GrStatement) -> Boolean) {
statements.firstOrNull(predicate)?.let { stmt ->
stmt.replaceWithStatementFromText(snippet)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.openapi.util.text.StringUtil
@@ -116,21 +117,51 @@ class KotlinBuildScriptManipulator(
scriptFile.changeKotlinTaskParameter("apiVersion", version, forTests)
override fun addKotlinLibraryToModuleBuildScript(
targetModule: Module,
scope: DependencyScope,
libraryDescriptor: ExternalLibraryDescriptor
) {
scriptFile.getDependenciesBlock()?.apply {
addExpressionIfMissing(
getCompileDependencySnippet(
libraryDescriptor.libraryGroupId,
libraryDescriptor.libraryArtifactId,
libraryDescriptor.maxVersion,
scope.toGradleCompileScope(module?.getBuildSystemType() == AndroidGradle)
)
)
val dependencyText = getCompileDependencySnippet(
libraryDescriptor.libraryGroupId,
libraryDescriptor.libraryArtifactId,
libraryDescriptor.maxVersion,
scope.toGradleCompileScope(scriptFile.module?.getBuildSystemType() == AndroidGradle)
)
if (usesNewMultiplatform()) {
val findOrCreateTargetSourceSet = scriptFile
.getKotlinBlock()
?.getSourceSetsBlock()
?.findOrCreateTargetSourceSet(targetModule.name.takeLastWhile { it != '.' })
val dependenciesBlock = findOrCreateTargetSourceSet?.getDependenciesBlock()
dependenciesBlock?.addExpressionIfMissing(dependencyText)
} else {
scriptFile.getDependenciesBlock()?.addExpressionIfMissing(dependencyText)
}
}
private fun KtBlockExpression.findOrCreateTargetSourceSet(moduleName: String) =
findTargetSourceSet(moduleName) ?: createTargetSourceSet(moduleName)
private fun KtBlockExpression.findTargetSourceSet(moduleName: String): KtBlockExpression? = statements.find {
it.isTargetSourceSetDeclaration(moduleName)
}?.getOrCreateBlock()
private fun KtExpression.getOrCreateBlock(): KtBlockExpression? = when (this) {
is KtCallExpression -> getBlock() ?: addBlock()
is KtReferenceExpression -> replace(KtPsiFactory(project).createExpression("$text {\n}")).cast<KtCallExpression>().getBlock()
is KtProperty -> delegateExpressionOrInitializer?.getOrCreateBlock()
else -> error("Impossible create block for $this")
}
private fun KtCallExpression.addBlock(): KtBlockExpression? = parent.addAfter(
KtPsiFactory(project).createEmptyBody(), this
) as? KtBlockExpression
private fun KtBlockExpression.createTargetSourceSet(moduleName: String) = addExpressionIfMissing("getByName(\"$moduleName\") {\n}")
.cast<KtCallExpression>()
.getBlock()
override fun getKotlinStdlibVersion(): String? = scriptFile.getKotlinStdlibVersion()
private fun KtBlockExpression.addCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? =
@@ -231,6 +262,7 @@ class KotlinBuildScriptManipulator(
private fun KtCallExpression.getBlock(): KtBlockExpression? =
(valueArguments.singleOrNull()?.getArgumentExpression() as? KtLambdaExpression)?.bodyExpression
?: lambdaArguments.lastOrNull()?.getLambdaExpression()?.bodyExpression
private fun KtFile.getKotlinStdlibVersion(): String? {
return findScriptInitializer("dependencies")?.getBlock()?.let {
@@ -262,6 +294,10 @@ class KotlinBuildScriptManipulator(
private fun KtFile.getPluginManagementBlock(): KtBlockExpression? = findOrCreateScriptInitializer("pluginManagement", true)
private fun KtFile.getKotlinBlock(): KtBlockExpression? = findOrCreateScriptInitializer("kotlin")
private fun KtBlockExpression.getSourceSetsBlock(): KtBlockExpression? = findOrCreateBlock("sourceSets")
private fun KtFile.getRepositoriesBlock(): KtBlockExpression? = findOrCreateScriptInitializer("repositories")
private fun KtFile.getDependenciesBlock(): KtBlockExpression? = findOrCreateScriptInitializer("dependencies")
@@ -289,7 +325,7 @@ class KotlinBuildScriptManipulator(
private fun KtFile.changeCoroutineConfiguration(coroutineOption: String): PsiElement? {
val snippet = "experimental.coroutines = Coroutines.${coroutineOption.toUpperCase()}"
val kotlinBlock = findOrCreateScriptInitializer("kotlin") ?: return null
val kotlinBlock = getKotlinBlock() ?: return null
addImportIfMissing("org.jetbrains.kotlin.gradle.dsl.Coroutines")
val statement = kotlinBlock.statements.find { it.text.startsWith("experimental.coroutines") }
return if (statement != null) {
@@ -306,7 +342,7 @@ class KotlinBuildScriptManipulator(
): PsiElement? {
if (usesNewMultiplatform()) {
state.assertApplicableInMultiplatform()
return findOrCreateScriptInitializer("kotlin")
return getKotlinBlock()
?.findOrCreateBlock("sourceSets")
?.findOrCreateBlock("all")
?.addExpressionIfMissing("languageSettings.enableLanguageFeature(\"${feature.name}\")")
@@ -505,3 +541,11 @@ class KotlinBuildScriptManipulator(
}
}
}
private fun KtExpression.isTargetSourceSetDeclaration(moduleName: String): Boolean = with(text) {
when (this@isTargetSourceSetDeclaration) {
is KtProperty -> startsWith("val $moduleName by") || initializer?.isTargetSourceSetDeclaration(moduleName) == true
is KtCallExpression -> startsWith("getByName(\"$moduleName\")")
else -> false
}
}
@@ -324,7 +324,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return
}
getManipulator(buildScript).addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor)
getManipulator(buildScript).addKotlinLibraryToModuleBuildScript(module, scope, libraryDescriptor)
buildScript.virtualFile?.let {
createConfigureKotlinNotificationCollector(buildScript.project)
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.idea.codeInsight.gradle
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.testFramework.runInEdtAndWait
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator
@@ -77,6 +80,49 @@ class GradleConfiguratorPlatformSpecificTest : GradleImportingTestCase() {
}
}
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatform() = doTestAddLibrary()
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatformGSK() = doTestAddLibrary()
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatformGSK2() = doTestAddLibrary()
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatformGSK3() = doTestAddLibrary()
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatformGSK4() = doTestAddLibrary()
@TargetVersions("4.7+")
@Test
fun testAddLibraryMultiplatformGSK5() = doTestAddLibrary()
private fun doTestAddLibrary() {
val files = importProjectFromTestData()
runInEdtAndWait {
myTestFixture.project.executeWriteCommand("") {
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
object : Module by myTestFixture.module {
override fun getName(): String = "jvmMain"
},
DependencyScope.COMPILE,
object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.3.50", "1.3.50") {
override fun getLibraryClassesRoots() = emptyList<String>()
})
}
checkFiles(files)
}
}
override fun testDataDirName(): String {
return "configurator"
}
@@ -87,12 +87,12 @@ fun isRepositoryConfigured(repositoriesBlockText: String): Boolean =
repositoriesBlockText.contains(MAVEN_CENTRAL) || repositoriesBlockText.contains(JCENTER)
fun DependencyScope.toGradleCompileScope(isAndroidModule: Boolean) = when (this) {
DependencyScope.COMPILE -> "compile"
DependencyScope.COMPILE -> "implementation"
// TODO: We should add testCompile or androidTestCompile
DependencyScope.TEST -> if (isAndroidModule) "compile" else "testCompile"
DependencyScope.TEST -> if (isAndroidModule) "implementation" else "testImplementation"
DependencyScope.RUNTIME -> "runtime"
DependencyScope.PROVIDED -> "compile"
else -> "compile"
DependencyScope.PROVIDED -> "implementation"
else -> "implementation"
}
fun RepositoryDescription.toGroovyRepositorySnippet() = "maven { url '$url' }"
@@ -12,5 +12,5 @@ apply plugin: 'kotlin'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
compile "org.jetbrains.kotlin:kotlin-reflect:1.0.0"
}
implementation "org.jetbrains.kotlin:kotlin-reflect:1.0.0"
}
@@ -5,5 +5,5 @@ plugins {
dependencies {
testCompile("junit:junit:4.12")
compile(kotlinModule("stdlib-jre8"))
compile(kotlinModule("reflect", "1.0.0"))
implementation(kotlinModule("reflect", "1.0.0"))
}
@@ -5,5 +5,5 @@ plugins {
dependencies {
testCompile("junit:junit:4.12")
compile(kotlinModule("stdlib-jre8"))
compile(kotlin("reflect"))
implementation(kotlin("reflect"))
}
@@ -7,5 +7,5 @@ val kotlin_version: String by extra
dependencies {
testCompile("junit:junit:4.12")
compile(kotlinModule("stdlib-jre8", kotlin_version))
compile(kotlinModule("reflect", kotlin_version))
implementation(kotlinModule("reflect", kotlin_version))
}
@@ -0,0 +1,58 @@
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
mavenCentral()
}
group 'com.example'
version '0.0.1'
apply plugin: 'kotlin-multiplatform'
kotlin {
jvm()
js()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
project {
dependencies {
implementation kotlin('stdlib-jdk8')
}
}
plainJvm {
dependencies {
jvm()
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
}
}
@@ -0,0 +1,63 @@
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
mavenCentral()
}
group 'com.example'
version '0.0.1'
apply plugin: 'kotlin-multiplatform'
kotlin {
jvm()
js()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
project {
dependencies {
implementation kotlin('stdlib-jdk8')
}
}
plainJvm {
dependencies {
jvm()
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
jvmMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.50"
}
}
}
}
@@ -0,0 +1,21 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
getByName("jvmMain") {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -0,0 +1,23 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
getByName("jvmMain") {
}
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
getByName("jvmMain") {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -0,0 +1,25 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting {
dependencies {
}
}
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -0,0 +1,22 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -0,0 +1,23 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting {
}
}
}
@@ -0,0 +1,26 @@
plugins {
kotlin("multiplatform") version "1.3.41"
}
repositories {
mavenCentral()
}
group = "com.example"
version = "0.0.1"
kotlin {
sourceSets {
jvm()
val commonMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("reflect"))
}
}
}
}
@@ -5,5 +5,5 @@ plugins {
dependencies {
testCompile("junit:junit:4.12")
compile(kotlinModule("stdlib-jre8"))
compile("org.a.b:lib:1.0.0")
implementation("org.a.b:lib:1.0.0")
}
@@ -4,6 +4,6 @@ plugins {
dependencies {
compile(kotlinModule("stdlib-jre8"))
testCompile("junit:junit:4.12")
testCompile(kotlinModule("test", "1.1.2"))
testImplementation("junit:junit:4.12")
testImplementation(kotlinModule("test", "1.1.2"))
}
@@ -4,6 +4,6 @@ plugins {
dependencies {
compile(kotlinModule("stdlib-jre8"))
testCompile("junit:junit:4.12")
testCompile(kotlin("test"))
testImplementation("junit:junit:4.12")
testImplementation(kotlin("test"))
}
@@ -18,5 +18,5 @@ repositories {
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0"
compile "org.jetbrains.kotlin:kotlin-reflect:1.1.0"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.1.0"
}