diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt index da9d0e2023d..bf6ca28a211 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt @@ -45,5 +45,7 @@ interface KotlinTarget: Named, HasAttributes { fun mavenPublication(action: Closure) fun mavenPublication(action: Action) + val preset: KotlinTargetPreset? + override fun getName(): String = targetName } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetsContainer.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetsContainer.kt new file mode 100644 index 00000000000..d4ae67f509a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetsContainer.kt @@ -0,0 +1,16 @@ +/* + * 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.gradle.plugin + +import org.gradle.api.NamedDomainObjectCollection + +interface KotlinTargetsContainer { + val targets: NamedDomainObjectCollection +} + +interface KotlinTargetsContainerWithPresets : KotlinTargetsContainer { + val presets: NamedDomainObjectCollection> +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts new file mode 100644 index 00000000000..57c3470599c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts @@ -0,0 +1,28 @@ +import org.gradle.jvm.tasks.Jar + +plugins { + kotlin("jvm") +} + +dependencies { + compile(gradleApi()) + compile(project(":kotlin-gradle-plugin-api")) + compile(project(":kotlin-native:kotlin-native-utils")) +} + +val generateMppTargetContainerWithPresets by generator( + "org.jetbrains.kotlin.generators.gradle.dsl.MppPresetFunctionsCodegenKt", + sourceSets["main"] +) + +generateMppTargetContainerWithPresets.run { + systemProperty( + "org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot", + project(":kotlin-gradle-plugin").projectDir.resolve("src/main/kotlin").absolutePath + ) +} + +// Workaround: 'java -jar' refuses to read the original dotted filename on Windows, 'Unable to access jarFile org.jetbrains.kotlin....jar' +tasks.named(generateMppTargetContainerWithPresets.name + "WriteClassPath").configure { + archiveName = generateMppTargetContainerWithPresets.name +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt new file mode 100644 index 00000000000..65adce78437 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt @@ -0,0 +1,34 @@ +/* + * 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.generators.gradle.dsl + +internal data class TypeName(val fqName: String, val typeArguments: List) + +internal fun typeName(fqName: String, vararg typeArgumentFlatFqNames: String): TypeName { + require(typeArgumentFlatFqNames.none { "<" in it }) { "generics won't render to short type names properly, use the full constructor" } + return TypeName( + fqName, + typeArgumentFlatFqNames.map { + TypeName( + it, + emptyList() + ) + }) +} + +internal fun TypeName.shortName() = fqName.split(".").last() + +internal fun TypeName.packageName() = fqName.substringBeforeLast(".") + +internal fun TypeName.renderShort(): String = + shortName() + typeArguments.takeIf { it.isNotEmpty() }?.joinToString(", ", "<", ">") { it.renderShort() }.orEmpty() + +internal fun TypeName.renderErased(): String = + shortName() + typeArguments.takeIf { it.isNotEmpty() }?.joinToString(", ", "<", ">") { "*" }.orEmpty() + +internal fun TypeName.collectFqNames(): Set = + setOf(fqName) + typeArguments.flatMap { it.collectFqNames() }.toSet() + diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt new file mode 100644 index 00000000000..ba594acab52 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt @@ -0,0 +1,74 @@ +/* + * 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.generators.gradle.dsl + +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets +import java.io.File + +fun main() { + generateKotlinTargetContainerWithPresetFunctionsInterface() +} + +private val parentInterface = KotlinTargetsContainerWithPresets::class + +private val presetsProperty = KotlinTargetsContainerWithPresets::presets.name + +private fun generateKotlinTargetContainerWithPresetFunctionsInterface() { + // Generate KotlinMutliplatformExtension subclass with member functions for the presets: + val functions = allPresetEntries.map { + generatePresetFunction(it, presetsProperty, "configureOrCreate") + } + + val parentInterfaceName = + typeName(parentInterface.java.canonicalName) + + val className = + typeName("org.jetbrains.kotlin.gradle.dsl.KotlinTargetContainerWithPresetFunctions") + + val imports = allPresetEntries + .flatMap { it.typeNames() } + .plus(parentInterfaceName) + .filter { it.packageName() != className.packageName() } + .flatMap { it.collectFqNames() } + .toSortedSet() + .joinToString("\n") { "import $it" } + + val generatedCodeWarning = "// DO NOT EDIT MANUALLY! Generated by ${object {}.javaClass.enclosingClass.name}" + + val code = listOf( + "package ${className.packageName()}", + imports, + generatedCodeWarning, + "interface ${className.renderShort()} : ${parentInterfaceName.renderShort()} {", + functions.joinToString("\n\n") { it.indented(4) }, + "}" + ).joinToString("\n\n") + + val outputSourceRoot = System.getProperties()["org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot"] + val targetFile = File("$outputSourceRoot/${className.fqName.replace(".", "/")}.kt") + targetFile.writeText(code) +} + +private fun generatePresetFunction( + presetEntry: KotlinPresetEntry, + getPresetsExpression: String, + configureOrCreateFunctionName: String +): String = """ + fun ${presetEntry.presetName}( + name: String = "${presetEntry.presetName}", + configure: ${presetEntry.targetType.renderShort()}.() -> Unit = { } + ): ${presetEntry.targetType.renderShort()} = + $configureOrCreateFunctionName( + name, + $getPresetsExpression.getByName("${presetEntry.presetName}") as ${presetEntry.presetType.renderErased()}, + configure + ) +""".trimIndent() + +private fun String.indented(nSpaces: Int = 4): String { + val spaces = String(CharArray(nSpaces) { ' ' }) + return lines().joinToString("\n") { "$spaces$it" } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt new file mode 100644 index 00000000000..ee3b6378203 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt @@ -0,0 +1,62 @@ +/* + * 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.generators.gradle.dsl + +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.presetName + +internal class KotlinPresetEntry( + val presetName: String, + val presetType: TypeName, + val targetType: TypeName +) + +internal fun KotlinPresetEntry.typeNames(): Set = setOf(presetType, targetType) + +private const val MPP_PACKAGE = "org.jetbrains.kotlin.gradle.plugin.mpp" + +private const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset" +private const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" + +private const val KOTLIN_ONLY_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinOnlyTarget" + +internal val jvmPresetEntry = KotlinPresetEntry( + "jvm", + typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"), + typeName( + KOTLIN_ONLY_TARGET_CLASS_FQNAME, + "$MPP_PACKAGE.KotlinJvmCompilation" + ) +) + +internal val jsPresetEntry = KotlinPresetEntry( + "js", + typeName("$MPP_PACKAGE.KotlinJsTargetPreset"), + typeName( + KOTLIN_ONLY_TARGET_CLASS_FQNAME, + "$MPP_PACKAGE.KotlinJsCompilation" + ) +) + +internal val androidPresetEntry = KotlinPresetEntry( + "android", + typeName("$MPP_PACKAGE.KotlinAndroidTargetPreset"), + typeName("$MPP_PACKAGE.KotlinAndroidTarget") +) + +internal val nativePresetEntries = HostManager().targets.map { (_, target) -> + KotlinPresetEntry( + target.presetName, + typeName(KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME), + typeName(KOTLIN_NATIVE_TARGET_CLASS_FQNAME) + ) +} + +internal val allPresetEntries = listOf( + jvmPresetEntry, + jsPresetEntry, + androidPresetEntry +) + nativePresetEntries \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt index 116718051b7..caf8113ff3e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt @@ -5,17 +5,64 @@ package org.jetbrains.kotlin.gradle.dsl +import org.gradle.api.InvalidUserCodeException import org.gradle.api.NamedDomainObjectCollection import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget -open class KotlinMultiplatformExtension : KotlinProjectExtension() { - lateinit var presets: NamedDomainObjectCollection> +open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTargetContainerWithPresetFunctions { + override lateinit var presets: NamedDomainObjectCollection> internal set - lateinit var targets: NamedDomainObjectCollection + override lateinit var targets: NamedDomainObjectCollection internal set internal var isGradleMetadataAvailable: Boolean = false internal var isGradleMetadataExperimental: Boolean = false + + fun metadata(configure: KotlinOnlyTarget.() -> Unit = { }): KotlinOnlyTarget = + @Suppress("UNCHECKED_CAST") + (targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME) as KotlinOnlyTarget).also(configure) + + fun targetFromPreset( + preset: KotlinTargetPreset, + name: String = preset.name, + configure: T.() -> Unit = { } + ): T = configureOrCreate(name, preset, configure) +} + +internal fun KotlinTarget.isProducedFromPreset(kotlinTargetPreset: KotlinTargetPreset<*>): Boolean = + preset == kotlinTargetPreset + +internal fun KotlinTargetsContainerWithPresets.configureOrCreate( + targetName: String, + targetPreset: KotlinTargetPreset, + configure: T.() -> Unit +): T { + val existingTarget = targets.findByName(targetName) + when { + existingTarget?.isProducedFromPreset(targetPreset) ?: false -> { + @Suppress("UNCHECKED_CAST") + configure(existingTarget as T) + return existingTarget + } + existingTarget == null -> { + val newTarget = targetPreset.createTarget(targetName) + targets.add(newTarget) + configure(newTarget) + return newTarget + } + else -> { + throw InvalidUserCodeException( + "The target '$targetName' already exists, but it was not created with the '${targetPreset.name}' preset. " + + "To configure it, access it by name in `kotlin.targets`" + + " or use the preset function '${existingTarget.preset?.name}'" + .takeIf { existingTarget.preset != null } + ) + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt new file mode 100644 index 00000000000..18122f738a2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt @@ -0,0 +1,160 @@ +package org.jetbrains.kotlin.gradle.dsl + +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets +import org.jetbrains.kotlin.gradle.plugin.mpp.* + +// DO NOT EDIT MANUALLY! Generated by org.jetbrains.kotlin.generators.gradle.dsl.MppPresetFunctionsCodegenKt + +interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithPresets { + + fun jvm( + name: String = "jvm", + configure: KotlinOnlyTarget.() -> Unit = { } + ): KotlinOnlyTarget = + configureOrCreate( + name, + presets.getByName("jvm") as KotlinJvmTargetPreset, + configure + ) + + fun js( + name: String = "js", + configure: KotlinOnlyTarget.() -> Unit = { } + ): KotlinOnlyTarget = + configureOrCreate( + name, + presets.getByName("js") as KotlinJsTargetPreset, + configure + ) + + fun android( + name: String = "android", + configure: KotlinAndroidTarget.() -> Unit = { } + ): KotlinAndroidTarget = + configureOrCreate( + name, + presets.getByName("android") as KotlinAndroidTargetPreset, + configure + ) + + fun androidNativeArm32( + name: String = "androidNativeArm32", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("androidNativeArm32") as KotlinNativeTargetPreset, + configure + ) + + fun androidNativeArm64( + name: String = "androidNativeArm64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("androidNativeArm64") as KotlinNativeTargetPreset, + configure + ) + + fun iosArm32( + name: String = "iosArm32", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("iosArm32") as KotlinNativeTargetPreset, + configure + ) + + fun iosArm64( + name: String = "iosArm64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("iosArm64") as KotlinNativeTargetPreset, + configure + ) + + fun iosX64( + name: String = "iosX64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("iosX64") as KotlinNativeTargetPreset, + configure + ) + + fun linuxX64( + name: String = "linuxX64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("linuxX64") as KotlinNativeTargetPreset, + configure + ) + + fun linuxArm32Hfp( + name: String = "linuxArm32Hfp", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("linuxArm32Hfp") as KotlinNativeTargetPreset, + configure + ) + + fun linuxMips32( + name: String = "linuxMips32", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("linuxMips32") as KotlinNativeTargetPreset, + configure + ) + + fun linuxMipsel32( + name: String = "linuxMipsel32", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("linuxMipsel32") as KotlinNativeTargetPreset, + configure + ) + + fun mingwX64( + name: String = "mingwX64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("mingwX64") as KotlinNativeTargetPreset, + configure + ) + + fun macosX64( + name: String = "macosX64", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("macosX64") as KotlinNativeTargetPreset, + configure + ) + + fun wasm32( + name: String = "wasm32", + configure: KotlinNativeTarget.() -> Unit = { } + ): KotlinNativeTarget = + configureOrCreate( + name, + presets.getByName("wasm32") as KotlinNativeTargetPreset, + configure + ) + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 297dc9a182a..ac82fcedda6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -42,6 +42,7 @@ abstract class KotlinOnlyTargetPreset( val result = KotlinOnlyTarget(project, platformType).apply { targetName = name disambiguationClassifier = name + preset = this@KotlinOnlyTargetPreset val compilationFactory = createCompilationFactory(this) compilations = project.container(compilationFactory.itemClass, compilationFactory) @@ -177,6 +178,7 @@ class KotlinAndroidTargetPreset( override fun createTarget(name: String): KotlinAndroidTarget { val result = KotlinAndroidTarget(name, project).apply { disambiguationClassifier = name + preset = this@KotlinAndroidTargetPreset } KotlinAndroidPlugin.applyToTarget( @@ -204,6 +206,7 @@ class KotlinJvmWithJavaTargetPreset( val target = KotlinWithJavaTarget(project, KotlinPlatformType.jvm, name).apply { disambiguationClassifier = name + preset = this@KotlinJvmWithJavaTargetPreset } AbstractKotlinPlugin.configureTarget(target) { compilation -> @@ -314,6 +317,7 @@ class KotlinNativeTargetPreset( val result = KotlinNativeTarget(project, konanTarget).apply { targetName = name disambiguationClassifier = name + preset = this@KotlinNativeTargetPreset val compilationFactory = KotlinNativeCompilationFactory(project, this) compilations = project.container(compilationFactory.itemClass, compilationFactory) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt index a3e059f6888..0b6d7a2dadb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -108,6 +108,9 @@ abstract class AbstractKotlinTarget( override fun mavenPublication(action: Closure) = mavenPublication(ConfigureUtil.configureUsing(action)) + + override var preset: KotlinTargetPreset? = null + internal set } internal fun KotlinTarget.disambiguateName(simpleName: String) = diff --git a/settings.gradle b/settings.gradle index c806bd774c7..737e63d8326 100644 --- a/settings.gradle +++ b/settings.gradle @@ -157,6 +157,7 @@ include ":kotlin-build-common", ":tools:kotlin-stdlib-gen", ":tools:kotlinp", ":kotlin-gradle-plugin-api", + ":kotlin-gradle-plugin-dsl-codegen", ":kotlin-gradle-plugin", ":kotlin-gradle-plugin-model", ":kotlin-gradle-plugin-test-utils-embeddable", @@ -293,6 +294,7 @@ project(':tools:kotlin-stdlib-js-merger').projectDir = "$rootDir/libraries/tools project(':tools:kotlin-stdlib-gen').projectDir = "$rootDir/libraries/tools/kotlin-stdlib-gen" as File project(':tools:kotlinp').projectDir = "$rootDir/libraries/tools/kotlinp" as File project(':kotlin-gradle-plugin-api').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-api" as File +project(':kotlin-gradle-plugin-dsl-codegen').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-dsl-codegen" as File project(':kotlin-gradle-plugin').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin" as File project(':kotlin-gradle-plugin-model').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-model" as File project(':kotlin-gradle-plugin-test-utils-embeddable').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-test-utils-embeddable" as File