Синтаксис Gradle: как это объяснить с точки зрения Groovy? - PullRequest
1 голос
/ 05 июля 2019

Мне трудно понять, как работает Gradle's Groovy DSL.

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

В моем ограниченном понимании Groovy следующий синтаксис tokenA tokenB { tokenC }, где все токены не являются ключевыми словами языка, tokenA будет методом, который мы вызываем с аргументами tokenB, а последний аргумент является закрытием. Я хотел бы думать, что я прав, но я знаю, что я неправ, потому что, вероятно, должна быть запятая после tokenB, чтобы этот анализ был правильным.

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

Я проверил несколько похожих вопросов, таких как , этот , но ответов, где я был достаточно ясен или полон, нет.

TL; DR

  1. Как токены task myTask { doLast {} } интерпретируются в Groovy?
  2. Использует ли Gradle стандартный Groovy-интерпретатор?
  3. Как myTask интерпретируется как идентификатор, если есть task, а не def или тип, стоящий за ним?
  4. Если позже в файл я добавил myTask { dependsOn myOtherTask }, как это интерпретируется?

Ответы [ 2 ]

1 голос
/ 08 июля 2019

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

  1. Если последний аргумент метода является закрытием, вы можете поставить закрытие после закрывающей скобки для аргументов метода.
class MyClass {
   void doStuff(String name, Closure c) {
      c.call()
   } 
} 

def o = new MyClass() 
o.doStuff('x') {
   println "hello" 
} 
  1. Вы можете реализовать метод, отсутствующий на вашем объекте. Если кто-то пытается вызвать метод, который не существует, вы можете сделать что-то
class MyClass {
    def methodMissing(String name, args) {
        println "You invoked ${name}(${args})" 
    }
} 
def o = new MyClass() {
   o.thisMethodDoesNotExist('foo')
}
  1. Вы можете установить делегата на закрытие
class MyBean {
   void include(String pattern) {...} 
   void exclude(String pattern) {...} 
} 
class MyClass {
   private MyBean myBean = new MyBean() 
   void doStuff(Closure c) {
      c.setDelegate(myBean)
      c.call()
   } 
} 

def o = new MyClass() 
o.doStuff {
   include 'foo' 
   exclude 'bar' 
} 

Эти 3 отличные функции в значительной степени объясняют "магическое" поведение, происходящее в скрипте gradle, когда Java-разработчики ломают голову.

Итак, давайте разберем ваш фрагмент

task myTask(type:Foo) { 
   doLast {...} 
}

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

Closure c = { 
   doLast {...} 
}
project.task(project.myTask([type: Foo.class], c)) 

Метод project.myTask(...) не существует, и поведение в конечном итоге реализуется с помощью функциональности methodMissing. Gradle установит делегата на закрытие экземпляра задачи. Таким образом, любые методы в замыкании будут делегированы вновь созданной задаче.

В конечном счете, вот что логически называется

Action<? extends Task> action = { task ->
   task.doLast {...} 
}
project.tasks.create('myTask', Foo.class, action)

См. TaskContainer.create (Строка, Класс, Действие)

1 голос
/ 06 июля 2019

(отказ от ответственности, я не отличный разработчик)

При запуске сборки (например, gradle clean) содержимое build.gradle сравнивается с объектом Project (созданным бегунком Gradle); см. Javadoc на API-Gradle Project ; также прочитайте полное резюме, поскольку оно содержит много информации. На этой странице они уточняют, что:

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

task myTask { } должно быть эквивалентно project.task('myTask'). Он создает новую задачу под названием «myTask» и добавляет задачу в текущий проект (см. Javadoc). Затем к объекту проекта добавляется свойство, так что к нему можно получить доступ как project.myTask. doLast {..} вызывает метод doLast для этого объекта задачи; см. Javadoc на Task-doLast


Итак, для некоторых из ваших очков:

  1. project.task('myTask').doLast(..) (может быть, с более подробной информацией о замыканиях здесь)
  2. делает (попробуйте собрать из github); но есть дополнительная обработка; файл build.gradle «внедряется» в экземпляр Project перед запуском сборки. Плюс много других шагов
  3. project.task('myTask')
  4. project.myTask.dependsOn(project.myOtherTask) (возможно, с дополнительным закрытием или с задействованным экземпляром Action). Это связано с тем, что задачи добавляются в проект в качестве свойств.

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

...