Могут ли расширения gradle обрабатывать ленивую оценку свойства? - программирование
Подтвердить что ты не робот

Могут ли расширения gradle обрабатывать ленивую оценку свойства?

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

apply plugin: myPlugin

//Provide properties for the applied plugin
myPluginProps {
    message = "Hello"
}

//Define a task that uses my custom task directly
task thisTaskWorksFine(type: MyTask) {
    input = myPluginProps.message
}

//Define a plugin that will apply a task of my custom type
class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            input = project.myPluginProps.message
        }
    }
}

//The extension used by my custom plugin to get input
class MyPluginExtension {
    def String message
}

//The task used by both the standard build section and the plugin
class MyTask extends DefaultTask {
    def String input

    @TaskAction
    def action() {
        println "You gave me this: ${input}"
    }
}

Результаты использования этого файла следующие:

$ gradle thisTaskWorksFine thisTaskWorksIncorrectly
:thisTaskWorksFine
You gave me this: Hello
:thisTaskWorksIncorrectly
You gave me this: null

BUILD SUCCESSFUL

Я считаю это очень неожиданным. На мой взгляд, применение задачи из плагина и запись напрямую должно приводить к тому же выводу, когда ему дают один и тот же ввод. В этом случае обе задания задаются myPluginProps.message как входные данные, но задача, применяемая плагином, является жадным и сначала оценивается как null. (Во время фазы применения?)

Единственное решение, которое я нашел, - это использовать блокировки в блоке конфигурации задачи плагина следующим образом:

//Define a plugin that will apply a task of my custom type
class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            input = { project.myPluginProps.message }
        }
    }
}

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

Я использую расширения здесь неправильно? Или они просто не адекватны? Официальная позиция, по-видимому, заключается в том, что мы должны использовать расширения, но я еще не нашел примеров, когда расширения могли бы делать то, что мне нужно. Я могу продвигаться вперед с использованием закрытий и написанием кучи шаблонных геттеров, которые закрывают eval и сеттеры, которые могут обрабатывать замыкания и нормальные типы, но это кажется очень против философии groovy и, следовательно, gradle. Я был бы очень рад, если есть способ, которым я могу использовать расширения и получить ленивую оценку автоматически.

4b9b3361

Ответ 1

EDIT

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


Обычное решение этой проблемы - использовать соглашение об отображении:

class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
       project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            conventionMapping.input = { project.myPluginProps.message }
        }
    }
}

и затем в задании:

class MyTask extends DefaultTask {
    def String input

    @TaskAction
    def action() {
        println "You gave me this: ${getInput()}"
    }

}

Обратите внимание, что я явно использовал getter для input - отображение соглашения не вступит в силу, если вы ссылаетесь на поле напрямую.

Ответ 2

Ответ Peter в моем вопросе здесь указывает, что функция условного обозначения определенно исчезнет. Лучше избегать этого.

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

class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.afterEvaluate {
            project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
                input = project.myPluginProps.message
            }
        }
    }
}