Kotlin/Package Maintainer Guide

This guide contains information for people interested in creating and maintaining ebuilds for packages that use Kotlin. Maintainers of Kotlin library packages such as dev-java/kotlin-stdlib and dev-java/kotlin-reflect should also consult the Kotlin library package maintainer guide.

The Kotlin ecosystem
Many Kotlin projects use Gradle as the build system, which is the first obstacle to build packages for those projects with Portage. As of February 2022, Gradle is not integrated with Portage yet, and there is not an eclass that can be used to build and package artifacts for a Gradle project easily. Invoking Gradle directly in an ebuild is not feasible because Gradle downloads project dependencies from the Internet on its own, requiring  to be disabled in Portage's FEATURES and preventing any dependencies installed by Portage to be reused, so it is against the best practices.

Yet it is still possible to build most Gradle-based Kotlin projects without Gradle thanks to the feasibility of reverse engineering and the standalone Kotlin command-line compiler. Those Gradle-based Kotlin projects use the Kotlin Gradle plugin, which calls the Kotlin compiler to compile Kotlin source files. The plugin prints the Kotlin compiler arguments it would use to Gradle debug logs controlled by, which are standard Kotlin compiler options recognized by the standalone compiler. In general, invoking the standalone compiler using the same set of compiler arguments the Kotlin Gradle plugin would use produces the same results.

Gentoo packages for the standalone compiler and Kotlin eclasses are provided to support this approach of building Kotlin packages within Portage. The eclasses provide an easy-to-use interface to the standalone compiler. By following this guide, the set of Kotlin compiler arguments required to build a Kotlin project can be discovered quickly, and an ebuild for the project can thus be created.

The current framework for Kotlin support on Gentoo focuses on the Kotlin/JVM platform, which is the most widely used and the most mature target platform for computer applications among all supported platforms. When a Kotlin project is built for the Kotlin/JVM platform, it can be well-integrated with other Java packages and JVM to augment the Java ecosystem on Gentoo. A Java package on Gentoo can use a Kotlin package as a dependency like if it were a normal Java library.

Eclasses
The following eclasses are available for packages that use Kotlin:


 * 
 * The eclass that provides the backbone for any eclasses and ebuilds that need to use the Kotlin compiler. It does not export any phases; rather, it provides various helper functions for decomposed steps to build a Kotlin package.
 * This eclass is suitable for packages whose Kotlin part is small and not the major part, like Reactor Core 3.4.14, which is a Java package with 415 Java source files and only 4 Kotlin source files.


 * 
 * The eclass for packages written entirely or mostly in Kotlin. It exports various   phase functions that call the helper functions in, so by inheriting this eclass, ebuilds need not call those helper functions on their own and do not need to define   phase functions at all if the default behaviors suffice.
 * This eclass adds dependency on the Kotlin compiler by default. If the Kotlin compiler is not always needed,  should be used instead.

These eclasses all inherit because they use the same approach as  for building a package targeting JVM: the build system employed by the upstream is circumvented if it is not integrated with Portage, and command-line tools are invoked directly to simulate the build steps performed by the build system.

There are some other files that are generally not needed by normal Kotlin packages; those eclasses are mainly for Kotlin library and compiler packages.

Obtaining Kotlin compiler arguments for a package
The most important step for creating a new Kotlin package is to find out the Kotlin compiler arguments that the Kotlin Gradle plugin would use to build the project. After the arguments have been collected, an ebuild can be written fairly quickly.

To summarize, the method of collecting the Kotlin compiler arguments is to build the project using Gradle with the  option and search for the arguments in the output. As Gradle debug logs are very verbose, finding the arguments in the output is not a trivial task. This section will introduce strategies that make this task easier.

An example of creating a package for a real-world Kotlin project will be presented below to demonstrate the entire procedure. The package being created in the example is Clikt 3.4.0, which uses Gradle as the build system.

Prepare the project's source tree
To build the project, its entire source tree, including the Gradle build script, needs to be downloaded. The source tree can either be unpacked from an archive or cloned using a version control system. In the latter case, make sure the version being packaged is checked out, unless a live ebuild is to be created.

If the project uses the Gradle Wrapper, using the wrapper is recommended so the exact Gradle version the upstream uses will be run. To find out if the Gradle Wrapper is used, check if the source tree has a script in its root directory. The project being packaged in this example uses the Gradle Wrapper, thus it will be invoked in the following steps.

Find out the Gradle compilation task for Kotlin/JVM
Gradle usually runs dozens of tasks to compile all sources of the project for the  lifecycle task, which causes the Gradle debug logs to contain too much information. Usually, only one of the tasks will invoke the Kotlin compiler to generate classes for Kotlin/JVM. By running only this task with the  option, the amount of debug log messages to search for is greatly reduced.

Projects that target multiple platforms (including the project being packaged in this example) have multiple tasks that will invoke the Kotlin compiler, and each of them generates the classes for only one platform. In this case, only the task for the Kotlin/JVM platform should be run; tasks for Kotlin/JS, Kotlin/Native, Kotlin for Android etc. should be ignored.

To check the compilation task Kotlin/JVM, run to get a list of all tasks that would be executed for the   lifecycle task, and then find out the proper task by using task names as clues.

As its name suggests, the  task shown in the example output is the compilation task for Kotlin/JVM.

Run the task with debug level logging enabled
After knowing the correct compilation task, the debug logs can be collected from the output of.

As of Kotlin 1.6, the Gradle Kotlin plugin prints the compiler arguments in a message that contains the string. There will also be two more messages before the message that are relevant to the compiler invocation:
 * The first message contains the Kotlin compiler class called. For Kotlin/JVM, the class should be org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.


 * The second message contains the classpath with which the Kotlin compiler was called. This message can usually be ignored.

The following is a truncated example of messages printed for a compiler invocation:

2022-02-01T18:50:22.341-0800 [DEBUG] [org.gradle.api.Task] [KOTLIN] Kotlin compiler class: org.jetbrains.kotlin.cli.jvm.K2JVMCompiler 2022-02-01T18:50:22.341-0800 [DEBUG] [org.gradle.api.Task] [KOTLIN] Kotlin compiler classpath: /home/leo/.nobackup/gradle/.gradle/caches/modules-2/files-2.1/... 2022-02-01T18:50:22.341-0800 [DEBUG] [org.gradle.api.Task] [KOTLIN] :clikt:compileKotlinJvm Kotlin compiler args: -Xallow-no-source-files -classpath /home/leo/.nobackup/gradle/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10/e80fe6ac3c3573a80305f5ec43f86b829e8ab53d/kotlin-stdlib-jdk8-1.6.10.jar:...

The output may still be too verbose to inspect. To make searching easier, any of the following strategies may be used:


 * Use a pager like for convenient scrolling and searching:


 * Write the output to a file for further inspection and print it to the console at the same time:


 * Filter out irrelevant lines and print only log messages for Kotlin compiler invocation:


 * If the output has been saved to a file, it can be filtered in a similar manner:

It is recommended that the message containing the compiler arguments is saved to a file to ease the inspection on it later on.

Make a human-readable list of compiler arguments
The length of Kotlin compiler arguments can make inspections on it difficult. To decompose the arguments into lines of individual options and values, can be used to replace spaces with newline characters. If the arguments have been saved to a file, the following command can be used to complete this:

It is recommended that this output is saved to a file too since it still contains some very long arguments that can be decomposed, such as the value of, and. For example, the following command saves the output to and prints it to the console at the same time:

Converting Kotlin compiler arguments to an ebuild
This section contains instructions to create an ebuild inheriting a Kotlin eclass and introduces Kotlin eclass variables corresponding to the Kotlin compiler arguments.

Eclass variables to be set before inherit
This subsection covers  variables, which are variables that should be defined before a Kotlin eclass is inherited.

Compatible Kotlin feature releases
A Kotlin project might be compatible with multiple Kotlin feature releases, even if it declares only one version in. To allow an ebuild to use any compatible Kotlin feature release to build the package instead of pin a specific feature release, the Kotlin eclasses require a KOTLIN_COMPAT variable to be set before  for declaring all compatible Kotlin feature release versions.

KOTLIN_COMPAT is similar to PYTHON_COMPAT and LUA_COMPAT : it must be declared as an array, and it should contain a list of identifiers for the Kotlin feature releases supported by the ebuild. The identifier format is  for Kotlin 1.x.

The Kotlin eclasses will generate KOTLIN_SINGLE_TARGET USE_EXPAND flags for the ebuild, which work like PYTHON_SINGLE_TARGET and LUA_SINGLE_TARGET. Those USE_EXPAND flags may be utilized by users to select the version of Kotlin compiler they would like to use to build the package before it is merged.

If a new package is being created, it is recommended that only a single identifier is added to KOTLIN_COMPAT first. More versions can be added later after compatibility tests.

Assuming the feature release of the Kotlin version declared in is 1. x, the initial identifier added to KOTLIN_COMPAT is suggested to be for the first feature release in the following list (from top to bottom) that has a supported KOTLIN_SINGLE_TARGET :
 * 1. x
 * 1.( x + 1)
 * 1.( x + 2)
 * 1.( x - 1)
 * 1.( x - 2) or lower, which are likely to be imcompatible

For example, Clikt 3.4.0 declares Kotlin 1.6.10 in under its source tree, which corresponds to feature release 1.6. At the time this example was created,  was not a supported KOTLIN_SINGLE_TARGET, nor were   and. was available, making Kotlin 1.5 the first version in the list that had a supported KOTLIN_SINGLE_TARGET. So, the initial value of KOTLIN_COMPAT should be.

Declaration of special USE flags
Similar to JAVA_PKG_IUSE in, the Kotlin eclasses recognize a KOTLIN_IUSE variable set before  that can be used to enable extra eclass functionality controlled by USE flags. Supported USE flags include:
 * 
 * Install an archive of the package's source files so they can be viewed from some IDEs.
 * Every ebuild should include this USE flag in KOTLIN_IUSE, unless there are no source files to archive (for instance, the ebuild is for a binary package or a metapackage).


 * 
 * Enable dependencies and/or preparations necessary to run the package's tests, which are controlled by.
 * If this USE flag is included, another eclass variable KOTLIN_TESTING_FRAMEWORKS should also be defined to include identifiers for testing frameworks to run in .  This variable is analogous to JAVA_TESTING_FRAMEWORKS in .  Supported values include:
 * : Run tests with JUnit 4
 * : Compare JAVA_JAR_FILENAME with JAVA_BINJAR_FILENAME using and  on available architectures

Dependencies
A Kotlin package's dependencies usually can be categorized into the following types:
 * Kotlin compiler
 * Providers of virtual/kotlin.


 * Kotlin libraries
 * Libraries that are a part of the Kotlin programming language itself, such as dev-java/kotlin-stdlib, dev-java/kotlin-stdlib-jdk8, dev-java/kotlin-reflect, etc.


 * JDK and JRE
 * Providers of and.


 * Classpath dependencies
 * Third-party Kotlin libraries, Java libraries, and other JVM libraries that should be present in the classpath for both during build time and JVM during runtime.


 * Build-only JVM libraries
 * JVM libraries that are needed only during build time.


 * Runtime-only JVM libraries
 * JVM libraries that are needed only during runtime.


 * Miscellaneous dependencies
 * Other non-JVM libraries and build tools.

This subsection will cover how each type of dependencies should be handled in a Kotlin package's ebuild.