В першій частині ми говорили про таски та їх стадії життєвого циклу. Але вже після публікації я зрозумів що перед ти як ми поринемо в особливості Gradle важливо зрозуміти з чим саме ми маємо справу - зрозуміти його синтаксис та перестати боятися вигляду файлу
Якщо з Groovy почуваєтесь на ти - не бачу проблем щоб не пропустити декілька абзаців.
Є один дужу важливий нюанс в Groovy, який вам необхідно зрозуміти для повного розуміння скриптів Gradle - Closure.
По суті це блок коду, який виконуються при виклику, а не при стоворенні. Подивимось на простй приклад Closure:
Або тут closure який приймає параметр:
Або якщо closure приймає тільки один параметр, він може виглядати як
Чи коли closure приймає декілька параметрів:
Крім того, тип аргументу опціональний, далі бачимо спрощений приклад без типів:
Ще одна гарна можливість closure може посилатися на змінні з поточного контексту(читай класу). За замовчуванням, поточний контекст - це клас створений в даному closure:
Ще одна можливість в тому що поточний контекст для closure може бути змінений викликом
Як ви могли помітити, в момент коли ми створюємо closure, змінна
В цьому випадку я модифікував теперішній стан контексту для closure прямо перед тим як виконати його, тож
Раніше ми також викоритсовували closure для передачі екземпляру іншого класу. А тепер переглянемо інші методи closure:
Ви тільки подивіться! Знаючи синтаксис Groovy ми розуміємо що саме коїться в даному прикладі!
Прекрасно, але ця інформація сама по собі насправді не допомагає... Тепер треба визначити де саме це все виконується.
А відповіддю буде - Project
Спробуємо знайти метод
Якщо будемо шукати
Якщо продовжимо читати документацію по
ScriptHandler from buildscript. Це означає, що обсяг виконня для closure, який ми передаємо як вхідний параметр, буде змінений на ScriptHandler. В нашому випадку ми передаємо closure який запускає метод
І ми його знаходимо - void dependencies(Closure configureClosure), щ згідно з документацією, конфігурує залежність для скрипту. Тут ми натикаємось на ще один термін: Executes the given closure against the DependencyHandler. Що означає те ж саме що й "lделегується до [чогось]" - цей closure буде виконано в рамках іншого класу (в нашому випадку - DependencyHandler)
Для повноти, давайте подивимось, що трапиться коли ми виконаємо closure
Отже в нашому closure ми зконфігуруємо
Це означає, що якщо ви бачите щось на кшталт
Подивимось на скрипт білду Android
Якщо придивитися до скрипту build - ви побачите що перед запуском методу
Але де нам знайти документацію плагінів Android? ЇЇ можна завантажити з офіційного веб сайту Android Tools.
Ми використали властивість
Тож якщо зараз ми запустимо
На практиці цього краще не робити.
Happy gradling!
Source.
build.gradle
. І за допомогою цієї статті я спробую заповнити дану прогалину.Синтаксис
Скрипти Gradle написані мовою Groovy, тож до того як ми почнемо їх аналізувати, я хотів би зачепити (коротко) деякі ключові концепти Groovy. Синтаксис Groovy деяким чином схожий на Java, тож сподіваюсь у вас не буде великих проблем з його розумінням.Якщо з Groovy почуваєтесь на ти - не бачу проблем щоб не пропустити декілька абзаців.
Є один дужу важливий нюанс в Groovy, який вам необхідно зрозуміти для повного розуміння скриптів Gradle - Closure.
Closures
Closure - це ключовий компонент який нам необхідно зрозуміти перед освоєнням Gradle. Closure це незалежний блок коду, який може приймати аргументи, повертати значення та бути присвоєним змінній. Це такий собі мікс між інтерфейсамиCallable
, Future
, та покажчиком на функцію.По суті це блок коду, який виконуються при виклику, а не при стоворенні. Подивимось на простй приклад Closure:
def myClosure = { println 'Hello world!' }
//виелик closure
myClosure()
#output: Hello world!
Або тут closure який приймає параметр:
def myClosure = {String str -> println str }
//виклик closure
myClosure('Hello world!')
#output: Hello world!
Або якщо closure приймає тільки один параметр, він може виглядати як
it
:def myClosure = {println it }
//виклик closure
myClosure('Hello world!')
#output: Hello world!
Чи коли closure приймає декілька параметрів:
def myClosure = {String str, int num -> println "$str : $num" }
//виклик closure
myClosure('my string', 21)
#output: my string : 21
Крім того, тип аргументу опціональний, далі бачимо спрощений приклад без типів:
def myClosure = {str, num -> println "$str : $num" }
//виклик closure
myClosure('my string', 21)
#output: my string : 21
Ще одна гарна можливість closure може посилатися на змінні з поточного контексту(читай класу). За замовчуванням, поточний контекст - це клас створений в даному closure:
def myVar = 'Hello World!'
def myClosure = {println myVar}
myClosure()
#output: Hello world!
Ще одна можливість в тому що поточний контекст для closure може бути змінений викликом
Closure#setDelegate()
. Ця можливість буде дуже важливою трохи пізніше:def myClosure = {println myVar} //Зсилаємося на myVar з класу MyClass
MyClass m = new MyClass()
myClosure.setDelegate(m)
myClosure()
class MyClass {
def myVar = 'Hello from MyClass!'
}
#output: Hello from MyClass!
Як ви могли помітити, в момент коли ми створюємо closure, змінна
myVar
ще не існує. І тут це повністю нормально - вона має бути присутньою в контексті closure в момент виконання. В цьому випадку я модифікував теперішній стан контексту для closure прямо перед тим як виконати його, тож
myVar
доступна.Передача closure як аргумента
Користь від closures - в можливості передавати closure іншим методам, що допомагає нам відокремити логіку від вииконання.Раніше ми також викоритсовували closure для передачі екземпляру іншого класу. А тепер переглянемо інші методи closure:
- Методи приймає один парамет - closure
myMethod(myClosure)
- Якщо метод приймає один парамет - дужки можна не використовувати
myMethod myClosure
- Внутрішній closure
myMethod {println 'Hello World'}
- Метод приймає два параметри
myMethod(arg1, myClosure)
- Те ж що й 4
myMethod(arg1, { println 'Hello World' })
- Якщо останній параметр closure - то його можна винести
myMethod(arg1) { println 'Hello World' }
Gradle
З синтаксисом трохи ознайомилися, тепер розберемося як же, все-таки, це відноситься до скриптів Gradle? Подивимось на простий приклад скрипту Gradle і постараємось його зрозуміти:buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
Ви тільки подивіться! Знаючи синтаксис Groovy ми розуміємо що саме коїться в даному прикладі!
- Метод
buildscript
що приймає значення closure:
def buildscript(Closure closure)
- Метод
allprojects
приймає closure:
def allprojects(Closure closure)
Прекрасно, але ця інформація сама по собі насправді не допомагає... Тепер треба визначити де саме це все виконується.
А відповіддю буде - Project
Project
Це ключ для розуміння скриптів Gradle:
Всі вирази на вищому рівні в скрипті build заделеговані в екземплярі Project
Це означає, що Project - є початковою точкою.Спробуємо знайти метод
buildscript
. Якщо будемо шукати
buildscript
- ми знайдемо блок скрипту buildscript {}
. Секундочку.. А що ж це таке, чорт забирай??? Згідно з документацією: Блок скриптів це виклик методу який бере closure як параметрДобре! Знайшли! Ось що насправді трапляється, коли ми викликаємо
buildscript { ... }
- ми запускаємо метод buildscript
який приймає Closure.Якщо продовжимо читати документацію по
buildscript
то дізнаємось: Delegates to: ScriptHandler from buildscript. Це означає, що обсяг виконня для closure, який ми передаємо як вхідний параметр, буде змінений на ScriptHandler. В нашому випадку ми передаємо closure який запускає метод
repositories(Closure)
і метод dependencies(Closure)
. З того часу як closure заделегований в ScriptHandler
, спробуємо знайти метод dependencies
в класі ScriptHandler
.І ми його знаходимо - void dependencies(Closure configureClosure), щ згідно з документацією, конфігурує залежність для скрипту. Тут ми натикаємось на ще один термін: Executes the given closure against the DependencyHandler. Що означає те ж саме що й "lделегується до [чогось]" - цей closure буде виконано в рамках іншого класу (в нашому випадку - DependencyHandler)
"delegates to [something]" та "configures [something]" - 2 вирази, що мають одне значення - closure буде виконано в зазначеному класі.Gradle постійно використовує стратегію делегатів, отже важливо тут розуміти термінологію.
Для повноти, давайте подивимось, що трапиться коли ми виконаємо closure
{classpath 'com.android.tools.build:gradle:1.2.3'}
в контексті DependencyHandler
. Згідно з документацією цей клас конфігурує залежності і має виглядати як: <configurationName> <dependencyNotation1>
Отже в нашому closure ми зконфігуруємо
classpath
для використання com.android.tools.build:gradle:1.2.3
як залежності.Блоки скриптів
За замовчуванням, існують попередньо встановлені блоки вProject
, але плагіни Gradle дозволяють додавати нові блоки самостійно!Це означає, що якщо ви бачите щось на кшталт
something { ... }
на верхньому рівні вашого білд скрипту і ви не можете знайти ні блок скриптів, ні метод, який приймає closure в документації - схоже на те, що один з плагінів, які ви використовувал, додав цей блок скриптів.
android
Script block
Подивимось на скрипт білду Android app/build.gradle
за замовчуванням:apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.trickyandroid.testapp"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Як ми можемо бачити, здається, що тут має бути методи android
який приймає Closure як параметр. Але якщо ми спробуємо знайти такий метод в документації Project
то не знайдемо нічого подібного. Причина проста - такого методу просто не має :)Якщо придивитися до скрипту build - ви побачите що перед запуском методу
android
- ми застосовуємо плагін com.android.application
! Це і є відповідь на наше запитання! Android наслідує Project
з блоком скриптів android
(що є просто методом, який приймає Closure та делегує його до lкласу1 AppExtension
).Але де нам знайти документацію плагінів Android? ЇЇ можна завантажити з офіційного веб сайту Android Tools.
Якщо ми відкриємо документацію AppExtension
то знайдемо всі методи та атрибути, які необхідні.Вправа
Спробуємо самотушки щось переналаштувати, бо ми тепер знаємо як це робиться :)В AppExtension
є блок скриптів testOptions
який делегує Closure в клас TestOptions
. Перейдемо до класу TestOptions
ми бачимо дві властивості: reportDir
та resultsDir
. Згідно з документацією, reportDir
несе відповідальність за місце розташування протоколу тестування. Змінимо його.android {
......
testOptions {
reportDir "$rootDir/test_reports"
}
}
Ми використали властивість
rootDir
з класу Project
що вказує на корінь папки проекту.Тож якщо зараз ми запустимо
./gradlew connectedCheck
, звіт про тестування відправиться в папку[rootProject]/test_reports
.На практиці цього краще не робити.
Happy gradling!
Source.
Комментариев нет:
Отправить комментарий