[Gradle] 3. Custom Plugin 만들기 1부

5

오늘 알아볼 주제는 Custom Plugin 만들기입니다!

Plugin을 만들어보기에 앞서서 Plugin이 무엇인지, 만들면 어떤것이 좋은지 알아보도록 합시다.

Plugin이란?

A Gradle plugin packages up reusable pieces of build logic, which can be used across many different projects and builds. Gradle allows you to implement your own plugins, so you can reuse your build logic, and share it with others.

PluginTask의 집합과도 같습니다.

Task는 Gradle이 다양한 작업을 수행하는 단위라고 표현을 하는데요.

만약 밥먹기, 똥싸기, 잠자기가 우리의 일상이라고 했을때, 이걸 하나로 묶을수 있는것이 Plugin 입니다.

아래의 플러그인은 현 프로젝트가 Android Framework를 쓸수있게끔 만들어주는 task들의 단위겠죠!?

Plugin을 만들면 왜좋을까?

Plugin을 사용하면 재사용 가능하게 만들수 있습니다.
그리고 남들에게 공유 또한 쉽게 할수 있습니다.


왼쪽과 오른쪽을 gradle에 선언한다 했을때, 무엇이 더 좋은것 같나요?

사실 저희 코드와 비교했을때, Plugin은 추상화와 닮아있습니다.
사용자가 내부로직(task)를 알지 못해도 사용할수 있기 때문이죠.
그래서 다른사람에게 내부로직을 설명하지 않아도 쉽게 배포가 가능합니다.

하지만, 단점도 존재하는데 플러그인이 많아지면 많아질수록, task를 선언했을때보다 더 느려진다고 합니다.
그러니 적당히 플러그인화시키는게 좋을것같네요.

Custom Plugin을 만들기전에 생각해야할 점

Custom Plugin을 만들기위해선 생각해야할 것이 하나있습니다.

바로 Custom Plugin을 둘 위치인데, 아래와 같이 3가지의 경우가 있습니다.

  • buildScript
    빌드 스크립트에 직접 플러그인 소스를 포함할수 있습니다.
    Plugin이 자동으로 컴파일되는 이점이 있으나, 정의된 빌드 스크립트 외부에서 플러그인 재사용이 불가능합니다.
build.gradle.kts

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>()

// Configure the extension using a DSL block
configure<GreetingPluginExtension> {
    message.set("Hi")
    greeter.set("Gradle")
}
  • buildSrc
    buildSrc의 디렉토리 경로에 넣을수 있습니다.
    모든 빌드 스크립트에서 볼수 있으나, 빌드외부에선 사용이 불가능합니다.

  • 독립형 프로젝트
    플러그인에 대한 별도의 프로젝트를 만들수 있습니다. 다른사람과 공유가 가능합니다.

현재 저의 프로젝트는 멀티모듈이면서 BuildSrc를 사용하고 있지않기 때문에 3 번째를 선택하겠습니다.

독립형으로 개발해보기

  1. 독립형으로 결정했다면, newProject 를 사용하여 Gradle Project를 생성합니다.

CustomPlugin을 만들기위해선 아래와 같은 종속성이 있어야 Class내에서org.gradle.api 접근이 가능해집니다.

 implementation("com.android.tools.build:gradle:x.x.x")
 implementation(" org.jetbrains.kotlin:kotlin-gradle-plugin:x.x.x")
  1. 이제 src/main에 class를 생성 합니다.

class를 만들고 Plugin<Project>를 상속하면 됩니다.

class AndroidHiltConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {

    }
}

target 의 Project는 Gradle 상호작용하는 가장 기본적인 API입니다.
해당 target을 호출하여, gradle의 모든 기능을 프로그래밍 방식으로 짤수가 있습니다.

  1. 아주 기본적인 plugin 호출부터 다루겠습니다.
    plugin을 추가하는것은 pluginManager.apply를 통해 가능합니다.
with(pluginManager) {
 apply("org.jetbrains.kotlin.kapt")
 apply("dagger.hilt.android.plugin")
}
  1. dependency 추가는 dependencies를 통해 추가가 가능합니다.
    또한 VersionCatalog에 있는 아티팩트 또한 VersionCatalogsExtensions를 통해 접근이 가능하나, 자동완성은 불가능합니다.
    또한 extenstion에 자세한 내용은 2부에 다룰 예정입니다.
 val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
 
target.dependencies {
 "implementation"(libs.findLibrary("hilt.android").get())
 "kapt"(libs.findLibrary("hilt.compiler").get())
 "kaptAndroidTest"(libs.findLibrary("hilt.compiler").get())
}

저는 해당 기능을 활용하여 아주 간단하게 Hilt를 대신하는 plugin을 만들어 냈습니다.

class AndroidHiltConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            // plugin을 통해 현재 Project의 Plugin을 가져올수 있다.
            target.pluginManager
            //힐트에 필요한 플러그인 추가 
            with(pluginManager) {
                apply("org.jetbrains.kotlin.kapt")
                apply("dagger.hilt.android.plugin")
            }

            val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
            //힐트에 대한 종속성 추가
            target.dependencies {
                "implementation"(libs.findLibrary("hilt.android").get())
                "kapt"(libs.findLibrary("hilt.compiler").get())
                "kaptAndroidTest"(libs.findLibrary("hilt.compiler").get())
            }
        }
    }
}
  1. 이제 만든 Gradle Plugin을 등록해보도록 합시다.
    app 내의 build.gradle.kts에서 등록이 가능합니다.
plugins {
    `kotlin-dsl`
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
    compileOnly(libs.android.gradlePlugin)
    compileOnly(libs.kotlin.gradlePlugin)
}

gradlePlugin {
    plugins {
        register("androidApplicationCompose") {
            id = "kongs.android.hilt"
            implementationClass = "AndroidHiltConventionPlugin"
        }
    }
}

gradlePluginregister를 활용하여 플러그인을 등록할 수 있는데 아래와 같은 구조를 사용해야합니다.

  • id = "다른 gradle에서 호출할 name"
  • implementationClass = "실행할 Class"
  1. 이제 해당 plugin을 사용할 일만 남았습니다.
    사용할 프로젝트의 setting.gradle에서 해당 프로젝트를 includeBuild해줍니다.
pluginManagement {
//복합 빌드의 지정된 경로에 플러그인 빌드를 포함합니다. 
//포함된 플러그인 빌드는 설정 및 프로젝트 플러그인을 제공할 수 있습니다.
    includeBuild("build-logic")
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven { url = uri("https://jitpack.io") }
    }
}
  1. gradleplugin을 적용합니다.
plugins {
 	//이제 필요가 없어졌다.
   //id("dagger.hilt.android.plugin")
    id("kongs.android.hilt")
}

다음시간엔 extension을 활용하여 좀 더 다양한 plugin을 다루는시간을 가져보겠습니다.
감사합니다 <3


참고

Android용 첫 번째 사용자 지정 Gradle 플러그인 만들기 — 1부: 기본 설정

커스텀 Gradle 플러그인 개발

Android 앱용 맞춤 Gradle 플러그인 작성(1부)

profile
쉽게 가르칠수 있도록 노력하자

1개의 댓글

comment-user-thumbnail
2023년 6월 10일

좋은 글 감사합니다~

답글 달기