Что такое gradle в android studio
Перейти к содержимому

Что такое gradle в android studio

  • автор:

Gradle

Gradle — система автоматической сборки, построенная на принципах Apache Ant и Apache Maven. В Eclipse использовалась система Ant, но большинство разработчиков даже не замечало её работы. В основном возможности системы использовались в конторах для автоматизации различных задач. В Android Studio такой номер не пройдёт. Gradle сопровождает вас во время разработки постоянно. Поначалу, если вы перешли с Eclipse, Gradle сильно раздражает своими действиями. Но позже вы оцените её удобство и может даже полюбите её.

Gradle не является изобретением для Android Studio, система была разработана раньше и использовалась в приложениях для Java, Scala и других языках.

Система сборки Gradle очень мощная и сложная, чтобы о ней рассказать в двух словах. Есть целые книги о ней. Сами команды в Gradle представляют собой обычный текст с использованием синтаксиса Groove для конфигурации. Но нам не нужно знать всё. Познакомимся поближе с системой и научимся пользоваться ей.

Создайте новый проект или откройте любой существующий проект из Android Studio и посмотрите на структуру проекта.

В последних версиях студии файлы Gradle выделили в отдельную папку Gradle Script. Раскройте её. В основном вас должен интересовать файл build.gradle, который относится к модулю. Рядом с этим файлом в скобках будет написано Module: app. Двойным щелчком откройте его, вы увидите, что файл является текстовым.

Также есть файл build.gradle, который относится к проекту. Но с ним работают реже. Так находятся настройки для репозиториев и самого Gradle.

Вернёмся к файлу модуля, вы увидите много интересной информации. Например, вы там можете увидеть настройки, которые раньше вы могли видеть в манифесте — номера версий, номера SDK и так далее. Забегая вперёд, скажу, что здесь можно добавить всего одну волшебную строчку и нужная библиотека сама скачается из интернета и установится в проекте. Красота!

Однако вернёмся в корневую папку. Кроме файлов build.gradle мы можем заметить файлы gradle.properties, settings.gradle и другие. Трогать их не нужно.

В корневой папке также есть файлы gradlew и gradlew.bat для работы с Gradle Wrapper. В принципе вам не нужно знать о них ничего. Но для параноиков есть информация — если вы часто импортируете проекты из неизвестных источников, то они содержат файл gradle/wrapper/gradle-wrapper.properties. Откройте его текстовым редактором и посмотрите на адрес у distributionUrl. Путь должен вести на официальный сай //services.gradle.org или на внутренний корпоративный сервер. Другие адреса должны вызвать тревогу.

Вы могли заметить, что по сравнению с Eclipse изменилась структура файлов. В папке app находится папка src, а ней папка main, в которых папки java, res и файл манифеста. Новая структура лучше отвечает требованиям Gradle для управления файлами.

Вы, например, можете создавать альтернативные папки с ресурсами и с помощью build.gradle подключить их к проекту.

 android < compileSdkVersion 20 buildToolsVersion "20.0.0" defaultConfig < applicationId "ru.alexanderklimov.hellokitty" minSdkVersion 16 targetSdkVersion 20 versionCode 1 versionName "1.0" >sourceSets < main < res < srcDirs = [ 'src/main/res', 'src/main/presentations/animations', 'src/main/presentations/layouts'] >> > > 

В этом примере мы указали, что существуют новая папка presentations в папке /src/main/ наряду с существующими папками java и res. Внутри созданной папки есть ещё две папки layout и animations, которые содержат файлы ресурсов.

Только помните, что вам нужно избегать конфликта имён при слиянии всех файлов при сборке.

Значение sourceSets указывает Gradle, какие папки следует использовать. Этим приёмом пользуются продвинутые программисты. Мы пока не будем использовать такую технику.

Другая полезная возможность — создавать разные версии приложений, например, демо-версию и платную версию. Немного об этом рассказано здесь.

Номер версии приложения и требования к версии Android прописаны в секции defaultConfig. Если у вас сохранились старые версии приложений, то в манифесте можете удалить данные записи. По-моему, там даже выводится соответствующая подсказка. Даже если вы эти данные в манифесте не удалите, то значения из gradle.build имеют больший приоритет и перепишут значения в манифесте при не совпадении.

 defaultConfig

Подключение библиотеки происходит одной строчкой. Например, нужно добавить библиотеку Picasso:

 dependencies

В Android Studio 3.0 используется новая версия Gradle, в которой compile считается устаревшей. Вместо него следует использовать новое слово implementation.

 implementation 'com.android.support:recyclerview-v7:27.0.0' 

Есть похожая команда, которая подключает библиотеку, которая будет использоваться только для отладки приложения и в релизной версии она не нужна.

 debugCompile 'junit:junit:4.12' // старый вариант testImplementation 'junit:junit:4.12' // новый вариант для Android Studio 3.0 

Далее включаете синхронизацию и через несколько секунд в папке появляется нужная библиотека, готовая к использованию. Сама библиотека скачивается с специального хранилища-репозитория JCenter. Данный репозиторий используется по умолчанию и прописан в buil.gradle проекта.

 repositories

Можно указать другой репозиторий, например, Maven Central.

 repositories

Для поиска через Maven-репозиторий используйте The Central Repository Search Engine.

Библиотеку можно подключать и старым способом, используя jar-файл, но такой способ уходит в прошлое.

 dependencies < compile files("libs/library1.jar", "libs/library2.jar") >

Сам файл нужно скопировать в папку /libs.

При любом изменении файла недостаточно его сохранить. Нужно также произвести синхронизацию. Наверху обычно появляется жёлтая полоска с ссылкой Sync Now.

Задаём имя APK при компиляции

Можно задать собственное имя при компиляции проекта. Например, так.

 defaultConfig

Получим имя MyName-1.0.12-release.apk

Оптимизатор кода R8

Оптимизатор кода R8 имеет следующие возможности: урезание байт-кода, сжатие, обфускация, оптимизация, удаление «синтаксического сахара», преобразование в DEX. Оптимизатор может производить все операции за один шаг, что даёт сильное улучшение производительности. R8 был введён в Android Gradle plugin 3.3.0. Вам нужно только включить его.

 android < buildTypes < release < minifyEnabled true > > > 

R8 разработан для работы с существующими ProGuard-правилами, хотя возможны ситуации, когда нужно переработать правила.

 #отключение R8 только для Android Library модулей android.enableR8.libraries = false

#отключение R8 для всех модулей android.enableR8 = false

Сжимаем итоговый APK

В Gradle 1.4 появилась возможность сжать итоговый файл, убрав неиспользуемые ресурсы, в том числе из библиотек, например, Google Play Services.

 buildTypes

Во время сборки приложения вы можете увидеть строку:

 :android:shrinkDebugResources Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33% . 

Другой способ убрать неиспользуемые ресурсы конфигурации. Например, вам не нужные локализованные ресурсы для всех языков, которые входят в библиотеку Google Play Services или Android Support Library и др. Оставим только нужные языки. Возможно, вы также не хотите поддерживать mdpi или tvdpi-разрешения в своём приложении. Мы можем установить языки и разрешения, которые используются в приложении, остальные будут исключены, что позволит уменьшить вес приложения.

 // build.gradle android < defaultConfig < resConfigs "en", "ru" resConfigs "nodpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi" >> 

Можно перенести ключи из манифеста.

Чтобы их не светить, например, если выкладывать код на Гитхабе, то сделаем так.

 defaultConfig

И в манифесте переделаем код.

В большинстве случаев это сработает, но иногда ключ требуется при компиляции и указанный пример может не сработать. В таком случае применяют другой вариант через manifestPlaceholders.

 defaultConfig

В манифесте пишем.

Класс BuildConfig

В статье LogCat упоминался способ быстрого отключения журналирования.

Суть в следующем. Когда вы создаёте новые переменные в блоках defaultConfig или buildTypes (ветки debug и release), то Gradle создаёт специальный класс BuildConfig, и вы можете получить доступ к этим переменным.

Например, добавим переменную в defaultConfig

 defaultConfig

На языке Java это равносильно String YOUR_TOKEN = «ABRAKADABRA»;

Теперь мы можем обратиться к созданной строке.

 String token = BuildConfig.YOUR_TOKEN; // Что-то делаем со своей строкой 

С секцией buildType ситуация интереснее. У секции есть два блока debug и release. Можно создать переменные с разными значениями, которые будут использоваться в зависимости от ситуации. Например, у вас есть собственное API для сервера. Для тестирования вы используете один адрес, а для финальной версии — другой адрес. Тогда вы просто указываете разные адреса в разных ветках. Переменные могут быть не только строковыми.

 buildTypes < debug < buildConfigField "String", "API_URL", '"http://developer.alexanderklimov.ru/api/debug/"' buildConfigField "boolean", "REPORT_CRASHES", "true" >release < . тут какие-то другие записи buildConfigField "String", "API_URL", '"http://developer.alexanderklimov.ru/api/release/"' buildConfigField "boolean", "REPORT_CRASHES", "false" >> 

Создаём код для перехода на веб-страницу.

 Uri addressUri = Uri.parse(BuildConfig.API_URL); Intent openLinkIntent = new Intent(Intent.ACTION_VIEW, addressUri); startActivity(openLinkIntent); 

Теперь вам не нужно переписывать каждый раз код. Загружаться будет страница по нужному адресу автоматически.

Разделяем отладочную и финальную версию

По такому же принципу можно организовать запуск новой версии программы, не затрагивая программу, которую вы установили с Google Play. Допустим вы на своём телефоне установили своё собственное приложение через Google Play. Теперь вам нужно написать новую версию и проверить на своём телефоне. Из-за конфликта имён новое тестируемое приложение перепишет финальную версию или вообще не установится. Поможет следующий трюк.

 buildTypes < debug < applicationIdSuffix ".debug" versionNameSuffix "-debug" resValue "string", "app_name", "AboutCat (debug)" >release < minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' resValue "string", "app_name", "AboutCat" >> 

В специальных переменных applicationIdSuffix и versionNameSuffix мы задаём суффиксы, чтобы избежать конфликта. А в переменной resValue указываем название программы для отладочной и финальных версий, чтобы на устройстве можно было их найти. Не забудьте при этом удалить строковый ресурс app_name в res/values/strings.xml, иначе получите ошибку при компиляции. Теперь можете спокойно запускать приложение с новым кодом, не боясь повредить своё любимое приложение.

Прячем секретную информацию

Следующий совет больше подходит для компаний. Когда подписывается приложение, то нужно указывать пароль, хранилище и т.д. Чтобы их не светить в студии, можно прописать их в переменных и поместить в секцию signingConfigs. Сервер сам найдёт нужные ключи и воспользуется ими в своих сценариях.

 signingConfigs < release < storeFile "$" keyAlias "$" storePassword "$" keyPassword "$" > > 

Автогенерация версии кода

Нашёл совет, сам не применял. Не обязательно вручную менять версию приложения в атрибутах versionCode и versionName, можно сделать через переменные, а они сами подставятся в нужное место. На любителя.

 def versionMajor = 1 def versionMinor = 0 def versionPatch = 0 android < defaultConfig < versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch versionName "$.$.$" > > 

settings.gradle

Файл settings.gradle обычно состоит из одной строчки.

 include ':app' 

Это означает, что у вас используется один проект для работы. Если вы будете подключать другие проекты, то здесь появятся новые строки.

gradle.properties (Project Properties)

Несколько советов по настройке файла gradle.properties.

Режим параллельного выполнения

В этом файле можно найти закомментированную строку # org.gradle.parallel=true. Если модули вашего проекта не используют друг друга как зависимости, создавая перекрёстные ссылки, можно включать режим параллельного выполнения, что ускорит скорость сборки до ~30%.

 org.gradle.parallel=true # включаем режим параллельного выполнения 

Gradle-демон

Включение на компьютере демона Gradle даст значительный прирост в скорости сборки.

 org.gradle.daemon=true # включаем демон 

Режим конфигурации при необходимости

Если в проекте используется много модулей, то можно включить режим конфигурации при необходимости. Ускорение будет заметно при большом количестве используемых модулей:

 org.gradle.configureondemand=true 

Меняем номер версии библиотек в одном месте

Очень часто в проекте используются взаимосвязанные библиотеки с одинаковыми номерами.

 dependencies < compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:design:23.4.0' compile 'com.android.support:percent:23.4.0' compile 'com.android.support:cardview-v7:23.4.0' compile 'com.android.support:gridlayout-v7:23.4.0' //play services compile 'com.google.android.gms:play-services-location:9.2.1' compile 'com.google.android.gms:play-services-gcm:9.2.1' >

Можно быстро поменять у всех номера через переменную. Для этого используется секция ext, в которой указывается переменная и номер версии. Затем в секции dependencies номер версии заменяется на имя переменной

 ext < supportLibraryVersion = '24.0.0'; playServicesVersion = '9.2.1' >dependencies < compile "com.android.support:appcompat-v7:$supportLibraryVersion" compile "com.android.support:design:$supportLibraryVersion" compile "com.android.support:percent:$supportLibraryVersion" compile "com.android.support:cardview-v7:$supportLibraryVersion" compile "com.android.support:gridlayout-v7:$supportLibraryVersion" //play services compile "com.google.android.gms:play-services-location:$playServicesVersion" compile "com.google.android.gms:play-services-gcm:$playServicesVersion" >

Обратите внимание, что одинарные кавычки заменяются на двойные, а символ $ указывает на строковый тип.

Расширенная версия с разными переменными в другом виде.

 ext.compileSdkProjectVersion= 24 ext.buildToolsProjectVersion= '24.0.0' ext.supportLibraryVersion = '24.0.0' ext.googlePlayVersion = '8.4.0’ android < compileSdkVersion compileSdkProjectVersion buildToolsVersion buildToolsProjectVersion //… >dependencies

Если в проекте используются несколько модулей с одинаковыми зависимостями, то эти записи можно перенести в корневой build.gradle, чтобы не менять номера версий в каждом модуле.

Настройки в Android Studio

Рассмотрим настройки, доступные в Android Studio. Закройте текущий проект, чтобы увидеть стартовое окно студии. В правой части нажмите на пункт Configure. В следующем окне выберите пункт Settings, чтобы оказаться в окне настроек студии. В левой части найдите пункт Build, Execution, Deployment, затем подпункт Build Tools, далее подпункт Gradle. По умолчанию, там всё чисто, только указан путь у Service directory path. Это были общие настройки.

Теперь рассмотрим настройки, относящиеся к проекту. Запустите любой проект в Android Studio. Выберите меню File | Settings. . Снова пройдитесь по пунктам Build, Execution, Deployment | Build Tools | Gradle. Вы увидите практически такое же окно с небольшими изменениями. Теперь поле Linked Gradle Projects не будет пустым, а также появятся дополнительные настройки. По умолчанию рекомендуют использовать Use default gradle wrapper.

Gradle Task

На правой стороне Android Studio имеется вертикальная вкладка Gradle, которую можно развернуть. Она содержит список задач (task), которая выполняет Gradle при работе с текущим проектом. Вы можете выделить любую из этих задач и запустить её двойным щелчком. Можно выделить несколько задач.

Узнать debug.keystore: MD5 и SHA1

Иногда требуется узнать значения debug.keystore: MD5 и SHA1. Обычно их получают через командную строку. Но это долго и неудобно, так как нужно помнить все аргументы. Есть способ проще. Открываем вкладку Gradle, нажимаем на кнопку со стрелками Refresh all Gradle Projects. Затем последовательно открываем элементы Tasks | android и запускаем команду signingReport. В нижнем окне Run увидите нужную информацию.

 Variant: debug Config: debug Store: C:\Users\klimo_000\.android\debug.keystore Alias: AndroidDebugKey MD5: BA:6F:23:49:2D:9A:9C:0C:44:75:E0:94:59:07:E0:22 SHA1: 9D:51:F4:B8:4B:15:57:4B:EC:79:67:DC:F4:7C:5B:FB:02:C6:A2:F7 Valid until: Thursday, 1 December 2044 

Gradle Console

Когда выполняется какая-то задача Gradle, то ход её выполнения можно увидеть в окне Gradle Console. Открыть её можно через вкладку Gradle Console в нижней правой части студии.

Terminal

Запускать задачи Gradle можно и в окне Terminal.

На панели инструментов имеется значок Sync Project with Gradle Files, которую следует использовать при редактировании файлов Gradle. Как правило, студия также выводит предупреждающее сообщение с ссылкой при изменении файла, которая делает ту же работу.

Добавление зависимостей через интерфейс студии

В статье описывался способ включения библиотеки в проект через редактирование файла build.gradle. Существует альтернативный вариант через настройки студии. Щёлкните правой кнопкой мыши на имени модуля (app) и выберите пункт Open Module Settings (быстрая клавиша F4). В правой части окна находятся вкладки, которые оказывают влияние на файл build.gradle. Например, вкладка Dependencies содержит подключаемые библиотеки.

Чтобы добавить новую зависимость, нажмите на значок с плюсом и выберите нужный вариант, например, Library dependency. Откроется список доступных библиотек из репозитория Maven.

Конфигурация в Android Studio Flamingo

Иногда студия начинает дурить, ругается на несовместимость каких версий библиотек, компиляторов и прочее. Вот и в версии Flamingo проект перестал собираться после какого-то обновления.

Мои настройки, после которого студия собрала проект.

 // Project plugins < id 'com.android.application' version '8.0.1' apply false id 'com.android.library' version '8.0.1' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false >// Module compileOptions < sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 >kotlinOptions

Дополнительное чтение

В примере работы с PDF-файлами в папке assets использована операция запрета на сжатие файлов, которое происходит по умолчанию.

Задачи Gradle — теория для общего развития.

Задачи Gradle

Задача (task) является основным компонентом процесса сборки в файле build.gradle. Задачи представляют собой именованные наборы инструкций, которые Gradle запускает, выполняя сборку приложения. Задачи Gradle являются полнофункциональными объектами, которыми вы можете управлять программно.

Объявление задач

Запустите любой ваш проект в Android Studio для дальнейших опытов. Откройте файл build.gradle, который относится к модулю app.

Простой способ создания задачи — указать имя задачи. В конце открытого файла добавляем строчку:

 task hello_kitty 

После любого изменения файла синхронизуруйтесь.

Мы объявили задачу по одному только имени. На правой стороне Android Studio имеется вертикальная вкладка Gradle, которую можно развернуть. Она содержит список задач (task), которая выполняет Gradle при работе с текущим проектом. Вы можете выделить любую из этих задач и запустить её двойным щелчком. Можно выделить несколько задач.

Найдите свою задачу, она будет находиться по пути :app | Tasks | other. Задачи сортируются по алфавиту. Запустите задачу двойным щелчком.

Когда выполняется какая-то задача Gradle, то ход её выполнения можно увидеть в окне Gradle Console. Открыть её можно через вкладку Gradle Console в нижней правой части студии. После выполнения задачи вы увидите что-то типа такого:

 Executing tasks: [hello_kitty] Configuration on demand is an incubating feature. :app:hello_kitty UP-TO-DATE BUILD SUCCESSFUL Total time: 1.569 secs 

Теперь, поняв, как создаются и запускаются задачи, продолжим эксперименты.

Операция задачи (Task Action)

Выполнение задачи не произведёт никакого результата, поскольку мы не присвоили ей ни одной операции (action). Операцию можно присвоить используя оператор сдвиг влево. Перепишем пример:

 task hello_kitty

Теперь у нас есть гибкая возможность добавления кода операции аддитивным способом, ссылаясь на объект задачи, который мы создали. Пример последовательного добавления операций задачи по одной:

 task hello_kitty hello_kitty hello_kitty

Сначала мы объявили задачу, затем добавили первую операцию с методом print, следом добавили вторую операцию с методом println. Результат будет таким же.

Конфигурация задачи

Расширим предыдущий пример, добавив блок конфигурации:

 task hello_kitty hello_kitty hello_kitty hello_kitty

Запустив файл, мы получим результат, который покажется нам нелогичным:

 Настраиваем базу данных :app:hello_kitty Соединяемся с базой данных Обновляем данные 

Для обозначения блока кода между парой фигурных скобок, в Groovy используется термин «замкнутое выражение» или «замыкание» (closure). Функции-замыкания подобны объектам, которые можно передавать методу как параметр или присваивать переменной, с возможностью последующего выполнения. Они будут повсеместно встречаться вам в Gradle, поскольку в высшей степени подходят в роли блоков, где можно определить конфигурационный код и код операций билда.

Последнее замкнутое выражение выглядит как очередной блок операции билда, и мы ожидаем что вывод его сообщения будет последним, но не первым. Оказывается замыкание, добавленное к имени задачи без оператора сдвиг влево совсем не добавляет новую операцию. Вместо этого добавился блок конфигурации. Конфигурационный блок задачи выполняется во время конфигурационной фазы жизненного цикла Gradle, которая предшествует фазе выполнения, во время которой выполняются операции задачи.

Каждый раз, когда Gradle запускает билд, процесс проходит через три фазы жизненного цикла: инициализация, конфигурация и выполнение. Выполнение — фаза, во время которой задачи билда выполняются в порядке, указанном в настройках их зависимостей. Конфигурация — фаза в которой объекты задачи собираются во внутреннюю объектную модель, обычно называемую направленным ациклическим графом. Инициализация — фаза, в которой Gradle принимает решение, какие объекты будут принимать участие в билде. Последняя фаза важна в многопроектных билдах.

Замыкания конфигурации аддитивны точно так же, как и замыкания операций. Поэтому мы можем написать код файла билда для предыдущего примера способом приведенным ниже, при этом результат выполнения будет тот же, что и раньше:

 task hello_kitty hello_kitty hello_kitty hello_kitty < print 'Настраиваем ' >hello_kitty

Gradle, прежде чем выполнить билд, создаёт его внутреннюю объектную модель. Каждая задача, которую вы объявляете, в действительности, становится объектом-задачей в пределах всего проекта билда. У объекта-задачи, как и у любого другого объекта, есть свойства и методы. И ещё мы можем управлять типом каждого объекта-задачи, обращаясь к функциональности, определённой в нём.

По умолчанию, каждой новой задаче присваивается тип DefaultTask. Подобно тому, как каждый класс наследуется от Object в Java, в Gradle каждая задача наследуется от данного типа — даже те задачи, которые расширяют возможности DefaultTask путём создания нового типа. На самом деле, DefaultTask-задачи не делают ничего специфичного, вроде компиляции кода или копирования файлов. Однако они содержат функционал, который требуется для взаимодействия с программной моделью проекта Gradle. Рассмотрим методы и свойства, которые имеет каждая задача в Gradle.

Методы DefaultTask

dependsOn(task)

Для вызывающей задачи добавляет задачу-зависимость. Задача-зависимость всегда запускается перед задачей, которая от неё зависит. Метод можно вызывать несколькими способами. Пример кода ниже показывает, как мы можем определить зависимость задачи loadTestData от createSchema:

 task createSchema // Объявляем зависимость 'loadTestData' от 'createSchema' // Остальные зависимости, определённые ранее, остаются неизменными task loadTestData < dependsOn createSchema >// Альтернативный способ указания той же зависимости task loadTestData < dependsOn // Делаем то же самое, используя одиночные кавычки (которые обычно не нужны) task loadTestData < dependsOn 'createSchema' >// Явный вызов метода объекта-задачи task loadTestData loadTestData.dependsOn createSchema // Краткая нотация для определения зависимостей task loadTestData(dependsOn: createSchema) 

Задача может зависеть от нескольких задач. Если задача loadTestData зависит от задач createSchema и compileTestClasses, мы пишем код следующим образом:

 task compileTestClasses task createSchema // Объявление зависимостей по одной task loadTestData < dependsOn // Передаём зависимости, как список переменной длины task loadTestData < dependsOn compileTestClasses, createSchema >// Явно вызываем метод объекта-задачи task loadTestData loadTestData.dependsOn compileTestClasses, createSchema // Краткая нотация для определения зависимостей // Обратите внимание на синтаксис списков Groovy task loadTestData(dependsOn: [ compileTestClasses, createSchema ]) 

doFirst(closure)

Добавляет блок исполняемого кода в начало операции задачи. Во время фазы выполнения запускается блок операции каждой задачи, участвующей в билде. Метод doFirst позволяет вам добавлять части логики в начало существующей операции, даже если эта операция уже определена в файле билда или внешнем модуле (plug-in), к которому у вас нет доступа. Многократные вызовы doFirst добавляют новые блоки с кодом операций в начало последовательности выполнения задачи.

Метод doFirst можно вызывать напрямую для объекта-задачи, передавая ему замыкание, которое содержит код, который будет выполнен перед текущей операцией задачи.

Как мы уже говорили, замыкание — это блок Groovy кода, заключённый между парой фигурных скобок. Замыкание можно передавать методу как любой другой объект. Возможность передавать методам замкнутые выражения является стилевой особенностью Groovy.

 task setupDatabaseTests setupDatabaseTests.doFirst
 :setupDatabaseTests create schema load test data BUILD SUCCESSFUL 

doFirst можно также вызывать в конфигурационном блоке задачи. Как мы уже говорили, конфигурационный блок — это часть исполняемого кода, которая запускается во время конфигурационной фазы билда, перед тем как будут выполнены операции задачи. Когда мы рассматривали выше конфигурацию задач, у вас мог возникнуть вопрос: где можно использовать конфигурационные блоки? Следующий пример покажет вам, как можно вызывать методы задачи внутри конфигурационного блока, что в перспективе делает очень выразительным синтаксис формата изменения поведения задачи.

Пример метода doFirst внутри конфигурационного блока задачи

 task setupDatabaseTests setupDatabaseTests < doFirst < println 'create schema' >> 

Повторные вызовы doFirst аддитивны. Код операции каждого предыдущего вызова сохраняется, и новое замыкание добавляется в начало списка, готовое выполниться в соответствующем порядке. Например, если нам нужно настроить базу данных для интеграционного тестирования (разбив этапы настройки по частям), можно использовать следующий код:

 task setupDatabaseTests setupDatabaseTests.doFirst < println 'create database schema' >setupDatabaseTests.doFirst

Результат выполнения предыдущего примера

 :setupDatabaseTests drop database schema create database schema load test data BUILD SUCCESSFUL 

В предыдущем примере разделение инициализации на три отдельных замыкания с вызовом doFirst() было, конечно же, несколько искусственным шагом. Однако, бывают случаи, когда исходный код задачи недоступен. Например, задача определена в другом файле билда, модифицировать который невозможно или непрактично. Рассматриваемый способ программной модификации недоступной логики открывает нам широкие возможности.

До сих пор в наших примерах использовался очень простой синтаксис, который раскрывает принципы работы Gradle за счёт многократных добавлений замыканий. Вероятнее всего, в реальном билде мы организуем задачу следующим образом (всё так же, вместо настоящих тестовых операций мы используем операторы println):

Пример. Повторные вызовы doFirst после рефакторинга

 // Исходное определение задачи (может быть недоступно для редактирования) task setupDatabaseTests // Наши изменения задачи (в файле, где мы можем вносить изменения) setupDatabaseTests < doFirst < println 'create database schema' >doFirst < println 'drop database schema' >> 

Обратите внимание на то что мы собрали вместе несколько вызовов doFirst внутри одного конфигурационного блока, после того как начальная операция уже была добавлена в задачу setupDatabaseTests.

doLast(closure)

Метод doLast очень похож на метод doFirst, с той лишь разницей, что он добавляет поведение в конец операции, а не в начало. Если вам нужно запустить блок кода после того как некоторая задача закончит выполнение, вы можете поступить следующим образом:

 task setupDatabaseTests setupDatabaseTests.doLast

Повторные вызовы doLast аддитивны

 task setupDatabaseTests setupDatabaseTests.doLast < println 'load test data' >setupDatabaseTests.doLast

onlyIf(closure)

Метод onlyIf представляет собой предикат, который определяет, будет ли выполнена задача. Значением предиката считается значение, возвращаемое замыканием. При помощи данного метода вы можете деактивировать выполнение задачи, которая иначе запустится в обычном порядке запуска последовательности зависимостей билда.

В Groovy последнее выражение внутри замыкания определяет его возвращаемое значение, даже если отсутствует оператор return. Метод Groovy, в котором определено только одно выражение, является функцией, возвращающей значение этого выражения.

Пример, в котором используется метод onlyIf

 task createSchema task loadTestData(dependsOn: createSchema) loadTestData.onlyIf

Два варианта запуска файла билда. Обратите внимание на разницу в результатах

 d:\project>gradle loadTestData :createSchema create database schema :loadTestData SKIPPED BUILD SUCCESSFUL Total time: 4.361 secs d:\project>gradle -Dload.data=true loadTestData :createSchema create database schema :loadTestData load test data BUILD SUCCESSFUL Total time: 2.005 secs 

При помощи метода onlyIf вы можете включать и отключать отдельные задачи, используя логику, выражаемую Groovy-кодом, что не ограничиваться одной лишь проверкой простого свойства System, которое мы использовали в примере. У вас есть возможности открывать файлы для чтения, вызывать Веб-сервисы, проверять логины-пароли и делать многое другое, что можно делать в коде.

Свойства DefaultTask

didWork

Свойство типа boolean, указывающее, завершилась ли задача успешно. Не все задачи устанавливают значение didWork к моменту завершения. Однако некоторые задачи, такие как Compile, Copy и Delete, устанавливают значение данного свойства для передачи информации о том что их операции выполнены либо успешно, либо с ошибками. Вычисление значения, указывающего на то, что задача уже выполнилась, специфично для разных задач. Вы можете установить значение didWork в вашей задаче для отражения результатов выполнения созданного вами кода сборки:

Отправка электронного письма для случая, когда компиляция прошла успешно

 apply plugin: 'java' task emailMe(dependsOn: compileJava) > 

enabled

Свойство типа boolean, указывающее на то, будет ли выполняться задача. Вы можете отключить выполнение задачи, установив свойству enabled значение false. Зависимости задачи выполняются в том же порядке, как если бы задача не была отключена.

 task templates task sendEmails(dependsOn: templates) sendEmails.enabled = false 

path

Свойство строчного типа, содержащее полный путь задачи. По умолчанию, путём задачи является имя задачи с символом двоеточие впереди.

Пример. Одноуровневый файл билда, который отображает путь единственной задачи определённой в нём

 task echoMyPath " > 

Результат выполнения предыдущего файла билда

 d:\project>gradle echoMyPath :echoMyPath THIS TASK'S PATH IS :echoMyPath BUILD SUCCESSFUL 

Двоеточие впереди указывает на то, что задача определена на верхнем уровне файла билда. Расположение задач на верхнем уровне, однако, не является обязательным. Gradle поддерживает зависимые подпроекты, или вложенные билды. Если задача определёна во вложенном билде с названием subProject, путь будет :subProject:echoMyPath.

logger

Ссылка на внутренний объект Gradle logger. В Gradle logger реализует интерфейс org.slf4j.Logger с несколькими дополнительными уровнями логирования. Ниже описаны уровни логирования, поддерживаемые объектом logger. Установка уровню логирования одного из значений ниже включает логирование на всех последующих уровнях, кроме WARN и QUIET: DEBUG. Для подробных сообщений логирования, которые нужны разработчику билда, однако не должны выводиться в момент выполнения билда в нормальном режиме. Если выбран данный уровень, Gradle автоматически использует расширенный формат, который в каждом сообщении вставляет метку времени, уровень логирования, и имя задачи, производящей логирование. Остальные уровни используют более краткий формат сообщений. INFO. Нужен для менее информативных сообщений билда, играющих второстепенную роль во время выполнения билда. LIFECYCLE. Малоинформативные сообщения об изменениях в жизненном цикле билда и процессе выполнениия самого инструмента, запустившего сборку проекта. Обычно генерируются самим Gradle. Данный уровень используется по умолчанию, когда Gradle запускается без опции командной строки -q. Данный уровень логирования назначается сообщениям, выводимым оператором println. WARN. Малоинформативные, но важные сообщения, информирующие о потенциальных проблемах билда. Когда уровень логирования установлен в WARN, сообщения уровня QUIET не выводятся. QUIET. Сообщения, которые выводятся даже если вывод сообщений был отключен параметром командной строки -q. (Выполнение билда с параметром -q делает QUIET уровнем логирования по умолчанию). Данный уровень логирования назначается сообщениям, выводимым оператором System.out.println. Когда уровень логирования установлен в QUIET, сообщения уровня WARN не выводятся. ERROR. Редкие, но важные сообщения, кототые выводятся на всех уровнях логирования. Сообщения информируют о завершении билда с ошибками. Если ERROR — текущий уровень логирования, вызовы System.out.println не будут выводиться в консольном окне.

Задача демонстрирует эффект применения всех уровней логирования. Несколько более сложный код Groovy устанавливает уровень логирования для каждой из возможных опций, определённых в списке. Таким образом, каждый раз осуществляется вывод сообщений на каждом из уровней логирования

 task logLevel logging.level = level def logMessage = "SETTING LogLevel=$" logger.error logMessage logger.error '-' * logMessage.size() logger.debug 'DEBUG ENABLED' logger.info 'INFO ENABLED' logger.lifecycle 'LIFECYCLE ENABLED' logger.warn 'WARN ENABLED' logger.quiet 'QUIET ENABLED' logger.error 'ERROR ENABLED' println 'THIS IS println OUTPUT' logger.error ' ' > > 

logging

Свойство logging даёт нам возможность управлять уровнем логирования. Как уже было показано в примере для свойства logger, уровень логирования билда можно получать и изменять, используя свойство logging.level.

description

Свойство description описывает назначение задачи небольшим количеством метаданных, доступных для понимания человека. Значение description можно указать несколькими способами:

 task helloWorld(description: 'Says hello to the world')

Два способа объявления поведения задачи и задания описания

 task helloWorld helloWorld < description 'Says hello to the world' >// Ещё один способ helloWorld.description 'Says hello to the world' 

temporaryDir

Свойство temporaryDir возвращает объект File, который ссылается на временную директорию текущего файла билда. Такая директория создаётся для временного хранения промежуточных результатов работы задачи, или для организации файлов, которые задача будет обрабатывать.

Динамические свойства

Как мы уже видели, задачи содержат набор внутренних свойств, играющих важную роль для пользователей Gradle. В добавок к этому, мы можем также определить в задаче новые свойства. Объект-задача работает как хеш-таблица, которая может хранить любые имена свойств и значения, которые мы присваиваем свойствам (при условии, что наши имена свойств не конфликтуют с именами встроенных свойств).

Рассмотрим пример: задача createArtifact зависит от задачи copyFiles. Цель copyFiles — собрать файлы из нескольких источников и скопировать их во временную директорию, которую createArtifact в дальнейшем преобразует в артифакт установки. Список файлов звисит от параметров билда, но для соответствия специфическим требованиям установленного приложения, в артифакте должен храниться манифест, перечисляющий файлы. Здесь очень удобно использовать динамическое свойство:

Пример. Билд-файл, в котором показан пример динамического свойства

 task copyFiles < // Где угодно находим файлы, копируем их // (здесь для наглядности используем фиксированный список) fileManifest = [ 'data.csv', 'config.json' ] >task createArtifact(dependsOn: copyFiles) " > 

Типы задач

Как мы уже говорили ранее, Задачи являются объектами, каждая задача имеет тип. Кроме типа DefaultTask, есть ещё другие типы задач для копирования, архивирования, запуска программ и других действий. Объявление типа задачи во многом похоже на наследование от базового класса в объектно-ориентированном языке. Таким образом, реализовав наследование, вы тут же получаете определённые свойства и методы в вашей задаче. Подобный синтаксис значительно укорачивает определение задач, при том что возможности по-прежнему остаются большими.

Рассмотрим несколько важных типов с примерами использования.

Copy

Задача Copy копирует файлы из одного места в другое. В простейшем случае — копирует файлы из одной директории в другую, с некоторыми дополнительными ограничениями по включению или исключению файлов, используя маски имён:

Простейший пример использования задачи Copy

 task copyFiles(type: Copy) < from 'resources' into 'target' include '**/*.xml', '**/*.txt', '**/*.properties' >

Jar

Задача Jar создаёт Jar-файл из файлов ресурсов. Задача данного типа c известным названием Jar определена в модуле ‘java’. Задача упаковывает *.class-файлы и ресурсы в Jar-файл с названием проекта, при этом использует обычный манифест. Результат сохраняется в директорию build/libs. Данная задача в высокой степени обладает гибкостью.

Пример использования задачи Jar

 apply plugin: 'java' task customJar(type: Jar) < manifest < attributes firstKey: 'firstValue', secondKey: 'secondValue' >archiveName = 'hello.jar' destinationDir = file("$/jars") from sourceSets.main.output > 

Обратите внимание — имя архива и целевая папка легко конфигурируются. Таким же образом можно менять значения файла манифеста, используя простой синтаксис словарей Groovy. Содержимое JAR-файла определяется строкой from sourceSets.main.output, которая включает .class-файлы. Метод from идентичен методу, который используется в примере CopyTask, что обнаруживает одну интересную деталь: задача Jar наследуется от задачи Copy. Зная эту особенность, вы можете ещё не заглянув в документацию сделать некоторые выводы о широких возможностях и порядке структуры классов, лежащей в основе задачи Jar.

destinationDir присваивается очень простое выражение. Было бы естественнее, если бы свойству destinationDir присваивалась строка. Но свойство работает с объектами java.io.File. На помощь приходит метод file(), который всегда доступен в коде билд файла Gradle. Он конвертирует строку в объект File.

Помните, вы всегда можете найти документацию, где описаны стандартные возможности Gradle, такие как задача Jar.

JavaExec

Задача JavaExec запускает Java-класс c методом main(). Запуск консольного Java-приложения может быть сопряжён с неудобствами. Однако данная задача избавляет от неудобств, интегрируя консольные Java-приложения в ваш билд:

Пользовательские типы задач

Иногда возможностей встроенных задач Gradle может быть не достаточно для решения вашей задачи. Тогда создание пользовательской задачи будет самым выразительным способом, который можно применить при разработке вашего билда. Gradle позволяет сделать это несколькими способами. Мы рассмотрим два наиболее распространённых.

Определение пользовательского типа задачи в файле билда

Допустим, в вашем билде нужно выполнить различные запросы к базе данных MySQL. В Gradle такая задача решается несколькими способами, но вы пришли к выводу, что создание пользовательской задачи будет наиболее выразительным решением. Простейший способ создания задачи — объявить её так, как показано в примере ниже:

Пользовательская задача для выполнения запросов в базе данных MySQL (из примера custom-task)

 task createDatabase(type: MySqlTask) < sql = 'CREATE DATABASE IF NOT EXISTS example' >task createUser(type: MySqlTask, dependsOn: createDatabase) < sql = "GRANT ALL PRIVILEGES ON example.* TO exampleuser@localhost IDENTIFIED BY 'passw0rd'" >task createTable(type: MySqlTask, dependsOn: createUser) < username = 'exampleuser' password = 'passw0rd' database = 'example' sql = 'CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY, username VARCHAR(100))' >class MySqlTask extends DefaultTask < def hostname = 'localhost' def port = 3306 def sql def database def username = 'root' def password = 'password' @TaskAction def runQuery() < def cmd if(database) < cmd = "mysql -u $-p$ -h $ -P $ $ -e " > else < cmd = "mysql -u $-p$ -h $ -P $ -e " > project.exec < commandLine = cmd.split().toList() + sql >> > 

Пользовательская задача MySqlTask наследуется от класса DefaultTask. Все пользовательские задачи должны наследоваться от класса DefaultTask, либо производного от него класса. (Пользовательская задача может наследоваться и от другого типа задачи, отличного от DefaultTask. См. выше параграф Типы задач, где описаны наиболее важные встроенные типы задач.) В терминах Groovy, в задаче объявлены свойства (такие как hostname, database, sql и т.д.). Далее объявлен метод runQuery(), который помечен аннотацией @TaskAction. При выполнении задачи runQuery() запустится.

Фактические задачи билда, определённые в начале файла билда, объявлены как задачи типа MySqlTask. Таким образом, они автоматически наследуют свойства и операцию базового класса задач. Для большинства свойств определены значения по умолчанию (однако для таких свойств как username и password значения, конечно же, специфичны для билда), потому остаётся лишь небольшая часть того, что нужно сконфигурировать, прежде чем выполнить каждую из задач. Для задач createDatabase и createUser конфигурируется всего лишь один SQL-запрос, остальные же значения в дальнейшем используются по умолчанию.

Задача createTable переопределяет свойства username, password и database. Таким образом, зависимости задачи создают новую базу данных и пользователя, отличные от административных настроек по умолчанию. Паттерн, который при необходимости переопределяет настройки конфигурации по умолчанию, широко применяется В Gradle.

Определение пользовательского типа задачи в дереве исходников

Если пользовательская задача очень велика, её код может существенно усложнять файл билда. Как было показано в примере выше, задача может состоять из нескольких строк простого кода. Однако на определённом этапе задача может развиться в свою собственную иерархию классов c зависимостями от внешнего API и необходимостью применить автоматизированное тестирование. Билд является кодом, а сложный код билда нужно рассматривать, как полноправного обитателя мира разработки кода. Такая задача в Gradle решается просто.

Когда логика пользовательской задачи перерастает разумные пределы файла билда, мы можем её перенести в директорию buildSrc, которая находится в корне проекта. Директория эта автоматически компилируется и добавляется в classpath билда. Мы изменим предыдущий пример, в котором будем использовать buildSrc:

Пример. Билд-файл использующий пользовательскую задачу, определённый во внешнем файле

 task createDatabase(type: MySqlTask) < sql = 'CREATE DATABASE IF NOT EXISTS example' >task createUser(type: MySqlTask, dependsOn: createDatabase) < sql = "GRANT ALL PRIVILEGES ON example.* TO exampleuser@localhost IDENTIFIED BY 'passw0rd'" >task createTable(type: MySqlTask, dependsOn: createUser)

Пример. Определение пользовательской задачи в директории buildSrc

 import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class MySqlTask extends DefaultTask < def hostname = 'localhost' def port = 3306 def sql def database def username = 'root' def password = 'password' @TaskAction def runQuery() < def cmd if(database) < cmd = "mysql -u $-p$ -h $ -P $ $ -e " > else < cmd = "mysql -u $-p$ -h $ -P $ -e " > project.exec < commandLine = cmd.split().toList() + sql >> > 

Заметим, что определение задачи в директории buildSrc полностью совпадает с кодом, включённым в скрипт билда в позапрошлом примере. Тем не менее, теперь у нас появляется работоспособная структура проекта, пригодная для совершенствования кода простой задачи, наращивания объектной модели, написания тестов и всего остального, что мы обычно делаем разрабатывая код.

Есть четыре способа, куда вы можете поместить пользовательский билд-код Gradle. Первый — добавить код собственно, в билд-скрипт, в блок операции задачи. Второй — создать внешний файл в директории buildSrc, как было только что сделано в последнем примере. Третий способ — импортировать внешний файл с билд-скриптом в наш основной билд-скрипт. Четвёртый — импорт внешнего модуля, написанного на Java или Goovy. Создание модулей в Gradle — отдельная тема, которую мы затрагивать не будем.

Структура проекта Gradle, использующего пользовательский код, помещённый в директорию buildSrc

 . ├── build.gradle ├── buildSrc │ └── src │ └── main │ └── groovy │ └── org │ └── gradle │ └── example │ └── task │ └── MySqlTask.groovy 

Откуда берутся задачи

До настоящего момента мы создавали задачи путём непосредственного написания кода в билд-скриптах Gradle, либо в директории buildSrc в виде кода Groovy. Такой подход хорош для изучения задач, так как даёт подробный обзор всех их особенностей. Всё же, большинство задач, которые вы будете использовать, не будут написаны вами. Они будут импортироваться из внешних модулей.

В простейшем примере сборки консольного приложения HelloWorld на Java, файл билда выглядит следующим образом:

 apply plugin: 'java' 

Применив модуль Java, билд-скрипт автоматически наследует набор задач, код которых вам не виден. Вы можете изменять поведение наследованных задач в блоках конфигурации, либо используя рассмотренные выше методы doFirst() и doLast(), для которых вам придётся писать код. Ключевой стратегией Gradle являются широкие возможности для расширения при малой сложности. Gradle предлагает вам большой набор функциональности посредством задач, подробности реализации которых вам не нужно знать, которые вы запускаете используя Gradle DSL (DSL — Domain Specific Language), а не множество запутанных инструкций кода Groovy.

Кроме того, в Gradle есть несколько встроенных задач, таких как tasks и properties. Такие задачи не импортируются из модулей или вашего кода. Они являются стандартом Gradle DSL.

В Gradle задачи являются фундаментальными единицами процесса сборки. Про них можно рассказать намного больше, чем может охватить введение.

Введение в Gradle

Работая с Kotlin Multiplatform Mobile для iOS разработчика главным испытанием становится не изучение Kotlin, а изучение билд системы Gradle, которая собирает мультиплатформенную библиотеку. В данном разделе разобрано что есть Gradle с перспективы iOS разработчиков.

Gradle​

Gradle это система сборки, имеющая гибкую систему конфигурации через плагины и позволяющая описывать конфигурацию сборки в виде groovy / kotlin файлов.

Задача Gradle, как и любой системы сборки, скомпилировать исходный код в исполняемое приложение, либо подключаемую библиотеку. Благодаря ему разработчику не требуется писать команды вызова компилятора kotlin и передавать ему список исполняемых файлов, подключенных библиотек и прочее.

Также Gradle имеет управление зависимостями (подключение внешних библиотек или разных модулей одного проекта). Зависимости скачиваются с Maven репозиториев, например mavenCentral (хоть сам Maven тоже является билдсистемой, но Gradle использует от него только репозитории, на которых хранятся скомпилированные опубликованные зависимости).

Gradle написан на java и является JVM (Java Virtual Machine) приложением, то есть для его использования требуется установленная на исполняемой машине JDK (Java Development Kit). Наиболее стабильная версия JDK — Oracle JDK (рекомендуется к скачиванию Oracle JDK 11 для работы с KMM).

Gradle имеет обширную, подробную документацию, доступную тут.

Gradle Daemon​

Это долгоживущий фоновый процесс, который запускается на выбранной JVM при первой сборке. Он помогает избежать затратного процесса начальной загрузки JVM, при этом кешируя данные о ваших предыдущих билдах в память, что заметно ускоряет скорость сборки проекта.

Подробнее о Gradle Daemon и как его подключать к проекту вы можете прочитать тут и тут.

Контекст для понимания дальнейших разделов​

  1. Gradle при каждом запуске проходит по нескольким фазам — инициализация, конфигурация, выполнение.
  2. Файлы gradle могут быть написаны как на groovy (тогда расширение просто .gradle , так и на Kotlin Script .gradle.kts ). При использовании Kotlin Script IDE предоставляет полноценный анализ с подсказками, поэтому мы используем только Kotlin Script вариант.

Составляющие конфигурации проекта​

Проект, использующий Gradle в качестве системы сборки, содержит:

  1. settings.gradle / settings.gradle.kts — настройки проекта, например подключение модулей проекта;
  2. build.gradle / build.gradle.kts — конфигурация конкретного gradle модуля;
  3. gradle.properties — файл содержащий набор ключ+значение передаваемыми в gradle.

settings.gradle​

Файл с настройками всего проекта (данные настройки влияют на все модули).
Может быть написан на groovy (тогда имя settings.gradle ) либо на kotlin — settings.gradle.kts .
Подробная информация в документации .
Код в данном файле выполняется в момент инициализации проекта (при каждом запуске градл происходит по стадиям инициализация, конфигурация, выполнение).

Пример содержимого с пояснениями:

// блок pluginManagement позволяет настроить работу с плагинами билдсистемы pluginManagement  // определяем список maven репозиториев, в которых нужно искать подключаемые плагины.  // Если данный блок не объявлять то будет использоваться gradlePluginPortal - https://plugins.gradle.org/  repositories  mavenCentral()  google()  > > // данный блок позволяет настроить для всех модулей проекта работу с зависимостями dependencyResolutionManagement  // определяем список maven репозиториев, в которых нужно искать подключаемые библиотеки.  repositories  mavenCentral()  google()  > > // подключение composite build - является темой для продвинутого погружения, обычно на проектах это не встретить // если кратко - это подключение другого самостоятельного gradle проекта к сборке нашего проекта, с возможностью подключать модули подключенного проекта как внешние зависимости в нашем проекте // https://docs.gradle.org/current/userguide/composite_builds.html includeBuild("network-generator") // подключение модулей проекта, каждый из них будет определяться как gradle модуль и будет читаться его build.gradle файл // двоеточие в пути обозначает уровень иерархии в файловой структуре. include(":network") include(":sample:mpp-library") 

Является упрощенным вариантом с moko-network.

(!) Основной сценарий когда iOS разработчику нужно работать с файлом settings.gradle — разработчик сам создает новый gradle модуль и нужно подключить его к билдсистеме. То есть добавляет include(«:mymodule») .

build.gradle​

Файл с конфигурацией модуля gradle проекта. Определяет всю логику сборки данного модуля (что собираем, как собираем).
Может быть написан на groovy (тогда имя build.gradle ) либо на kotlin — build.gradle.kts .
Подробная информация в документации.

Пример содержимого с пояснениями:

// подключение плагинов, которые и содержат всю основую логику сборки plugins  // плагин для сборки android библиотек. Требуется у нас в проектах так как мы собираем из мультиплатформы android код, помимо ios  // подробнее - https://developer.android.com/studio/build/index.html  id("com.android.library")  // плагин мультиплатформы. дает возможность собирать kotlin код разными компиляторами - Kotlin/JVM, Kotlin/JS, Kotlin/Native.  // подробнее - https://kotlinlang.org/docs/mpp-dsl-reference.html  id("org.jetbrains.kotlin.multiplatform")  // наш плагин мобильной мультиплатформы, упрощает настройку градл проектов для mobile использования (android, ios)  // подробнее - https://github.com/icerockdev/mobile-multiplatform-gradle-plugin  id("dev.icerock.mobile.multiplatform")  // плагин для генерации кода сериализации в момент компиляции, от библиотеки kotlinx.serialization  // подробнее - https://github.com/Kotlin/kotlinx.serialization  id("org.jetbrains.kotlin.plugin.serialization") > // объявление зависимостей данного модуля. Чем меньше зависимостей объявлено, тем быстрее будет производиться компиляция модуля. // зависимости ищутся в репозиториях, которые могут быть указаны как в самом build.gradle, так и в settings.gradle централизованно dependencies  // подключение зависимости к common коду, в виде реализации (implementation). Это означает что классы данной зависимости не будут видны вне данного модуля, без явного ее подключения.  commonMainImplementation(Deps.Libs.MultiPlatform.coroutines)  // подключение зависимости к common коду, транзитивно (api). Это означает что классы данной зависимости будут видны вне данного модуля при подключении нашего модуля.  commonMainApi(Deps.Libs.MultiPlatform.kotlinSerialization)  commonMainApi(Deps.Libs.MultiPlatform.ktorClient)  // подключение зависимости к андроид таргету, транзитивно. Классы данной зависимости видны только в androidMain сорссете.  androidMainApi(Deps.Libs.Android.ktorClientOkHttp)  // подключение зависимости к ios таргету, транзитивно. Классы данной зависимости видны только в iosMain сорссете.  iosMainApi(Deps.Libs.Ios.ktorClientIos)  // подключение другого модуля нашего проекта, в виде реализации  commonMainImplementation(project(":network"))  // подключение зависимостей к общему коду тестов, в виде реализации.  commonTestImplementation(Deps.Libs.MultiPlatform.ktorClientMock)  commonTestImplementation(Deps.Libs.MultiPlatform.Tests.kotlinTest)  commonTestImplementation(Deps.Libs.MultiPlatform.Tests.kotlinTestAnnotations)  // подключение зависимостей к андроид таргету тестов, в виде реализации.  androidTestImplementation(Deps.Libs.Android.Tests.kotlinTestJUnit) > 

Является упрощенным вариантом с moko-network.

Основные сценарий когда iOS разработчику нужно работать с файлом build.gradle :

  1. Подключение новой зависимости к модулю
  2. Подключение плагина с дополнительным функционалом ( например moko-resources / moko-network)

gradle.properties​

Файл с опциями выполнения gradle.
Подробнее в документации.

Пример содержимого с пояснениями:

# сколько максимум оперативной памяти gradle может использовать org.gradle.jvmargs=-Xmx4096m # выключение опции "конфигурация налету", так как многомодульные проекты с ней ломаются часто org.gradle.configureondemand=false # включение параллельной сборки - разные gradle модули могут выполнять свои задачи параллельно org.gradle.parallel=true # какой вариант кодстайла котлина используеся в проекте - используется IDE для включения верного кодстайла kotlin.code.style=official # специальные флаги для активации Commonizer чтобы в iosMain видно было методы ios, а не только в iosArm64 и iosX64 # подробнее тут - https://www.youtube.com/watch?v=Q99HvynwjtY # https://kotlinlang.org/docs/migrating-multiplatform-project-to-14.html#try-the-hierarchical-project-structure kotlin.native.enableDependencyPropagation=false kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.enableCompatibilityMetadataVariant=true # использование androidX библиотек для андроида, нужно android gradle plugin android.useAndroidX=true # отключение предупреждения о том что используется ios шорткат для настройки таргетов ios mobile.multiplatform.iosTargetWarning=false # путь до xcode проекта или воркспейса, используется Kotlin Multiplatform Mobile плагином для Android Studio чтобы запускать ios приложение с отладчиком # Подробнее https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile xcodeproj=./sample/ios-app 

Является упрощенным вариантом с moko-network.

Gradle Sync

Система сборки Gradle не связана напрямую с IDE и расчитана в первую очередь на работу без UI, через консоль. Но в IDEA и Android Studio реализована полная интеграция с Gradle, позволяющая запускать команды Gradle, видеть модули Gradle и прочее. Чтобы IDE могла считать конфигурацию проекта используется импорт проекта, называется действие Gradle Sync.

gradle sync in android studio

Кнопка для запуска Gradle Sync в Android Studio:

После успешного завершения импорта проекта, через Gradle Sync, мы получаем проиндексированный проект, в котором каждый gradle модуль обработан и считаны подключенные зависимости, настройки проекта (используется ли котлин, мультиплатформа и прочее):

project panel

Также после импорта проекта в IDE доступна панель работы с Gradle — в ней можно посмотреть все Gradle модули и все задачи, которые доступны в каждом модуле:

gradle tasks panel

Самая полезная, и часто используемая для iOS разработчиков задача — скомпилировать iOS фреймворк и перенести в директорию для Cocoapods.

Зовется она syncMultiPlatformLibraryDebugFrameworkIosX64 (добавляется плагином mobile-multiplatform). Где:

  • MultiPlatformLibrary — имя фреймворка, который будет получен на выходе
  • Debug — конфигурация сборки (для разработки собираем дебаг с отладочной инфой, Release делает CI)
  • IosX64 — таргет, который должен быть собран (то есть iOS для запуска в симуляторе на x64 машине)

Для разработки используем именно Debug + IosX64, так как этот вариант имеет оптимизацию на уровне Kotlin/Native компилятора с множеством кешей. Работает быстрее всех остальных вариантов сборки фреймворка.

gradle cocoapods task

Внесение изменений в конфигурацию

Какие блоки конфигурации можно использовать в конкретном build.gradle зависит от подключенных к данному проекту плагинов. Каждый плагин может добавлять свои блоки конфигурации и свои задачи.

Например, плагин org.jetbrains.kotlin.multiplatform добавляет блок kotlin и множество задач типа compileKotlinIosX64 (если в блоке kotlin включен таргет iosX64 ).

Детальная информация о том какие настройки доступны в блоке kotlin доступна на сайте документации.

Помимо документации узнать досутпный функционал предоставляемый плагином можно используя подсказки IDEA, когда используется Gradle Kotlin DSL, вместо Groovy. В таком случае, при успешно завершенной индексации (после клика на Gradle Sync) можно использовать автозавершение кода и переход к объявлению.

gradle kotlin dsl autocomplete

Использование автодополнения (либо подождать при наборе кода, либо нажать ctrl + space )

Использование перехода к объявлению типа ( Cmd + left click ):

Gradle

Gradle — система для автоматизации сборки приложений и сбора статистики об использовании программных библиотек, применяющая языки Groovy, Java, JavaScript, Kotlin и т. д., а также решения из фреймворков Apache Ant и Apache Maven.

Освойте профессию «Java-разработчик»

Gradle написана на языках Java, Groovy и Kotlin, также поддерживает плагины на JavaScript, C++, Swift, Scala. Система распространяется как программное обеспечение с открытым исходным кодом по лицензии Apache License 2.0.

Назначение Gradle

Ручная сборка приложения для запуска на устройстве требует много времени и ресурсов. Она затягивает процесс разработки и релиза.

Чтобы ускорить его, программисты создают скрипты, автоматизирующие выполнение типовых задач. Это сокращает время сборки, но усложняет работу над командным проектом другим разработчикам.

Проблему решили системы автоматической сборки приложений. Первая — Ant — появилась в 2000 году. В 2004 году вышла Maven. Для описания сценариев сборки системы использовали язык разметки XML. Его недостаток — неконтролируемое разрастание при попытках собрать крупный многомодульный проект.

Профессия / 14 месяцев
Java-разработчик

Освойте востребованный язык

Group 1321314345 (4)

В 2006 году появилась первая версия Gradle. Она позволила решить основные проблемы:

  • реализацию больших многомодульных сборок путем последовательного и параллельного выполнения основных и добавочных модулей;
  • ускорение сборки за счет кеширования компонентов через общую сеть с использованием Gradle Build Cache;
  • управление зависимостями, то есть подключаемыми к проекту фреймворками, библиотеками и другими компонентами;
  • упрощение рабочего процесса за счет автоматизации типовых задач и веб-визуализации сборки.

Gradle использует многие решения, которые применяются в Ant и Maven. Три системы широко интегрированы друг с другом: проект, разработанный для одной из них, можно собрать в другой. Например, основным источником подключаемых внешних компонентов в Gradle является хранилище Maven.

Особенности Gradle

Использование диалекта Groove и Kotlin вместо XML

Оба объектно-ориентированных языка разработаны как дополнения к Java, используют похожий синтаксис, совместимы с теми же библиотеками и другим Java-кодом. Благодаря ему стало проще писать сценарии сборки и сравнительно быстро создавать сложные проекты.

Выполнение задач, основанное на направленных ациклических графах

В них узлы связаны не циклами, а путями, которые могут выходить из начального узла и приходить в конечный пункт различными способами. Ациклические графы оптимизируют внутреннюю объектную модель приложения и управление зависимостями, повышают гибкость сборки.

Два примера описывают выполнение одной и той же задачи:

Код на Gradle проще и меньше. Разница заметна при реализации сборок с большим числом зависимостей.

Декларативный характер сборки

Разработчик просто задает цели, желаемый конечный результат, а система сама подбирает пути его достижения.

Структурирование сборки

Благодаря использованию общих принципов проектирования Gradle позволяет создать удобный, понятный и быстро реализуемый проект.

Проработанный API

Подробный и хорошо продуманный программный интерфейс упрощает отслеживание и настройку конфигурации сборки, контроль ее исполнения.

Универсальность

Система совместима с задачами Ant, инфраструктурой репозитория (сетевого хранилища) Maven и lvy, где можно опубликовать и получить зависимые компоненты. Есть также конвертеры для превращения Maven pom.xml в скрипт Gradle.

Станьте Java-разработчиком
и создавайте сложные сервисы
на востребованном языке

Простота миграции

Gradle легко приспосабливается к любой структуре, благодаря чему можно переносить с него и на него проекты, разработанные для других систем сборок.

Открытый код

Система распространяется по свободной лицензии Apache Software License (ASL), поэтому сторонние разработчики смогли написать множество полезных плагинов, библиотек и других компонентов. Они расширяют функционал и создают полноценную Gradle-экосистему.

Gradle Wrapper

Опция разрешает реализацию сборок, созданных в Gradle, на машинах, где система не установлена. Это упрощает непрерывную интеграцию серверов.

Гибкость языка

Groove, в отличие от жестко заданных XML-иерархий Ant и Maven, дает разработчику больше вариантов действия, позволяет оптимизировать проект.

Поддержка каскадной модели

Система сама определяет, какие компоненты дерева разработки были изменены или остались прежними. И, соответственно, какие зависящие от них задачи нужно перезапустить и обновить.

Как работает Gradle

В общем виде Gradle создает текстовый файл сценария сборки (BuildScript), состоящей из следующих компонентов:

  • Project (проект). Это конечный результат работы сценария. Проект представляет собой JAR-файл, веб-приложение или zip-файл, включающий Java-архивы, созданные другими проектами. Проект может состоять как из одного модуля (одномодульный проект), так и из нескольких подмодулей (многомодульный проект). В многомодульном проекте каждый подмодуль можно рассматривать как самостоятельный Gradle-проект.
  • Task (задачи). Это набор действий с проектом. Задачи могут быть связаны друг с другом. Под сборкой проекта понимается выполнение одной или нескольких связанных задач. Проект считается успешно собранным, когда выполняется задача или набор связанных задач. Как правило, результат сборки – набор артефактов, дистрибутивов, их размещение в репозитории, установка и запуск на другом компьютере в Сети.
  • Dependency (зависимости). В ходе сборки могут быть использованы зависимости. Они бывают внешними и внутренними. Внешние — библиотеки, созданные разработчиками со всего мира и расположенные в репозиториях (repositories). Внутренние (подмодули в многомодульных проектах) — библиотеки, разработанные внутри текущего проекта и оформленные в виде подмодуля.

Описание проекта находится в файле build.gradle, настройки указываются в файле gradle.settings. Функциональность скрипта build.gradle может быть расширена с помощью служебного подпроекта buildSrc внутри проекта, а также подключаемых модулей — плагинов. Плагины подразделяются на встроенные и сторонние. Сторонние плагины можно найти на официальном сайте.

Как скачать и установить Gradle

Для получения дистрибутива нужно перейти на официальный сайт Gradle на страницу релизов и скачать последнюю версию.

Есть два варианта дистрибутива:

  • binary-only — только двоичные файлы, достаточные для работы;
  • complete — те же файлы, что и в первом варианте, плюс документация и исходный код.

Для установки достаточно распаковать содержимое zip-архива в любую доступную папку.

Чтобы запустить Gradle, нужно указать в командной строке полный путь до файла gradle (для Linux и MacOS) или gradle.bat (для Windows).

Для более простого использования можно создать переменную окружения GRADLE_HOME, указывающую путь с распакованным дистрибутивом, и добавить папку $GRADLE_HOME/bin/ (для Linux и MacOS) или %GRADLE_HOME%\bin\ (для Windows) к переменной PATH.

Java-разработчик

Java уже 20 лет в мировом топе языков программирования. На нем создают сложные финансовые сервисы, стриминги и маркетплейсы. Освойте технологии, которые нужны для backend-разработки, за 14 месяцев.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *