5

I'm working on a mixed java and kotlin project by using maven.

The problem I'm facing right now is, maven-compiler-plugin runs before compiling kotlin-maven-plugin.

[INFO] --- maven-compiler-plugin:3.0:compile (default-compile) @annotation --- 
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 11 source files to /Users/hannes/workspace/tikxml/annotation/target/classes 
[INFO] --- kotlin-maven-plugin:1.0.0-beta-4583:compile (compile) @ annotation 
[INFO] Kotlin Compiler version 1.0.0-beta-4583

In my java source code I'm referencing classes written in kotlin. But javac runs before kotlinc. Hence, maven interrupts with compiler errors.

My pom (parent pom, I use submodules) looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.sonatype.oss</groupId>
        <artifactId>oss-parent</artifactId>
        <version>7</version>
    </parent>

    ...

    <modules>
        <module>core</module>
        <module>annotation</module>
        <module>processor</module>
    </modules>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.7</java.version>
        <kotlin.version>1.0.0-beta-4583</kotlin.version>
    </properties>


    <build>
        <pluginManagement>
            <plugins>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.0</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
            </plugins>


        </pluginManagement>

        <plugins>

            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                                <source>src/main/java</source>
                            </sourceDirs>
                        </configuration>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                                <source>src/test/java</source>
                            </sourceDirs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>
sockeqwe
  • 15,574
  • 24
  • 88
  • 144

4 Answers4

9

The Kotlin documentation on Using Maven suggests explicitly binding the kotlin-maven-plugin execution to the process-sources phase. Since maven-compiler-plugin is bound to the compile phase, binding kotlin-maven-plugin to the process-sources phase makes it run first.

<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>

    <executions>
        <execution>
            <id>compile</id>
            <phase>process-sources</phase>
            <goals> <goal>compile</goal> </goals>
        </execution>

        <execution>
            <id>test-compile</id>
            <phase>process-test-sources</phase>
            <goals> <goal>test-compile</goal> </goals>
        </execution>
    </executions>
</plugin>
Chris Nauroth
  • 9,614
  • 1
  • 35
  • 39
6

Although it's more verbose, you can compile both Java and Kotlin sources in the compile phase with the following configuration:

<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>
    <executions>
        <execution>
            <id>kotlin-compile</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>kotlin-test-compile</id>
            <phase>test-compile</phase>
            <goals>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.5</version>
    <executions>
        <execution>
            <id>default-compile</id>
            <phase>none</phase>
        </execution>
        <execution>
            <id>default-testCompile</id>
            <phase>none</phase>
        </execution>
        <execution>
            <id>java-compile</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>java-test-compile</id>
            <phase>test-compile</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Normally, plugins are executed in the order they are declared in the POM, but the executions of default-compile and default-testCompile are special because they're built-in, so they always go first. The above configuration gets around that by disabling the default compile executions, and declaring maven-compiler-plugin with new compilation executions after the kotlin-maven-plugin. In this way, you can get all compilation to properly occur during the compile phase of the build lifecycle.

heenenee
  • 19,914
  • 1
  • 60
  • 86
  • Important note for those, who use **Tiles Maven Plugin**. It [changes execution IDs](https://github.com/repaint-io/maven-tiles#execution-ids) inside the tile to reflect tile GAV coordinates. So in order for `maven-compiler-plugin` executions override to work `` tags should be added to the `default-compile` and `default-testCompile` executions to preserve specified IDs. – Ilya Serbis Feb 21 '21 at 14:25
1

A simple but effective solution would be to change the two phases (the <phase> element) applied to the executions of the Kotlin Maven Plugin, from compile to process-sources and from test-compile to process-test-sources.

You want the Kotlin part to be executed before the Java one. Maven by default will invoke the Maven Compiler Plugin as part of the compile phase for source code, and test-compile for test code. Moving the Kotlin part to their previous phases would then make the flow as you wished.

If you wanted the opposite (first Java then Kotlin), then you could have moved the Kotlin part to the next phases (as an example: from compile to process-classes and from test-compile to process-test-classes).

For further details about Maven phases, check the official documentation.

A_Di-Matteo
  • 26,902
  • 7
  • 94
  • 128
1

In case you are open to using a different build system, dependent projects are easy to set up with Kobalt:

val p1 = javaProject { ... }

val p2 = kotlinProject(p1) { ... }

This makes project p2 depend on p1. You can also mix Java and Kotlin projects.

Cedric Beust
  • 15,480
  • 2
  • 55
  • 55