Главная идея статьи - это показать как заставить ЭТО (kotlin & maven toolchain) работать вместе. Детального описания Maven toolchain здесь не будет, не хочу заниматься банальным переводом руководств.
Начну с прелюдии. Как котлинисту, мне новые версии java как-то по боку, но тут в JDK 22 подъехала годнота - panama/foreign вышла из инкубатора. Для тех кто не в теме, эта фича дает вам возможность вызывать нативный код из 'динамической библиотеки' dll/so (прямо из java кода). Теперь вы можете вызывать системные функции сами, а не подключать неизвестные вам библиотеки.
Вкратце о Maven toolchain.
Эта фича позволяет подключать нужную версию jdk (или других инструментов) автоматически. До апреля 2024 года maven toolchain плагин был довольно слабенький (по сравнению с gradle toolchains) - он позволял выбирать toolchain/jdk только из
Но вот недавно (в апреле 2024) подъехала новая версия, которая поддерживает
$home/.m2/toolchains.xml файл
может подхватывать текущий JDK ($JAVA_HOME), если он удовлетворяет заданным критериям
делает поиск в стандартных директориях (например, C:/Program Files/...)
Реально прикольная штука, ничего вообще делать не нужно. Проверил на Windows - реально работает. Какое стандартное расположение жабы в Linux - не имею понятия, но можно хотя бы ввести разные JAVA_XXX_HOME (это уже неплохо).
делает поиск в переменных окружения по паттерну (например: JAVA11_HOME, JAVA22_HOME). Паттерн конфигурируем.
Между прочим, gradle toolchains тоже поддерживают этот подход, но насколько я знаю, переменные нужно добавлять файл проекта вручную.
Сейчас maven toolchain даже немного лучше своего собрата из gradle. В gradle есть неприятный баг по игнорированию vendor (и др атрибутов) из $home/.m2/toolchains.xml, в результате невозможно отличить Oracle (standard) JDK от Oracle Graal JDK.
Перейдем к главному.
У нас всё ещё есть одна проблемка - maven kotlin plugin не дружит с maven toolchain plugin. По крайней мере я не нашел как ему сказать, чтобы он подружился.
Но... у maven kotlin plugin есть конфигурационный параметр jdkHome, который мапится на maven property "toolchain.jdk.version". Это и будет нашим спасением - нужно взять JDK home, найденный toolchain plugin и установить его в соответсвующее свойство. Как по мне решение +- надежное (весь рискованный код помещен в try/catch), но это уже ваш выбор использовать ли его в production, или только в домашнем проекте. В худшем случае, оно просто не будет работать и вы просто вернетесь к старой доброй установке JAVA_HOME.
<project> ... <properties> <!-- Required JDK version in maven-artifact format. --> <toolchain.jdk.version>[22,)</toolchain.jdk.version> <!-- Required java-version in numeric format. --> <java.version>22</java.version> <java.release>${java.version}</java.release> <kotlin.version>1.7.0</kotlin.version> <kotlin.compiler.languageVersion>1.7</kotlin.compiler.languageVersion> <kotlin.compiler.jvmTarget>15</kotlin.compiler.jvmTarget> <kotlin.compiler.incremental>true</kotlin.compiler.incremental> <kotlin-maven-plugin.version>${kotlin.version}</kotlin-maven-plugin.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-toolchains-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <goals> <goal>select-jdk-toolchain</goal> </goals> </execution> </executions> <configuration> <version>${toolchain.jdk.version}</version> <!-- Optional old config. Let's keep it to avoid IDEA warning. --> <toolchains> <jdk> <version>${toolchain.jdk.version}</version> </jdk> </toolchains> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.4.0</version> <executions> <execution> <id>use-maven-toolchain-jdk-for-kotlin</id> <goals> <goal>bsh-property</goal> </goals> <configuration> <source><![CDATA[ import org.apache.maven.plugin.descriptor.PluginDescriptor; String toolChainJdk = null; try { PluginDescriptor pd = new PluginDescriptor(); pd.setGroupId("org.apache.maven.plugins"); pd.setArtifactId("maven-toolchains-plugin"); Map pluginContext = session.getPluginContext(pd, project); if (pluginContext == null) throw new IllegalStateException("maven-toolchains-plugin plugin context is not found. Probably it is not set up."); // A bit risky part of code: I'm not 100% sure that this class will be used in the future. // It is not specified in the org.apache.maven.model.ConfigurationContainer // (there is just java.lang.Object, but it must be some DOM) Object toolchainPluginContext = pluginContext.get("toolchain-jdk"); if (toolchainPluginContext != null) { // We can use type Object in bean-shell script instead of Xpp3Dom. // Real type is XML DOM object (currently type is org.codehaus.plexus.util.xml.Xpp3Dom class) Object config = toolchainPluginContext.getConfiguration(); if (config != null && config.getChildCount() > 0) toolChainJdk = config.getChild(0).getValue().trim(); } } catch (Exception ex) { log.error("toolchain-jdk is not found. " + ex.getMessage(), ex); // Or we can rethrow error just there. // throw new IllegalStateException("toolchain-jdk is not found.", ex); } String requiredJdkVersion = project.getProperties().getProperty("toolchain.jdk.version"); if (toolChainJdk != null && !toolChainJdk.isEmpty()) { project.getProperties().setProperty("kotlin.compiler.jdkHome", toolChainJdk); log.info("toolchain-jdk for version '" + requiredJdkVersion + "' is " + toolChainJdk); log.info("It will be used for kotlin compiler"); } else { String currentJavaHome = System.getProperty("java.home"); log.info("toolchain-jdk for version '" + requiredJdkVersion + "' is not found."); log.info(" Possible reasons"); log.info(" * default java_home matches required java version "); log.info(" * maven-toolchains-plugin is not configured properly"); log.info(" "); log.info("Default " + currentJavaHome + " will be used."); } ]]> </source> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Полные исходники на github
Полезные ссылки
Maven Toolchains Plugin home
Конфигурация (или сырцы SelectJdkToolchainMojo)
On mojohaus
