Извлечь общие методы из скрипта сборки Gradle

86

У меня есть скрипт сборки Gradle ( build.gradle), в котором я создал несколько задач. Эти задачи состоят в основном из вызовов методов. Вызываемые методы также есть в сценарии сборки.

Теперь вот ситуация:

Я создаю изрядное количество сценариев сборки, которые содержат разные задачи, но используют те же методы, что и исходный сценарий. Таким образом, я хотел бы каким-то образом извлечь эти «общие методы», чтобы я мог легко использовать их повторно вместо того, чтобы копировать их для каждого нового скрипта, который я создаю.

Если бы Gradle был PHP, идеальным было бы что-то вроде следующего:

//script content
...
require("common-methods.gradle");
...
//more script content

Но, конечно, это невозможно. Или это?

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

Заранее спасибо!


ОБНОВИТЬ:

Мне удалось извлечь методы в другой файл

(используя apply from: 'common-methods.gradle'),

Итак, структура выглядит следующим образом:

parent/
      /build.gradle              // The original build script
      /common-methods.gradle     // The extracted methods
      /gradle.properties         // Properties used by the build script

Выполнив задачу из build.gradle, я столкнулся с новой проблемой: очевидно, методы не распознаются, когда они находятся common-methods.gradle.

Есть идеи, как это исправить?

Питер VDE
источник
Вы уверены, что вам вообще нужно писать методы? Вы упустите некоторые из преимуществ Gradle, если будете писать сценарии сборки в терминах методов, и самое главное, чтобы инкрементная сборка работала правильно, потребуется дополнительная работа. Предполагаемая абстракция - использовать и повторно использовать Task . Вы также можете создавать собственные задачи . Возможно, вам стоит рассмотреть возможность включения реализаций, которые у вас теперь есть в методах, в задачи.
Alpar
@Alpar и другие ; какой цели служит создание чего-то вроде a timestamp()или currentWorkingDirectory()методов как task-s (например). Служебные функции и подобные вещи номинально скалярны - они не были бы задачами, за исключением того, что есть ограничения на повторное использование кода, встроенные в Gradle и большинство систем сборки. Мне нравится СУХОЙ мир, где я могу сделать вещь ОДИН раз и использовать ее повторно. Фактически, расширяя пример @Pieter VDE, я также использую root.gradleшаблон " " для моего родительского проекта - файл build.gradle обычно определяет некоторые особенности проекта, а затем просто apply ${ROOT}...
будет
Если вам нужен централизованный способ работы со свойствами, возможно, вам поможет этот вопрос: stackoverflow.com/questions/60251228/…
GarouDan

Ответы:

76

Невозможно поделиться методами, но вы можете поделиться дополнительными свойствами, содержащими замыкание, что сводится к одному и тому же. Например, объявитьext.foo = { ... } in common-methods.gradle, используйте apply from:для применения сценария, а затем вызовите закрытие с помощью foo().

Питер Нидервизер
источник
1
Это действительно помогает! Но у меня есть вопрос по этому поводу: как насчет методов, которые что-то возвращают? Fe File foo(String f)станет ext.foo = { f -> ... }, можно тогда просто сделать что-нибудь вроде File f = foo(...):?
Питер VDE
2
Видимо, возможен вопрос в моем предыдущем комментарии. Так что спасибо, Питер, за ответ на этот вопрос!
Питер VDE
1
@PeterNiederwieser Почему это невозможно? Gradle.org думает иначе: docs.gradle.org/current/userguide/…
Игорь Ганапольский
1
@IgorGanapolsky спасибо за ссылку. Интересно, как я могу использовать значение, сгенерированное в отдельном файле сборки в gradle.build - так это было бы очень полезно :)
kiedysktos
@IgorGanapolsky Как ссылка, которой вы поделились, может помочь в контексте вопроса Питера из VDE?
t0r0X 09
161

Основываясь на ответе Петра , я экспортирую свои методы следующим образом:

Содержание helpers/common-methods.gradle:

// Define methods as usual
def commonMethod1(param) {
    return true
}
def commonMethod2(param) {
    return true
}

// Export methods by turning them into closures
ext {
    commonMethod1 = this.&commonMethod1
    otherNameForMethod2 = this.&commonMethod2
}

И вот как я использую эти методы в другом скрипте:

// Use double-quotes, otherwise $ won't work
apply from: "$rootDir/helpers/common-methods.gradle"

// You can also use URLs
//apply from: "https://bitbucket.org/mb/build_scripts/raw/master/common-methods.gradle"

task myBuildTask {
    def myVar = commonMethod1("parameter1")
    otherNameForMethod2(myVar)
}

Подробнее о преобразовании методов в замыкания в Groovy.

Маттиас Браун
источник
есть ли какая-то конкретная причина использовать имя закрытия как ext?
Anoop
1
@AnoopSS Мы добавляем два замыкания к дополнительным свойствам Gradle . Эти дополнительные свойства объединены в объект с именем ext.
Маттиас Браун,
Можем ли мы каким-то образом преобразовать значение в наш класс, который определен во включенном файле?
GarouDan
Вероятно, неплохо было бы опубликовать отдельный вопрос с примером кода по этому поводу, @GarouDan.
Маттиас Браун
7

Используя dsl Kotlin, он работает так:

build.gradle.kts :

apply {
  from("external.gradle.kts")
}

val foo = extra["foo"] as () -> Unit
foo()

external.gradle.kts :

extra["foo"] = fun() {
  println("Hello world!")
}
Саймон Л
источник
1
Отлично, есть ли способ поделиться актуальным типом? вы в основном теряете безопасность типа и помощь компилятора ... если бы вы могли поделиться классом, содержащим ваши методы, вы могли бы использовать компилятор.
vach
0

Другой подход для Kotlin DSL может быть:

мой-plugin.gradle.kts

extra["sum"] = { x: Int, y: Int -> x + y }

settings.gradle.kts

@Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T

apply("my-plugin.gradle.kts")

val sum = uncheckedCast<(Int, Int) -> Int>(extra["sum"])

println(sum(1, 2))
ГаруДан
источник