1. Gradle и Wrapper: gradle vs gradlew
В начале очень легко перепутать две похожие вещи: gradle как программу и gradle Wrapper как способ её запускать. Звучат они почти одинаково — как «кофе» и «кофеварка», только последствия путаницы обычно бодрят сильнее утреннего эспрессо. Давайте спокойно разберём, что именно вы запускаете и почему в учебном проекте мы выбираем только один путь.
Представьте, что вы пишете в терминале:
# Запуск Gradle, который установлен в системе (локально на вашей машине)
gradle build
Это означает: «возьми Gradle, который установлен на моём компьютере, и попробуй собрать проект». А теперь сравните с:
# Запуск Gradle через Wrapper (версия Gradle берётся из настроек проекта)
./gradlew build
Это уже означает: «возьми Gradle, который прописан в проекте, и собери проект». Разница кажется косметической — всего‑то две точки и слэш, — но инженерно это два разных мира.
Системный gradle живёт по правилам вашей машины. У вас может быть Gradle 8.x, у вашего друга — 9.x, а у преподавателя — вообще ничего (и он не обязан угадывать, что вы там поставили). В итоге получается классическая ситуация: «у меня собирается», «у тебя не собирается», «а давай ты просто переустановишь всё на всякий случай». Это выглядит как план, но это плохой план.
Wrapper делает наоборот: кладёт в репозиторий «точку входа», которая гарантирует, что проект запускается одним и тем же Gradle независимо от того, кто его запускает. В нашем курсе это особенно важно, потому что baseline зафиксирован: Gradle Wrapper 9.4.0, Kotlin DSL, Java 25. Нам нужно, чтобы вы могли открывать любой пример из лекции и получать тот же результат, а не проходить квест «угадай версию».
Для наглядности можно запомнить короткую формулу:
| Команда | Кто выбирает версию Gradle | Почему это важно |
|---|---|---|
|
ваша ОС / ваша локальная установка | проект становится «привязанным к машине» |
|
сам проект (через Wrapper) | проект становится воспроизводимым |
И да, иногда системного gradle у вас вообще не будет — и это нормально. В нашем курсе не нужно отдельно ставить Gradle в систему. Если вам очень хочется что‑то поставить руками — лучше поставьте себе напоминание всегда собирать проект через Wrapper. Оно полезнее.
2. Wrapper: файлы и роли
Когда вы впервые видите Wrapper‑файлы в проекте, рука так и тянется подумать: «О, мусор! Это же не Java‑код». Реакция почти естественная, особенно если до этого вы писали маленькие программы, которые живут в одном файле. Но для backend‑проекта, даже учебного, эти файлы — как ключи от квартиры: вы можете быть прекрасным разработчиком, но без ключей внутрь не попадёте.
Минимальный Wrapper‑набор выглядит так:
# Структура файлов, которые обеспечивают запуск Gradle через Wrapper
readlater-starter/
├── gradlew
├── gradlew.bat
└── gradle/
└── wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
Давайте переведём это с формата «увидел в дереве проекта» на человеческий смысл.
gradlew — это скрипт для macOS/Linux. Он запускается как исполняемый файл из корня проекта и является вашим главным входом. gradlew.bat — то же самое, но для Windows (batch‑скрипт). Это не «два разных варианта на выбор», а два варианта для разных ОС.
В папке gradle/wrapper/ лежат два критически важных файла. gradle-wrapper.properties — конфигурация Wrapper, в ней как раз фиксируется, какую версию Gradle использовать. gradle-wrapper.jar — маленькая Java‑программа (да, jar), которая умеет скачать нужный Gradle и потом запустить его так, как надо проекту.
Чтобы было проще, вот небольшая «шпаргалка» в виде таблицы:
| Файл | Зачем нужен | Можно ли удалять |
|---|---|---|
|
запуск Gradle через Wrapper на macOS/Linux | нет |
|
запуск Gradle через Wrapper на Windows | нет |
|
фиксирует версию Gradle и настройки дистрибутива | нет |
|
реализация Wrapper, которая скачивает и запускает Gradle | нет |
Здесь легко зависнуть на вопросе: а откуда эти файлы вообще берутся в самом первом состоянии проекта? Их не пишут руками. Обычно они уже приходят вместе со стартовым каркасом проекта — например, если вы создаёте Gradle‑проект из шаблона или берёте готовый учебный репозиторий. Дальше Wrapper живёт в репозитории как часть проекта, и все команды идут только через него.
Слово «нет» в колонке «можно ли удалять» — не потому, что мы вредные. А потому, что если вы удалите Wrapper‑файлы, проект перестанет иметь универсальную точку входа, и мы откатимся в эпоху «поставь Gradle сам как‑нибудь».
3. Версия Gradle в gradle-wrapper.properties
Если вы ищете в проекте место, где «на самом деле» зафиксирована версия Gradle, это не build.gradle.kts и не IDE‑настройка. Это файл gradle/wrapper/gradle-wrapper.properties. Он маленький, скучный и именно поэтому надёжный: никакой магии, только текст, который читает Wrapper.
Самая важная строка там обычно выглядит так:
# Откуда Wrapper скачивает Gradle (и по этой строке фиксируется версия)
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
Эта запись буквально означает: «если нужно запустить Gradle, возьми дистрибутив gradle-9.4.0-bin.zip с официального сайта Gradle». То есть версия 9.4.0 зафиксирована в проекте. Не «последняя на сегодня», не «какая установлена» и не «та, что у меня в IDE». А та, с которой этот репозиторий должен жить.
Почему это важно даже для новичка, который пока не собирает многомодульных монстров? Потому что Gradle — живой инструмент. В разных версиях отличаются детали Kotlin DSL, доступные возможности, поведение некоторых задач и даже сообщения об ошибках. А вы сейчас учитесь, и вам нужно, чтобы пример из лекции совпадал с тем, что происходит на вашем компьютере.
У Wrapper есть и другие свойства: где хранить скачанный дистрибутив, как раскладывать файлы и так далее. Но в рамках нашего курса достаточно держать в голове один якорь: distributionUrl фиксирует версию Gradle и делает запуск воспроизводимым.
Ещё одна деталь: bin в имени архива означает «binary distribution», то есть обычный рабочий вариант. Существуют и другие варианты дистрибутива, но мы не превращаем этот курс в курс по build‑инженерии. Наша цель проще: одинаковый запуск у всех.
4. Первый запуск: скачивание Gradle
Первый запуск Wrapper часто пугает новичков: вы вводите команду, и вместо мгновенного «успех!» видите, как что‑то скачивается, шуршит, распаковывается. Тут важно не сделать вывод «у меня всё сломалось» и не побежать переустанавливать Java, IDE и, на всякий случай, клавиатуру.
Механика примерно такая: когда вы запускаете ./gradlew ..., Wrapper проверяет, есть ли нужная версия Gradle в локальном кэше. Если нет — скачивает по distributionUrl, распаковывает и только потом запускает нужную задачу (build, run и т.д.).
В упрощённом виде это выглядит так:
# Вся логика ниже — про то, что делает Wrapper перед тем, как запустить сборку
Вы запускаете ./gradlew build
→ Wrapper читает gradle-wrapper.properties
→ Видит gradle-9.4.0-bin.zip
→ Если Gradle 9.4.0 ещё нет локально — скачивает
→ Запускает Gradle 9.4.0 и выполняет build
Где это хранится? Обычно в домашней директории пользователя, в папке .gradle. То есть скачанный Gradle — это не часть репозитория, а локальный кэш на вашей машине. Поэтому первый запуск может быть заметно медленнее, а второй и третий — уже значительно быстрее.
Если хочется увидеть, что Wrapper действительно живой и работает, можно безопасно выполнить команду, которая ничего не компилирует, а просто показывает информацию о Gradle:
# Проверяем, какую версию Gradle реально запускает Wrapper
./gradlew --version
# Gradle 9.4.0
# Kotlin: ...
# JVM: ...
Комментарий под командой — это не точный «байт в байт» вывод, он может отличаться деталями. Но смысл вы ловите: вы увидите версию Gradle, и это будет именно та версия, что зафиксирована в Wrapper.
Если первая загрузка идёт долго, чаще всего причина прозаична: медленный интернет, первый запуск, иногда корпоративный прокси. Но на уровне курса мы воспринимаем «первый запуск качает Gradle» как нормальную часть жизни проекта. Важно только помнить: это происходит один раз на версию Gradle и потом кэшируется.
5. Задачи через Wrapper
Когда говорят «запускать через Wrapper», это не философия, а вполне конкретный формат команд. Вы запускаете скрипт из корня проекта и передаёте ему имя задачи. Если бы Gradle был кофемашиной, то Wrapper был бы кнопкой «включить», а задача — командой «сделай латте». Да, сравнение странное, но оно внезапно помогает запомнить структуру.
Общий шаблон команды такой:
# <taskName> — это имя задачи Gradle, например build/run/test и т.д.
./gradlew <taskName>
Например, самые базовые задачи, которые уже скоро будут у нас активно использоваться, выглядят так:
# Собрать проект (компиляция, тесты, упаковка — зависит от настроек проекта)
./gradlew build
# Запустить приложение (если в проекте настроена задача run)
./gradlew run
На Windows вместо ./gradlew обычно используется gradlew.bat (и это именно имя файла, а не «что‑то из IDE»):
REM На Windows используем batch-скрипт Wrapper из корня проекта
gradlew.bat build
gradlew.bat run
Поскольку даже здесь легко запутаться, вот маленькая таблица‑напоминалка:
| ОС | Команда |
|---|---|
| macOS / Linux | |
| Windows | |
Теперь про странный префикс ./. Он означает «запусти файл из текущей директории». На Unix‑подобных системах (Linux/macOS) текущая папка не входит в PATH по умолчанию, и это сделано специально из соображений безопасности. Поэтому gradlew без ./ часто не запускается. Не потому, что «у вас не тот терминал», а потому, что так устроена система.
Ещё одна полезная привычка: всегда запускать команды из корня проекта — из той папки, где лежат gradlew, build.gradle.kts и settings.gradle.kts. Если вы стоите где‑нибудь в глубине src/main/java, Wrapper физически может не находиться по пути, и команда не сработает. Это не «Gradle странный», это вы просто стоите не там.
6. Wrapper в репозитории
На старте Wrapper‑файлы часто воспринимают как что‑то временное: «ну я же уже скачал Gradle, зачем хранить эти скрипты». И здесь важно поймать главную идею: Wrapper хранится в репозитории не потому, что вам лень поставить Gradle. Он хранится потому, что проект должен уметь запускаться сам по себе, без предварительных танцев.
В командной разработке проект живёт так: кто‑то клонирует Git‑репозиторий, открывает README и запускает команды. Если в проекте нет Wrapper, первая команда превращается в «сначала поставь Gradle нужной версии». А дальше начинаются уточнения: «а какую версию?», «а где скачать?», «а что если у меня уже стоит другая?», «а это точно ничего не ломает?».
Wrapper обрывает эту ветку вопросов на корню. Он говорит: «вот вход, вот версия, запускай». Именно поэтому Wrapper‑файлы должны быть закоммичены в Git вместе с кодом. И именно поэтому любые .gitignore, которые случайно игнорируют gradle-wrapper.jar или gradle-wrapper.properties, — это диверсия против воспроизводимости.
Важное уточнение: папка build/, которую Gradle создаёт при сборке, — это действительно генерируемый артефакт, его мы обычно не коммитим. А вот gradle/wrapper/ — это не «результат сборки», а часть описания того, как собирать. Воспринимайте это как инструкции к проекту, которые должны лежать рядом с самим проектом.
7. Схема запуска Wrapper
У новичка очень часто возникает внутренний дискомфорт: «я запускаю ./gradlew build, и где‑то там что‑то происходит, но я не понимаю что». И это нормальная эмоция. Мы сейчас не изучаем внутренности Gradle, но важно убрать ощущение чёрного ящика хотя бы на уровне общей схемы.
Вот упрощённая блок‑схема того, что происходит:
flowchart TD
A["Вы в корне проекта запускаете ./gradlew build"] --> B["Скрипт gradlew / gradlew.bat"]
B --> C["Wrapper (gradle-wrapper.jar)"]
C --> D["Читает gradle-wrapper.properties и узнаёт версию Gradle"]
D --> E{"Нужная версия Gradle уже скачана?"}
E -->|Нет| F["Скачивает Gradle дистрибутив и кладёт в локальный кэш"]
E -->|Да| G["Использует локальный кэш"]
F --> H["Запускает Gradle нужной версии"]
G --> H["Запускает Gradle нужной версии"]
H --> I["Gradle выполняет задачу build/run по правилам проекта"]
Эта схема важна тем, что показывает: Wrapper не «заменяет Gradle». Он просто гарантирует, что Gradle будет правильной версии и что запуск всегда начинается одинаково.
И теперь можно аккуратно привязать это к нашему проекту. ReadLater Starter — это репозиторий, который должен запускаться одинаково у всех студентов. Поэтому мы будем везде — в README, в примерах команд и в собственных привычках — использовать только Wrapper‑команды. Это станет «правилом игры» проекта — таким же, как «Java‑код лежит в src/main/java», а не где попало.
8. Типичные ошибки при работе с Wrapper
Ошибка №1: запускать gradle build вместо ./gradlew build.
Это одна из самых частых путаниц. Команда gradle build обращается к Gradle, установленному в вашей системе (если он вообще установлен), и может иметь другую версию. В результате вы тратите время не на обучение и проект, а на угадывание, почему пример из лекции не совпадает с вашим выводом в консоли.
Ошибка №2: удалять или не коммитить Wrapper‑файлы, потому что они «служебные».
Кажется логичным «почистить проект» и убрать лишнее. Но Wrapper — это не мусор и не кэш. Это часть контракта репозитория: как именно этот проект должен запускаться. Если в репозитории нет Wrapper, у другого человека нет гарантированного способа воспроизвести ваш запуск.
Ошибка №3: путать gradlew и gradlew.bat и пытаться запускать не тот файл на своей ОС.
На macOS/Linux вы обычно запускаете ./gradlew. На Windows — gradlew.bat. Попытка запускать «не свой» скрипт часто заканчивается странными сообщениями: терминал не понимает формат файла, права не те, кодировка не та. Проще принять это как факт: есть два входа для двух семейств ОС.
Ошибка №4: запускать Wrapper не из корня проекта.
Команды ./gradlew ... имеют смысл, когда вы стоите в директории, где лежит файл gradlew. Если вы случайно ушли в src/main/java и пытаетесь запускать оттуда, то получите «No such file or directory» или аналогичное сообщение. Это не «Gradle не работает», а вы просто стоите не в той папке.
Ошибка №5: пугаться долгого первого запуска и принимать скачивание Gradle за сбой.
При первом запуске Wrapper действительно может скачать дистрибутив Gradle. Это нормальная механика, а не ошибка. Если интернет есть и ссылка в distributionUrl корректна, дальше всё будет работать быстрее за счёт кэша.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ