Czym jest Gradle

Starając się opisać Gradle jednym zdaniem powiedziałbym, że Gradle jest narzędziem służącym do budowania projektów1. Pozwala ono na zautomatyzowanie tego procesu. Używa się do tego tak zwanego języka domenowego – DSL (ang. Domain Specific Language), który ułatwia wykonywanie standardowych zadań związanych z budowaniem projektu.

Jeśli do tej pory miałeś styczność wyłącznie z niezbyt dużymi projektami, nad którymi pracowałeś samodzielnie prawdopodobnie nie odczuwałeś potrzeby używania narzędzi tego typu. Jednak przy większych projektach narzędzie, które pozwala na zautomatyzowanie tego procesu jest bardzo pomocne.

Inne narzędzia do budowania projektów

Oczywiście Gradle nie jest jedynym narzędziem, które pomaga przy budowaniu projektów. Wymienić tu trzeba byłoby kilka innych jak Ant, Maven, Ivy, Make czy Buildr. Oczywiście nie jest to kompletna lista.

Dodatkowo problem budowania projektu występuje w każdym języku programowania, więc analogiczne narzędzia występują także dla innych języków.

Instalacja Gradle

Gradle sam w sobie jest programem, abyś mógł go używać musisz „zainstalować” go na swoim komputerze. Najnowszą wersję Gradle możesz ściągnąć z tej strony. Następnie rozpakuj ściągnięty plik, ustaw zmienną środowiskową GRADLE_HOME, która będzie wskazywała na katalog, w którym rozpakowałeś wcześniej ściągniętą paczkę.

Następnie zmodyfikuj zmienną PATH (linux/macos) lub Path (windows), tak żeby zawierała katalog bin znajdujący się wewnątrz wcześniej ustawionego GRADLE_HOME.

Tutaj znajdziesz krótki filmik, pokazujący jak zmodyfikować zmienną Path w systemie Windows, w podobny sposób możesz dodać zmienną GRADLE_HOME.

Po takim zestawie ustawień i ponownym uruchomieniu terminala powinieneś móc wywołać polecenie gradle --version, które wypisze na konsolę informacje o Twojej wersji Gradle.

Pobierz opracowania zadań z rozmów kwalifikacyjnych

Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.

W czym może pomóc Gradle

Jak wspomniałem wcześniej Gradle służy do budowania projektów. Pod pojęciem budowania projektów tak naprawdę kryje się cała masa drobnych czynności. Zaczynając od najbardziej podstawowych, takich jak kompilowanie kodu źródłowego czy tworzenie pliku ze skompilowanymi klasami, na przykład pliku JAR (ang. Java Archive)2.

Jednak to nie koniec, dobrze byłoby uruchomić wszystkie testy, które sprawdzają poprawność działania kodu przed utworzeniem pliku JAR. W tym także pomoże Ci Gradle. Gradle pomoże też przy zarządzaniu zależnościami projektu.

Zarządzanie zależnościami

Większe projekty bazują na zewnętrznych bibliotekach. Przykładem takich zewnętrznych bibliotek jest na przykład Hibernate, Spring czy Guava.

Zewnętrzne biblioteki dostępne są jako skompilowane klasy spakowane w pliki JAR. Można je ściągnąć z tak zwanych repozytoriów. Jednym z najczęściej używanych repozytoriów jest centralne repozytorium Maven’a.

Tak samo jak Twój projekt może zależeć od innych bibliotek, podobnie może być z tymi bibliotekami, też mogą mieć swoje zależności. Innymi słowy Twój projekt może mieć tak zwane zależności pośrednie/przechodnie. Jeśli Twój projekt wymaga wielu dodatkowych bibliotek, zarządzanie wszystkimi zależnościami (pośrednimi i bezpośrednimi) nie jest takim łatwym zadaniem.

Gradle pobiera zależności, które wskażesz (w odpowiedni sposób), zajmując się także zależnościami pośrednimi.

Konwencje

Programując w Javie (i nie tylko) dobrze jest stosować do pewnych przyjętych konwencji, które możemy spotkać w wielu projektach. Takie podejście pomaga w pracy nad różnymi projektami, wprowadza swego rodzaju porządek. Gradle także używa takich konwencji, poniżej opiszę dwie z nich, strukturę katalogów w projekcie i sposób identyfikowania projektu. Pomogą one w zrozumieniu podstaw DSL, które znajdą się w kolejnych akapitach.

Struktura projektu

Przy prostych projektach, nie ma potrzeby używania specjalnej struktury dla projektu. Jednak przy tych bardziej zaawansowanych pewna konwencja ułatwia zrozumienie tego co w danym projekcie się dzieje. Gdzie szukać plików z testami, w którym miejscu mogą znajdować się pliki z kodem źródłowym, gdzie może znajdować się plik JAR, który powstał po zbudowaniu projektu. To wszystko można osiągnąć, dzięki pewnej konwencji, która jest powszechnie stosowana w świecie projektów Java.

Proszę spójrz na przykład poniżej, pokazuje on strukturę katalogów w projekcie 01_witaj_swiecie, który stworzyłem na potrzeby tego artykułu (użyłem tu programu tree) do pokazania struktury katalogów):

$ tree .
.
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── pl
    │   │       └── samouczekprogramisty
    │   │           └── Hello.java
    │   └── resources
    │       └── log4j.ini
    └── test
        ├── java
        │   └── pl
        │       └── samouczekprogramisty
        │           └── HelloTest.java
        └── resources
            └── log4j.ini

Projekt ten zawiera jedną klasę Hello, znajdującą się w pakiecie pl.samouczekprogramisty i odpowiadający jest test znajdujący się w pliku HelloTest.java. Proszę zauważ, że oba te pliki znajdują się w zupełnie różnych katalogach, odpowiednio src/main/java i src/test/java. Tego typu podejście pozwala na oddzielenie kodu aplikacji od testów.

Na produkcyjnym środowisku nie potrzebujemy testów, potrzebne są wyłącznie klasy, które zapewniają poprawne działanie aplikacji. Taki podział pozwala osiągnąć ten cel w bardzo prosty sposób.

Dodatkowo w tej strukturze znajdują się także katalogi src/main/resources i src/test/resources, zawierają one odpowiednio konfigurację dla właściwej aplikacji i konfigurację dla testów.

Bezpośrednio w katalogu projektu znajduje się plik build.gradle, który zawiera informacje jak budować taki projekt.

Unikalna identyfikacja projektu

Przed powstaniem Gradle do budowania projektu używałem między innymi Maven’a. Wraz z Maven’em używanym na szerszą skalę rozpowszechniło się pewne standardowe nazewnictwo, które pozwala jednoznacznie zidentyfikować projekt. Służy do tego trójka:

  • groupId,
  • artifactId,
  • version.

Gradle także używa tej trójki, jednak pod troszkę innymi nazwami, są to odpowiednio group, name i version.

group to pierwszy identyfikator. Konwencja zakłada, że zaczynał się on będzie od odwróconej domeny, podobnie jak package w klasach. Do odwróconej domeny można dołączyć dodatkowe człony, które dokładniej specyfikują „grupę” projektu. W przypadku samouczka może to być pl.samouczekprogramisty czy pl.samouczekprogramisty.kursjava.

name to drugi identyfikator, jest on częścią finalnej nazwy pliku JAR ze skompilowanymi klasami. W przypadku projektu, z przykładami do kursu Java na Samouczku name może mieć wartość examples czy code-samples.

version określa wersję projektu. Standardowo wersję określa się przez trójkę liczb oddzielonych kropkami na przykład 1.0.0 czy 5.0.12. Dodatkowo jeśli jest to wersja deweloperska można do niej dołączyć -SNAPSHOT uzyskując 1.0.0-SNAPSHOT.

Finalnie nazwa pliku JAR ze skompilowanymi klasami będzie składała się z name i version oddzielonych minusem, na przykład code-samples-1.0.0-SNAPSHOT.jar, czy examples-5.0.12.jar.

Podstawy Gradle DSL

Gradle do działania potrzebuje konfiguracji. Domyślnie konfigurację umieszcza się w pliku build.gradle. Wewnątrz tego pliku możemy umieszczać komendy, które następnie zostaną wykonane przez gradle. Poniżej pokażę kilka podstawowych konstrukcji dostępnych w DSL dostarczonym przez Gradle.

Zadania

Gradle działa w oparciu o zadania. Wewnątrz nich definiujemy co tak naprawdę gradle powinien zrobić. Na przykład zadaniem może być utworzenie pliku JAR czy uruchomienie testów. Zadania te definiujemy wewnątrz pliku build.gradle. Poniższy przykład pokazuje prosty plik, który zawiera wyłącznie jedno zadanie buildJar, wypisujące na konsolę odpowiedni komunikat:

task buildJar {
    println 'now I am building JAR file, in theory'
}

Jeśli następnie uruchomisz gradle poleceniem gradle -q buildJar na konsoli pokaże się napis now I am building JAR file, in theory. Gratuluję, właśnie uruchomiłeś swój pierwszy plik konfigurujący budowanie projektu, co prawda niewiele on jeszcze robi, ale od czegoś trzeba zacząć :).

Zależności między zadaniami

Gradle pozwala także na wprowadzanie zależności pomiędzy zadaniami. Dzięki temu mechanizmowi możemy określić kolejność, w której zadania powinny być uruchamiane. Na przykład przed zbudowaniem pliku JAR uruchom testy jednostkowe.

Proszę spójrz na przykład poniżej, który rozbudowuje poprzedni fragment:

task runAllTests {
    println 'now I am checking if all tests are passing, in theory'
}

task buildJar(dependsOn: runAllTests) {
    println 'now I am building JAR file, in theory'
}

Uruchamiając gradle poleceniem gradle -q buildJar na konsoli pokaże się

now I am checking if all tests are passing, in theory
now I am building JAR file, in theory

Program gradle

Teraz trochę wyjaśnień, gradle -q buildJar, uruchamia zadanie buildJar zdefiniowane w pliku build.gradle. Przełącznik -q (lub --quiet) wyłącza część informacji wypisywanych na konsolę. Teraz przeanalizuję wyjście komendy z przełącznikiem --console=verbose, który pokazuje trochę więcej informacji:

$ gradle buildJar --console=verbose

> Configure project :
now I am checking if all tests are passing, in theory
now I am building JAR file, in theory

> Task :runAllTests UP-TO-DATE
> Task :buildJar UP-TO-DATE

BUILD SUCCESSFUL in 0s

Pierwsza linijka informuje o przygotowaniu gradle do pracy. Kolejne dwie to komunikaty, które znasz. Pod spodem pokazana jest informacja o stanie dwóch uruchomionych zadań. Zauważ, że Gradle uwzględnił zależności pomiędzy zadaniami – uruchomił runAllTests przed buildJar, tak jak było to zdefiniowane w build.gradle. Ostatnia linijka informują o tym, że proces budowania się powiódł i ile trwał.

Wtyczki

Gradle wspiera tak zwane wtyczki. Zawierają one zestaw gotowych zadań, które możesz uruchamiać. Przykładem takiej wtyczki jest java, która zawiera zestaw zadań przydatnych przy projektach. Wtyczki dodajemy w pliku build.gradle w następujący sposób:

apply plugin: 'java'

Można też je dodać stosując trochę inny zapis:

plugins {
    id 'java'
}

Załóżmy, że plik build.gradle zawiera wyłącznie tę linijkę. Zobacz co zostanie wypisane na konsolę po uruchomieniu gradle build --console=verbose (build jest jednym z zadań udostępnionych przez wtyczkę):

$ gradle build --console=verbose
> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE

BUILD SUCCESSFUL in 0s
1 actionable task: 1 up-to-date

Widzisz jakie zadania zostały uruchomione? Cała masa ;). Na przykład kompilowanie kodu (:compileJava), kompilowania testów (:compileTestJava), uruchomienia testów (:test) czy budowanie pliku JAR (:jar).

Także w tym przypadku widać jak Gradle rozstrzyga zależności pomiędzy zadaniami zdefiniowanymi przez wtyczkę. Mimo tego, że uruchomiłem zadanie build, wcześniej uruchomiona została seria zadań, od których zależy build.

Zależności

Wspomniałem o tym, że Gradle pomaga w zarządzaniu zależnościami – tym razem chodzi o zależności od innych projektów. Tutaj także z pomocą przychodzi wtyczka java.

Pozwala ona na określenie repozytoriów z których powinny być ściągane zależności. Na przykład poniższy fragment poinstruuje gradle aby użył domyślnego repozytorium Maven’a:

repositories {
    mavenCentral()
}

Następnie możemy już opisywać zależności jak w przykładzie poniżej:

dependencies {
    compile group: 'com.google.guava', name: 'guava', version: '27.0-jre'
}

Powyższa sekcja mówi, że nasz kod potrzebuje w trakcie kompilacji innej biblioteki. W tym przypadku jest to biblioteka guava w wersji 27.0-jre. Gradle pozwala też na troszkę krótszy zapis. Poniższy przykład da dokładnie ten sam efekt co poprzedni:

dependencies {
    compile 'com.google.guava:guava:27.0-jre'
}

Słówko compile w powyższych przykładach mówi o tym, w jakiej sytuacji będziemy potrzebowali tej zależności. Mówimy w tym przypadku o obszarze (ang. scope) gdzie dana zależność będzie używana. Na początku wystarczy abyś wiedział, że istnieje wiele takich obszarów, najczęściej używane z nich to compile i testCompile. Ten drugi określa zależności używane i dostępne wyłącznie w trakcie testów. W bardziej zaawansowanych przypadkach gradle pozwala na tworzenie swoich własnych obszarów.

Poniższy przykład pokazuje jak może wyglądać przykładowy plik build.gradle z wieloma zależnościami:

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.google.guava:guava:27.0-jre'
    testCompile 'junit:junit:4.12'
}

Program gradle pozwala na wypisane wszystkich zależności możesz to zrobić uruchamiając gradle dependencies.

Wiesz, że poznałeś właśnie nowy język?

Tak naprawdę, Gradle w pliku konfiguracyjnym do projektu używa języka skryptowego Groovy. Język domenowy, którego używa się w pliku build.gradle jest na tyle rozbudowany, że w większości przypadków nie będzie potrzeby używania języka Groovy, jednak pamiętaj o tym, kiedy będziesz chciał zrobić coś nietypowego (na przykład wysłać SMS-a do siebie jeśli testy nie będą przechodziły, chociaż jak znam życie, ktoś już napisał wtyczkę, która to robi i możesz jej użyć bazując wyłącznie na DSL udostępnionym przez tę wtyczkę). W rzeczywistości sam DSL także jest poprawnym kodem Groovy ;).

Od wersji 5.0 Gradle pozwala także na użycie języka Kotlin do pisania skryptów budowania.

Projekty Gradle w IntelliJ IDEA.

IntelliJ IDEA domyślnie wspiera projekty budowane przy pomocy Gradle. Poniżej pokazuję sposób w jaki możesz założyć przykładowy projekt. Aby to zrobić wybierz z menu File, New i następnie Project. Pokaże Ci się się następujące okienko.

Gradle nowy projekt IntelliJ IDEA

Zaznaczasz gradle i klikasz next. Kolejne okienko, to nic innego jak uzupełnienie wcześniej omówionych groupId, artifactId i version, które będą identyfikowały Twój projekt:

Gradle nowy projekt IntelliJ IDEA

Kolejny ekran to informacja dla IntelliJ IDEA jak powinien nazywać się projekt, domyślnie uzupełnia to pole wartością artifactId oraz gdzie na dysku projekt, powinien być utworzony.

Gradle nowy projekt IntelliJ IDEA

Po przejściu przez tę serię kroków IntelliJ IDEA utworzy pusty projekt wraz z plikiem build.gradle. Warto jest rzucić okiem na to co się w nim znajduje.

Plik build.gradle utworzony przez IntelliJ IDEA

Poniżej znajduje się plik build.gradle, który utworzył za mnie IntelliJ IDEA.

plugins {
    id 'java'
}

group 'pl.samouczekprogramisty'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

Pierwszy blok włącza wtyczkę do obsługi Javy.

Kolejne dwie linijki informują o grupie i wersji – dwóch komponentach pozwalających na unikalną identyfikację projektu. Atrybut name został pominięty, przyjmuje on wartość domyślną czyli nazwę katalogu, w którym znajduje się projekt.

Ta linijka wymaga trochę szerszego omówienia. Java ewoluowała, na przestrzeni lat pojawiały się kolejne wersje. Wersje te wprowadzały pewne konstrukcje językowe, które nie były dostępne wcześniej. sourceCompatibility informuje kompilator javac (poprzez przełącznik -source) jakiej wersji Javy trzeba użyć do kompilowania klas z kodem.

Kolejny blok mówi o repozytoriach, z których powinny być ściągane zależności. Jak widzisz IntelliJ IDEA domyślnie zakłada, że korzystali będziemy z centralnego repozytorium Maven’a.

Ostatni blok specyfikuje zależności, domyślnie występuje tylko jedna zależność dostępna w trakcie kompilowania testów (testCompile), jest to biblioteka JUnit w wersji 4.12.

Podsumowanie

Po przeczytaniu artykułu dowiedziałeś się podstaw o Gradle. Wiesz już czym są zadania, jak łączyć je między sobą. Utworzyłeś swój pierwszy projekt w Gradle w IntelliJ IDEA. Przeczytałeś o konwencjach stosowanych w większych projektach programistycznych.

Temat bynajmniej nie jest wyczerpany. W kolejnych artykułach, bez wstępu, któremu poświęcony jest ten artykuł skupię się na omawianiu bardziej zaawansowanych możliwości Gradle. Możesz na przykład zajrzeć do artykułu opisującego pierwszy projekt z Gradle.

Mam nadzieję, że artykuł przypadł Ci do gustu. Jeśli nie chcesz pominąć kolejnych artykułów dopisz się do mojego newslettera i polub Samouczka na Facebook’u.

Na koniec mam do Ciebie standardową prośbę, podziel się linkiem do artykułu ze znajomymi, zależy mi na dotarciu do jak największej liczby zapaleńców chcących uczyć się i doskonalić w programowaniu :). Do następnego razu!

  1. Oczywiście z racji swoje elastyczności Gradle może być użyte także w wielu innych przypadkach, jednak to budowanie projektów jest tym standardowym. 

  2. Programy, które napiszemy pakowane są w paczki, tego typu paczki używane są do uruchamiania programów w środowisku produkcyjnym. 

Pobierz opracowania zadań z rozmów kwalifikacyjnych

Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.

Kategorie:

Ostatnia aktualizacja:

Autor: Marcin Pietraszek


Nie popełnia błędów tylko ten, kto nic nie robi ;). Bardzo możliwe, że znajdziesz błąd, literówkę, coś co wymaga poprawy. Jeśli chcesz możesz samodzielnie poprawić tę stronę. Jeśli nie chcesz poprawiać błędu, który udało Ci się znaleźć będę wdzięczny jeśli go zgłosisz. Z góry dziękuję!

Zostaw komentarz