====== Gradle Custom Task ====== * [[http://www.gradle.org/docs/current/userguide/custom_tasks.html|Gradle Custom Task]] Gradle은 두가지 타입의 태스크를 지원한다. 하나는 간단한 태스크로 액션 클로저를 사용해 정의한다. 가장 기본적인 형태이다. 이 타입의 태스크는 클로저에 태스크의 행위를 기술한다. 빌드 스크립트에서 단 한 번만 쓸법한 태스크를 구현할 때 좋다. 다른 타입은 "확장(enhanced) 태스크"로 행위를 태스크에 모두 내장시키고 그 태스크를 설정할 수 있는 프라퍼티들을 제공해준다. [[gradle:task|Gradle Task]] 에서 살펴보았다. 대부분의 Gradle 플러그인은 확장 태스크를 사용한다. 확장 태스크는 간단한 태스크에서 처럼 태스크의 행위를 구현할 필요가 없다. 단지 태스크를 선언하고 태스크 프라퍼티를 설정해주면 된다. 이 방법은 여러 서로 다른 곳에서 서로 다를 빌드에서 행위를 재사용할 수 있게 해준다. 확장 태스크의 행위와 프라퍼티는 태스크 클래스로 정의된다. 확장 태스크를 선언할 때는 타입 혹은 태스크의 클래스를 명시한다. 사용자 정의 태스크를 구현하는 것은 쉽다. 원하는 어떤 언어로든지 사용자 정의 태스크를 구현하고 바이트코드로 컴파일해서 제공하기만 하면 된다. 그래도 Gradle API가 Groovy에 맞춰져 있으므로 Groovy로 만드는 것이 제일 편할 것이다. ===== 태스크 클래스 패키징 ===== ==== 빌드 스크립트 ==== 빌드 스크립트 안에서 클래스를 만들어 구현한다. 자동으로 컴파일되어 클래스패스에 추가된다. 태스크 클래스를 정의한 빌드 스크립트에서만 사용가능하다. ==== buildSrc 프로젝트 ==== 추천하는 방식. [[gradle:organizing_build_logic|Gradle Organizing Build Logic]]에 나온 ''buildSrc'' 프로젝트에 넣는다. ''rootProjectDir/buildSrc/src/main/groovy'' 디렉토리 아래에 넣는 것이다. Gradle이 자동으로 컴파일, 테스트, 클래스패스 추가를 해준다. 이렇게 할 경우 모든 프로젝트의 빌드스크립트에서 태스크 클래스를 사용할 수 있다. 빌드 자체의 외부에는 노출되지 않는다. ==== 독립 프로젝트 ==== 태스크 클래스용 독립 프로젝트를 만들고 jar로 묶어서 배포한다. ===== 간단한 사용자 정의 태스크 클래스 만들기 ===== [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Task.html|Task]]의 구현체인 [[http://www.gradle.org/docs/current/dsl/org.gradle.api.DefaultTask.html|DefaultTask]]를 상속하여 구현한다. 태스크에 어떤 행위를 추가하려면 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/TaskAction.html|TaskAction]] 어노테이션을 지정한 메소드를 추가하면 된다. 태스크가 실행 될 때 해당 메소드가 호출된다. 하지만 꼭 메소드를 정의할 필요는 없다. 태스크 클래스 생성자에서 ''doFirst()'' 혹은 ''doLast()''를 행위를 지정한 클로저를 인자로 줘서 호출해도 된다. * ''build.gradle'' task hello(type: GreetingTask) class GreetingTask extends DefaultTask { @TaskAction def greet() { println 'hello from GreetingTask' } } // 혹은 생성자를 이용한 다음도 가능하다. class GreetingTask extends DefaultTask { GreetingTask() { doFirst { println "hello from Greeting Task constructor" } } } * 실행하면 > gradle -q hello hello from GreetingTask 태스크에 프라퍼티를 추가하여 커스터마이징을 할 수 있게 하자. 태스크는 POGO이므로 태스크 객체에 프라퍼티를 지정하거나 메소드를 호출 할 수 있다. * 프라퍼티 추가된 태스크 ''build.gradle'' // 기본값사용 task hello(type: GreetingTask) // greeting 변경 task greeting(type: GreetingTask) { greeting = 'greetings from GreetingTask' } class GreetingTask extends DefaultTask { def String greeting = 'hello from Greeting Task' @TaskAction def greet() { println greeting } } * 실행하면 > gradle -q hello greeting hello from GreetingTask greetings from GreetingTask ''project''로 현재 프로젝트의 인스턴스에 접근할 수 있다. ''project.rootProject''로 최상위 프로젝트에 접근할 수 있다. ===== 독립 프로젝트 ===== 이제 태스크를 독립 프로젝트로 분리하여 배포하고 공유할 수 있도록 해보자. 이는 간단한 Groovy 프로젝트로 태스크 클래스를 포함하는 JAR를 생성하기만 하면 된다. Groovy 프로젝트로 만들고 Gradle API를 컴파일시 의존성에 추가한다. package를 ''org.gradle''로 지정해야 실제 사용시 import를 안하고 사용할 수 있다. * ''build.gradle'' apply plugin: 'groovy' dependencies { compile gradleApi() groovy localGroovy() } * ''src/main/groovy/org/gradle/GreetingTask.groovy'' package org.gradle // build.gradle에서는 자동으로 추가되지만 여기서는 명시적으로 import 해야한다. import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class GreetingTask extends DefaultTask { String greeting = 'hello from GreetingTask' @TaskAction def greet() { println greeting } } ==== 다른 프로젝트에서 태스크 클래스 사용하기 ==== 태스크 클래스를 빌드 스크립트의 클래스패스에 추가해야 한다. ''buildscript { }'' 블럭을 사용하면 된다. [[gradle:organizing_build_logic|Gradle Organizing Build Logic]]에서 빌드 스크립트에 외부 의존성 추가하는 부분을 참조한다. 아래 예제는 로칼 리포지토리에 태스크 클래스를 배포한 상황을 예로 들고 있다. * 다른 프로젝트에서 사용자 정의 태스크 사용하기 buildscript { // 빌드 스크립트 리포지토리에 추가하고 repositories { maven { url uri('../repo') } } // classpath 구성에 의존성을 추가한다. dependencies { classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT' } } task greeting(type: org.gradle.GreetingTask) { greeting = 'howdy!' } ==== 태스크 클래스의 단위 테스트 작성하기 ==== [[http://www.gradle.org/docs/current/javadoc/org/gradle/testfixtures/ProjectBuilder.html|ProjectBuilder]] 클래스를 사용하여 [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html|Project]] 인스턴스를 만들어낼 수 있다. 이를 가지고 태스크 클래스를 테스트하면 된다. * ''src/test/groovy/org/gradle/GreetingTaskTest.groovy'' class GreetingTaskTest { @Test public void canAddTaskToProject() { Project project = ProjectBuilder.builder().build() def task = project.task('greeting', type: GreetingTask) assertTrue(task instanceof GreetingTask) } }