vscode-kotlin
vscode-kotlin copied to clipboard
IntelliJ Maven Projects are not working
Not sure whether this is a bug or an enhancement.
Problem description
It seems, that in some cases people use Kotlin with Maven and export it from IntelliJ. This is the case for study homework at university. But it seems, it doesn't work by default. The pom.xml
file is incomplete and maven commands or the launch configuration don't work.
Extension version
I am running the latest available version in the marketplace that is tagged as "preview".
How to fix it
It took me a lot of time but I figured out how to edit the pom.xml
to make it run. This is what needs to be in the pom.xml
to run.
Required dependencies (stdlib and testing):
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
Required build elements (these were missing as well!):
-
<sourceDirectory>src/main/kotlin</sourceDirectory>
(the main source directory, could also besrc/main/java
orsrc/main/resources
or sthg. like that) -
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
(same as before but withtest
instead ofmain
)
Required plugins (Kotlin compilation, Maven assembly):
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
Optional plugin (Java compilation, for mixed projects):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
<executions>
<!-- Replacing default-compile as it is treated specially by maven -->
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<!-- Replacing default-testCompile as it is treated specially by maven -->
<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>
Optional, these are the properties I use:
- java.version
-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
<kotlin.version>1.8.22</kotlin.version>
(a new one as of this writing) - kotlin.compiler.jvmTarget
- main.class
- junit.version
If one of these things is missing, it's likely that the project won't run.
MainKt ClassNotFoundException
This happens sometimes because some Maven plugin (or whatever it is) puts Kotlin Source files in the classpath ./target/classes
and puts all compiled .class
files in some other path ./target/kotlin-ic/compile/classes
.
If then the Kotlin-Launcher tries to execute the application, given the main class, it won't find it. ~~I also noticed that the Kotlin-specific classes directory is sometimes flattened, sometimes not, which also led to ClassNotFoundExceptions on startup, because the classes were not in the right directory.~~
I wrote a simple bash script that copies all classes to the correct classpath location:
#!/bin/bash
fromDir="./target/kotlin-ic/compile/classes"
classpath="./target/classes"
for kotlinFile in $(find ${classpath} -name "*.kt")
do
destinationDirectory="$(dirname "${kotlinFile}")"
fromDirectory="${fromDir}/${destinationDirectory#${classpath}/}"
for className in $(grep -P '(?<=\bclass )[A-Z]\w*' "${fromDirectory}" --no-filename --only-matching) "$(basename "${kotlinFile%.*}")"
do
cp -t "${destinationDirectory}" $(find "${fromDirectory}" -name "${className}*.class")
done
done
[I edited the script to copy rather than to move the files. It otherwise makes problems with incremental builds.]
A lot of what you mention that needs to be added is simply standard Kotlin setup for Maven. I'm surprised that IntelliJ does not export them. Does the project even compile with mvn clean install
without adding it?!? 😱
This is probably also not a VSCode extension issue, but a language server issue. @fwcd , maybe we could move it?
A lot of what you mention that needs to be added is simply standard Kotlin setup for Maven. I'm surprised that IntelliJ does not export them. Does the project even compile with
mvn clean install
without adding it?!? screamThis is probably also not a VSCode extension issue, but a language server issue. @fwcd , maybe we could move it?
Yes, I am surprised too. I redownloaded our provided project, unzipped it into a new folder, opened that one in VSCode and then tried mvn clean install
as you said but it causes a build failure after all after downloading 43MiB of stuff.
The literal pom.xml
structure which we obtained for our project is this:
<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>
<groupId>{{…}}</groupId>
<artifactId>{{…}}</artifactId>
<version>{{…}}</version>
<packaging>jar</packaging>
<description>{{… Exercise …}}</description>
<properties>
<java.version>11</java.version>
<kotlin.version>1.3.72</kotlin.version>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
</properties>
<dependencies>
<dependency>
<groupId>{{… their group name …}}</groupId>
<artifactId>{{… their framework name …}}</artifactId>
<version>{{…}}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
</configuration>
<executions>
<execution>
<id>compile</id>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
<executions>
<!-- Replacing default-compile as it is treated specially by maven -->
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<!-- Replacing default-testCompile as it is treated specially by maven -->
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<archive>
<manifest>
<mainClass>{{… MainKt here …}}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>{{…}}</id>
<name>{{… their open repo name …}}</name>
<url>{{…}}</url>
</repository>
<repository>
<id>gradle-m2</id>
<name>gradle-m2</name>
<url>https://plugins.gradle.org/m2/</url>
</repository>
<repository>
<id>kotlin-eap</id>
<name>kotlin-eap</name>
<url>https://dl.bintray.com/kotlin/kotlin-eap/</url>
</repository>
<repository>
<id>kotlin-native</id>
<name>kotlin-native</name>
<url>https://dl.bintray.com/jetbrains/kotlin-native-dependencies/</url>
</repository>
</repositories>
</project>
I do not even know why there is a Gradle repo when they use Maven. The pom.xml
is manually edited at one place where they inserted their own repository element (indentation is awry).
Apparently, this project just works when importing it into IntelliJ (and I think, this has been their recommended IDE) but I don't know other people from my course who got this running in VSCode. All my team mates switched because they didn't get it running. Then I compared the given pom.xml
against a Kotlin-Maven tutorial, from scratch, and added all the missing elements. That worked. My team mates did the first homework in IntelliJ.
Note that I even inserted that <phase>compile</phase>
element inside the Kotlin-maven-plugin by myself.
I also created some nice launch.json and tasks.json config files for running the project. But this is rather a feature request, to have some standard tasks being generated right away for maven projects. I currently copy them from project to project.