Gradle은 의존성 기반의 프로그래밍용 언어이다. 이 말은 태스크를 정의하고 또한 태스크들 사이의 의존성도 정의 할 수 있다는 뜻이다.
Gradle은 태스크들이 의존성의 순서에 따라 실행되고, 오직 한 번만 실행될 것임을 보장한다.
Gradle은 태스크를 실행하기 전에 완전한 의존성 그래프를 구축한다.
Gradle 빌드는 3 단계로 구분된다.
gradle
명령행에 인자로 지정한 태스크 이름과 현재 디렉토리를 기반으로 태스크를 결정하여 선택된 것들을 실행한다.
빌드 파일 말고도 설정 파일도 있다. 설정 파일은 명명규칙에 따라 Gradle이 자동 인식한다. 기본 파일명은 settings.gradle
이다.
설정 파일은 초기화 단계에서 실행된다. 멀티 프로젝트는 무조건 최상위 프로젝트에 settings.gradle
이 있어야 한다. 어느 프로젝트가 멀티 프로젝트 빌드에 속하는지를 여기서 정한다. Gradle Multi Project 참조. 단일 프로젝트 빌드에서는 설정 파일이 없어도 된다.
settings.gradle
println 'This is executed during the initialization phase.'
> gradle test This is executed during the initialization phase. This is executed during the configuration phase. This is also executed during the configuration phase. :test This is executed during the execution phase. BUILD SUCCESSFUL
빌드 스크립트에서는 프라퍼티 접근과 메소드 호출이 project 객체로 위임 된다. 유사하게 설정 파일에서는 Settings settings
객체로 위임 된다.
멀티 프로젝트 빌드는 Gradle을 한 번실행하는 동안 하나 이상의 프로젝트를 빌드한다. 설정 파일에 소속 프로젝트를 지정해준다. Gradle Multi Project 참조.
멀티 프로젝트는 항상 단일 최상위 아래에 트리 구조로 표현된다. 트리의 각 요소는 프로젝트를 나타낸다. 프로젝트는 경로로 표현된다. 보통 경로는 프로젝트의 파일 시스템에서의 물리적 위치로 구성된다. 하지만 이것도 설정 가능하다. 프로젝트 트리는 settings.gradle
파일에서 생성되며 기본적으로 최상위 프로젝트 아래에 있다고 가정한다. 하지만 최상위 프로젝트의 위치도 설정 파일에서 바꿀 수 있다.
settings.gradle
include 'project1', 'project2', 'project2:child1'
include
메소드는 인자로 프로젝트 경로를 받는다. 이 경로는 파일 시스템에서의 상대 경로로 간주된다. 예를들어 경로가 'services:api'이면 최상위 프로젝트에 상대 경로로 'services/api' 디렉토리로 간주된다. 트리의 잎(leaf)만 지정한다. 이 말은, 'services:hotels:api'를 지정하면 세개의 프로젝트인 'services', 'services:hotels', 'services:hotels:api'가 생성된다는 뜻이다.
settings.gradle
includeFlat 'project3', 'project4'
includeFlat
메소드는 디렉토리 이름을 인자로 받는다. 이 디렉토리들은 최상위 디렉토리와 같은 레벨에 존재해야 한다. 이 디렉토리들의 위치는 멀티 프로젝트 트리에서 최상위 프로젝트의 자식 프로젝트로 간주된다.
설정 파일에서 만들어진 멀티 프로젝트 트리는 프로젝트 기술자(project descriptors)라는 것으로 만들어진다. 이 기술자를 변경할 수 있다.
ProjectDescriptor를 참조한다.
Gradle이 단일 혹은 멀티 프로젝트 빌드를 실행할지 판단하는 기준이 있다. 설정 파일이 있는 디렉토리에서 멀티 프로젝트를 빌드를 실행하면 판단이 쉽다. 하지만 서브 프로젝트 아무데서나 빌드를 실행할 수도 있다. settings.gradle
이 없는 곳에서 빌드를 실행하면
master
라는 디렉토리에서 settings.gradle
을 찾는다.settings.gradle
을 찾는다.settings.gradle
가 존재하면 현재 프로젝트가 멀티 프로젝트 계층에 속하는지 판단한다. 아니라면 단일 프로젝트로 실행하고 맞다면 멀티 프로젝트로 빌드를 실행한다.
이런 식으로 작동하는 이유는 멀티 프로젝트 일 경우 모든 멀티프로젝트 빌드 구성을 생성해야하기 때문이다. -u
명령행 옵션을 주면 부모 디렉토리에서 설정파일 찾는 것을 막는다. 이 경우에는 항상 단일 프로젝트로 실행한다. settings.gradle
파일이 있는 곳에서 -u
는 아무 기능도 없다.
설정 파일 자동 탐색은 물리적으로 계층/단층 레이아웃인 멀티 프로젝트에서만 작동한다. 단층 레이아웃에서는 위에서 기술한 명명규칙을 지켜야 한다. Gradle은 멀티 프로젝트에 임의의 물리적 레이아웃을 지원하지만 이 때는 항상 설정 파일이 있는 곳에서 빌드를 실행해야 한다.
Gradle은 빌드에 참여하는 모든 프로젝트에 대해 Project 객체를 생성한다. 각 프로젝트는 기본적으로 탑레벨 디렉토리를 name
으로 갖는다. 최상위를 제외한 모든 프로젝트는 부모 프로젝트가 있고, 자식 프로젝트를 가질 수 있다.
라이프싸이클을 진행하는 동안 빌드 스크립트에서 알림을 받을 수 있다. 알림은 특별한 리스너 인터페이스를 구현하거나 혹은 알림이 발생했을 때 실행할 클로저를 제공해 주는 두가지 방식으로 구현한다.
프로젝트를 평가하기 직전과 직후에 알림을 받을 수 있다. 빌드 스크립트에서 모든 정의가 적용된 이후에 추가적인 구성을 수행할 때나 로깅 혹은 프로파일링을 하고자 할 때 사용한다.
projectA.gradle
hasTests = true
> gradle -q test Adding test task to project ':projectA' Running tests for project ':projectA'
Project.afterEvaluate()를 사용하여 프로젝트 평가 뒤에 실행할 클로저를 추가 하였다.
아무 프로젝트든 평가한 뒤에 알림을 받는 것도 가능하다. afterProject
는 프로젝트 평가의 성공 여부와 무관하게 호출된다.
> gradle -q test Evaluation of root project 'buildProjectEvaluateEvents' succeeded Evaluation of project ':projectA' succeeded Evaluation of project ':projectB' FAILED
Gradle 객체에 ProjectEvaluationListener 를 추가해도 된다.
build.gradle
에서 특정 하위 프로젝트를 콕 집어서 지정project(':sub-project').afterEvaluate { // .... }
srcDir
을 설정하는 build.gradle
tasks.whenTaskAdded { task -> task.srcDir = 'src/main/java' } task a println "source dir is $a.srcDir"
> gradle -q a source dir is src/main/java
TaskContainer에 Action을 추가해도 된다.
release
가 있는지 여부에 따라 버전 변경하는 build.gradle
task distribution << { println "We build the zip with version=$version" } task release(dependsOn: 'distribution') << { println 'We release now' } gradle.taskGraph.whenReady {taskGraph -> if (taskGraph.hasTask(release)) { version = '1.0' } else { version = '1.0-SNAPSHOT' } }
release
태스크의 실행여부에 따라 버전값이 달라진다. TaskExecutionGraph에 TaskExecutionGraphListener를 추가해도 된다.
어떤 태스크이든 실행 직전과 직후에 알림을 받을 수 있다.
build.gradle
task ok task broken(dependsOn: ok) << { throw new RuntimeException('broken') } gradle.taskGraph.beforeTask { Task task -> println "executing $task ..." } gradle.taskGraph.afterTask { Task task, TaskState state -> if (state.failure) { println "FAILED" } else { println "done" } }
> gradle -q broken executing task ':ok' ... done executing task ':broken' ... FAILED
TaskExecutionGraph에 TaskExecutionListener를 추가해도 된다.