Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Детально о задачах Gradle

Anna | 2.06.2014 | нет комментариев
Перевод 2-й главы вольно распространяемой книги Building and Testing with Gradle

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

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

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

Существует примитивный метод создания задачи. Всё что необходимо — указать имя задачи:

Пример 1. Объявление задачи по одному только имени
task hello

Исполнив команду gradle hello, получаем итог:

Пример 2. Отчёт Gradle о новой задаче
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
dependencies - Displays all dependencies declared in root project '__project'.
dependencyInsight - Displays the insight into a specific dependency in root project '__project'.
help - Displays a help message
projects - Displays the sub-projects of root project '__project'.
properties - Displays the properties of root project '__project'.
tasks - Displays the tasks runnable from root project '__project'.

Other tasks
-----------
hello

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


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

Пример 3. Добавление примитивной операции

task hello << {
    println 'hello, world'
}

Операторы, такие как << («сдвиг налево» из Java), могут быть перегружены в Groovy для метаморфозы поведения в зависимости от объектов с которыми они работают. В данном случае << перегружен в Gradle для добавления блока кода в список операций, которые исполняет задача. Сдвиг налево является эквивалентом способа doLast(), тот, что мы разглядим ниже.

Сейчас у нас есть эластичная вероятность добавления кода операции аддитивным методом, ссылаясь на объект задачи, тот, что мы сотворили:

Пример 4. Последовательное добавление операций задачи по одной
task hello

hello << {
    print 'hello, '
}

hello << {
    println 'world'
}

Сейчас мы вновь получили приятель нам итог выполнения билда:

Пример 5. Итог выполнения билда с операциями, добавленными по одной
d:\project>gradle hello
:hello
hello, world

BUILD SUCCESSFUL

Total time: 1.916 secs

Данное поведение банально, впрочем оно раскрывает значимый правило: задачи не являются статическими неизменяемыми объявлениями операций билда; задачи — полнофункциональные объекты программной среды Gradle. Помимо добавления в них операций аддитивным методом в произвольных местах файла билда, у нас есть ещё и другие вероятности. Посмотрим какие.

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


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

Пример 6. Комбинирование конфигурации и операции задачи

task initializeDatabase

initializeDatabase << {
    println 'connect to database'
}

initializeDatabase << {
    println 'update database schema'
}

initializeDatabase {
    println 'configuring database connection'
}


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

Пример 7. Итог выполнения файла билда, сделанного выше

d:\project>gradle initializeDatabase
configuring database connection
:initializeDatabase
connect to database
update database schema

BUILD SUCCESSFUL

Total time: 3.088 secs

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


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

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


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

Пример 8. Добавление конфигурационных блоков

task initializeDatabase

initializeDatabase << {
    println 'connect to database'
}

initializeDatabase << {
    println 'update database schema'
}

initializeDatabase {
    print 'configuring '
}

initializeDatabase {
    println 'database connection'
}

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

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

Задачи являются объектами


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

Пример 13. Вызов способа doFirst внутри конфигурационного блока задачи

task setupDatabaseTests << {
    println 'load test data'
}

setupDatabaseTests {
    doFirst {
        println 'create schema'
    }
}


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

Пример 14. Повторные вызовы doFirst владеют свойством аддитивности

task setupDatabaseTests << {
    println 'load test data'
}

setupDatabaseTests.doFirst {
    println 'create database schema'
}

setupDatabaseTests.doFirst {
    println 'drop database schema'
}

Пример 15. Итог выполнения предыдущего примера

d:\project>gradle setupDatabaseTests
:setupDatabaseTests
drop database schema
create database schema
load test data

BUILD SUCCESSFUL

Total time: 3.126 secs


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

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

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

// Начальное определение задачи (может быть недостижимо для редактирования)
task setupDatabaseTests << {
    println 'load test data'
}

// Наши метаморфозы задачи (в файле, где мы можем вносить метаморфозы)
setupDatabaseTests {
    doFirst {
        println 'create database schema'
    }
    doFirst {
        println 'drop database schema'
    }
}


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

doLast(closure)


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

Пример 17. Применение способа doLast
task setupDatabaseTests << {
    println 'create database schema'
}

setupDatabaseTests.doLast {
    println 'load test data'
}
Пример 18. Повторные вызовы doLast аддитивны
task setupDatabaseTests << {
    println 'create database schema'
}

setupDatabaseTests.doLast {
    println 'load test data'
}

setupDatabaseTests.doLast {
    println 'update version table'
}

Как теснее говорилось ранее, в параграфе Операция задачи, оператор << является ещё одним методом вызова способа doLast().

onlyIf(closure)

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

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

Пример 19. Билд-файл, в котором применяется способ onlyIf
task createSchema << {
    println 'create database schema'
}

task loadTestData(dependsOn: createSchema) << {
    println 'load test data'
}

loadTestData.onlyIf {
    System.properties['load.data'] == 'true'
}
Пример 20. Два варианта запуска файла билда. Обратите внимание на разницу в итогах
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 к моменту заключения. Впрочем некоторые задачи, такие как CompileCopy и Delete, устанавливают значение данного свойства для передачи информации о том что их операции исполнены либо удачно, либо с ошибками. Вычисление значения, указывающего на то, что задача теснее выполнилась, специфично для различных задач. Вы можете установить значение didWork в вашей задаче для отражения итогов выполнения сделанного вами кода сборки:

Пример 21. Отправка электронного письма для случая, когда компиляция прошла удачно
apply plugin: 'java'

task emailMe(dependsOn: compileJava) << {
    if (tasks.compileJava.didWork) {
        println 'SEND E-MAIL ANNOUNCING SUCCESS'
    }
}
Пример 22. Итоги выполнения билда с испльзованием didWork
d:\project>gradle emailMe
:compileJava
:emailMe
SEND E-MAIL ANNOUNCING SUCCESS

BUILD SUCCESSFUL

Total time: 4.232 secs
enabled

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

Пример 23. Отключение задачи
task templates << {
    println 'process email templates'
}

task sendEmails(dependsOn: templates) << {
    println 'send emails'
}

sendEmails.enabled = false
Пример 24. Билд с отключенной задачей. Обратите внимание, связанность всё так же запускается
d:\project>gradle -b enabled.gradle sendEmails
:templates
process email templates
:sendEmails SKIPPED

BUILD SUCCESSFUL

Total time: 3.271 secs

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

path

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

Пример 25. Одноуровневый файл билда, тот, что отображает путь исключительной задачи определённой в нём
task echoMyPath << {
    println "THIS TASK'S PATH IS ${path}"
}
Пример 26. Итоги выполнения предыдущего файла билда
d:\project>gradle echoMyPath
:echoMyPath
THIS TASK'S PATH IS :echoMyPath

BUILD SUCCESSFUL

Total time: 3.135 secs

Двоеточие впереди указывает на то, что задача определена на верхнем ярусе файла билда. Расположение задач на верхнем ярусе, впрочем, не является непременным. 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 не будут выводиться в консольном окне.

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

task logLevel << {

    def levels = ['DEBUG', 'INFO', 'LIFECYCLE', 'QUIET', 'WARN', 'ERROR']

    levels.each { level ->
        logging.level = level
        def logMessage = "SETTING LogLevel=${level}"

        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 ' '
    }
}

Пример 28. Итог выполнения прошлого файла билда

d:\project>gradle logLevel
19:35:44.677 [LIFECYCLE] [class org.gradle.TaskExecutionLogger]
19:35:44.699 [ERROR] [org.gradle.api.Task] SETTING LogLevel=DEBU
19:35:44.732 [ERROR] [org.gradle.api.Task] ---------------------
19:35:44.747 [DEBUG] [org.gradle.api.Task] DEBUG ENABLED
19:35:44.760 [INFO] [org.gradle.api.Task] INFO ENABLED
19:35:44.775 [LIFECYCLE] [org.gradle.api.Task] LIFECYCLE ENABLED
19:35:44.788 [WARN] [org.gradle.api.Task] WARN ENABLED
19:35:44.801 [QUIET] [org.gradle.api.Task] QUIET ENABLED
19:35:44.812 [ERROR] [org.gradle.api.Task] ERROR ENABLED
19:35:44.857 [QUIET] [system.out] THIS IS println OUTPUT
19:35:44.868 [ERROR] [org.gradle.api.Task]
SETTING LogLevel=INFO
---------------------
INFO ENABLED
LIFECYCLE ENABLED
WARN ENABLED
QUIET ENABLED
ERROR ENABLED
THIS IS println OUTPUT

SETTING LogLevel=LIFECYCLE
--------------------------
LIFECYCLE ENABLED
WARN ENABLED
QUIET ENABLED
ERROR ENABLED
THIS IS println OUTPUT

SETTING LogLevel=QUIET
----------------------
QUIET ENABLED
ERROR ENABLED
THIS IS println OUTPUT

SETTING LogLevel=WARN
---------------------
WARN ENABLED
QUIET ENABLED
ERROR ENABLED
THIS IS println OUTPUT

SETTING LogLevel=ERROR
----------------------
ERROR ENABLED

BUILD SUCCESSFUL

Total time: 2.184 secs

logging


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

description

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

Пример 29. Единовременно задаём изложение задачи и поведение
task helloWorld(description: 'Says hello to the world') << {
    println 'hello, world'
}
Пример 30. Два метода объявления поведения задачи и задания изложения
task helloWorld << {
    println 'hello, world'
}

helloWorld {
    description 'Says hello to the world'
}

// Ещё один метод
helloWorld.description 'Says hello to the world'
temporaryDir

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

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

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

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

Пример 31. Билд-файл, в котором показан пример динамического свойства
task copyFiles {
    // Где желательно находим файлы, копируем их
    // (тут для наглядности используем фиксированный список)
    fileManifest = [ 'data.csv', 'config.json' ]
}

task createArtifact(dependsOn: copyFiles) << {
    println "FILES IN MANIFEST: ${copyFiles.fileManifest}"
}
Пример 32. Итог выполнения файла билда в прошлом примере
d:\project>gradle -q createArtifact
FILES IN MANIFEST: [data.csv, config.json]

Типы задач


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

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

Copy

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

Пример 33. Примитивный пример испльзования задачи Copy
task copyFiles(type: Copy) {
    from 'resources'
    into 'target'
    include '**/*.xml', '**/*.txt', '**/*.properties'
}

Jar

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

Пример 34. Примитивный пример применения задачи Jar
apply plugin: 'java'

task customJar(type: Jar) {
    manifest {
        attributes firstKey: 'firstValue', secondKey: 'secondValue'
    }
    archiveName = 'hello.jar'
    destinationDir = file("${buildDir}/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.

Помните, вы неизменно можете обнаружить документацию docs/dsl/index.html, где описаны типовые вероятности Gradle, такие как задача Jar. Изложение всех вероятностей задачи Jar лежит за рамками нашей главы.

JavaExec

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

Пример 35. Задача Gradle запускает консольное Java-приложение (пример взят из javaexec-task)
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'commons-codec:commons-codec:1.6'
    runtime 'commons-codec:commons-codec:1.6'
}

task encode(type: JavaExec, dependsOn: classes) {
    main = 'org.gradle.example.commandline.MetaphoneEncoder'
    args = "The rain in Spain falls mainly in the plain".split().toList()
    classpath sourceSets.main.output.classesDir
    classpath configurations.runtime
}
package org.gradle.example.commandline;

import org.apache.commons.codec.language.Metaphone;

public class MetaphoneEncoder {
    public static void main(String args[]) {
        Metaphone codec = new Metaphone();
        String phrase = "";
        String encoded = "";
        for (String s : args) {
            phrase  = s.toUpperCase()   " ";
            encoded  = codec.encode(s)   " ";
        }

        System.out.println("PHRASE ="   phrase);
        System.out.println("ENCODED="   encoded);
    }
}

В задаче encode свойству classpath присваивается configurations.runtimeconfigurations это коллекция зависимостей, объединённых по всеобщим знакам. В нашем случае runtime содержит те зависимости, которые обязаны быть доступны для приложения во время выполнения. Такие зависимости хороши от зависимостей, которые нужны для компиляции (compile), запуска тестов, либо зависимостей, которые необходимы для компиляции и запуска единовременно, но представлены такой средой выполнения, как сервер приложений. Качество configurations в Gradle это коллекция всех конфигураций, определённых в билде, всякая из которых в свою очередь является коллекцией реальных зависимостей.

В файле билда объявлена внешняя связанность от библиотеки Apache Commons Codec. Мы компилируем наш Java-файл, после этого формируем командную строку запуска приложения, применяя путь скомпилированного .class файла и JAR-связанность. В файле билда указываем класс, в котором запустится способ main()(org.gradle.example.commandline.MetaphoneEncoder), задаём ему параметры командной строки в форме списка, и указываем нужные элементы classpath. В данном случае мы можем условно сослаться на основные классы, доступные в sourceSets, и на все зависимости, объявленные в конфигурации runtime. Задача описанная в примере будет трудиться даже если мы определим уйма других зависимостей из различных репозиториев, включая статические зависимости в директории плана.

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

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

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

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

Пример 36. Пользовательская задача для выполнения запросов в базе данных 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 ${username} -p${password} -h ${hostname} -P ${port} ${database} -e "
    }
    else {
      cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e "
    }
    project.exec {
      commandLine = cmd.split().toList()   sql
    }
  }
}

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

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

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

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

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

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

Пример 37. Билд-файл использующий пользовательскую задачу, определённый во внешнем файле
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))'
}
Пример 38. Определение пользовательской задачи в директории 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 ${username} -p${password} -h ${hostname} -P ${port} ${database} -e "
    }
    else {
      cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e "
    }
    project.exec {
      commandLine = cmd.split().toList()   sql
    }
  }
}

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

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

Пример 39. Конструкция плана Gradle, использующего пользовательский код, помещённый в директориюbuildSrc
Источник: programmingmaster.ru

 

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB