Spis treści

Gradle - notatki

Notatki o Gradle

Co powinnam wiedzieć o Gradle? Jak stworzyć projekt z podmodułami? A może najpierw: jak stworzyć prosty projekt? Albo jak używać Gradle z Kotlinem?

Notatki nieuporządkowane

Ten wpis to zbiór bardzo luźnych notatek, trochę zbyt chaotycznych by traktować je jako pełnowartościowy wpis, uzupełnianych na bieżąco w miarę jak będę poznawać dokumentację Gradle-a.

Mogą być niepełne, a nawet niepoprawne. Jestem w trakcie budowania modelu, jak Gradle działa.

Rodzaje skryptów

Istnieją trzy typy skryptów:

  • główny skrypt budujący: jest reprezentowany przez klasę Project
  • istnieją też skrypty inicjalizacyjne - służą do przygotowania środowiska przed uruchomieniem właściwego skryptu; nie mają dostępu do buildSrc
  • oraz skrypty z ustawieniami - służące do utworzenia i skonfigurowania hierarchii projektów będących częścią buildu; reprezentowane przez klasę Settings

Gradle DSL jest opisany w Gradle Build Language Reference

Cykl życia

Wywołanie gradle ... z wiersza poleceń rozpoczyna trzy fazy w “cyklu życia” procesu budowania. Te trzy fazy to:

  • inicjalizacja (initialization)- Gradle określa, jakie projekty mają być budowane i tworzy dla każdego z nich instancję Project
  • konfiguracja (configuration)- konfigurowane są obiekty w projekcie/projektach: w tej fazie wykonywane są (uruchamiane) skrypty budujące każdego z projektów (notatka: uruchamiany jest kod wewnątrz closure każdego taska o ile nie jest on zamknięty w blok doLast czy doFirst)
  • uruchomienie (exeuction) - Gradle ustala, które taski (utworzone i skonfigurowane w fazie konfiguracji) mają być wykonane

Jak czytać kod - notatki

O tym, jak pisać skrypty w Groovy: Źródło

Właściwości, metody, bloki

Właściwości niekwalifikowane mapują się na metody w klasie Project (a wewnątrz bloku mogą oznaczać metody obiektu delegowanego). Sygnatura takiej metody określa jej parametry, a jeśli ostatni z nich jest typu closure lub Action, jest to zwykle widoczne w składni jako blok.

Właściwości mogą jednak pochodzić z różnych źródeł:

  • objekt Project wspomniany wyżej
  • właściwości extra projektu: dowolna mapa klucz-wartość
  • rozszerzenia extentions dodawane do projektu przez pluginy jako wartość read only o nazwie takiej, jak rozszerzenie
  • właściwości convention zwykle dodawane do projektu przez pluginy (mogą być do zapisu albo do odczytu) - reprezentowane przez obiekt Convention
  • zadania (tasks) zadeklarowane w skrypcie; są dostępne przez swoją nazwę - dla każdego taska tworzona jest właściwość o takiej nazwie jak nazwa taska; taka właściwość jest tylko do odczytu

Zależności dla skryptu budującego

Można je zadeklarować w bloku buildscript (w którym jest konfigurowana instancja ScriptHandler) - zostaną dodane do classpath-a bieżącego skryptu. Np.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        "classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
    }
}

tasks.register("encode") {
    doLast {
        val encodedString = Base64().encode("hello world\n".toByteArray())
        println(String(encodedString))
    }
}

W powyższym fragmencie fragment dependencies {...} odpowiada wywołaniu getDependencies() na instancji Scripthandler (zwracającemu DependencyHandler). Zależności są zgrupowane w konfiguracje - tutaj występuje konfiguracja o nazwie “classpath” .

Konfiguracja to nazwany zbiór zależności.

W ogólności, dodanie zależności wygląda tak:

1
2
3
 dependencies {
     configurationName dependencyNotation
 }

Java - pluginy

Podstawowe typy projektów tworzonych w Javie to biblioteka oraz aplikacja. Dla każdego z tych typów powstał odrębny plugin, odpowiednio: java-library oraz application, z których każdy bazuje na pluginie java.

application

Wcześniej pluginy były włączane w skrypcie budującym przez bezpośrednie wywołanie apply, jednak teraz zalecane jest użycie bloku plugins; na przykład dla aplikacji:

1
2
3
4
5
6
7
8

plugins {
    application
}

application {
    mainClass.set("org.gradle.sample.Main")
}

Uruchomienie z argumentami wiersza poleceń:

1
gradle run --args="foo --bar"

java library

Java Library Plugin - konfigurujący projekt Gradle do pracy z Javą - posiada trzy konfiguracje standardowe:

  • implementation - zależności potrzebne do skompilowania kodu produkcyjnego, ale nie będące częścią API wystawianego przez bibliotekę
  • API - zależności potrzebne do skompilowania kodu produkcyjnego, które mają być również “widoczne” dla kodu produkcyjnego
  • testImplementation - zależności do skompilowania i uruchomienia źródeł testowych.

Co z tego zrozumiałam: Dzięki wprowadzeniu tych konfiguracji do classpath-a aplikacji/biblioteki korzystającej z pewnej innej biblioteki zostaną dołączone jedynie te zależności, które są w owej pewnej bibliotece zadeklarowane jako ‘API’. “Przechodnie” zależności typu “implementation” nie “zaśmiecają” classpatha.

Zaraz. Przypomina mi to sposób deklarowania zależności w modułach Javy (requires oraz requires transitive). Jak się mają takie deklaracje do konfiguracji Gadle? Sprawdzam…

Użycie modułów

Tutaj dokumentacja dotycząca użycia modułów Javy w Gradle.

To bardzo ciekawe - moduły w javie i konfgiracje zależności w Gradle w zasadzie się pokrywają, jednak ich spójność nie jest (na razie - a więc w przyszłości może będzie?) automatycznie sprawdzana.

Deklarowanie modułu klasy głównej

Uruchamiając klasę w aplikacji wielomodułowej (w rozumieniu Java 9) należy wskazać również nazwę głównego modułu w którym znajduje się główna klasa:

1
2
3
4
application {
    mainModule.set("org.gradle.sample.app") // name defined in module-info.java
    mainClass.set("org.gradle.sample.Main")
}

Ćwiczenia

Oto garść ćwiczeń, które wymyśliłam sobie po to, żeby poznać lepiej system budowania Gradle. Przy okazji ich wykonywania będę rozszerzać sktypt(y) budujący(e) tak, aby pokryły kolejne wymagania.

  • Utworzyć skrypt dla zbudowania prostej aplikacji z testami.
  • Uruchomić aplikację.
  • Uruchomić testy.
  • Wydzielić część kodu zewnętrznej biblioteki - zdefiniować ją jako podprojekt
  • Użyć biblitekę w aplikacji - jak sprawdzić, czy zależności typu implementation użyte w bibliotece rzeczywiście nie trafiły na classpath aplikacji?
  • Przerobić aplikację i bibliotekę na moduły Javy.
  • Uruchomić aplikację modularną.
  • Uruchomić testy
  • Stworzyć dystrybucję aplikacji (jlink?).
  • Budując aplikację GUI, wygenerować splashscreen z datą oraz SHA commita, z którego pochodzi build.
  • Użyć bazy danych - innej dla testów (z innymi danymi), innej dla aplikacji. Bez Spring Boota.