Рубрики
Без рубрики

Создавайте исполняемые банки Kotlin, используя Gradle

Kotlin отлично подходит для создания небольших утилит командной строки, которые могут быть упакованы и распространены как обычные файлы JAR. Помеченный kotlin, gradle, java, jar.

Эта статья была первоначально опубликована в мой блог

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. Таким образом, у вас обычно есть две папки в разделе srcsrc/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) } }
}

Дополнительные Ссылки

Оригинал: “https://dev.to/preslavrachev/create-executable-kotlin-jars-using-gradle”