1. Gradle i Wrapper: gradle vs gradlew
Na początku bardzo łatwo pomylić dwie podobne rzeczy: gradle jako program i gradle Wrapper jako sposób jego uruchamiania. Brzmią prawie tak samo — jak „kawa” i „ekspres do kawy”, tylko że konsekwencje pomyłki zwykle pobudzają mocniej niż poranne espresso. Spokojnie omówmy, co dokładnie uruchamiasz i dlaczego w projekcie edukacyjnym wybieramy tylko jedną drogę.
Wyobraź sobie, że piszesz w terminalu:
# Uruchomienie Gradle, który jest zainstalowany w systemie (lokalnie na Twojej maszynie)
gradle build
To znaczy: „weź Gradle, który jest zainstalowany na moim komputerze, i spróbuj zbudować projekt”. A teraz porównaj z:
# Uruchomienie Gradle przez Wrapper (wersja Gradle brana jest z ustawień projektu)
./gradlew build
To z kolei oznacza: „weź Gradle, który jest zapisany w projekcie, i zbuduj projekt”. Różnica wydaje się kosmetyczna — wszystkiego dwie kropki i ukośnik — ale inżyniersko to dwa różne światy.
Systemowy gradle żyje według reguł Twojej maszyny. Możesz mieć Gradle 8.x, Twój kolega — 9.x, a prowadzący — w ogóle nic (i nie musi zgadywać, co tam zainstalowałeś). W efekcie wychodzi klasyczna sytuacja: „u mnie się buduje”, „u Ciebie się nie buduje”, „a może po prostu wszystko zainstalujesz od nowa na wszelki wypadek”. To wygląda jak plan, ale to zły plan.
Wrapper robi odwrotnie: kładzie w repozytorium „punkt wejścia”, który gwarantuje, że projekt uruchamia się tym samym Gradle niezależnie od tego, kto go uruchamia. W naszym kursie jest to szczególnie ważne, bo baseline jest ustalony: Gradle Wrapper 9.4.0, Kotlin DSL, Java 25. Potrzebujemy, żebyś mógł otworzyć dowolny przykład z wykładu i dostać ten sam wynik, a nie przechodzić questa „zgadnij wersję”.
Dla poglądowości można zapamiętać krótki wzór:
| Polecenie | Kto wybiera wersję Gradle | Dlaczego to jest ważne |
|---|---|---|
|
Twój system / Twoja lokalna instalacja | projekt staje się „przywiązany do maszyny” |
|
sam projekt (przez Wrapper) | projekt staje się powtarzalny |
I tak, czasem systemowego gradle w ogóle u Ciebie nie będzie — i to jest w porządku. W naszym kursie nie trzeba osobno instalować Gradle w systemie. Jeśli bardzo Ci się chce coś zainstalować ręcznie — lepiej ustaw sobie przypomnienie, żeby zawsze budować projekt przez Wrapper. To bardziej przydatne.
2. Wrapper: pliki i role
Kiedy po raz pierwszy widzisz pliki Wrappera w projekcie, ręka aż się rwie pomyśleć: „O, śmieci! To przecież nie jest kod Javy”. Reakcja prawie naturalna, zwłaszcza jeśli wcześniej pisałeś małe programy, które żyją w jednym pliku. Ale dla projektu backendowego, nawet edukacyjnego, te pliki są jak klucze od mieszkania: możesz być świetnym programistą, ale bez kluczy do środka nie wejdziesz.
Minimalny zestaw Wrappera wygląda tak:
# Struktura plików, które zapewniają uruchamianie Gradle przez Wrapper
readlater-starter/
├── gradlew
├── gradlew.bat
└── gradle/
└── wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
Przetłumaczmy to z formatu „zobaczyłem w drzewie projektu” na ludzki sens.
gradlew to skrypt dla macOS/Linux. Uruchamia się jako plik wykonywalny z korzenia projektu i jest Twoim głównym wejściem. gradlew.bat — to samo, ale dla Windows (skrypt batch). To nie są „dwa różne warianty do wyboru”, tylko dwa warianty dla różnych systemów operacyjnych.
W katalogu gradle/wrapper/ leżą dwa krytycznie ważne pliki. gradle-wrapper.properties — konfiguracja Wrappera, w niej właśnie jest utrwalone, jakiej wersji Gradle używać. gradle-wrapper.jar — mały program w Javie (tak, jar), który potrafi ściągnąć potrzebny Gradle, a potem uruchomić go tak, jak chce projekt.
Dla ułatwienia oto mała „ściąga” w postaci tabeli:
| Plik | Po co jest potrzebny | Czy można usuwać |
|---|---|---|
|
uruchamianie Gradle przez Wrapper na macOS/Linux | nie |
|
uruchamianie Gradle przez Wrapper na Windowsie | nie |
|
utrwala wersję Gradle i ustawienia dystrybucji | nie |
|
implementacja Wrappera, która pobiera i uruchamia Gradle | nie |
Tu łatwo zawiesić się na pytaniu: a skąd te pliki w ogóle biorą się w samym pierwszym stanie projektu? Nie pisze się ich ręcznie. Zwykle przychodzą już razem ze startowym szkieletem projektu — na przykład jeśli tworzysz projekt Gradle z szablonu albo bierzesz gotowe repozytorium edukacyjne. Dalej Wrapper żyje w repozytorium jako część projektu i wszystkie polecenia idą tylko przez niego.
Słowo „nie” w kolumnie „czy można usuwać” — nie dlatego, że jesteśmy złośliwi. A dlatego, że jeśli usuniesz pliki Wrappera, projekt przestanie mieć uniwersalny punkt wejścia, i wrócimy do epoki „zainstaluj sobie Gradle jakoś sam”.
3. Wersja Gradle w gradle-wrapper.properties
Jeśli szukasz w projekcie miejsca, gdzie „tak naprawdę” utrwalona jest wersja Gradle, to nie jest to build.gradle.kts ani ustawienie IDE. To plik gradle/wrapper/gradle-wrapper.properties. Jest mały, nudny i właśnie dlatego niezawodny: żadnej magii, tylko tekst, który czyta Wrapper.
Najważniejsza linia tam zazwyczaj wygląda tak:
# Skąd Wrapper pobiera Gradle (i właśnie tą linijką utrwalana jest wersja)
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
Ten zapis dosłownie znaczy: „jeśli trzeba uruchomić Gradle, weź dystrybucję gradle-9.4.0-bin.zip z oficjalnej strony Gradle”. Czyli wersja 9.4.0 jest utrwalona w projekcie. Nie „najnowsza na dziś”, nie „która jest zainstalowana” i nie „ta, co u mnie w IDE”. A ta, z którą to repozytorium ma żyć.
Dlaczego to jest ważne nawet dla nowicjusza, który na razie nie buduje wielomodułowych potworów? Bo Gradle to żywe narzędzie. W różnych wersjach różnią się szczegóły Kotlin DSL, dostępne możliwości, zachowanie niektórych zadań, a nawet komunikaty błędów. A Ty się teraz uczysz i potrzebujesz, żeby przykład z wykładu pokrywał się z tym, co dzieje się na Twoim komputerze.
Wrapper ma też inne właściwości: gdzie przechowywać ściągniętą dystrybucję, jak rozkładać pliki i tak dalej. Ale w ramach naszego kursu wystarczy trzymać w głowie jedną kotwicę: distributionUrl utrwala wersję Gradle i czyni uruchomienie powtarzalnym.
Jeszcze jeden szczegół: bin w nazwie archiwum oznacza „binary distribution”, czyli zwykły wariant roboczy. Istnieją też inne warianty dystrybucji, ale nie zamieniamy tego kursu w kurs inżynierii buildu. Nasz cel jest prostszy: jednakowe uruchomienie u wszystkich.
4. Pierwsze uruchomienie: pobranie Gradle
Pierwsze uruchomienie Wrappera często straszy nowicjuszy: wpisujesz polecenie, a zamiast natychmiastowego „sukces!” widzisz, jak coś się pobiera, szurka, rozpakowuje. Tu ważne jest nie wyciągnąć wniosku „u mnie wszystko się zepsuło” i nie biec przeinstalowywać Javy, IDE, a na wszelki wypadek klawiatury.
Mechanika jest mniej więcej taka: gdy uruchamiasz ./gradlew ..., Wrapper sprawdza, czy w lokalnym cache'u jest potrzebna wersja Gradle. Jeśli nie ma — pobiera po distributionUrl, rozpakowuje i dopiero potem uruchamia odpowiednie zadanie (build, run itd.).
W uproszczonej formie wygląda to tak:
# Cała logika niżej — to, co robi Wrapper zanim uruchomi build
Uruchamiasz ./gradlew build
→ Wrapper czyta gradle-wrapper.properties
→ Widzi gradle-9.4.0-bin.zip
→ Jeśli Gradle 9.4.0 jeszcze nie ma lokalnie — pobiera
→ Uruchamia Gradle 9.4.0 i wykonuje build
Gdzie to jest przechowywane? Zazwyczaj w katalogu domowym użytkownika, w folderze .gradle. Czyli pobrany Gradle to nie część repozytorium, tylko lokalny cache na Twojej maszynie. Dlatego pierwsze uruchomienie może być wyraźnie wolniejsze, a drugie i trzecie — już znacząco szybsze.
Jeśli chce się zobaczyć, że Wrapper rzeczywiście żyje i działa, można bezpiecznie wykonać polecenie, które niczego nie kompiluje, a po prostu pokazuje informacje o Gradle:
# Sprawdzamy, którą wersję Gradle naprawdę uruchamia Wrapper
./gradlew --version
# Gradle 9.4.0
# Kotlin: ...
# JVM: ...
Komentarz pod poleceniem to nie jest dokładny „bajt w bajt” output, może się różnić szczegółami. Ale sens łapiesz: zobaczysz wersję Gradle i będzie to dokładnie ta wersja, która jest utrwalona we Wrapperze.
Jeśli pierwsze pobieranie idzie długo, najczęściej powód jest prozaiczny: wolny internet, pierwsze uruchomienie, czasem korporacyjne proxy. Ale na poziomie kursu odbieramy „pierwsze uruchomienie pobiera Gradle” jako normalną część życia projektu. Ważne jest tylko pamiętać: dzieje się to raz na wersję Gradle i potem jest cache'owane.
5. Zadania przez Wrapper
Gdy mówi się „uruchamiać przez Wrapper”, to nie filozofia, tylko całkiem konkretny format poleceń. Uruchamiasz skrypt z korzenia projektu i przekazujesz mu nazwę zadania. Gdyby Gradle był ekspresem do kawy, to Wrapper byłby przyciskiem „włącz”, a zadanie — poleceniem „zrób latte”. Tak, porównanie dziwne, ale nieoczekiwanie pomaga zapamiętać strukturę.
Ogólny szablon polecenia jest taki:
# <taskName> to nazwa zadania Gradle, na przykład build/run/test itd.
./gradlew <taskName>
Na przykład najbardziej podstawowe zadania, które już niedługo będą u nas aktywnie używane, wyglądają tak:
# Zbuduj projekt (kompilacja, testy, pakowanie — zależy od ustawień projektu)
./gradlew build
# Uruchom aplikację (jeśli w projekcie skonfigurowane jest zadanie run)
./gradlew run
Na Windowsie zamiast ./gradlew zazwyczaj używa się gradlew.bat (i to jest właśnie nazwa pliku, a nie „coś z IDE”):
REM Na Windowsie używamy skryptu batch Wrappera z korzenia projektu
gradlew.bat build
gradlew.bat run
Skoro nawet tutaj łatwo się pogubić, oto mała tabela-przypominajka:
| System | Polecenie |
|---|---|
| macOS / Linux | |
| Windows | |
A teraz o dziwnym prefiksie ./. Oznacza on „uruchom plik z bieżącego katalogu”. W systemach uniksowych (Linux/macOS) bieżący katalog nie wchodzi w PATH domyślnie, i jest tak zrobione specjalnie ze względów bezpieczeństwa. Dlatego gradlew bez ./ często się nie uruchamia. Nie dlatego, że „masz nie ten terminal”, tylko dlatego, że tak jest zbudowany system.
Jeszcze jeden przydatny nawyk: zawsze uruchamiać polecenia z korzenia projektu — z tego katalogu, gdzie leżą gradlew, build.gradle.kts i settings.gradle.kts. Jeśli stoisz gdzieś w głębi src/main/java, Wrapper fizycznie może nie znajdować się po drodze, a polecenie nie zadziała. To nie „Gradle jest dziwny”, to po prostu stoisz nie tam.
6. Wrapper w repozytorium
Na starcie pliki Wrappera często odbierane są jako coś tymczasowego: „no przecież już pobrałem Gradle, po co przechowywać te skrypty”. I tu ważne jest złapać główną ideę: Wrapper przechowuje się w repozytorium nie dlatego, że Ci się nie chce zainstalować Gradle. Przechowuje się dlatego, że projekt musi umieć uruchomić się sam z siebie, bez wstępnych tańców.
W zespołowej pracy projekt żyje tak: ktoś klonuje repozytorium Git, otwiera README i uruchamia polecenia. Jeśli w projekcie nie ma Wrappera, pierwsze polecenie zamienia się w „najpierw zainstaluj Gradle potrzebnej wersji”. A dalej zaczynają się dopytywania: „a jaką wersję?”, „a gdzie pobrać?”, „a co jeśli mam już inną?”, „a to na pewno niczego nie zepsuje?”.
Wrapper ucina tę gałąź pytań u korzeni. Mówi: „oto wejście, oto wersja, uruchamiaj”. Właśnie dlatego pliki Wrappera muszą być scommitowane w Git razem z kodem. I właśnie dlatego wszelkie .gitignore, które przypadkiem ignorują gradle-wrapper.jar albo gradle-wrapper.properties — to dywersja przeciw powtarzalności.
Ważne uściślenie: katalog build/, który Gradle tworzy podczas budowania, to rzeczywiście generowany artefakt, jego zazwyczaj nie commitujemy. A oto gradle/wrapper/ — to nie „wynik budowania”, tylko część opisu tego, jak budować. Traktuj to jako instrukcję do projektu, która ma leżeć obok samego projektu.
7. Schemat uruchamiania Wrappera
U nowicjusza bardzo często pojawia się wewnętrzny dyskomfort: „uruchamiam ./gradlew build, i gdzieś tam coś się dzieje, ale nie rozumiem co”. I to jest normalna emocja. Nie studiujemy teraz wnętrzności Gradle, ale ważne jest zdjąć poczucie czarnej skrzynki przynajmniej na poziomie ogólnego schematu.
Oto uproszczony schemat blokowy tego, co się dzieje:
flowchart TD
A["W korzeniu projektu uruchamiasz ./gradlew build"] --> B["Skrypt gradlew / gradlew.bat"]
B --> C["Wrapper (gradle-wrapper.jar)"]
C --> D["Czyta gradle-wrapper.properties i pozna wersję Gradle"]
D --> E{"Potrzebna wersja Gradle jest już pobrana?"}
E -->|Nie| F["Pobiera dystrybucję Gradle i kładzie w lokalnym cache"]
E -->|Tak| G["Używa lokalnego cache"]
F --> H["Uruchamia Gradle potrzebnej wersji"]
G --> H["Uruchamia Gradle potrzebnej wersji"]
H --> I["Gradle wykonuje zadanie build/run według reguł projektu"]
Ten schemat ważny jest tym, że pokazuje: Wrapper nie „zastępuje Gradle”. Po prostu gwarantuje, że Gradle będzie właściwej wersji i że uruchomienie zawsze zaczyna się tak samo.
A teraz można starannie powiązać to z naszym projektem. ReadLater Starter — to repozytorium, które ma uruchamiać się jednakowo u wszystkich studentów. Dlatego wszędzie — w README, w przykładach poleceń i we własnych nawykach — będziemy używać tylko poleceń Wrappera. To stanie się „regułą gry” projektu — taką samą, jak „kod Javy leży w src/main/java”, a nie gdzie popadnie.
8. Typowe błędy przy pracy z Wrapperem
Błąd nr 1: uruchamiać gradle build zamiast ./gradlew build.
To jedna z najczęstszych pomyłek. Polecenie gradle build odwołuje się do Gradle zainstalowanego w Twoim systemie (jeśli w ogóle jest zainstalowany) i może mieć inną wersję. W efekcie tracisz czas nie na naukę i projekt, tylko na zgadywanie, dlaczego przykład z wykładu nie zgadza się z Twoim outputem w konsoli.
Błąd nr 2: usuwać albo nie commitować plików Wrappera, bo są „służbowe”.
Wydaje się logiczne „posprzątać projekt” i usunąć zbędne. Ale Wrapper to nie śmieci ani cache. To część kontraktu repozytorium: jak dokładnie ten projekt ma się uruchamiać. Jeśli w repozytorium nie ma Wrappera, druga osoba nie ma gwarantowanego sposobu na odtworzenie Twojego uruchomienia.
Błąd nr 3: mylić gradlew i gradlew.bat i próbować uruchamiać nie ten plik na swoim systemie.
Na macOS/Linux zwykle uruchamiasz ./gradlew. Na Windowsie — gradlew.bat. Próba uruchamiania „nie swojego” skryptu często kończy się dziwnymi komunikatami: terminal nie rozumie formatu pliku, prawa nie te, kodowanie nie to. Łatwiej przyjąć to za fakt: są dwa wejścia dla dwóch rodzin systemów operacyjnych.
Błąd nr 4: uruchamiać Wrapper nie z korzenia projektu.
Polecenia ./gradlew ... mają sens, gdy stoisz w katalogu, gdzie leży plik gradlew. Jeśli przypadkiem zaszedłeś do src/main/java i próbujesz uruchamiać stamtąd, dostaniesz „No such file or directory” albo podobny komunikat. To nie „Gradle nie działa”, tylko po prostu stoisz w nie tym katalogu.
Błąd nr 5: bać się długiego pierwszego uruchomienia i brać pobieranie Gradle za awarię.
Przy pierwszym uruchomieniu Wrapper rzeczywiście może pobrać dystrybucję Gradle. To normalna mechanika, a nie błąd. Jeśli internet jest, a link w distributionUrl jest poprawny, dalej wszystko będzie działać szybciej dzięki cache.
GO TO FULL VERSION