2020.04.14 추가
Groovy 빌드 스크립트는 대부분 configuration 같다. 예를 들면 프로젝트의 일부 property 설정, 종속성 구성, task 선언 등이 있다. 이런 Configuration은 Groovy 언어 구성을 기반으로 한다. 이 입문서는 이러한 Configuration이 무엇인지 설명하며 가장 중요한 것은 Gradle의 API 문서와 어떻게 관련되는지에 대한 내용이다.
Groovy는 Java를 기반으로 하는 객체 지향 언어이므로 속성과 메소드가 객체에 적용된다. 어떤 경우에는 객체가 암시적인데 특히 빌드 스크립트의 최상위 레벨에서 그렇다 (e.g. {} 블록 안에 중첩되지 않음)
다음은 규정되지 않은 property 및 블록을 포함하는 빌드 스크립트의 일부이다.
version = '1.0.0.GA'
configurations {
...
}
version
과 configurations {}
모두 org.gradle.api.Project
의 일부이다.
이 예제는 모든 Groovy 빌드 스크립트가 암시적인 프로젝트의 인스턴스에 의해 어떻게 지원받는지에 대한 방식을 보여준다. 규정되지 않은 요소가 어디에 정의됐는지 모르는 경우 항상 프로젝트 API 도큐먼트를 확인하여 올바른 위치인지 확인해야한다.
빌드 스크립트에서 Groovy MetaClass 프로그래밍 기술을 사용하지 마라. Gradle은 동적 런타임 속성을 추가하기위한 자체 API를 제공한다.
Groovy의 특수한 MetaClass 프로그래밍을 사용하면 빌드간에 많은 양의 메모리를 유지하므로 Gradle daemon이 메모리 부족을 초래할 수 있다.
🔎 Gradle deamon guide
Gradle은 JVM에서 실행되며 초기화 시간이 요구되는 여러 라이브러리를 사용한다. 결과적으로 이를 시작하는데 성능이 떨어질 수가 있는데, 이에 대한 해결책이 Gradle daemon이다. Gradle daemon은 다른 방법보다 훨씬 빨리 빌드를 실행하는 오래 지속되는 백그라운드 프로세스이다. 프로젝트에 대한 데이터를 메모리에 보관하여 부트 스트랩 프로세스를 피하고 캐싱을 활용해 이를 수행한다. 데몬으로 Gradle 빌드를 실행하기 위해선 사용 여부를 설정하기만 하면 된다.
<obj>.<name> // 속성값 가져오기
<obj>.<name> = <value> // 새로운 값으로 속성 설정
"$<name>" // 문자열에 속성값 포함된 경우
"${<obj>.<name>}" // 위와 동일
/***
Example
*/
version = '1.0.1'
myCopyTask.description = 'Copies some files'
file("$buildDir/classes")
println "Destination: ${myCopyTask.destinationDir}"
속성(property)은 객체의 일부 상태를 나타낸다. = 기호를 통해 보고 있는 속성이 무엇인지 명확히 할 수 있다. 그렇지 않다면, 다른 꾸밈이 없이 <obj>로 시작하는 이름도 property이다.
이름이 규정되지 않은 경우는 다음 중 하나 일 수 있다.
플러그인은 프로젝트 객체에 자체 속성을 추가 할 수 있다. API 도큐먼트 리스트의 모든 속성은 코어 플러그인으로부터 추가돼있다. 속성의 출처를 찾는 것이 힘들다면 빌드에서 사용 중인 플러그인의 도큐먼트를 확인하도록 해라.
코어가 아닌 플러그인으로 추가된 빌드 스크립트에서 프로젝트 속성을 참조할 경우, 속성이 프로젝트 객체에 속할 것이 분명하므로 접두사로
project
를 고려해라.
🔎API document 내의 property
Groovy DSL reference는 빌드 스크립트 내에서 property로서 사용될 수 있음을 표현하고 있으나, property는 뒷단에서 메소드로서 구현되기 때문에 Javadocs는 오로지 메소드만 표시한다.
property 이름은 일반적으로 소문자로 시작하지만, 메소드 이름은 대문자로 시작한다. 따라서 getter 메소드 getProjectVersion()는 projectVersion의 property에 해당한다. 이 규칙은 이름이 두 개 이상의 대문자로 시작할 때는 적용되지 않는다. (e.g. getRAM()은 property로써의 RAM에 해당한다.)
/***
Example
*/
project.getVersion() //아래와 동일
project.version
project.setVersion('1.0.1') //아래와 동일
project.version = '1.0.1'
<obj>.<name>() // Method call with no arguments
<obj>.<name>(<arg>, <arg>) // Method call with multiple arguments
<obj>.<name> <arg>, <arg> // 위와 동일, 괄호 생략한 경우
/***
Example
*/
myCopyTask.include '**/*.xml', '**/*.properties' //<obj>.<name> <arg>, <arg>
ext.resourceSpec = copySpec() // `copySpec()` comes from `Project`
// <obj>.<name>()
file('src/main/java') //'file( .. )' comes from 'Project'
// <obj>.<name>(<arg>)
println 'Hello, World!' // <obj>.<name> <arg> 괄호 생략됨
Gradle은 종종 메서드를 객체의 상태를 구성하는데에 사용하는 반면에, 메소드는 객체의 일부 동작을 나타낸다. 메소드는 argument나 빈 괄호로 식별 가능하다. 메서드에게 argument가 없는 경우와 같이 빈 괄호가 필요가 필요한 경우도 있으므로, 항상 괄호를 사용하는 것이 (해석하기에) 간단할 수 있다.
Gradle에는 메소드가 콜렉션 기반의 프로퍼티와 동일한 이름인 경우, 이 메소드는 그 콜렉션에 값을 추가한다.
블록 또한 메소드이며, 마지막 argument에 대한 특별한 타입일 뿐이다.
<obj>.<name> {
...
}
<obj>.<name>(<arg>, <arg>) {
...
}
/***
Example
*/
configurations {
assets
}
sourceSets { //sourceSets.main.java.srcDirs = ['src']
main {
java {
srcDirs = ['src']
}
}
}
project(':util') {
apply plugin: 'java-library'
}
블록은 빌드 요소의 여러 측면을 한번에 구성하기 위한 메커니즘이다. configuration을 중첩하는 방법을 제공함으로써 구조화된 데이터 형식을 나타내게 해준다.
블록에는 두가지 중요한 측면이 있다.
1. 특정한 signature를 가진 메소드로 구현된다.
2. 규정되지 않은 메소드 및 property의 대상("delegate, 대리인")을 변경할 수 있다.
둘 다 Groovy 언어가 지닌 기능을 기반으로 하며 다음 섹션에서 설명한다.
Block method signatures
signature나 좀 더 구체화된 표현을 통해 블록 뒤에 구현된 메소드를 좀 더 쉽게 식별 할 수 있다. 만약 메소드가 블록에 해당하는 경우 :
예를 들어, Project.copy(Action)은 이런 요구 사항과 일치하므로 다음 구문을 사용할 수 있다.
copy {
into "$buildDir/tmp"
from 'custom-resources'
}
어떻게 into()와 from() 작업을 하는 걸까? 이것들은 메소드가 분명하지만 API 도큐먼트의 어디서 찾을 수 있을까? 정답은 객체의 위임을 이해함으로써 나온다.
Delegation
규정되지 않은(unqualified) 속성을 찾을 수 있는 속성 목록의 섹션 중 하나의 공통 장소는 프로젝트 오브젝트에 있다. 그러나 블록 내부에 그런 규정되지 않은 속성 및 방법의 대체 소스인 the block’s delegate object
가 존재한다.
이 개념을 설명하기 위해 이전 섹션의 마지막 예를 다시 보도록 하자.
copy {
into "$buildDir/tmp"
from 'custom-resources'
}
이 예제의 모든 메소드와 특성은 규정되지 않았다. Project API 도큐먼트 내에서 copy()와 buildDir을 찾겠지만 into()와 from()은 어떡할 것인가? 이는 copy {} 블록의 위임(delegate)에 의해 해결된다. deledate의 유형이 무엇인지 알아내기 위해선 해당하는 API 도큐먼트를 참조하도록 한다.
블럭 메소드의 signature에 따라 delegate 유형을 결정하는 두 가지 방법이 있다.
2020.04.15 추가
이 인터페이스는 빌드 파일에서 Gradle과 상호 작용하는데 사용하는 기본 API이다. Project로 부터 모든 Gradle 기능에 프로그래밍 방식으로 엑세스 가능하다.
Project와 build.gradle 파일 사이에는 일대일 관계가 있다. 필드 초기화 중 Gradle은 빌드에 참여할 각 프로젝트에 대한 객체를 다음과 같이 조립한다.
Project.evaluationDependsOnChildren()
이나 Project.evaluationDependsOn(java.lang.String)
를 사용해 명시적 평가 종속성(evaluation dependency)을 추가함으로써 무시 할 수 있다.✔️Project.evaluationDependsOnChildren() : 각 하위 프로젝트에 대한 평가 종속성이 있음을 선언한다.
✔️Project.evaluationDependsOn(java.lang.String) : 주어진 경로를 가진 프로젝트에 대한 평가 종속성이 있음을 선언한다.
프로젝트는 기본적으로 task 객체의 모음이다. 각 task는 클래스 컴파일, 단위 테스트 실행, WAR 파일 압축과 같은 기본 작업을 수행하다. TaskContainer의 create() 메소드 중 하나를 사용해 프로젝트에 task를 추가할 수 있다.(e.g. TaskContainer.create(java.lang.String)
). TaskContainer의 lookup 메소드 중 하나를 사용하여 기존 task를 찾을 수 있다.(e.g. TaskCollection.getByName(java.lang.String)
)
프로젝트는 일반적으로 task를 수행하는데 필요한 많은 의존성이 있다. 또한 프로젝트는 일반적으로 다른 프로젝트에서 사용할 수 있는 많은 아티책트를 생성한다. 이러한 의존성은 configuration으로 그룹화되며 리포지토리에서 검색, 업로드 할 수 있다.
Project.getConfigurations()
메소드의 반환값인 ConfigurationContainer
를 사용해 configuration을 관리할 수 있다. Project.getDependencies()
메소드의 반환값인 DependencyHandler
는 dependency를 관리한다.Project.getArtifacts()
메소드의 반환값인 ArtifactHandler
는 아티팩트를 관리한다.Project.getRepositories()
메소드의 반환값인 RepositoryHandler
는 레포지토리를 관리한다.메소드 | output | 관리하는 대상 |
---|---|---|
Project.getConfigurations() | ConfigurationContainer | configuration |
Project.getDependencies() | DependencyHandler | dependency |
Project.getArtifacts() | ArtifactHandler | 아티팩트 |
Project.getRepositories() | RepositoryHandler | 레포지토리 |
프로젝트는 프로젝트의 계층구조를 따라 정렬된다. 프로젝트에는 이름과 계층 구조에서 프로젝트를 고유하게 식별하는 정규화된 경로가 존재한다.
프로젝트 구성을 모듈화하고 재사용하는데 사용할 수 있다. 플러그인은 PluginAware.apply(java.util.Map)
메소드를 사용하거나 PluginDependenciesSpec 플러그인 스크립트 블록을 사용해 적용할 수 있다.
Gradle은 프로젝트를 구성하기 위해 프로젝트 인스턴스에 대한 프로젝트 빌드 파일을 실행한다. 스크립트가 사용하는 모든 property 또는 메소드는 관련 프로젝트 객체에 위임된다. 즉, 스크립트에서 프로젝트 인터페이스의 메소드와 property를 직접 사용할 수 있다.
For example:
defaultTasks('some-task') // Delegates to Project.defaultTasks()
reportsDir = file('reports') // Delegates to Project.file() and the Java Plugin
또한 프로젝트 property를 사용해 프로젝트 인스턴스에 엑세스 할 수 있다. 이는 경우에 따라 스크립트를 더욱 명확하게 만든다. 예를 들어 프로젝트 이름을 엑세스 하는 경우 name
대신 project.name
을 사용할 수 있다.
프로젝트에는 5개의 속성 scope가 있으며 property를 검색한다. 빌드 파일에서 이름을 사용하거나, 프로젝트의 Project.property(java.lang.String)
메소드를 호출함으로써 그러한 property를 엑세스 할 수 있다.
스코프scope
는 다음과 같다.
Project.getRootProject()
를 이용해 rootProject의 property를 엑세스할 수 있다.convention
객체를 통해 프로젝트에 property와 메소드를 추가할 수 있다.📌프로퍼티를 읽을 때 프로젝트는 위의 스코프를 순서대로 검색하고, 해당 프로퍼티를 처음 찾은 스코프에서 값을 반환한다. 자세한 내용은 Project.property(java.lang.String)를 참조하라.
Object property(String propertyName)
요청한 프로퍼티의 값을 반환한다. 이 메소드는 다음과 같이 프로퍼티를 찾는다.
1. 이 프로젝트 객체에 해당하는 이름이 있으면, 값을 반환한다.
2. 이 프로젝트에 해당하는 이름의 확장자(extension)이 있으면 확장자를 반환한다.
3. 이 프로젝트의 컨벤션 객체에 해당 이름으로 지정된 프로퍼티가 있으면 값을 반환한다.
4. 이 프로젝트에 해당 이름의 추가 프로퍼티가 있으면 값을 반환한다.
5. 이 프로젝트에 해당 이름의 task가 있으면 task를 반환한다.
6. 이 프로젝트의 조상 프로젝트에서 해당 이름의 컨벤션 프로퍼티나 추가 프로퍼티를 검색한다.
7. 찾을 수 없으면 MissingPropertyException
이 발생한다.
📌프로퍼티를 작성할 때 프로젝트는 위의 스코프를 순서대로 검색하고 프로퍼티를 처음 찾은 스코프에서 값을 설정한다. 해당 프로퍼티를 찾지 못하면 예외가 발생한다. 자세한 내용은 Project.setProperty(java.lang.String, java.lang.Object)를 참조하라.
void setProperty(String name, Object value)
해당 프로젝트의 속성을 설정한다.
이 메소드는 다음 위치에서 지정된 이름의 프로퍼티를 검색하고, 프로퍼티를 발견하는 첫번째 스코프에서 값을 설정한다.
1. 해당 프로젝트 오브젝트 자신. 예를 들어 rootDir 프로젝트의 프로퍼티
2. 프로젝트의 컨벤션. 예를 들어 srcRootName 플러그인의 프로퍼티
3. 프로젝트의 추가 프로퍼티.
속성을 찾을 수 없으면 MissingPropertyException
이 발생한다.
모든 추가 프로퍼티는 ext
네임 스페이스를 통해 정의해야한다. (🤔커스텀 프로퍼티라고 이해하면 될듯) 추가 속성이 정의되면 소유 객체(아래 예시에서 각 프로젝트, task, 하위 프로젝트)에서 직접 사용할 수 있으며 읽거나 업데이트 또한 가능하다. 네임 스페이스를 통해 초기 선언만 수행하면 된다.
project.ext.prop1 = "foo"
task doStuff {
ext.prop2 = "bar"
}
subprojects { ext.${prop3} = false }
ext
또는 소유한 객체를 통해 추가 프로퍼티를 읽는다.
ext.isSnapshot = version.endsWith("-SNAPSHOT")
if (isSnapshot) {
// do snapshot stuff
}
프로젝트에는 5개의 메소드 scope가 잇으며 메소드를 검색한다.
Task.configure(groovy.lang.Closure)
메소드는 제공된 클로저와 연관된 task에 대한 메소드를 호출한다.void compile(Closure configureClosure).
링크에서 모든 내용 확인 가능