[Gradle] 빌드 생명 주기

2023. 3. 18. 22:07Build Tools/Gradle

빌드 단계

Gradle 빌드는 세 가지 단계를 거친다.

1. 초기화(Initialization)

Gradle은 하나 또는 멀티 프로젝트 빌드를 지원한다. 초기화 단계에서 Gradle은 빌드에 포함될 프로젝트를 결정하고 이러한 각 프로젝트에 대한 Project 인스턴스를 생성한다.

2. 구성(Configuration)

이 단계에서는 프로젝트 객체를 구성한다. 빌드에 포함된 모든 프로젝트의 빌드 스크립트가 실행된다.

3. 실행(Execution)

Gradle은 실행될 구성 단계에서 생성 및 구성된 task의 서브셋을 결정한다. 서브셋은 task gradle command와 현재 디렉터리에 전달된 이름 인수에 의해 결정된다. 그 후 Gradle이 선택한 각 task를 실행한다.

Settings file

빌드 스크립트 파일 외에 Gradle은 settings file을 정의한다. settings file은 네이밍 컨벤션을 통해 Gradle에 의해 결정된다. 이 파일의 기본 이름은 settings.gradle이다. 이 챕터의 나중에 Gradle이 settings file을 어떻게 찾는지 설명한다.

settings file은 초기화 단계에 실행된다. 멀티 프로젝트 빌드는 멀티 프로젝트 계층 구조의 루트 프로젝트 안에 반드시 settings.gradle 파일을 가져야 한다. 이는 필수인데 settings file은 멀티 프로젝트 빌드에 포함된 프로젝트를 정의하기 때문이다. 단일 프로젝트 빌드인 경우 settings file은 선택적이다. 포함된 프로젝트를 정의하는 것 외에도, 빌드 스크립트 classpath에 라이브러리를 추가하려고 할 때 필요할 수도 있다.

예제 1. 단일 프로젝트 빌드

// settings.gradle
rootProject.name = 'basic'
println 'This is executed during the initialization phase.'
// build.gradle
println 'This is executed during the configuration phase.'

tasks.register('configured') {
    println 'This is also executed during the configuration phase, because :configured is used in the build.'
}

tasks.register('test') {
    doLast {
        println 'This is executed during the execution phase.'
    }
}

tasks.register('testBoth') {
    doFirst {
        println 'This is executed first during the execution phase.'
    }
    doLast {
      println 'This is executed last during the execution phase.'
    }
    println 'This is executed during the configuration phase as well, because :testBoth is used in the build.'
}
  • gradle test testBoth의 실행 결과
$ gradle test testBoth
This is executed during the initialization phase.

> Configure project :app
This is executed during the configuration phase.
This is executed during the configuration phase as well, because :testBoth is used in the build.

> Task :app:test
This is executed during the execution phase.

> Task :app:testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

빌드 스크립트에서 프로퍼티 접근과 메소드 호출은 프로젝트 객체에 위임된다. 비슷하게 settings file에 작성된 프로퍼티 접근과 메소드 호출은 settings 객체에게 위임된다.

초기화

단일 또는 멀티 프로젝트 빌드인지 Gradle은 어떻게 알까? 만약 당신이 디렉터리에 settings.gradle 파일에서 멀티 프로젝트 빌드를 트리거했다면, Gradle은 빌드에서 그것을 구성하기 위해 사용한다. Gradle은 또한 당신이 빌드에 포함된 여러 서브 프로젝트 안의 빌드를 실행하도록 해준다. 만약 당신이 settings.gradle 파일 없이 프로젝트 내부에서 Gradle을 실행했다면, Gradle은 settings.gradle을 찾기 위해 다음 방법을 따른다.

  • 부모 디렉터리에서 settings.gradle을 찾는다.
  • 찾지 못했다면, 빌드는 단일 프로젝트 빌드로써 실행된다.
  • 만약 settings.gradle이 발견되었다면, Gradle은 발견한 settings.gradle 파일에 현재 프로젝트가 멀티 프로젝트 계층에 포함되었는지 확인한다. 포함되지 않았다면 빌드는 단일 프로젝트로써 실행된다. 포함되었다면 멀티 프로젝트 빌드가 실행된다.

위 행위의 목적이 무엇일까? Gradle은 프로젝트가 멀티 프로젝트 빌드의 서브 프로젝트인지 아닌지 결정하기 위해 필요하다. 물론 서브프로젝트라면 서브 프로젝트와 의존된 프로젝트들이 빌드된다. 하지만 Gradle은 모든 멀티 프로젝트 빌드를 위한 빌드 구성을 만들기 위해 필요하다. 만약 현재 프로젝트가 settings.gradle 파일을 포함하고 있으면 빌드는 항상 다음과 같이 실행된다.

  • settings.gradle 파일이 멀티 프로젝트 계층을 정의하지 않았으면 단일 프로젝트로 빌드한다.
  • settings.gradle 파일이 멀티 프로젝트 계층을 정의했으면 멀티 프로젝트로 빌드한다.

settings.gradle 파일을 위한 자동 탐색은 프로젝트 경로가 디스크에서 물리적인 서브 프로젝트 레이아웃과 매칭된 기본 프로젝트 레이아웃의 멀티 프로젝트 빌드에서만 작동한다. Gradle은 멀티 프로젝트 빌드를 위해 임의적인 물리적 레이아웃을 지원한다. 하지만 이러한 임의적인 레이아웃인 경우 설정 파일이 있는 디렉터리에서 빌드를 실행해야 한다.

Gradle은 빌드에 포함된 모든 프로젝트에 대해 Project 객체를 생성한다. 멀티 프로젝트 빌드의 경우 설정 객체(루트 프로젝트 포함)에 지정된 프로젝트이다. 각 프로젝트 객체는 기본적으로 최상위 디렉터리의 이름과 같은 이름을 가지며 루트 프로젝트를 제외한 모든 프로젝트에는 상위 프로젝트가 있다. 모든 프로젝트에는 하위 프로젝트가 있을 수 있다.

단일 프로젝트 빌드의 구성 및 실행

단일 프로젝트 빌드의 경우 초기화 후 실행 흐름은 매우 간단하다. 빌드 스크립트는 초기화 단계에서 생성된 프로젝트 객체에 대해 실행된다. 그런 다음 Gradle은 커맨드 인수로 전달된 것과 같은 이름을 가진 task를 찾는다. task 이름이 있는 경우 전달한 순서대로 별도의 빌드로 실행된다.

빌드 스크립트에서 생명 주기에 대한 응답

프로젝트 평가(evaluation)

프로젝트가 평가되기 전후에 즉시 알림을 받을 수 있다. 이는 빌드 스크립트의 모든 정의가 적용된 후 추가 구성을 수행하거나 커스텀한 로깅 또는 프로파일링을 수행하는 등의 작업을 하기 위해 사용될 수 있다.

특정 속성이 설정된 각 프로젝트에 테스트 작업 추가

// build.gradle
allprojects {
    afterEvaluate { proejct ->
        if (project.hasTests) {
            println "Adding test task to $proejct"
            project.task('test') {
                doLast {
                    println "Running tests for $project"
                }
            }
        }
    }
}

실행 결과

Adding test task to project ':project-a'
Running tests for project ':project-a'

알림

gradle.afterProject { proejct ->
    if (project.state.failure) {
        println "Evaluation of $project FAILED"
    } else {
        println "Evaludation of $project succeeded"
    }
}