Введение

Темой данного обзора будет система автоматической сборки Gradle. По английски системы сборки называются Build Tools. Зачем это вообще нужно? Ручная сборка проектов на Java довольно трудоёмкий процесс. Нужно правильно указать нужные проекту библиотеки и фрэймворки, от которых проект зависит. Здесь можно прочитать отличную статью на хабре: "Работа с Java в командной строке". Рано или поздно вы начнёте создавать какие-то скрипты для автоматизации этого процесса. А теперь представте что так делают все разработчики по всему миру и каждый пишет заново то, что кто-то уже написал для своего проекта. И тогда появились в свет системы сборки проектов, которые автоматизируют этот процесс. Кроме того, позволяют с одной стороны собрать Вам проект так, как Вы этого хотите, с другой стороны предоставляют вам более менее стандартизированные средства. Альтернативой Gradle является система автоматической сборки Maven. Эти две системы сборки с одной стороны разные, а с другой стороны имеют и ряд сходств. На эту тему на сайте Gradle есть материал: "Migrating from Maven to Gradle". Как сказано в этом руководстве, Gradle и Maven имеют разницу во взгляде на то, как собирать проект. Gradle основан на графе задач (task), которые могут зависеть друг от друга. Задачи выполняют какую-то работу. Maven же использует модель определённых фаз (phase), к которым присоединяются определённые "цели" (goals). В этих goals и выполняется какая-то работа. Однако, при таких разных подходах обе системы сборки следуют одному соглашению и управление зависимостями происходит схоже. Чтобы начать использовать Gradle необходимо его скачать. В google или в yandex вводим "Gradle Build Tool" и в первых результатах видим официальный сайт: https://gradle.org. На главной странице Gradle есть ссылка с текстом "Docs", которая ведёт на документацию Gradle. Для начала нам нужно установить (Install) Gradle, поэтому нам интересен раздел документации "Installing Gradle". Существует множество способов установки, в том числе способ "по старинке", т.е. вручную ("Installing manually"). Скачиваем согласно инструкции файл типа "binary-only", который будет иметь название вида gradle-5.1.1-bin.zip. Далее распаковываем архив и настраиваем переменную среды окружения PATH согласно инструкции. Главное, после выполнения инструкции команда gradle -v показывает версию установленного Gradle. Может возникнуть проблема с тем, что при определении расположения система найдёт Gradle не там, где Вы хотите. Поэтому, на Windows можно выполнить (на *nix есть свои аналоги): for %i in (gradle.bat) do @echo. %~$PATH:i Теперь, пожалуй, можно начинать знакомство.

Инициализация проекта Gradle

Сразу хочется отметить, что Gradle — это про выполнение задач, называемых task (буду называть их тасками). Таски предоставляются различными плагинами (plugins). Подробнее про плагины советую прочитать в официальной документации: "Using Gradle Plugins". Есть набор "Core Plugins", которые есть всегда, когда установлен Gradle. Есть разные категории этих плагинов, но нас интересует категория "Utility". В этом наборе есть плагин "Build Init Plugin", который предоставляет таски для инициализации (Initialization) Gradle проекта. Нас интересует создание типа проекта: "java-application". Выполним Gradle таск: gradle init --type java-application Ответим попутно на некоторые вопросы, например о том, что мы хотим использовать Groovy DSL (стандартный язык описания задач для Gradle) и фрэймворк тестирования JUnit (об этом мы поговорим в другом обзоре). После создания мы получим следующий набор файлов:
Во-первых, после инициализации мы получаем преднастроенный на нашу версию Gradle специальный враппер - это такой специальный скрипт. Про него подробнее советую прочитать в официальной документации — "The Gradle Wrapper". Во-вторых, мы видим Gradle Build Script – файл build.gradle. Это – главный файл, в котором описывается то, какие библиотеки и фрэймворки использует наш проект, какие плагины нужно подключить к проекту и описывает различные таски. Подробнее про данный файл советую прочитать в официальной документации: "Build Script Basics".

Plugins и Tasks

Если посмотреть сейчас на на содержимое Build Script, то мы увидим секцию plugins:
plugins {
    id 'java'
    id 'application'
}
Это те самые плагины, про которые мы говорили ранее. А если есть плагины, то есть и задачи, которые нам теперь доступны. Мы можем выполнить команду gradle tasks и увидеть, что мы сейчас можем сделать с проектом:
Например, выполнив gradle run мы запустим main класс нашего java приложения:
Как мы видим, снизу написано так же 2 actionable tasks: 1 executed, 1 up-to-date Что это значит? Это значит, что всего было выполнено 2 таска: Причём 1 действительно выполнен, а один не выполнялся, т.к. он up-to-date, то есть состояние актуальное и ничего выполнено не было. Мы можем выполнить так называемый "Dry Run": gradle run -m Выполним эту команду, мы увидим, какие будут выполнены таски, чтобы выполнить таск run:
Как мы видим, всего было выполнено целых 4 задачи: прежде чем выполнился run он выполнил по зависимости таск classes. Тас classes сам имеет 2 зависимости и поэтому он выполнил ещё и compileJava и processResources. Когда мы выполняем задачу, мы можем выполнить её с просмотром определённого уровня логов (уровень логирования определяет, на сколько важные сообщения мы хотим видеть). Например, мы можем выполнить gradle run -i. Это нам будет показывать в том числе информационные сообщения вида:
Task :classes UP-TO-DATE
Skipping task ':classes' as it has no actions.
Подробнее про логирование в Gradle советую обратиться к официальной документации: "Gradle Logging". Как мы видим, таск classes был пропущен, потому что он UP-TO-DATE, то есть состояние актуальное, ничего делать не надо, поэтому действий не было (no actions). Это из-за того, что по умолчанию в Gradle есть "Up-to-date checks" или так называемый инкрементальный билд. Подробнее про этот механизм можно почитать в документации Gradle: "Up-to-date checks (AKA Incremental Build)". Но этот механизм можно отключить, выполнив таск с указанием флага --rerun-tasks. Например, gradle run --rerun-tasks. Тогда мы увидим: 2 actionable tasks: 2 executed Как видно, количество выполненных тасков учитывает только первый уровень графа, то есть сам таск run и те таски, от которых он напрямую зависит, то есть classes. Таски, от которых зависит classes здесь не считаются (хотя они и выполняются, когда выполняется таск classes). Про задачи так же следует прочитать:

Dependencies

Одной из главных задач любой системы сборки – управление зависимостями, то есть тем, какие библиотеки/фрэймворки нужны нашему проекту. Система сборки должна обеспечить их наличие в нужный момент и нужным образом собрать конечный артефакт нашего приложения. По умолчанию после gradle init для java-application мы увидим следующее содержимое в билд скрипте:
dependencies {
    implementation 'com.google.guava:guava:26.0-jre'
    testImplementation 'junit:junit:4.12'
}
Тут сразу понятно, что мы подключаем. Но без некоторого понимания непонятно, что за implementation и testImplementation? Тут надо опять обратиться к документации Gradle, благо документация у Gradle написана прекрасно. Называется это "Managing Dependency Configurations". Как сказано в документации, каждая зависимость объявляется с некоторым scope - областью, в рамках которой данная зависимость будет доступна. Этот скоуп (scope) и обозначается некоторой конфигурацией (configuration), каждая из которых имеет уникальное имя. Так же интересно, что множество Gradle плагинов добавляют предзаданные конфигурации. Чтобы узнать, какие у нас есть конфигурации можно выполнить: gradle --console plain dependencies Таким образом мы увидим список всех доступных конфигураций и относящихся к ним зависимостей. Мы можем отфильтровать этот список так, чтобы видеть только сами доступные конфигурации: gradle --console plain dependencies | find " - " Как же понять, что нам использовать? Тут немного придётся почитать. Т.к. мы используем плагин "Java", то начнём с его документации и раздела "Dependency management". Тут мы видим, что раньше была конфигурация (он же скоуп), называемая "compile" и обозначала "зависимость, нужная во время компиляции". Но потом его заменили (на англ. Superseded) на implementation. Подробнее про замену можно прочитать в разделе "API and implementation separation". Получается, эта зависимость будет на "compile classpath". Но иногда мы хотим, чтобы наша зависимость была включена в итоговый артефакт. Зачем? Например, у нас будет выполняемый jar, который должен сам в себе содержать всё необходимое. Что же тогда нам делать? Во-первых, такой поддержки "из коробки" (то есть по умолчанию, без каких-либо дополнительных действий) нет. Объясняется это тем, что каждый хочет архив собрать по своему, а Gradle старается быть минималистичным. Мы так же не можем исполльзовать jar архивы на classpath (без дополнительных манипуляций в коде), т.к. это так не работает (Подробнее см. "Oracle: Adding Classes to the JAR File's Classpath"). Поэтому, самым красивым способом является следующий код в билд скрипте:
jar {
    manifest {
        attributes 'Main-Class': 'jrgradle.App'
    }
    from configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
В настройках таска jar мы указываем то, что будет добавлено в манифест jar-файла (см. "Oracle: Setting an Application's Entry Point"). А дальше мы говорим, что все зависимости, которые нужны были для компиляции, мы их включим в jar. Альтернативой может послужить использование плагина "Gradle Shadow Plugin". Может показаться сложным, но другие плагины могут упрощать жизнь. Например, при создании веб-приложения (в отличии от обычного выполняемого java приложения) мы будем использовать особый плагин - "Gradle War Plugin", который имеет другое поведение и там жизнь наша будет проще (все нужные зависимости будут сложены в отдельный особый каталог самим плагином. Такая работа регламентируется тем, как должны быть устроены веб-приложения. Но это уже совсем другая история).

Итоги

Gradle – является отличным выбором в качестве систем сборки проектов. Подтверждением тому является то, что его используют разработчики таких известных проектов, как Spring и Hibernate. Выше были рассмотрены лишь самые базовые вещи. За ними скрыт миллион особенностей и возможностей, которые появляются у разработчиков. Gradle так же поддерживает создание многомодульных проектов, что не рассмотрено в данном обзоре, но есть отличный tutorial у самого Gradle: "Creating Multi-project Builds". Надеюсь, данный обзор так же продемонстрировал, что документация у Gradle написана на 5+ и по ней легко можно найти нужное, если понимать, куда примерно смотреть. А это придёт, когда понимаешь основы. Кроме того, у Gradle шикарные tutorial. Закончить хочется небольшим списком того, что ещё можно посмотреть по Gradle:
#Viacheslav