Gradle generated buildSrc + Kotlin = how do I upgrade to Java 17? - kotlin

I'm starting a new Kotlin project, and I used Gradle 7.2 to generate the project structure and the buildSrc scripts. I'm not including them here because I have not changed them - I'm just using whatever Gradle generated.
I'm getting the following message as part of the build:
'compileJava' task (current target is 17) and 'compileKotlin' task (current target is 1.8) jvm target compatibility should be set to the same Java version.
I can't find where in the buildSrc and the generated Gradle files the 1.8 target is set. How can I tell the Kotlin compiler to use the Java 17 target?

The recommended way to address that from Kotlin 1.5.30 is to use the Java Toolchain feature:
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}

Related

Dependencies between Android native modules (prefab) fail to build

Our Android application consists of 40-some Android Library Modules (ALMs), each of which also builds a C++ shared library with externalNativeBuild and CMake. So far we had the dependencies between these libs set up like this:
The dependent ALM references the dependency ALM with api project(':lib')
The dependent CMake script references the dependency .so with add_library(SHARED IMPORTED lib) and set_target_properties(lib PROPERTIES IMPORTED_LOCATION ...) and a relative path.
Recently we had to upgrade to the latest Android API version. This started off a cascade because now we were getting deprecated warnings in code generated by the navigation-ktx library, but upgrading that requires upgrading Gradle and the Android Gradle plugin. After that I started getting errors like liblib.so, needed by 'project', missing and no known rule to make it.
It looks like the latest Gradle parallelizes build tasks more heavily, and this means the dependent CMake/Ninja builds are being started concurrently with their dependencies, resulting in this error because the dependency is not yet built. I figured out that what we were doing was not entirely supported, but there is a "supported" way to do that now, so I refactored our entire build to use Prefab.
Now I started getting other errors, alternating between:
1.
C++ build system [prefab] failed while executing ...
Usage: prefab [OPTIONS] PACKAGE_PATH...
Error: Invalid value for "PACKAGE_PATH": Directory ... is not readable.
ld: error: undefined symbol ...
I looked into build/intermediates and found that in the 2nd case, the cmake config script was generated incorrectly: instead of add_library(lib::lib SHARED IMPORTED ) it had add_library(lib::lib INTERFACE IMPORTED) like it it was a header only library, and there was no IMPORTED_LOCATION set in the file.
What am I doing wrong and what should I do to unbreak our build?
It looks like the toolchain support for prefab interdependencies within a project is not quite finished. Others are reporting the same kind of errors at https://issuetracker.google.com/issues/265544858:
This appears to be a race condition with generating prefab cmake files.
It says in https://issuetracker.google.com/issues/221231432 that the header-only cmake config is generated to satisfy Android Studio's IDE features (completion, etc.) before the library is actually built.
Treat as-yet-unconfigured modules as if they are Header-only libraries for Android Studio purposes. This works because Android Studio doesn't care about linker flags for the purposes of providing language services.

Why does Gradle force a Kotlin version, and how to fix it?

I have a simple Gradle project that includes the following configuration:
plugins {
kotlin("jvm")
}
This does different things when running with different gradle versions:
$ ./gradlew wrapper --gradle-version=7.5
$ ./gradlew --version
Kotlin: 1.6.21
$ ./gradlew wrapper --gradle-version=7.4.2
$ ./gradlew --version
Kotlin: 1.5.31
$ ./gradlew wrapper --gradle-version=7.2
$ ./gradlew --version
Kotlin: 1.5.21
OK, so Gradle is determining the version of the Kotlin plugin. Bonus questions: Why does Gradle do this? Is this version link documented somewhere?
I'd like to specify the Kotlin version myself. So I change my config:
plugins {
kotlin("jvm") version "1.6.10"
}
Now Gradle complains:
Plugin request for plugin already on the classpath must not include a version
I've checked, and nowhere else in my project do I define a Gradle version explicitly.
Main question: How do I make Gradle use a Kotlin version of my choice?
From my experience the below configuration works fine for a single-project Gradle project with most gradle version.
plugins {
kotlin("jvm") version "1.6.10"
}
Example: https://github.com/jimmyiu/demo-spring-cache. I changed the kotlin version from 1.6.21 to 1.7.10 which works fine.
Are you working on a multi-project Gradle project with buildSrc folder? Since buildSrc is a special subproject which build your custom kotlin-dsl which can be used by Gradle.
This can answer your bonus question: for simplicity, buildSrc should be built to something that can be understood by your project specified Gradle version, hence it must have the same kotlin version as your selected Gradle.
If you root project set the Kotlin version to a version that not align with your Gradle, buildSrc project cannot be built.
To properly configure this, you can:
// root project’s build.gradle.kts
plugins {
kotlin("jvm") version "1.6.10" apply false
}
// in each subproject (except buildSrc):
plugins {
java
kotlin("jvm")
}
About the output of ./gradlew --version, the Kotlin version showing in the output is the build information of that Gradle version, but not your project.
$./gradlew --version
------------------------------------------------------------
Gradle 6.6.1
------------------------------------------------------------
Build time: 2020-08-25 16:29:12 UTC
Revision: f2d1fb54a951d8b11d25748e4711bec8d128d7e3
Kotlin: 1.3.72
Groovy: 2.5.12
Ant: Apache Ant(TM) version 1.10.8 compiled on May 10 2020
JVM: 11.0.15 (Private Build 11.0.15+10-Ubuntu-0ubuntu0.20.04.1)
OS: Linux 5.13.0-52-generic amd64
As an example, Gradle 6.6.1 is built by Kotlin 1.3.72 so that the buildSrc folder should use Kotlin 1.3.72, but the rest of your project can use another Kotlin version.
Reference:
detail explanation of buildSrc: Configure Kotlin extension for Gradle subprojects
suggested solution reference: https://discuss.kotlinlang.org/t/specifying-the-kotlin-version-in-gradle-multi-project-builds/23838
About apply false: https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl

How to use Kotlinx serialization with jvm plugin

I can't use Kotlinx serialization with the Kotlin JVM plugin
In the instructions for Groovy DSL:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.5.0'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.5.0'
}
Because I already use the org.jetbrains.kotlin.jvm, I want to use it with the JVM plugin but the instructions do not explicitly show how.
In build.gradle, I tried using:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.5.0'
...
}
In code:
import kotlinx.serialization.*
And, I get a compilation error (Unresolved reference: serialization). How can I use kotlinx serialization with the JVM plugin? Also, I need it in the Groovy DSL syntax. The instructions already show it for the Kotlin DSL but I don't use it. Can anyone help?
The plugin is not sufficient to use Kotlinx Serialization, you also need the runtime library to use classes from the kotlinx.serialization.* packages.
This is covered in the docs:
https://github.com/Kotlin/kotlinx.serialization#dependency-on-the-json-library
In Gradle, this means you need to add Kotlinx serialization as a dependency in the dependencies block:
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
}
If you need other formats than JSON, you'll need to add the corresponding artifact instead.

Error:Unable to resolve version for dependency 'org.jetbrains.kotlin:kotlin-stdlib-jre7:jar'

I have a project written in java I am integrating kotlin with using gradle. I am trying to follow https://kotlinlang.org/docs/reference/using-gradle.html
My build.gradle
buildscript {
ext.kotlin_version = '1.1.51'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: "kotlin"
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7"
}
according to Kotlin Error : Could not find org.jetbrains.kotlin:kotlin-stdlib-jre7:1.0.7 the library is only available in kotlin v1.1 and up. When I deploy this library and use it in my android project I get the error in the title.
you need to replace
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7"
with
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
Starting with Kotlin 1.1.2 and if you're targeting JDK 7 or JDK 8, you can use extended versions of the Kotlin standard library which contain additional extension functions for APIs added in new JDK versions.
Use one of the following dependencies:
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"

How to run Kotlin class from the command line?

I understand this question has been asked before, but none of the information there has helped me.
Here is my situation: I can't run a compiled Kotlin class. When I try to run it like I would a normal java class I get the following:
C:\Users\User\Desktop>java _DefaultPackage
Exception in thread "main" java.lang.NoClassDefFoundError: jet/runtime/Intrinsics
at _DefaultPackage.main(Finder.kt)
Caused by: java.lang.ClassNotFoundException: jet.runtime.Intrinsics
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 1 more
This led me to believe that the Kotlin runtime was simply missing, given that output. So I tried the following:
C:\Users\User\Desktop>java -cp kotlin-runtime.jar _DefaultPackage
Error: Could not find or load main class _DefaultPackage
Which made me think that maybe I needed to add the class file to my declared classpath so:
C:\Users\User\Desktop>java -cp kotlin-runtime.jar';_DefaultPackage.class _DefaultPackage
Error: Could not find or load main class _DefaultPackage
What am I missing?
Knowing the Name of Your Main Class
To run a Kotlin class you are actually running a special class that is created at the file level that hold your main() and other functions that are top-level (outside of a class or interface). So if your code is:
// file App.kt
package com.my.stuff
fun main(args: Array<String>) {
...
}
Then you can execute the program by running the com.my.stuff.AppKt class. This name is derived from your filename with Kt appended. You can change the name of this class within the file by adding this file-targeted annotation:
#file:JvmName("Foo")
You can also put your main() into a class with a companion object and make it static using the JvmStatic annotation. Therefore your class name is the one you chose:
// file App.kt
package com.my.stuff
class MyApp {
companion object {
#JvmStatic fun main(args: Array<String>) {
...
}
}
}
Now you just run the class com.my.stuff.MyApp
What other JAR files do I need?
You need your application JAR and any dependencies. For Kotlin specific JARs when outside of Maven/Gradle you need a Kotlin distribution which contains:
kotlin-stdlib.jar (the standard library)
kotlin-reflect.jar only if using Kotlin reflection
kotlin-test.jar for unit tests that use Kotlin assertion classes
Running from Intellij
If in Intellij (if it is your IDE) you can right click on the main() function and select Run, it will create a runtime configuration for you and show the fully qualified class name that will be used. You can always use that if you are unsure of the name of the generated class.
Running from Gradle
You can also use the Gradle Application plugin to run a process from Gradle, or to create a runnable system that includes a zip/tgz of your JAR and all of its dependencies, and a startup script. Using the example class above, you would add this to your build.gradle:
apply plugin: 'application'
mainClassName = 'com.my.stuff.AppKt'
// optional: add one string per argument you want as the default JVM args
applicationDefaultJvmArgs = ["-Xms512m", "-Xmx1g"]
And then from the command-line use:
// run the program
gradle run
// debug the program
gradle run --debug-jvm
// create a distribution (distTar, distZip, installDist, ...)
gradle distTar
Running Directly from Java Command-line
If you have a runnable JAR, and assuming KOTLIN_LIB points to a directory where Kotlin runtime library files reside:
java -cp $KOTLIN_LIB/kotlin-stdlib.jar:MyApp.jar com.my.stuff.AppKt
See the notes above about other JAR files you might need. A slight variation if you have a runnable JAR (with the manifest pointing at com.my.stuff.AppKt as the main class):
java -cp $KOTLIN_LIB/kotlin-stdlib.jar -jar MyApp.jar
Running using the Kotlin command-line tool
If you install Kotlin tools via Homebrew or other package manager. (on Mac OS X brew update ; brew install kotlin) Then it is very simple to run:
kotlin -cp MyApp.jar com.my.stuff.AppKt
This command adds the stdlib to the classpath provided, then runs the class. You may need to add additional Kotlin libraries as mentioned in the section above "Running from Java."
Creating runnable JAR with the Kotlin compiler
This is not very common since most people use other build tools, but the Kotlin compiler can create a runnable Jar that solves this for you (see http://kotlinlang.org/docs/tutorials/command-line.html) when it bundles the runtime and your code together. Although this isn't as common when using tools such as Maven and Gradle, or IDE builds. Then run using the normal Java:
java -jar MyApp.jar
You can compile using kotlinc as follows:
$ kotlinc hello.kt -include-runtime -d hello.jar
and then you can run jar file as follows:
$ java -jar hello.jar
I struggled with this for a while as well. You're close, you just need to include your jar in the classpath and pass the qualified name of your "main" class as the primary argument to java.
There are a couple of ways to run Kotlin from the command line, depending on whether you're using the Java bytecode compiler (kotlinc) or the native compiler (kotlinc-native).
If you get an error message about -include-runtime, read the message carefully. You may be using the Kotlin native compiler instead of the Kotlin bytecode compiler. If you want to use the native compiler, see the second section below.
kotlinc (Java bytecode)
It's important to consider that you may not be in the same folder as your main.kt when you try to run your project, and it's not always clear how to tell Kotlin (or Java) to find it.
Assuming that:
You're trying to compile and run from a Windows command prompt.
You have this project structure (after IntelliJ Idea sets up a command-line Kotlin project):
MyNiftyProject
src
main
kotlin
main.kt
You're in the project root.
And you have this main.kt file:
fun main(args: Array<String>) {
println("Hello World!")
}
Compile like this:
kotlinc src/main/kotlin/main.kt -include-runtime -d MyNiftyProject.jar
This puts MyNiftyProject.jar in the root of your project.
Then run like this using the java command-line tool:
java -jar MyNiftyProject.jar
Or run like this using the kotlin command-line tool:
kotlin -classpath MyNiftyProject.jar MainKt
kotlinc-native
For native code, the process is somewhat simpler. Using the same setup as above:
kotlinc-native src/main/kotlin/main.kt -o MyNiftyProject
This puts MyNiftyProject.exe in the root of your project.
Then you just run it as a normal Windows executable:
MyNiftyProject.exe
if you are using a jar file that is generated from IntellijeIdead, you should run the kotlin file like java files but consider that kotlin file name will be something like that :
your file: test.kt
your output in the jar: testKt
java -cp file.jar yourPackage.testKt