원문 - Developing Custom Gradle Plugins


  • 플러그인 패키징(Packaging a plugin)
  • 간단한 플러그인 작성(Writing a simple plugin)
  • 플러그인을 구성 가능하게 만들기(Making the plugin configurable)
  • 커스텀 태스크 및 플러그인에서 파일 작업(Working with files in custom tasks and plugins)
  • 익스텐션 프로퍼티스를 태스크 프로퍼티스에 매핑(Mapping extension properties to task properties)
  • 독립형 프로젝트(A standalone project)
  • 사전 컴파일된 스크립트 플러그인(Precompiled script plugins)
  • 플러그인에 대한 테스트 작성(Writing tests for your plugin)
  • 더 자세한 내용(More details)
  • 뒷 이야기(Behind the scenes)

커스텀 그레이들 플러그인 개발(Developing Custom Gradle Plugins)

그레이들 플러그인은 다양한 프로젝트와 빌드에서 사용할 수 있는 재사용 가능한 빌드 로직 조각을 패키지화한다. 그레아들을 사용하면 자체 플러그인을 구현할 수 있으므로 빌드 로직을 재사용하고 다른 사람과 공유할 수 있다.

구현이 JVM 바이트코드로 컴파일되면 원하는 언어로 그레이들 플러그인을 구현할 수 있다. 예제에서는 독립 실행형 플러그인 프로젝트의 구현 언어로 자바를 사용하고 빌드 스크립트 플러그인 예제에서는 그루비 또는 코틀린을 사용합니다. 일반적으로 정적 타입이 지정되는 자바 또는 코틀린을 사용하여 구현된 플러그인은 그루비를 사용하여 구현된 동일한 플러그인보다 성능이 더 좋다.

플러그인 패키징(Packaging a plugin)

플러그인 소스를 넣을 수 있는 곳은 여러 군데 있다.

  • 빌드 스크립트(Build script) 빌드 스크립트에 플러그인 소스를 직접 포함할 수 있다. 이는 사용자가 아무 것도 하지 않고도 플러그인이 자동으로 컴파일되어 빌드 스크립트의 클래스패스에 포함된다는 이점이 있다. 그러나 플러그인은 빌드 스크립트 외부에 표시되지 않으므로 플러그인이 정의된 빌드 스크립트 외부에서 플러그인을 재사용할 수 없다.

  • buildSrc project 플러그인의 소스를 rootProjectDir/buildSrc/src/main/java 디렉토리(또는 선호하는 언어에 따라 rootProjectDir/buildSrc/src/main/groovy 또는 rootProjectDir/buildSrc/src/main/kotlin)에 넣을 수 있다. 그레이들은 플러그인을 컴파일 및 테스트하고 빌드 스크립트의 클래스패스에서 사용할 수 있도록 하는 작업을 담당한다. 플러그인은 빌드에서 사용되는 모든 빌드 스크립트에 표시된다. 그러나 빌드 외부에는 표시되지 않으므로 플러그인이 정의된 빌드 외부에서는 플러그인을 재사용할 수 없다.

buildSrc 프로젝트에 대한 자세한 내용은 그레이들 프로젝트 구성을 참고하자.

  • 독립형 프로젝트 플러그인에 대한 별도의 프로젝트를 생성할 수 있다. 이 프로젝트는 여러 빌드에서 사용하고 다른 사람들과 공유할 수 있는 JAR을 생성하고 게시한다. 일반적으로 이 JAR에는 일부 플러그인이 포함되거나 여러 관련 태스크 클래스가 단일 라이브러리로 묶일 수 있다. 또는 둘의 조합될 수 있다.

예제에서는 작업을 단순하게 만들기 위해 빌드 스크립트의 플러그인부터 시작한다. 그런 다음 독립형 프로젝트를 만드는 방법을 살펴보자.

간단한 플러그인 작성(Writing a simple plugin)

그레이들 플러그인을 생성하려면 Plugin 인터페이스를 구현하는 클래스를 작성해야 한다. 플러그인이 프로젝트에 적용되면 그레이들은 플러그인 클래스의 인스턴스를 생성하고 인스턴스의 Plugin.apply() 메서드를 호출한다. 프로젝트 객체는 파라미터로 전달되며, 플러그인은 필요에 따라 프로젝트를 구성하는 데 사용할 수 있다. 다음 샘플에는 프로젝트에 hello 태스크를 추가하는 인사말 플러그인이 포함되어 있다.

예제 1. 커스텀 플러그인

build.gradle

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello from the GreetingPlugin'
            }
        }
    }
}

// 플러그인 적용
apply plugin: GreetingPlugin

build.gradle.kts

class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.task("hello") {
            doLast {
                println("Hello from the GreetingPlugin")
            }
        }
    }
}

// 플러그인 적용
apply<GreetingPlugin>()

gradle -q hello의 출력

> gradle -q hello
Hello from the GreetingPlugin

한 가지 주목할 점은 플러그인이 적용되는 각 프로젝트에 대해 플러그인의 새 인스턴스가 생성된다는 것이다. 또한 Plugin 클래스는 제네릭 타입이다. 이 예제에서는 Project 타입을 타입 파라미터로 수신한다. 대신 플러그인은 세팅 스크립트에 플러그인을 적용할 수 있는 Settings 타입의 파라미터를 수신하거나, 초기화 스크립트에 플러그인을 적용할 수 있는 Gradle 타입의 파리미터를 수신할 수 있다.

플러그인을 구성 가능하게 만들기(Making the plugin configurable)

대부분의 플러그인은 플러그인 작동 방식을 커스텀하는 데 사용할 빌드 스크립트 및 기타 플러그인에 대한 일부 구성 옵션을 제공한다. 플러그인은 익스텐션(extension) 객체를 사용하여 이를 수행한다. 그레이들 Project에는 프로젝트에 적용된 플러그인에 대한 모든 세팅과 프로퍼티스가 포함된 ExtensionContainer 객체가 연결되어 있다. 이 컨테이너에 익스텐션 객체 추가하여 플러그인에 대한 구성을 제공할 수 있다. 익스텐션 객체는 단순히 구성을 나타내는 자바 빈 프로퍼티스가 있는 객체다.

프로젝트에 간단한 익스텐션 객체를 추가해 보자. 여기에서는 인사말을 구성할 수 있는 인사말 익스텐션 객체를 프로젝트에 추가한다.

Let’s add a simple extension object to the project. Here we add a greeting extension object to the project, which allows you to configure the greeting.

Example 2. A custom plugin extension

build.gradle

interface GreetingPluginExtension {
    Property<String> getMessage()
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // '인사말' 익스텐션 객체 추가
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        extension.message.convention('Hello from GreetingPlugin')
        // 익스텐션 객체의 구성을 사용하는 태스크 추가
        project.task('hello') {
            doLast {
                println extension.message.get()
            }
        }
    }
}

apply plugin: GreetingPlugin

// 익스텐션 구성
greeting.message = 'Hi from Gradle'

build.gradle.kts

interface GreetingPluginExtension {
    val message: Property<String>
}

class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        // '인사말' 익스텐션 객체 추가
        val extension = project.extensions.create<GreetingPluginExtension>("greeting")
        extension.message.convention("Hello from GreetingPlugin")
        // 익스텐션 객체의 구성을 사용하는 태스크 추가
        project.task("hello") {
            doLast {
                println(extension.message.get())
            }
        }
    }
}

apply<GreetingPlugin>()

// 익스텐션 구성
the<GreetingPluginExtension>().message.set("Hi from Gradle")

gradle -q hello의 출력

> gradle -q hello
Hi from Gradle

이 예제에서 GreetingPluginExtensionmessage라는 프로퍼티를 가진 객체다. 익스텐션 객체가 Greeting이라는 이름으로 프로젝트에 추가된다. 그러면 이 객체는 익스텐션 객체와 동일한 이름을 가진 프로젝트 프로퍼티스로 사용할 수 있게 된다.

단일 플러그인에 지정해야 하는 여러 관련 프로퍼티스가 있는 경우가 많다. 그레이들은 각 익스텐션 객체에 대한 구성 블록을 추가하므로 설정을 함께 그룹화할 수 있다. 다음 예제에서는 이것이 어떻게 작동하는지 보여준다.

예제 3. 구성 블록이 포함된 커스텀 플러그인

build.gradle

interface GreetingPluginExtension {
    Property<String> getMessage()
    Property<String> getGreeter()
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        project.task('hello') {
            doLast {
                println "${extension.message.get()} from ${extension.greeter.get()}"
            }
        }
    }
}

apply plugin: GreetingPlugin

// DSL 블록을 사용하여 익스텐션 구성
greeting {
    message = 'Hi'
    greeter = 'Gradle'
}

build.gradle.kts

interface GreetingPluginExtension {
    val message: Property<String>
    val greeter: Property<String>
}

class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val extension = project.extensions.create<GreetingPluginExtension>("greeting")
        project.task("hello") {
            doLast {
                println("${extension.message.get()} from ${extension.greeter.get()}")
            }
        }
    }
}

apply<GreetingPlugin>()

// DSL 블록을 사용하여 익스텐션 구성
configure<GreetingPluginExtension> {
    message.set("Hi")
    greeter.set("Gradle")
}

gradle -q hello의 출력

> gradle -q hello
Hi from Gradle

이 예에서는 여러 설정을 configure<GreetingPluginExtension> 블록 내에서 함께 그룹화할 수 있다. 빌드 스크립트(GreetingPluginExtension)의 구성 기능에 사용되는 타입은 익스텐션 타입과 일치해야 합니다. 그러면 블록이 실행될 때 블록의 수신자가 익스텐션이 된다.

이러한 방식으로 익스텐션 객체를 사용하면 그레이들 DSL을 확장하여 플러그인에 대한 프로젝트 프로퍼티 및 DSL 블록을 추가한다. 그리고 익스텐션 객체는 단순한 일반 객체이기 때문에 익스텐션 객체에 프로퍼티와 메서드를 추가하여 플러그인 블록 내에 중첩된 고유 DSL을 제공할 수 있다.

프로젝트 익스텐션 개발(Developing project extensions)

커스텀 그레이들 타입 개발에서 프로젝트 익스텐션 구현에 대해 자세히 알아볼 수 있다.

커스텀 태스크 및 플러그인에서 파일 작업(Working with files in custom tasks and plugins)

커스텀 태스크 및 플러그인을 개발할 때 파일 위치 입력를 입력 받을 때 매우 유연하게 대처하는 것이 좋다. 파일 또는 디렉토리 위치를 선택하려면 그레이들의 managed 프로퍼티스와 project.layout을 사용해야 한다다. 이를 통해 실제 위치는 파일이 필요할 때만 확인되며 빌드 구성 중에 언제든지 재구성될 수 있다.

예제 4. 파일 프로퍼티스 지연 평가

build.gradle

abstract class GreetingToFileTask extends DefaultTask {

    @OutputFile
    abstract RegularFileProperty getDestination()

    @TaskAction
    def greet() {
        def file = getDestination().get().asFile
        file.parentFile.mkdirs()
        file.write 'Hello!'
    }
}

def greetingFile = objects.fileProperty()

tasks.register('greet', GreetingToFileTask) {
    destination = greetingFile
}

tasks.register('sayGreeting') {
    dependsOn greet
    doLast {
        def file = greetingFile.get().asFile
        println "${file.text} (file: ${file.name})"
    }
}

greetingFile.set(layout.buildDirectory.file('hello.txt'))

build.gradle.kts

abstract class GreetingToFileTask : DefaultTask() {

    @get:OutputFile
    abstract val destination: RegularFileProperty

    @TaskAction
    fun greet() {
        val file = destination.get().asFile
        file.parentFile.mkdirs()
        file.writeText("Hello!")
    }
}

val greetingFile = objects.fileProperty()

tasks.register<GreetingToFileTask>("greet") {
    destination.set(greetingFile)
}

tasks.register("sayGreeting") {
    dependsOn("greet")
    doLast {
        val file = greetingFile.get().asFile
        println("${file.readText()} (file: ${file.name})")
    }
}

greetingFile.set(layout.buildDirectory.file("hello.txt"))

gradle -q sayGreeting의 출력

> gradle -q sayGreeting
Hello! (file: hello.txt)

이 예제에서는, greet 태스크 대상 프로퍼티를 클로저/프로바이더로 구성한다. 이는 Project.file(java.lang.Object) 메서드로 평가되어 마지막에 클로저/프로바이더의 반환 값을 File 객체로 변환한다. 분. 위의 예제에서는 태스크에 사용하도록 구성한 후 GreetingFile 프로퍼티를 지정한다는 것을 알 수 있다. 이러한 종류의 지연 평가는 파일 프로퍼티을 설정할 때 모든 값을 수락한 다음 프로퍼티를 읽을 때 해당 값을 확인하는 이점이있다.

익스텐션 프로퍼티스를 태스크 프로퍼티스에 매핑(Mapping extension properties to task properties)

익스텐션을 통해 빌드 스크립트에서 사용자 입력을 캡처하고, 이를 커스텀 태스크의 입력/출력 프로퍼티스에 매핑하는 것은 유용한 패턴이다. 빌드 스크립트 작성자는 익스텐션에 의해 정의된 DSL과만 상호 작용한다. 명령형 로직은 플러그인 구현에 숨겨져 있다.

그레이들은 이를 지원하기 위해 태스크 구현 및 익스텐션에 사용할 수 있는 몇 가지 타입을 제공한다. 자세한 내용은 지연 구성을 참고하자.

독립형 프로젝트(A standalone project)

이제 플러그인을 게시하고 다른 사람들과 공유할 수 있도록 독립 실행형 프로젝트로 이동한다. 이 프로젝트는 플러그인 클래스가 포함된 JAR을 생성하는 단순한 Java 프로젝트이다. 플러그인을 패키징하고 게시하는 가장 쉽고 권장되는 방법은 자바 그레이들 플러그인 개발 플러그인을 사용하는 것이다. 이 플러그인은 자동으로 자바 플러그인을 적용하고, API 구성에 gradleApi() 의존성을 추가하고, 결과 JAR 파일에 필수 플러그인 디스크립터(descriptors)를 생성하고, 게시 시 사용할 플러그인 마커 아티팩트를 구성한다. 다음은 프로젝트의 간단한 빌드 스크립트이다.

예제 5. 커스텀 플러그인을 위한 빌드

build.gradle

plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        simplePlugin {
            id = 'org.example.greeting'
            implementationClass = 'org.example.GreetingPlugin'
        }
    }
}

build.gradle.kts

plugins {
    `java-gradle-plugin`
}

gradlePlugin {
    plugins {
        create("simplePlugin") {
            id = "org.example.greeting"
            implementationClass = "org.example.GreetingPlugin"
        }
    }
}

플러그인 ID 생성(Creating a plugin id)

플러그인 ID는 자바 패키지와 유사한 방식으로 정규화된다(예: 역방향 도메인 이름). 이는 충돌을 방지하는 데 도움이 되며 유사한 소유권을 가진 플러그인을 그룹화하는 방법을 제공한다.

플러그인 ID는 네임스페이스(조직에 대한 합리적인 포인터)와 그것이 제공하는 플러그인명을 반영하는 컴포넌트의 조합이어야 한다. 예를 들어 “foo”라는 깃헙 계정이 있고 플러그인 이름이 “bar”인 경우 적합한 플러그인 ID는 com.github.foo.bar일 수 있다. 마찬가지로, 플러그인이 baz 조직에서 개발된 경우 플러그인 ID는 org.baz.bar일 수 있다.

플러그인 ID는 다음을 준수해야 한다.

  • 영숫자 문자, ‘.’ 및 ‘-‘를 포함할 수 있다.
  • 하나 이상의 ‘.’를 포함해야 한다. 플러그인명과 네임스페이스를 구분하는 문자다.
  • 일반적으로 네임스페이스에는 소문자 역방향 도메인 이름 규칙을 사용한다.
  • 일반적으로 이름에는 소문자만 사용한다.
  • org.gradlecom.gradleware 네임스페이스는 사용할 수 없다. ‘.’문자로 시작하거나 끝날 수 없다.
  • 연속된 ‘.’문자를 포함할 수 없다(예: ‘..’).

플러그인 ID와 패키지 이름 사이에는 일반적인 유사점이 있지만 일반적으로 패키지 이름은 플러그인 ID에 필요한 것보다 더 자세하다. 예를 들어 플러그인 ID의 컴포넌트로 “gradle”을 추가하는 것이 합리적으로 보일 수 있지만 플러그인 ID는 그레이들 플러그인에만 사용되므로 이는 불필요하다. 일반적으로 좋은 플러그인 ID에는 소유권을 식별하는 네임스페이스와 이름만 있으면 된다.

플러그인 게시(Publishing your plugin)

조직 내에서 사용하기 위해 플러그인을 내부적으로 게시하는 경우 다른 코드 아티팩트처럼 게시할 수 있다. 아티팩트 게시에 대한 아이비 및 메이븐 장을 참고하자.

더 넓은 그레이들 커뮤니티에서 사용할 플러그인을 게시하는 데 관심이 있는 경우 그레이들 플러그인 포털에 게시할 수 있다. 이 사이트는 그레이들 커뮤니티에서 제공하는 플러그인에 대한 정보를 검색하고 수집하는 기능을 제공한다. 이 사이트에서 플러그인을 사용 가능하게 만드는 방법에 대한 해당 장을 참고하자.

다른 프로젝트에서 플러그인 사용하기(Using your plugin in another project)

빌드 스크립트에서 플러그인을 사용하려면, 프로젝트 세팅 파일의 PluginManagement {} 블록에서 리포지터리를 구성해야 한다. 다음 예제에서는 플러그인이 로컬 리포지터리에 게시되었을 때 이를 수행할 수 있는 방법을 보여준다.

예제 6. 다른 프로젝트에서 커스텀 플러그인 사용하기

그루비 *** settings.gradle

pluginManagement {
    repositories {
        maven {
            url = uri(repoLocation)
        }
    }
}

build.gradle

plugins {
    id 'org.example.greeting' version '1.0-SNAPSHOT'
}

코틀린 *** settings.gradle.kts

pluginManagement {
    repositories {
        maven {
            url = uri(repoLocation)
        }
    }
}

build.gradle.kts

plugins {
    id("org.example.greeting") version "1.0-SNAPSHOT"
}

java-gradle-plugin 없이 게시된 플러그인에 대한 참고 사항(Note for plugins published without java-gradle-plugin)

자바 그레이들 플러그인 개발 플러그인을 사용하지 않고 플러그인을 게시한 경우, 플러그인 DSL이 플러그인을 찾는 데 필요한 플러그인 마커 아티팩트가 게시에 없다. 이런 경우, 다른 프로젝트의 플러그인 문제를 해결하기 위해 권장되는 방법은 아래와 같이 프로젝트 설정 파일의 PluginManagement {} 블록에 ResolutionStrategy을 추가하는 것이다.

예제 7. 플러그인 마커 아티팩트가 없는 플러그인에 대한 해결 전략

settings.gradle

resolutionStrategy {
    eachPlugin {
        if (requested.id.namespace == 'org.example') {
            useModule("org.example:custom-plugin:${requested.version}")
        }
    }
}

settings.gradle.kts

resolutionStrategy {
    eachPlugin {
        if (requested.id.namespace == "org.example") {
            useModule("org.example:custom-plugin:${requested.version}")
        }
    }
}

사전 컴파일된 스크립트 플러그인(Precompiled script plugins)

그레이들을 사용하면 독립 실행형 프로젝트로 작성된 플러그인 외에도 그루비 또는 코틀린 DSL로 작성된 빌드 로직을 사전 컴파일된 스크립트 플러그인으로 제공할 수 있다. src/main/groovy 디렉토리에 *.gradle 파일로 작성하거나 src/main/kotlin 디렉토리에 *.gradle.kts 파일로 작성한다.


미리 컴파일된 스크립트 플러그인명에는 두 가지 중요한 제한 사항이 있다.

  • org.gradle로 시작할 수 없다.
  • 내장 플러그인 ID와 동일한 이름을 가질 수 없다.

이렇게 하면 미리 컴파일된 스크립트 플러그인이 자동으로 무시되지 않는다. ***

미리 컴파일된 스크립트 플러그인은 클래스 파일로 컴파일되고 jar로 패키지된다. 모든 의도와 목적을 위해 이 플러그인은 바이너리 플러그인이며 플러그인 ID로 적용하고 테스트하고 바이너리 플러그인으로 게시할 수 있다. 실제로 해당 플러그인 메타데이터는 그레이들 플러그인 개발 플러그인을 사용하여 생성된다.


그레이들 6.0으로 빌드된 코틀린 DSL 사전 컴파일된 스크립트 플러그인은 이전 버전의 그레이들에서 사용할 수 없다. 이 제한은 그레이들의 향후 버전에서 제거될 예정이다.

그루비 DSL 사전 컴파일된 스크립트 플러그인은 그레이들 6.4부터 사용할 수 있다. 그루비 DSL 사전 컴파일된 스크립트 플러그인은 그레이들 5.0 이상을 사용하는 프로젝트에 적용될 수 있다. ***

사전 컴파일된 스크립트 플러그인을 적용하려면 플러그인 스크립트의 파일명(.gradle.kts 확장자 제외)과 해당 (선택 사항) 패키지 선언에서 파생된 ID를 알아야 한다.

예를 들어, src/main/kotlin/java-library-convention.gradle.kts 스크립트의 플러그인 ID는 java-library-convention이다(패키지 선언이 없다고 가정). 마찬가지로 src/main/kotlin/my/java-library-convention.gradle.ktsmy라는 패키지 선언이 있으면 my.java-library-convention이라는 플러그인 ID가 생성된다.

미리 컴파일된 스크립트 플러그인을 구현하고, 사용하는 방법을 보여주기 위해 buildSrc 프로젝트를 기반으로 한 예제를 살펴보자.

먼저, kotlin-dsl 플러그인을 적용하는 buildSrc/build.gradle.kts 파일이 필요하다.

예제 8. 미리 컴파일된 스크립트 플러그인 활성화

buildSrc/build.gradle

plugins {
    id 'groovy-gradle-plugin'
}

buildSrc/build.gradle.kts

plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}

또한 buildSrc/settings.gradle 파일을 생성하는 것이 좋다. 이 파일은 비워 둘 수 있다.

다음으로, buildSrc/src/main/groovy 디렉토리에 새로운 java-library-convention.gradle 파일을 생성하고 그 내용을 다음과 같이 설정한다.

예제 9. 간단한 스크립트 플러그인 만들기

buildSrc/src/main/groovy/java-library-convention.gradle

plugins {
    id 'java-library'
    id 'checkstyle'
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

checkstyle {
    maxWarnings = 0
    // ...
}

tasks.withType(JavaCompile) {
    options.warnings = true
    // ...
}

dependencies {
    testImplementation("junit:junit:4.13")
    // ...
}

buildSrc/src/main/kotlin/java-library-convention.gradle.kts

plugins {
    `java-library`
    checkstyle
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

checkstyle {
    maxWarnings = 0
    // ...
}

tasks.withType<JavaCompile> {
    options.isWarnings = true
    // ...
}

dependencies {
    testImplementation("junit:junit:4.13")
    // ...
}

이 스크립트 플러그인은 단순히 자바 라이브러리 및 Checkstyle 플러그인을 적용하고 구성한다. 이는 실제로 플러그인을 기본 프로젝트, 즉 미리 컴파일된 스크립트 플러그인을 적용하는 프로젝트에 적용한다는 점에 유의하자.

마지막으로, 다음과 같이 스크립트 플러그인을 루트 프로젝트에 적용한다.

예제 10. 미리 컴파일된 스크립트 플러그인을 메인 프로젝트에 적용

build.gradle

plugins {
    id 'java-library-convention'
}

build.gradle.kts

plugins {
    `java-library-convention`
}

미리 컴파일된 스크립트 플러그인에 외부 플러그인 적용

미리 컴파일된 스크립트 플러그인에 외부 플러그인을 적용하려면 플러그인 빌드 파일의 플러그인 프로젝트 구현 클래스패스에 추가해야 한다.

buildSrc/build.gradle

plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.bmuschko:gradle-docker-plugin:6.4.0'
}

buildSrc/build.gradle.kts

plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.bmuschko:gradle-docker-plugin:6.4.0")
}

그런 다음 미리 컴파일된(precompiled) 스크립트 플러그인에 적용할 수 있다.

buildSrc/src/main/groovy/my-plugin.gradle

plugins {
    id 'com.bmuschko.docker-remote-api'
}

buildSrc/src/main/kotlin/my-plugin.gradle.kts

plugins {
    id("com.bmuschko.docker-remote-api")
}

이 경우 플러그인 버전은 의존성 선언에 정의된다.

플러그인에 대한 테스트 작성

ProjectBuilder 클래스를 사용하면 플러그인 구현을 테스트할 때 사용할 프로젝트 인스턴스를 생성할 수 있다.

예제: 사용자 정의 플러그인 테스트

src/test/java/org/example/GreetingPluginTest.java

public class GreetingPluginTest {
    @Test
    public void greeterPluginAddsGreetingTaskToProject() {
        Project project = ProjectBuilder.builder().build();
        project.getPluginManager().apply("org.example.greeting");

        assertTrue(project.getTasks().getByName("hello") instanceof GreetingTask);
    }
}

더 자세한 내용(More details)

플러그인은 종종 커스텀 태스크 타입도 제공한다. 자세한 내용은 커스텀 그레이들 태스크 타입 개발을 참고하자.

그레이들은 플러그인을 포함하여 그레이들 타입을 개발할 때 유용한 여러 기능을 제공한다. 자세한 내용은 커스텀 그레이들 타입 개발을 참고하자.

그레이들 플러그인을 개발할 경우 빌드 로그에 정보를 기록할 때 주의해야 한다. 민감한 정보(예: 자격 증명, 토큰, 특정 환경 변수)를 기록하는 것은 보안 취약점이다. 공용 지속적 통합 서비스의 빌드 로그는 전 세계에서 볼 수 있으며 이러한 민감한 정보를 노출할 수 있다.

뒷 이야기(Behind the scenes)

그렇다면 그레이들은 플러그인 구현을 어떻게 찾나? 대답은 - JAR의 META-INF/gradle-plugins 디렉토리에 플러그인 ID와 일치하는 프로퍼티스 파일을 제공해야 하며, 이는 자바 그레이들 플러그인 개발 플러그인에서 처리된다.

예제: 커스텀 플러그인 작성

ID가 org.example.greeting이고 구현 클래스가 org.example.GreetingPlugin인 플러그인이 제공된다.

src/main/resources/META-INF/gradle-plugins/org.example.greeting.properties

implementation-class=org.example.GreetingPlugin

프로퍼티스 파일명은 플러그인 ID와 일치하고 리소스 폴더에 배치되며,implementation-class 프로퍼티는 플러그인 구현 클래스를 식별한다.