1. Gradle как собеседник: сначала вопрос, потом команда
Если спросить новичка: «Что делать, когда сборка не работает?», часто звучит что-то вроде: «Ну… нажать Run ещё раз… может, повезёт?». Это нормальная стадия — примерно как пытаться починить Wi‑Fi взглядом. Но на практике полезнее относиться к Gradle как к инструменту, который умеет объяснять, что он знает о вашем проекте. Ему просто нужно задать правильный вопрос.
Вся сегодняшняя лекция о том, что у Gradle есть четыре очень «дружелюбные» команды, которые почти ничего не ломают, зато хорошо показывают состояние проекта. tasks отвечает на вопрос «какие кнопки вообще есть», help --task — «что делает конкретная кнопка», dependencies — «какие библиотеки реально участвуют», а --version — «какой Gradle и какая Java сейчас используются». Это не теория ради теории: эти команды экономят время каждый раз, когда проект ведёт себя не так, как вы ожидали.
Важно: все команды ниже я буду писать в виде ./gradlew .... На Windows обычно будет gradlew.bat .... Смысл один: мы запускаем именно Wrapper из репозитория, а не «что-то, что установлено на этой машине».
2. ./gradlew tasks: карта возможностей вашего проекта
Команда tasks — это буквально оглавление возможностей Gradle-проекта. В IDE вы видите кнопочки и меню, но Gradle умеет показать список задач официально и в одном формате для всех. Это особенно полезно, когда вы открыли чужой репозиторий, когда проект стал сложнее или когда вы просто забыли, как называется нужная задача. Спойлер: так будет регулярно, и это нормально.
Запускаем в корне проекта ReadLater Starter (то есть там, где лежит build.gradle.kts и gradlew):
# Печатаем список доступных задач (краткий список по группам)
./gradlew tasks
В ответ вы увидите довольно много текста. Цель — не читать его как книгу, а научиться быстро выхватывать смысл: задачи сгруппированы по категориям. Например, если у вас подключен application plugin (а он у нас подключен), вы увидите секцию Application tasks. Примерно так (вывод немного отличается между версиями, но общая идея стабильна):
Application tasks
run - Runs this project as a JVM application
А благодаря java plugin обычно будет секция Build tasks и задачи вроде build, jar, classes. Например:
Build tasks
build - Assembles and tests this project.
jar - Assembles a jar archive containing the classes of the 'main' feature.
Обратите внимание на важный психологический момент. До этого курса многие воспринимают Gradle как «что-то, что собирает проект». Команда tasks показывает, что Gradle — это не одна кнопка, а набор действий. А вы, как разработчик, выбираете нужное действие. Это как кухня: плита умеет и греть, и кипятить, и жарить. Если вы всё время жмёте одну кнопку «Сделай еду», результат будет… философский.
Иногда ./gradlew tasks показывает не все задачи. Это не баг и не заговор: часть задач считается продвинутыми и скрыта из краткого списка, чтобы новичку не вываливать в лицо сотни строк. Если хочется увидеть вообще всё, можно использовать флаг --all:
# Показываем вообще все задачи, включая "advanced" (вывод будет очень большим)
./gradlew tasks --all
Пока не нужно влюбляться в этот вывод и пытаться его выучить. Достаточно запомнить идею: если вы не знаете, как проект «умеет жить», tasks — первый ответ.
И да, это ещё один аргумент, почему запуск через IDE не должен быть единственным способом. IDE может добавить свои сценарии и спрятать часть Gradle-реальности. tasks показывает то, что действительно есть в проекте, а не то, что IDE «угадала».
3. ./gradlew help --task: паспорт задачи
После tasks появляется следующий естественный вопрос: «Окей, задача есть. Но что она точно делает?» Тут в дело вступает команда help --task. Она хороша тем, что вместо легенд и догадок («кажется, build — это то же самое, что jar…») вы получаете короткое описание прямо от Gradle.
Например, хотим понять задачу run:
# Смотрим подробности конкретной задачи (описание, тип, группу и путь)
./gradlew help --task run
Вывод будет примерно таким (сокращу до сути):
Detailed task information for run
Path
:run
Type
JavaExec
Description
Runs this project as a JVM application
Group
application
Из этого можно вынести сразу несколько практических вещей. Во‑первых, Path :run подтверждает, что задача существует в нашем проекте и живёт на корневом уровне — у нас один проект, без модулей. Во‑вторых, Description обычно совпадает с тем, что вы видели в tasks, но здесь формулировка точнее и стабильнее. В‑третьих, Group объясняет, почему задача оказалась в секции Application tasks.
Точно так же можно уточнить, что делает build:
# Уточняем, что именно Gradle вкладывает в задачу build
./gradlew help --task build
Со временем это превращается в очень полезную привычку. Вы видите в README или в чужом чате фразу «просто запусти assemble» и, вместо того чтобы нервно кивать, спокойно пишете ./gradlew help --task assemble. После этого уже можно понять, подходит ли эта задача именно вам.
Полезный трюк: help --task хорош не только для «главных» задач. Например, сегодня у нас есть команда dependencies, и она тоже является задачей. Можно сделать так:
# Даже "диагностические" команды — это задачи, у них тоже есть описание и опции
./gradlew help --task dependencies
И вы увидите, что это не магическая команда, а обычная задача, у которой есть опции и смысл. Здесь же часто можно заметить, что у задачи есть параметры (например, можно смотреть зависимости для конкретной конфигурации). Мы не обязаны запоминать все опции прямо сейчас — главное, что вы умеете находить первоисточник.
4. ./gradlew dependencies: смотрим реальные библиотеки
В предыдущей лекции мы выяснили неприятную для наивного мышления вещь: добавив одну зависимость, вы почти всегда получаете «букет» транзитивных зависимостей. То есть реальный набор библиотек в проекте — это дерево. Команда dependencies как раз печатает это дерево. И это тот редкий случай, когда длинный вывод Gradle — не наказание, а честный ответ на вопрос «что у меня реально на classpath?».
Самый простой вариант — без уточнений:
# Печатаем зависимости по всем конфигурациям (обычно очень много вывода)
./gradlew dependencies
Вы увидите много секций. Почему много? Потому что Gradle показывает зависимости по конфигурациям. Условно: есть зависимости, которые нужны при компиляции (compileClasspath), при запуске (runtimeClasspath), при тестах и так далее. Даже если вы пока не пишете тесты, Gradle всё равно умеет показывать эти конфигурации — это часть взрослой модели проекта.
Новичку обычно удобнее начать с двух конфигураций: compileClasspath и runtimeClasspath. Если вывод слишком большой, можно попросить показать дерево только для одной конфигурации. Это не какая-то продвинутая магия, а просто способ сделать вывод читаемым:
# Сужаем вывод до одной конфигурации: зависимости, которые реально нужны при запуске
./gradlew dependencies --configuration runtimeClasspath
Теперь представим фрагмент вывода, похожий на то, что будет в нашем ReadLater Starter после подключения стека логирования:
# Пример фрагмента дерева зависимостей (ветки рисуются через +--- и \---)
# runtimeClasspath — то, что попадает в classpath при запуске приложения
runtimeClasspath - Runtime classpath of source set 'main'.
+--- ch.qos.logback:logback-classic:1.5.32
| +--- ch.qos.logback:logback-core:1.5.32
| \--- org.slf4j:slf4j-api:2.0.17
\--- tools.jackson.core:jackson-databind:3.0.4
+--- tools.jackson.core:jackson-annotations:3.0.4
\--- tools.jackson.core:jackson-core:3.0.4
Смысл читается так: верхний уровень — это то, что в итоге присутствует в runtime classpath. Внутри каждого узла дерева перечислено, что подтянулось вместе с ним. Если вы увидели slf4j-api и не помните, чтобы добавляли его сами, не спешите обвинять IDE в колдовстве. Возможно, logback-classic привёл его транзитивно — и это как раз нормальная ситуация.
Теперь самое полезное: команда dependencies помогает отвечать на конкретные вопросы. Например, «почему у меня на запуске не находится класс?» — вероятно, зависимость не попала в runtimeClasspath. Или «почему в проекте две версии одной библиотеки?» — это тоже видно в дереве, хотя сложные случаи разрешения конфликтов мы сегодня разбирать не будем.
Ещё один полезный сценарий: у вас в build.gradle.kts стоит runtimeOnly("ch.qos.logback:logback-classic:1.5.32"), и вы хотите проверить, что logback-core тоже подтянулся. dependencies — самый честный способ убедиться, что мир такой, как вы думаете.
И последнее: не пугайтесь того, что вывод большой. Читабельность приходит так же, как чтение логов: сначала вы видите «много букв», а потом начинаете замечать паттерны (+---, \---, уровни вложенности) и быстро выхватывать важные ветки.
5. ./gradlew --version: проверяем, чем именно вы запускаете проект
Команда --version выглядит почти смешно короткой, но её практическая ценность огромна. Она отвечает на вопрос: «Какая версия Gradle сейчас запустилась, на какой JVM и вообще где мы находимся?» Когда всё работает, про это легко забыть. Когда не работает — это одна из первых команд, которые экономят полчаса.
Запускаем:
# Проверяем версию Gradle wrapper и JVM, на которой реально стартует сборка
./gradlew --version
Вывод будет примерно такой (сокращенный, но смысловой):
Gradle 9.4.0
Build time: 2025-xx-xx
Kotlin: 2.x.x
Groovy: 4.x
Ant: 1.10.x
Launcher JVM: 25 (Oracle/OpenJDK ...)
OS: Windows 11 / macOS / Linux
На что смотреть в контексте нашего курса. Во‑первых, Gradle 9.4.0 — это наша фиксированная версия Wrapper. Если вы видите другую, значит, скорее всего, запустили не Wrapper, а локальный Gradle, или у вас как-то подменился Wrapper. Во‑вторых, Launcher JVM: 25 — это проверка, что сборка действительно стартует на Java 25. Когда на машине одновременно стоят JDK 17 и JDK 25, жизнь иногда любит устраивать сюрпризы. --version помогает поймать их на месте преступления.
Есть и третий нюанс, особенно полезный «в команде», даже если команда пока состоит из вас и будущего вас через два месяца. Когда вы будете писать README или просить помощи у коллеги, фраза «у меня не работает build» слишком общая. А фраза «у меня Gradle 9.4.0, JVM 25, но ./gradlew build падает вот так» — это уже нормальная инженерная коммуникация.
6. Маленькая методика: как избегать паники
Паника в сборке обычно возникает не потому, что всё сложно, а потому, что в голове нет последовательности действий. Поэтому полезно иметь мини-алгоритм: сначала понять, что вы вообще пытаетесь сделать, потом выбрать команду, и только затем читать вывод. Gradle хорош тем, что почти всегда рассказывает правду — просто иногда слишком подробным голосом.
Вот простая блок-схема, которая помогает «разруливать» типовые ситуации:
flowchart TD
%% Мини-алгоритм: от вопроса → к диагностической команде Gradle
A["Появилась проблема или вопрос по проекту"] --> B{"Что я хочу узнать?"}
B -->|Какие возможности есть?| C["./gradlew tasks"]
B -->|Что делает конкретная задача?| D["./gradlew help --task <name>"]
B -->|Какие библиотеки реально участвуют?| E["./gradlew dependencies"]
B -->|Какая версия Gradle/JVM сейчас?| F["./gradlew --version"]
C --> G[Нашёл нужную задачу]
D --> G
E --> H[Нашёл нужную ветку дерева]
F --> I[Убедился в baseline]
А чтобы было ещё проще, можно держать в голове табличку «вопрос → команда → куда смотреть». Здесь нет магии, просто сокращаем путь от «непонятно» к «конкретно»:
| Ситуация/вопрос | Команда | На что смотреть в выводе |
|---|---|---|
| “Какой командой это вообще делается?” | ./gradlew tasks | Нужная секция (Application, Build), наличие run/build/jar |
| “А run точно существует? И что он делает?” | ./gradlew help --task run | Description, Group, Path |
| “Откуда в проекте взялась вот эта библиотека?” | ./gradlew dependencies --configuration runtimeClasspath | Ветка дерева, кто подтянул транзитивно |
| “Почему у меня разные версии окружения и у соседа всё работает?” | ./gradlew --version | Gradle version, Launcher JVM |
Заметьте: ни одна из этих команд не требует «менять сборку». Это чистая диагностика. Сначала вы наблюдаете проект таким, какой он есть, а уже потом — в следующей лекции про baseline — будем говорить о том, как держать его стабильным.
7. Типичные ошибки при работе с Gradle-командами
Ошибка №1: запускать команды не из корня проекта.
Это классика: вы открыли терминал в src/main/java, набрали ./gradlew tasks, а терминал честно говорит: «нет такого файла». В такие моменты хочется обидеться на Gradle, но виновата география. Корень проекта — там, где лежат gradlew, build.gradle.kts, settings.gradle.kts. Если вы не в корне, Gradle не может быть вашим проводником: у него нет карты местности.
Ошибка №2: использовать gradle, а не ./gradlew.
Это тихая и опасная ошибка. gradle запускает локально установленный Gradle (если он установлен), а ./gradlew запускает версию, зафиксированную в репозитории. Иногда они совпадают, и вы думаете: «всё нормально». А потом внезапно начинают отличаться — и проект «ломается сам». В рамках курса правильная привычка простая: команды всегда начинаются с ./gradlew (или gradlew.bat на Windows).
Ошибка №3: пытаться прочитать весь вывод tasks или dependencies как роман.
Gradle выводит много текста, и новичок пытается «всё понять». В итоге не понимает ничего и устаёт. На практике вывод нужно читать как справочник: нашли нужную секцию, посмотрели 5–10 строк вокруг, остановились. Ваш мозг — не лог-файл, не надо прогонять через него гигабайты.
Ошибка №4: ожидать, что tasks или help “починят проект”.
tasks и help --task — диагностические команды. Они ничего не собирают и не запускают. Иногда новичок запускает ./gradlew tasks, видит список и думает: «почему приложение не стартануло?» Потому что вы попросили Gradle рассказать, что он умеет, а не делать работу. Это как открыть меню в ресторане и удивиться, что еда не появилась на столе.
Ошибка №5: смотреть зависимости “в целом”, но не уточнять конфигурацию.
./gradlew dependencies без параметров честно показывает всё подряд: compile, runtime, test, и это легко превращается в шум. Если вопрос про запуск приложения, то чаще всего полезнее смотреть runtimeClasspath. Если вопрос «почему не компилируется импорт», чаще полезнее compileClasspath. Мы пока не превращаем это в науку, но привычка «сначала выбрать правильную конфигурацию» сильно ускоряет диагностику.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ