Gradle
Gradle 개념 정리
Gradle
Gradle은 빠르고 신뢰할 수 있으며, 적응력이 뛰어난 오픈 소스 빌드 자동화 도구로, 우아하고 확장 가능한 선언적 빌드 언어를 갖추고 있습니다.
왜 Gradle인가?
Gradle는 널리 사용되는 성숙한 도구로, 활발한 커뮤니티와 강력한 개발자 생태계를 가지고 있습니다.
주요 장점:
- JVM을 위한 최고의 빌드 시스템
- Gradle은 JVM을 위한 가장 인기 있는 빌드 시스템이며, Android와 Kotlin 멀티플랫폼 프로젝트의 기본 시스템입니다. 풍부한 커뮤니티 플러그인 생태계를 자랑합니다.
- 다양한 빌드 시나리오 자동화
- Gradle은 내장된 기능, 서드파티 플러그인, 또는 사용자 정의 빌드 로직을 사용하여 광범위한 소프트웨어 빌드 시나리오를 자동화할 수 있습니다.
- 고수준의 선언적이고 표현력 있는 빌드 언어
- Gradle은 읽고 쓰기 쉬운 고수준의 선언적이고 표현력 있는 빌드 언어를 제공합니다.
- 빠르고 확장 가능
- Gradle은 빠르고 확장 가능하며, 모든 크기와 복잡성의 프로젝트를 빌드할 수 있습니다.
- 신뢰할 수 있는 결과
- Gradle은 증분 빌드, 빌드 캐싱, 병렬 실행 등의 최적화를 통해 신뢰할 수 있는 결과를 제공합니다.
- Build Scan® 서비스
- Gradle, Inc.는 Build Scan®이라는 무료 서비스를 제공하여 빌드에 대한 광범위한 정보와 인사이트를 제공합니다. 문제를 식별하거나 디버깅 도움을 위해 스캔을 공유할 수 있습니다.
Core Concept
- 프로젝트
- Gradle 프로젝트는 애플리케이션이나 라이브러리와 같이 빌드할 수 있는 소프트웨어의 한 부분을 의미합니다.
- 단일 프로젝트 빌드: 루트 프로젝트라 불리는 단일 프로젝트를 포함합니다.
- 다중 프로젝트 빌드: 하나의 루트 프로젝트와 여러 개의 하위 프로젝트를 포함합니다.
- Gradle 프로젝트는 애플리케이션이나 라이브러리와 같이 빌드할 수 있는 소프트웨어의 한 부분을 의미합니다.
- 빌드 스크립트
- 빌드 스크립트는 Gradle에게 프로젝트를 빌드하기 위한 단계들을 상세히 기술합니다.
- 각 프로젝트는 하나 이상의 빌드 스크립트를 포함할 수 있습니다.
- 빌드 스크립트는 Gradle에게 프로젝트를 빌드하기 위한 단계들을 상세히 기술합니다.
- 의존성 관리
- 의존성 관리는 프로젝트에서 필요한 외부 리소스를 선언하고 해결하는 자동화된 기법입니다.
- 각 프로젝트는 Gradle이 빌드 중에 해결할 여러 외부 의존성을 포함합니다.
- 의존성 관리는 프로젝트에서 필요한 외부 리소스를 선언하고 해결하는 자동화된 기법입니다.
- 태스크
- 태스크는 코드 컴파일이나 테스트 실행과 같은 기본 작업 단위입니다.
- 각 프로젝트는 빌드 스크립트나 플러그인 내에서 정의된 하나 이상의 태스크를 포함합니다.
- 태스크는 코드 컴파일이나 테스트 실행과 같은 기본 작업 단위입니다.
- 플러그인
- 플러그인은 Gradle의 기능을 확장하고 선택적으로 프로젝트에 태스크를 추가하는 데 사용됩니다.
Gradle 프로젝트 구조
많은 개발자들은 기존 프로젝트를 통해 처음으로 Gradle과 상호작용하게 됩니다.
프로젝트의 루트 디렉토리에 gradlew 및 gradlew.bat 파일이 존재하는 것은 Gradle이 사용된다는 명확한 지표입니다.
Gradle 프로젝트는 다음과 비슷한 구조를 가질 것입니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
project
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle(.kts)
├── subproject-a
│ ├── build.gradle(.kts)
│ └── src
└── subproject-b
├── build.gradle(.kts)
└── src
- gradle : wrapper 파일들과 다른 파일들을 저장하는 디렉토리 입니다.
- libs.versions.toml : 의존성 관리를 위한 Gradle 버전 카탈로그입니다
- gradle.w : gradle wrapper script
- setting.gradle : 루트 프로젝트 명과 서브 프로젝트들을 정의하는 세팅 파일
- build.gradle : 두 서브 프로젝트의 빌드 스크립트
- src : 프로젝트 소스 코드
Gradle Wrapper
Gradle build를 실행할 때 권장되는 방법은 Gradle wrapper를 사용하는 것입니다.
Wrapper script는 선언된 버전의 Gradle을 호출하며, 필요할 경우 사전에 다운로드합니다.
Gradle wrapper 이해하기
1
2
3
4
5
6
7
.
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
- gradle-wrapper.jar : 이 작은 JAR 파일은 Gradle 래퍼 코드를 포함하고 있습니다. 이 파일은 프로젝트에 맞는 Gradle의 올바른 버전을 다운로드하고 설치하는 역할을 합니다.
- gradle-wrapper.properties : 이 파일은 Gradle 래퍼의 구성 속성을 포함하고 있습니다. 예를 들어, Gradle을 다운로드할 배포 URL과 배포 유형(ZIP 또는 TARBALL)을 지정합니다.
- gradlew : 이 쉘 스크립트는 (유닉스 기반 시스템에서) gradle-wrapper.jar을 감싸는 래퍼 역할을 합니다. 유닉스 기반 시스템에서 Gradle을 수동으로 설치하지 않고도 Gradle 작업을 실행하는 데 사용됩니다.
settings.gradle
세팅 파일은 gradle 프로젝트의 엔트리 포인트입니다.
세팅 파일의 주목적은 서브 프로젝트들을 추가하는 것입니다.
settings script
세팅 파일은 다음과 같이 작성할 수 있습니다.
1
2
3
4
5
rootProject.name = 'root-project'
include('sub-project-a')
include('sub-project-b')
include('sub-project-c')
- rootProject.name : 프로젝트 이름
- include : 서브 프로젝트 추가
build.gradle
보통 빌드 스크립트는 빌드 설정, 태스크, 플러그임들을 설정합니다.
빌드 파일에서 두 종류의 의존성을 추가할 수 있습니다.
- Gradle과 빌드 스크립트가 의존하는 라이브러리 및/또는 플러그인.
- 프로젝트 소스(즉, 소스 코드)가 의존하는 라이브러리.
dependency management basics
Gradle 빌드 스크립트는 외부 의존성이 필요할 수 있는 프로젝트를 빌드하는 과정을 정의합니다. 의존성은 JAR 파일, 플러그인, 라이브러리 또는 프로젝트 빌드를 지원하는 소스 코드를 의미합니다.
version catalog
버전 카탈로그는 libs.versions.toml 파일에 의존성 선언을 중앙 집중화하는 방법을 제공합니다.
이 카탈로그는 하위 프로젝트 간에 의존성과 버전 구성을 공유하는 작업을 단순화합니다. 또한, 대규모 프로젝트에서 팀이 라이브러리와 플러그인의 버전을 강제할 수 있게 합니다.
버전 카탈로그는 일반적으로 네 개의 섹션으로 구성됩니다:
[versions]
: 플러그인과 라이브러리가 참조할 버전 번호를 선언합니다.[libraries]
: 빌드 파일에서 사용할 라이브러리를 정의합니다.[bundles]
: 의존성 세트를 정의합니다.[plugins]
: 플러그인을 정의합니다.
1
2
3
4
5
6
7
8
9
10
[versions]
androidGradlePlugin = "7.4.1"
mockito = "2.16.0"
[libraries]
googleMaterial = { group = "com.google.android.material", name = "material", version = "1.1.0-alpha05" }
mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "androidGradlePlugin" }
dependency 선언하기
버전 카탈로그에 명시한 의존성은 다음과 같이 사용할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
plugins {
alias(libs.plugins.androidApplication)
}
dependencies {
// Dependency on a remote binary to compile and run the code
implementation(libs.googleMaterial)
// Dependency on a remote binary to compile and run the test code
testImplementation(libs.mockitoCore)
}
Task
task는 컴파일, jar 파일 생성, javadoc 생성 혹은 레포지토리 배포와 같은 특정 작업 단위를 지정할 수 있습니다.
./gradlew tasks
명령어로 실행 가능한 task 들을 확인할 수 있고 ./gradlew run
명령어로 실행 가능합니다. task 별로 의존성이 있어서 build 같은 작업을 수행하면 compileJava 태스크가 먼저 실행되게 됩니다.
Plugin
Gradle은 플러그인 시스템을 기반으로 구축되어 있습니다. Gradle 자체는 주로 정교한 의존성 해결 엔진과 같은 인프라로 구성되어 있으며, 나머지 기능은 플러그인에서 제공합니다.
플러그인은 Gradle 빌드 시스템에 추가적인 기능을 제공하는 소프트웨어입니다.
플러그인들은 빌드 스크립트에 추가되어 새로운 태스크, 설정 혹은 빌드 스크립트를 작성할 수 있습니다.
플러그인 적용하기
플러그인은 아래와 같이 적용할 수 있습니다.
1
2
3
plugins {
id «plugin id» version «plugin version»
}
core plugin
gradle에 기본적으로 포함된 플러그인들입니다. core plugin에는 다음과 같은 플러그인들이 있습니다.
- java : 자바 프로젝트를 빌드하는데 도움을 줍니다.
- groovy : groovy 소프 파일을 컴파일하고 테스트하는 용도
- ear : 엔터프라이즈 애플리케이션을 위한 ear 파일 빌드
core plugin은 다음과 같이 버전 정보가 필요하지 않습니다.
1
2
3
plugins {
id("java")
}
community plugin
gradle 커뮤니티에서 개발된 플러그인들이고 스프링 부트 플러그인도 커뮤니티 플러그인입니다.
1
2
3
plugins {
id("org.springframework.boot") version "3.1.5"
}
local plugins
특정 프로젝트나 조직에서 공개적으로 공유하지 않고 사용하는 플러그인입니다. 로컬 플러그인 만드는 법
multi project
소규모 프로젝트 혹은 모놀리식 애플리케이션들은 하나의 빌드 파일 혹은 소스 트리를 사용할 수 있지만, 프로젝트가 작은 모듈들로 나눠지는 경우가 흔하다.
multi-project structure
settings.gradle 파일은 모든 서브 프로젝트들을 명시하고, 서브 프로젝트들은 build.gradle 파일이 있어야 한다.
multi-project standards
gradle 커뮤니티는 멀티 프로젝트를 위한 두가지 프로젝트 구조를 제안합니다.
- buildSrc : 서브 프로젝트와 같은 디렉터리로 모든 빌드 로직을 포함합니다.
- buildSrc 디렉토리에 커스텀 task, plugin 들을 작성해서 사용할 수 있습니다.
- build-logic : 재사용 가능한 빌드 로직을 포함하는 Gradle 프로젝트 루트의 빌드 디렉토리입니다.
identifying project structure
./gradlew -q projects
명령어로 프로젝트 구조를 확인할 수 있습니다. 서브 프로젝트에 gradle 명령어를 수행하기 위해서는 ./gradlew :app:tasks
같은 명령어를 이용해서 사용할 수 있습니다.
build lifecycle
gradle는 어떤 task를 실행하기 전에 task graph를 빌드합니다.
build phases
gradle build는 3가지 단계를 통해 진행됩니다.
- Initialization phase
- settings.gradle 파일을 찾습니다.
- Settings 인스턴스를 생성합니다.
- settings 파일로부터 빌드할 프로젝트들을 찾고 각 프로젝트 인스턴스를 생성합니다.
- Configuration
- build 파일로부터 요구되는 task에 task graph를 생성합니다.
- Execution
writing settings files
settings file은 모든 gradle 빌드에 엔트리 포인트입니다.
세팅 파일의 목적 중 하나는 빌드에 필요한 모든 프로젝트들을 선언할 수 있습니다.
settings object
settings object는 gradle api의 일부입니다.
https://docs.gradle.org/current/javadoc/org/gradle/api/initialization/Settings.html
setting object groovy 문서
settings properties는 다음과 같습니다.
Name | Description |
---|---|
buildCache | build cache 설정 |
plugins | 세팅에 적용할 플러그인들 |
rootDir | 빌드할 루트 디렉터리, 루트 프로젝트의 프로젝트 디렉터리 |
rootProject | 빌드의 루트 프로젝트 |
settings | settings 오브젝트 |
settings의 메소드는 다음과 같습니다.
Name | Description |
---|---|
include() | 프로젝트를 빌드에 추가 |
includeBuild() | 특정 경로에 빌드 추가 |
setting script structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pluginManagement { // 프로젝트에 적용할 플러그인 레포지토리 설정
repositories {
gradlePluginPortal()
google()
}
}
plugins { // 세팅에 적용할 플러그인 설정
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}
rootProject.name = 'root-project'
dependencyResolutionManagement { // 플러그인 의존성 관련 설정
repositories {
mavenCentral()
}
}
include('sub-project-a')
include('sub-project-b')
include('sub-project-c')
build script
gradle build life cycle에서 프로젝트 루트 디렉터리에 있는 settings 파일을 이용해서 루트 프로젝트와 서브 프로젝트를 찾습니다. 찾은 프로젝트마다 gradle은 Project instance를 생성합니다. gradle은 Project마다 상응하는 build script 파일을 찾아 configuration phase에서 사용합니다.
project object
project 오브젝트는 gradle api의 일부입니다.
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
groovy project 오브젝트 문서
project properties는 다음과 같습니다.
Name | Type | Description |
---|---|---|
name | string | 프로젝트 디렉터리 이름 |
path | string | 유효한 프로젝트 명 |
description | string | 프로젝트 설명 |
dependencies | DependencyHandler | 프로젝트 의존성 핸들러 |
repositories | RepositoryHandler | 프로젝트 repository 핸들러 |
layout | ProjectLayout | 프로젝트 주요 location에 엑세스 제공 |
group | Object | 프로젝트 그룹 |
version | Object | 프로젝트 버전 |
project의 메소드는 다음과 같습니다.
Name | Description |
---|---|
uri() | 파일 경로 |
task() | 태스크 생성 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
plugins { // 빌드에 플러그인 적용
id 'org.jetbrains.kotlin.jvm' version '1.9.0'
id 'application'
}
repositories { // 의존성을 찾을 repository 명시
mavenCentral()
}
dependencies { // 의존성 설정
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'com.google.guava:guava:32.1.1-jre'
}
application {
mainClass = 'com.example.Main'
}
tasks.named('test') { // task 등록 & 설정
useJUnitPlatform()
}
Task
task는 빌드가 수행하는 작업 단위입니다. 수행하는 작업으로는 클래스 컴파일, jar 파일 생성, 자바독 생성 혹은 레포지토리 배포 같은 작업이 될 수 있습니다.
./gradlew build
명령어를 실행하면 build와 함께 build가 의존하는 다른 task도 함께 실행됩니다.
task들은 빌드 스크립트 혹은 플러그인을 통해 추가됩니다.
task classification
실행 가능한 task에는 두 가지 종류가 있습니다.
- actionable tasks : 실행 가능한 액션과 연관된 task ex) compileJava
- lifecycle task : 액션이 연관되지 않은 task, 라이프사이클은 많은 actionable task를 실행하기 위해 사용합니다. ex) build, assemble
task registration and action
아래와 같이 task를 등록할 수 있다.
1
2
3
4
5
tasks.register('hello') {
doLast {
println 'Hello world!'
}
}
빌드 스크립트에 hello
라는 task를 TaskContainer API를 이용해서 추가했습니다. 등록한 task는 다음과 같은 명령어를 통해 확인 가능합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
$ ./gradlew :app:tasks --all
Initialization phase
> Task :app:tasks
------------------------------------------------------------
Tasks runnable from project ':app'
------------------------------------------------------------
Application tasks
-----------------
run - Runs this project as a JVM application
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the classes of the 'main' feature.
testClasses - Assembles test classes.
Distribution tasks
------------------
assembleDist - Assembles the main distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the 'main' feature.
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of project ':app'.
resolvableConfigurations - Displays the configurations that can be resolved in project ':app'.
tasks - Displays the tasks runnable from project ':app'.
Verification tasks
------------------
check - Runs all checks.
test - Runs the test suite.
Other tasks
-----------
compileJava - Compiles main Java source.
compileTestJava - Compiles test Java source.
components - Displays the components produced by project ':app'. [deprecated]
dependentComponents - Displays the dependent components of components in project ':app'. [deprecated]
hello
model - Displays the configuration model of project ':app'. [deprecated]
processResources - Processes main resources.
processTestResources - Processes test resources.
startScripts - Creates OS specific scripts to run the project as a JVM application.
등록된 태스크는 다음과 같은 명령어로 사용 가능합니다.
1
2
3
4
5
$ ./gradlew hello
Initialization phase
> Task :app:hello
Hello world!
task group and description
위에서 선언한 task를 다음과 같이 변경하고 태스크를 확인해보면 다음과 같은 결과가 나옵니다.
1
2
3
4
5
6
7
tasks.register("hello") {
group = "Custom"
description = "A lovely greeting task."
doLast {
println("Hello world!")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ ./gradlew :app:tasks --all
Initialization phase
> Task :app:tasks
------------------------------------------------------------
Tasks runnable from project ':app'
------------------------------------------------------------
Application tasks
-----------------
run - Runs this project as a JVM application
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the classes of the 'main' feature.
testClasses - Assembles test classes.
Custom tasks
------------
hello - A lovely greeting task.
hello task에 그룹과 설명이 추가된 것을 확인할 수 있습니다.
task dependencies
1
2
3
4
5
6
7
8
9
10
11
tasks.register('hello') {
doLast {
println 'Hello world!'
}
}
tasks.register('intro') {
dependsOn tasks.hello
doLast {
println "I'm Gradle"
}
}
intro 태스크를 실행해보면 hello 태스크가 선행되어 실행됩니다.
1
2
3
4
5
6
7
8
$ ./gradlew intro
Initialization phase
> Task :app:hello
Hello world!
> Task :app:intro
I'm Gradle
writing tasks
다음과 같이 커스텀 태스크를 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class CreateFileTask : DefaultTask() {
@TaskAction
fun action() {
val file = File("myfile.txt")
file.createNewFile()
file.writeText("HELLO FROM MY TASK")
}
}
tasks.register<CreateFileTask>("createFileTask", ) {
group = "custom"
description = "Create myfile.txt in the current directory"
}
위 태스크를 실행하면 다음과 같이 새로운 파일이 생성되는 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ./gradlew createFileTask
BUILD SUCCESSFUL in 1s
11 actionable tasks: 2 executed, 9 up-to-date
[16:08:24] [cost 2.077s] ./gradlew createFileTask
[16:08:26] dongjunkim ~/IdeaProjects/kotlin_project
$ ll
total 64
drwxr-xr-x 14 dongjunkim staff 448 Aug 6 16:08 .
drwxr-xr-x@ 38 dongjunkim staff 1216 Aug 6 15:56 ..
-rw-r--r-- 1 dongjunkim staff 214 Aug 6 15:56 .gitattributes
-rw-r--r-- 1 dongjunkim staff 103 Aug 6 15:56 .gitignore
drwxr-xr-x 8 dongjunkim staff 256 Aug 6 16:08 .gradle
drwxr-xr-x 4 dongjunkim staff 128 Aug 6 16:05 app
drwxr-xr-x 7 dongjunkim staff 224 Aug 6 16:07 buildSrc
drwxr-xr-x 4 dongjunkim staff 128 Aug 6 15:56 gradle
-rwxr-xr-x 1 dongjunkim staff 8706 Aug 6 15:56 gradlew
-rw-r--r-- 1 dongjunkim staff 2918 Aug 6 15:56 flutergradlew.bat
drwxr-xr-x 4 dongjunkim staff 128 Aug 6 15:56 list
-rw-r--r-- 1 dongjunkim staff 18 Aug 6 16:08 myfile.txt
-rw-r--r-- 1 dongjunkim staff 549 Aug 6 15:56 settings.gradle.kts
drwxr-xr-x 4 dongjunkim staff 128 Aug 6 15:56 utilities