Unresolved reference: KotlinCompilerVersion in build.gradle.kts - kotlin

In build.gradle.kts file, I include this code on the top line. Then I use KotlinCompilerVesion.VERSION below.
import org.jetbrains.kotlin.config.KotlinCompilerVersion
Some code works fine, but some code failed:
It seems like only plugins block can not enable this import.
Here works fine:
dependencies {
Implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
Implementation(kotlin("test", KotlinCompilerVersion.VERSION))
}
Here always wrong:
plugins {
id("com.android.application")
kotlin("android")
kotlin("android.extensions")
/*
* Error: Unresolved reference: KotlinCompilerVersion
*/
id("kotlinx-serialization") version KotlinCompilerVersion.VERSION
/*
* Error: Unresolved reference: KotlinCompilerVersion
*/
id("kotlinx-serialization") version "$KotlinCompilerVersion.VERSION"
/*
* Error: Unresolved reference: KotlinCompilerVersion
*/
id("kotlinx-serialization") version "${KotlinCompilerVersion.VERSION}"
}
How can I use it correctly in here, without declare an ext.xxxVersion var?

Yes, the syntax of the plugins DSL is limited because Gradle parses it before parsing the rest of the file, which requires the definition to be static (no references to variables, constants, etc., pretty much no code is allowed). See Limitations of the plugins DSL for details.
The way I handle defining Kotlin version only once is by actually using the version I specify in the plugins block as canonical in other sections of the file (I picked this up from this blog post by Simon Wirtz) like so:
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
val kotlinVersion = plugins.getPlugin(KotlinPluginWrapper::class.java)
.kotlinPluginVersion
plugins {
kotlin("jvm") version "1.3.30"
}
dependencies {
implementation(platform(kotlin("bom", kotlinVersion)))
implementation(kotlin("stdlib-jdk8"))
}
But yeah, in the plugins block it needs to be static and unfortunately repeated if needed.
Update: I actually just learned it's possible to use constants in the plugins DSL, but they must be defined before Gradle parses the build.gradle.kts file, i.e. from buildSrc. I noticed this in the fuel library. In essence, create buildSrc/build.gradle.kts with the following contents:
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
Then create buildSrc/src/main/kotlin/Constants.kt with the following contents:
object Kotlin {
const val version = "1.3.30"
}
And then you'll be able to use the following in your main build.gradle.kts:
plugins {
kotlin("jvm") version Kotlin.version
}

Related

How can I apply a plugin to itself using Kotlin DSL?

We have an existing plugin project which configures various things (including static analysis), where we want to apply the plugin to the project itself.
The way this currently works for plugins written in Java is, you add the Java src dir to the buildSrc project, and then classes built there can be used in the main project. So I'm trying to get the same thing working for plugins written as Kotlin scripts.
But when I try to build it, compiling buildSrc fails with:
e: C:\Users\Trejkaz\Documents\test\self-applying-gradle-plugin\src\main\kotlin\example.common.gradle.kts: (1, 1): Unresolved reference: allprojects
> Task :buildSrc:compileKotlin FAILED
What's missing in order to make this work?
Further investigation:
If I put a copy of the files in buildSrc/src/main/kotlin, that works.
If I put a copy of the files in buildSrc/src/main/kotlin2 and use srcDirs to set that directory, that fails too. So it really looks like something isn't letting me relocate sources at all.
I pushed a repo to play with this here but what follows is the contents of the build scripts in case it's ever deleted.
The main build.gradle.kts:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-gradle-plugin`
`kotlin-dsl`
// Matching version in Gradle
kotlin("jvm") version "1.5.31"
}
apply(from = "common-build.gradle.kts")
apply(plugin = "example.common") // 👈 trying to apply the compiled plugin here
group = "org.example"
version = "1.0-SNAPSHOT"
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
In buildSrc/build.gradle.kts, we have this - note that it adds a source dir for the sources in the main directory:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-gradle-plugin`
`kotlin-dsl`
// Matching version in Gradle
kotlin("jvm") version "1.5.31"
}
apply(from = "../common-build.gradle.kts")
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
kotlin {
sourceSets["main"].kotlin.srcDir("../src/main/kotlin")
}
common-build.gradle.kts has everything common to both build scripts which we've figured out how to move to a common location (notably, the KotlinCompile isn't there, later I'll figure out why I can't move that as well):
repositories {
mavenCentral()
}
dependencies {
// Needed to compile Kotlin stuff but not added by the plugin for some reason
"implementation"("org.jetbrains.kotlin:kotlin-scripting-jvm")
}
The plugin script, src/main/kotlin/example.common.gradle.kts, contains:
allprojects {
// Configure something
}
This turns out to be a bug in Gradle's kotlin-dsl plugin.
The workaround is to add the source dirs before applying the plugin.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-gradle-plugin`
`kotlin-dsl` apply false
// Matching version in Gradle
kotlin("jvm") version "1.5.31"
}
apply(from = "../common-build.gradle.kts")
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
kotlin {
sourceSets["main"].kotlin.srcDir("../src/main/kotlin")
}
// Workaround for https://github.com/gradle/gradle/issues/21052 -
// apply kotlin-dsl plugin last, because it erroneously fetches source dirs eagerly.
apply(plugin = "org.gradle.kotlin.kotlin-dsl")

Configure Kotlin extension for Gradle subprojects

I'm setting up a multi-module Gradle project based on Kotlin for the JVM. Since the root project does not contain any code, the Kotlin plugin should only be applied to subprojects.
build.gradle.kts (root project)
plugins {
kotlin("jvm") version "1.6.20" apply false
}
subprojects {
apply(plugin = "kotlin")
group = "com.example"
repositories {
mavenCentral()
}
dependencies {}
kotlin {
jvmToolchain {
check(this is JavaToolchainSpec)
languageVersion.set(JavaLanguageVersion.of(11))
}
}
}
Trying to set a toolchain causes the build to fail at the kotlin {...} extension:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun DependencyHandler.kotlin(module: String, version: String? = ...): Any defined in org.gradle.kotlin.dsl
public fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec defined in org.gradle.kotlin.dsl
It works fine if I copy the extension definition to each subproject build script, but why isn't it available in the main script?
This is one of my favourite things to fix in Gradle, and really shows off the flexibility that's possible (as well as demonstrating why Gradle can be complicated!)
First I'll give a bit of background info on the subprojects {} DSL, then I'll show how to fix your script, and finally I'll show the best way to share build logic with buildSrc convention plugins. (Even though it's last, I really recommend using buildSrc!)
Composition vs Inheritance
Using allprojects {} and subprojects {} is really common, I see it a lot. It's more similar to how Maven works, where all the configuration is defined in a 'parent' build file. However it's not recommended by Gradle.
[A], discouraged, way to share build logic between subproject is cross project configuration via the subprojects {} and allprojects {} DSL constructs.
Gradle Docs: Sharing Build Logic between Subprojects
(It's probably common because it's easy to understand - it makes Gradle work more like Maven, so each project inherits from one parent. But Gradle is designed for composition. Further reading: Composition over inheritance: Gradle vs Maven)
Quick fix: 'Unresolved reference'
The error you're seeing is basically because you haven't applied the Kotlin plugin.
plugins {
kotlin("jvm") version "1.6.20" apply false // <- Kotlin DSL won't be loaded
}
The kotlin { } configuration block is a very helpful extension function that is loaded when the Kotlin plugin is applied. Here's what it looks like:
/**
* Configures the [kotlin][org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension] extension.
*/
fun org.gradle.api.Project.`kotlin`(configure: Action<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kotlin", configure)
// (note: this is generated code)
So if we don't have the extension function, we can just call configure directly, and thus configure the Kotlin extension.
subprojects {
// this is the traditional Gradle way of configuring extensions,
// and what the `kotlin { }` helper function will call.
configure<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension> {
jvmToolchain {
check(this is JavaToolchainSpec)
languageVersion.set(JavaLanguageVersion.of(11))
}
}
// without the Kotlin Gradle plugin, this helper function isn't available
// kotlin {
// jvmToolchain {
// check(this is JavaToolchainSpec)
// languageVersion.set(JavaLanguageVersion.of(11))
// }
// }
}
However, even though this works, using subprojects {} has problems. There's a better way...
buildSrc and Convention Plugins
buildSrc is, basically, a standalone Gradle project, the output of which we can use in the main project's build scripts. So we can write our own custom Gradle plugins, defining conventions, which we can selectively apply to any subproject in the 'main' build.
(This is the key difference between Gradle and Maven. In Gradle, a subproject can be configured by any number of plugins. In Maven, there's only one parent. Composition vs Inheritance!)
The Gradle docs have a full guide on setting up convention plugins, so only I'll briefly summarise the solution here.
1. Set up ./buildSrc
Create a directory named buildSrc in your project root.
Because buildSrc is a standalone project, create a ./buildSrc/build.gradle.kts and ./buildSrc/settings.gradle.kts files, like usual for a project.
In ./buildSrc/build.gradle.kts,
apply the kotlin-dsl plugin
add dependencies on Gradle plugins that you want to use anywhere in your project
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl` // this will create our Gradle convention plugins
// don't add the Kotlin JVM plugin
// kotlin("jvm") version embeddedKotlinVersion
// Why? It's a long story, but Gradle uses an embedded version of Kotlin,
// (which is provided by the `kotlin-dsl` plugin)
// which means importing an external version _might_ cause issues
// It's annoying but not important. The Kotlin plugin version below,
// in dependencies { }, will be used for building our 'main' project.
// https://github.com/gradle/gradle/issues/16345
}
val kotlinVersion = "1.6.20"
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}
Note that I've used the Maven repository coordinates for the Kotlin Gradle plugin, not the plugin ID!
You can also add other dependencies into ./buildSrc/build.gradle.kts if you like. If you wanted to parse JSON in a build script, then add a dependency on a JSON parser, like kotlinx-serialization.
2. Create a convention plugin
Create your Kotlin JVM convention that you can apply to any Kotlin JVM subproject.
// ./buildSrc/src/main/kotlin/my/project/convention/kotlin-jvm.gradle.kts
package my.project.convention
plugins {
kotlin("jvm") // don't include a version - that's provided by ./buildSrc/build.gradle.kts
}
dependencies {
// you can define default dependencies, if desired
// testImplementation(kotlin("test"))
}
kotlin {
jvmToolchain {
check(this is JavaToolchainSpec)
languageVersion.set(JavaLanguageVersion.of(11))
}
}
}
Don't forget to add the package declaration! I've forgotten it a few times, and it causes errors that are hard to figure out.
3. Applying the convention plugin
Just like how Gradle plugins have IDs, so do our convention plugins. It's the package name + the bit before .gradle.kts. So in our case the ID is my.project.convention.kotlin-jvm
We can apply this like a regular Gradle plugin...
// ./subprojects/my-project/build.gradle.kts
plugins {
id("my.project.convention.kotlin-jvm")
}
(Convention plugins can also import other convention plugins, using id("..."))
Also, since we're using Kotlin, there's an even nicer way. You know how there are included Gradle plugins, like java and java-library. We can import our convention plugins the same way!
// ./subprojects/my-project/build.gradle.kts
plugins {
// id("my.project.convention.kotlin-jvm")
my.project.convention.`kotlin-jvm` // this works just like id("...") does
}
Note the backticks around the plugin ID - they're needed because of the hyphen.
(caveat: this non-id("...") way doesn't work inside buildSrc, only in the main project)
Result
Now the root ./build.gradle.kts can be kept really clean and tidy - it only needs to define the group and version of the project.
Because we're using convention plugins and not blanket subprojects, each subproject can be specialised and only import convention plugins that it needs, without repetition.
Site note: sharing repositories between buildSrc and the main project
Usually you want to share repositories between buildSrc and the main project. Because Gradle plugins are not specifically for projects, we can write a plugin for anything, including settings.gradle.kts!
What I do is create a file with all the repositories I want to use...
// ./buildSrc/repositories.settings.gradle.kts
#Suppress("UnstableApiUsage") // centralised repository definitions are incubating
dependencyResolutionManagement {
repositories {
mavenCentral()
jitpack()
gradlePluginPortal()
}
pluginManagement {
repositories {
jitpack()
gradlePluginPortal()
mavenCentral()
}
}
}
fun RepositoryHandler.jitpack() {
maven("https://jitpack.io")
}
(the name, repositories.settings.gradle.kts, isn't important - but naming it *.settings.gradle.kts should mean IntelliJ provides suggestions, however this is bugged at the moment.)
I can then import this as a plugin in the other settings.gradle.kts files, just like how you were applying the Kotlin JVM plugin to subprojects.
// ./buildSrc/settings.gradle.kts
apply(from = "./repositories.settings.gradle.kts")
// ./settings.gradle.kts
apply(from = "./buildSrc/repositories.settings.gradle.kts")

Apply local jar-plugin without using maven

I'd like to load my custom plugin from a local jar. The jar file compiles fine and when I check it, the manifest and the plugin class are there.
gradlePlugin {
plugins {
create("asdf") { // <-- I really call it "asdf" in the kts script
id = "asdf"
implementationClass = "pluginTest.TestPlugin"
version = "1.4.0"
}
}
}
The plugin doesn't do anything useful yet as it should be a proof-of-concept to make sure it actually works at all:
class TestPlugin : Plugin<Project> {
override fun apply(project: Project) {
println("Hallo TestPlugin!")
}
}
I then try to use it like this in another project:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(files("..\\..\\path\\to\\pluginTest.jar"))
}
}
plugins {
id("asdf") version "1.4.0"
}
but it keeps telling me that:
Plugin [id: 'asdf', version: '1.4.0'] was not found in any of the following sources:
What am I missing here? I use Gradle v6.5.
When you have the plugin jar on the classpath, you can't have a version number in the plugin application. I guess this is because you can't have multiple jars with different versions on the classpath in the first place, so specifying a version here doesn't make any sense (except perhaps to validate that you are using the correct one). This won't fix the problem, but it is a start.
To be honest, I don't know why your approach still won't work. The buildscript block is supposed to set up dependencies for that particular script, and that should make the plugin visible to it. It doesn't for some reason.
Perhaps this is a bug or perhaps this is just an undocumented limitation on the use of the plugin {} block. Maybe you could ask over at the Gradle forums or create an issue for it. However, there are workarounds that don't involve publishing to a (local) Maven repository, which I agree can be a bit annoying.
If you use "apply from" instead of "plugins {}", it works. For some reason, the former can see the buildscript classpath whereas the latter can't:
// build.gradle (Groovy DSL)
buildscript {
dependencies {
classpath(files("..\\..\\path\\to\\pluginTest.jar"))
}
}
apply from: "asdf"
Alternatively, move the buildscript plugin from the build.gradle file to the settings.gradle file. This makes is available to the entire build classpath and will make it work with the plugin block:
// settings.gradle (Groovy DSL):
buildscript {
dependencies {
classpath(files("..\\..\\path\\to\\pluginTest.jar"))
}
}
// build.gradle (Groovy DSL)
plugins {
id("asdf")
}
Lastly, just in case you haven't considered it already, you may be able to add the plugin as a composite build. This will create a source dependency to the plugin and has the advantage that transitive dependencies will be carried over (the ones you put in the plugin's own dependency block) and that it will be built automatically if not up-to-date. I use this approach for integration testing my plugins and also sometimes to apply them to my other real projects to test them in a bigger setting before publishing new versions.
Do that with either:
// settings.gradle (Groovy DSL):
includeBuild("..\\..\\path\\to\\plugin")
// build.gradle (Groovy DSL):
plugins {
id("asdf")
}
Or without hard-coding it in the build (so you can dynamically switch between local and published versions):
// build.gradle (Groovy DSL):
plugins {
id("asdf") version "1.4.0" // Version is optional (will be ignored when the command line switch below)
}
// Run with:
./gradlew --include-build "..\\..\\path\\to\\plugin" build
With #BjørnVester's answer I figured it out!
You need to put the buildscript in settings.gradle.kts as it doesn't get executed in the build.gradle.kts even when placed before plugins.
buildscript {
repositories {
flatDir {
dirs("..\\reusable-kotlin\\build\\libs") // <-- folder with jars
}
}
dependencies {
classpath("com.hedev.kotlin:reusable-kotlin:1.4.0")
}
}
But there's a catch! You must use the file-name of the jar in the classpath's name identifier that goes like this:
group:file-name:version
The file gradle will look for will be file-name-version.jar or file-name.jar which you'll see in the error message if you make a mistake (I added the _ on purpose to trigger the error):
Could not resolve all artifacts for configuration 'classpath'.
Could not find com.hedev.kotlin:reusable-kotlin_:1.4.0. Searched in the following locations:
- file:/C:/some/path/reusable-kotlin/build/libs/reusable-kotlin_-1.4.0.jar
- file:/C:/some/path/reusable-kotlin/build/libs/reusable-kotlin_.jar
In order for this to work I also had to add the group property to the plugin itself:
gradlePlugin {
plugins {
create("asdf") {
id = "asdf"
implementationClass = "com.hedev.kotlin.gradle.TestPlugin"
version = "1.4.0"
group = "com.hedev.kotlin"
}
}
}
Finally you can apply it in build.gradle.kts with (no version here):
plugins {
id("asdf")
}

Runing coroutines in IntelliJ Kotlinx error

I'm trying to run a native Kotlin project using coroutines using IntelliJ IDEA Community 2020.
Here is how my build.gradle looks:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.72'
}
repositories {
mavenCentral()
}
kotlin {
// For ARM, should be changed to iosArm32 or iosArm64
// For Linux, should be changed to e.g. linuxX64
// For MacOS, should be changed to e.g. macosX64
// For Windows, should be changed to e.g. mingwX64
mingwX64("mingw") {
binaries {
executable {
// Change to specify fully qualified name of your application's entry point:
entryPoint = 'sample.main'
// Specify command-line arguments, if necessary:
runTask?.args('')
}
}
}
sourceSets {
// Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
// in gradle.properties file and re-import your project in IDE.
mingwMain {
}
mingwTest {
}
}
}
// Use the following Gradle tasks to run your application:
// :runReleaseExecutableMingw - without debug symbols
// :runDebugExecutableMingw - with debug symbols
And here is a simple KT file:
package sample
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
val deferred = async(Dispatchers.Unconfined, CoroutineStart.LAZY) {
println("Running Async Unconfined: on thread ${Thread.currentThread().name} has run.")
42
}
val result = deferred.await()
println("Async Unconfined Result is ${result}")
}
I installed the maven plugin under Project Structure | Module and screenshot attached.
Nevertheless, I'm getting "Unresolved References..." error. Attached screenshot...
Request if someone can help me to resolve this please?
Thanks
In your Project Structure > Modules > Dependencies tab, you selected Runtime as the scope, which makes the dependency only available at runtime (usually used for transitive dependencies). Try selecting Compile here.

How to build kotlinx.coroutines in Kotlin/Native (test version 0.23.4-native-1)

This question is a continuation of this thread:
https://github.com/Kotlin/kotlinx.coroutines/issues/246#issuecomment-407023156
I am trying to use org.jetbrains.kotlinx:kotlinx-coroutines-core-native:0.23.4-native-1 in a Kotlin/Native project targeting iOS.
build.gradle:
buildscript {
repositories {
mavenCentral()
maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" }
}
dependencies {
classpath 'org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.8'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.51"
}
}
apply plugin: 'kotlin-platform-native'
repositories {
jcenter()
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
sourceSets {
main {
component {
target 'ios_arm32', 'ios_arm64', 'ios_x64'
outputKinds = [KLIBRARY]
}
}
}
dependencies {
expectedBy project(':common')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:0.23.4-native-1"
}
The kotlinx:kotlinx-coroutines-core-native dependency doesn't seem to work, as the produces build errors like:
error: unresolved reference: coroutines
import kotlinx.coroutines.experimental.*
^
If I manually include the artifact dependencies such as org.jetbrains.kotlinx:kotlinx-coroutines-core-native_release_ios_x64:0.10.3-native, then I get a complier exception:
exception: java.lang.IllegalStateException: Could not find "atomicfu-native"
This error persists, even if I also add org.jetbrains.kotlinx:atomicfu-native:0.10.3-native dependency.
Here is a list of things to check for (I have been through this, and finally made it work) :
Enable Gradle metadata. It's required to retrieve the coroutines dependencies. To do so, add this line in your "settings.gradle" file, after all the "include" instructions :
enableFeaturePreview('GRADLE_METADATA')
use gradle 4.7 (newer version are incompatible with the meta data of the current coroutines library, they require something with 0.4 version and the current published one uses 0.3)
In the iOS module :
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:0.23.4-native-1"
In your common module :
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4"
If you have a js module, it may fail due to the gradle metadata feature. You can fix it by adding this before each of your "repositories" blocks (https://github.com/srs/gradle-node-plugin/issues/301)
repositories.whenObjectAdded {
if (it instanceof IvyArtifactRepository) {
metadataSources {
artifact()
}
}
}
Hope this will be enough !