Эта статья была первоначально опубликована в мой блог
Kotlin отлично подходит для создания небольших утилит командной строки, которые могут быть упакованы и распространены как обычные файлы JAR. Этот краткий учебник покажет вам, как:
- Создайте проект Gradle, поддерживающий Kotlin
- Добавить стартовую функцию
- Настройте свою сборку так, чтобы она вызывала эту функцию при выполнении JAR.
Настройка зависимостей Kotlin
apply plugin: 'java'
apply plugin: 'kotlin'
//… other stuff, you typically find in a Gradle build file
dependencies {
// other dependencies …
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
buildscript {
ext.kotlin_version = '1.0.2'
//...
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
Вы также захотите, чтобы ваша среда разработки (я предполагаю, что вы используете IntelliJ или Android Studio) пометила каталог, в котором находится исходный код Kotlin, как исходный каталог. Поскольку Kotlin и Java – лучшие друзья, совершенно нормально сохранять одну и ту же структуру пакетов. Однако хорошей практикой является физическое отделение кода Kotlin от кода Java. Таким образом, у вас обычно есть две папки в разделе src – src/main/java для классов Java и src/main/kotlin для классов Kotlin. То же самое и с тестами. Опять же, в вашем файле build.gradle добавьте следующее:
sourceSets {
main.java.srcDirs += 'src/main/kotlin/'
test.java.srcDirs += 'src/test/kotlin/'
}
Используя IntelliJ, вы могли бы положиться на ИДЕЮ, которая поможет вам во всем этом, но я хотел показать вам основы, так как не всегда можно полагаться на удобство IDE. Чтобы убедиться, что все работает так, как должно, перейдите в каталог вашего проекта и создайте новую сборку:
grade clean build
Если все настроено правильно, вы сможете увидеть задачу с именем compile Kotlin , которую успешно выполнила сборка.
Напишите свою первую программу Kotlin
В отличие от Java, Kotlin более дружелюбен к функциям, которые находятся за пределами какой-либо области видимости класса. Вы можете создать Основной класс, содержащий функцию main() , или вы можете создать main() верхнего уровня, не обязательно заключая его в класс. Возможно, вы не смогли бы найти никакой разницы в таком кратком примере, но я нахожу возможность создания функций верхнего уровня полезной для сокращения шаблонного кода.
Вот обязательный пример Hello World. Создайте файл с произвольным именем (скажем, Main ) и расширением .kt , и напишите просто:
fun main(args : Array) { println("Hello, world!") }
Обратите внимание, что добавление пакета необязательно, так же как и окончание строк точкой с запятой. Однако, чтобы сохранить согласованность с моим Java-кодом, я обычно добавляю и то, и другое и ожидаю, что люди, с которыми я работаю, сделают то же самое.
Настройте свою сборку Gradle для создания исполняемого файла JAR
Функции main , которую мы только что добавили, достаточно для проверки настройки исполняемого файла JAR, который затем можно будет вызвать, просто выполнив:
java -jar.jar
Если вы просто попытаетесь создать свой проект, а затем выполните приведенную выше команду, мы получим следующее сообщение:
no main manifest attribute.jar
Это означает, что мы должны настроить jar задача, которую выполняют сборки Java Gradle, и сообщите ей, какова отправная точка нашего проекта. В проекте Java это был бы путь к классу, в котором находится наша функция main() :
jar {
manifest {
attributes 'Main-Class': 'com.myname.myprojectname.Main'
}
}
Подожди минутку? Мы определили нашу функцию main() вне какой-либо области видимости класса. Это верно и в то же время не совсем верно. На самом деле, чтобы поддерживать согласованность на уровне байт-кода и обратную совместимость с JVM, компилятор Kotlin добавляет все функции верхнего уровня в соответствующие классы. В нашем случае класс, созданный компилятором Kotlin, будет иметь то же имя, что и имя файла, в котором находится наша функция, плюс суффикс Тыс. Тонн . Это означает, например, что если наш файл называется Main.kt , компилятор Kotlin сгенерирует класс с именем MainKt.class и добавьте его в сгенерированный файл JAR. Зная это, можно было бы переписать конфигурацию Gradle выше следующим образом:
jar {
manifest {
attributes 'Main-Class': 'com.myname.myprojectname.MainKt'
}
}
Примечание: Вы можете указать имя, с которым должен быть скомпилирован этот класс, добавив аннотацию области действия файла поверх вашего файла, даже до объявления пакета :
@file:JvmName("MainCls")
Это новое имя можно использовать в конфигурации манифеста JAR, как показано выше.
Несмотря на то, что мы правильно указали наш основной класс в конфигурации манифеста JAR, если мы попытаемся выполнить нашу основную функцию с помощью jar -jar , мы все равно увидим сообщение об ошибке:
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
at com.preslavrachev.imdbparser.MainKt.main(Main.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
Опытные разработчики Java быстро распознают этот тип исключений. По умолчанию, когда Gradle (а также Maven) упаковывает некоторые файлы классов Java в файл JAR, предполагается, что приложение будет ссылаться на этот файл JAR, где все его зависимости также доступны в пути к классу загружаемого приложения. Чтобы выполнить JAR без необходимости указывать путь к его зависимостям, необходимо указать системе сборки, чтобы она взяла все зависимости, на которые ссылается эта JAR, и скопировала их как часть самой JAR. В сообществе Java это известно как “толстая БАНКА”. В “толстой БАНКЕ” все зависимости попадают в путь к классам загружаемого приложения, поэтому код может выполняться без проблем. Единственным недостатком создания толстых банок, конечно, является их растущий размер файла (что отчасти объясняет название), хотя в большинстве ситуаций это не вызывает большой озабоченности. Чтобы указать Gradle копировать все зависимости JAR, необходимо просто изменить вышеупомянутую конфигурацию задачи JAR, добавив следующий фрагмент кода:
jar {
manifest {
attributes 'Main-Class': 'com.preslavrachev.imdbparser.MainKt'
}
// This line of code recursively collects and copies all of a project's files
// and adds them to the JAR itself. One can extend this task, to skip certain
// files or particular types at will
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
Дополнительные Ссылки
- Gradle – Создание файла Jar с зависимостями
- Создание работоспособной банки с помощью Gradle | Стековый поток
Оригинал: “https://dev.to/preslavrachev/create-executable-kotlin-jars-using-gradle”