====== Gradle 파일 다루기 ======
* [[http://www.gradle.org/docs/current/userguide/working_with_files.html|Working With Files]]
===== 파일 객체 확보 =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file(java.lang.Object)|Project.file()]] 메소드로 프로젝트에 상대적인 경로에 있는 파일 객체를 얻을 수 있다.
// 프로젝트 디렉토리에 대해 상대 경로
File configFile = file('src/config.xml')
// 절대 경로
configFile = file(configFile.absoluteFile)
// 현재 명령이 실행된 위치의 상대 경로로 된 파일 객체 사용
configFile = file(new File('src/config.xml'))
* ''file()'' 메소드에 인자로 아무것이나 넘겨도 되며, 자동으로 이를 File 객체로 바꾸는 시도를 한다. 보통은 String, File 객체를 사용한다.
* 보통은 프로젝트경로에 상대 경로로 지정되며, 절대 경로 문자열이 넘어올 경우이면 절대 경로를 사용한다.
* ''file()'' 메소드는 ''file:/some/path.xml''같은 URL도 인식한다.
* ''file('상대경로')'' : 현재 디렉토리와 무관하게 프로젝트 디렉토리에 상대 경로로 간주.
* ''new File('경로')'' : 현재 디렉토리에 상대 경로
* ''file()'' 메소드로 절대 경로를 가리키려면 ''file(new File('경로').absoluteFile)'' 형태를 사용해야 한다.
===== File Collections =====
* [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileCollection.html|FileCollection]] Interface. 말 그대로 파일들의 컬렉션이다.
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:files(java.lang.Object...)|Project.files()]] 메소드로 파일 컬렉션 인스턴스를 생성할 수 있다.
* ''file()''처럼 프로젝트에 상대적인 경로 우선이다.
* 일반 용법
FileCollection collection = files('src/file1.txt', new File('src/file2.txt'), ['src/file3.txt', 'src/file4.txt'])
* 다양한 용법
// 일반 컬렉션 처럼 반복 가능
collection.each {File file ->
println file.name
}
// 다른 컬렉션으로 변경
Set set = collection.files
Set set2 = collection as Set
List list = collection as List
String path = collection.asPath
File file = collection.singleFile
File file2 = collection as File
// 컬렉션 합치기, 빼기
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')
* ''files()''에 클로저 혹은 Callable 인스턴스를 인자로 주기. 클로저에서 files() 메소드의 인자가 될 수 있는 값을 리턴해주면 된다.
task list << {
File srcDir
// 클로저로 파일 컬렉션 생성하기. 현재 시점에 srcDir == null 이지만, 늦은 초기화를 하기 때문에 문제가 없다.
def collection = files { println ">> ${srcDir}"; srcDir.listFiles() }
srcDir = file('src')
println "Contents of $srcDir.name"
// 여기서 collection 초기화 하면서 위에서 정의한 클로저 실행.
collection.collect { relativePath(it) }.sort().each { println it }
srcDir = file('src2')
println "Contents of $srcDir.name"
// collection을 다시 초기화 하면서 위에서 정의한 클로저 재실행.
collection.collect { relativePath(it) }.sort().each { println it }
}
* files()의 인자로 가능한 다른 타입들
* FileCollection : 펼쳐진 상태로 추가된다.
* Task : 태스크의 oputput 파일이 파일 컬렉션이 추가된다.
* TaskOutputs : TaskOutputs의 출력 파일들이 추가된다.
* **파일 컬렉션은 늦은 초기화로 수행**된다. 즉, 미래에 만들어질 파일을 파일 컬렉션으로 만들어도 된다.
===== 파일 Tree =====
* [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileTree.html|FileTree]] Interface. 계층구조로 된 파일의 컬렉션. FileCollection을 구현하고 있다.
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:fileTree(java.util.Map)|Project.fileTree()]] 메소드로 객체 생성.
* Ant 스타일의 include/exclude가 가능하다.
* FileTree 생성하는 법
task list << {
// 기준 디렉토리를 지정하여 FileTree 생성
FileTree tree = fileTree(dir: 'src/main')
// 패턴 추가/제외
tree.include '**/*.java'
tree.exclude '**/Abstract*'
// 경로를 바로 줘서 생성
tree = fileTree('src').include('**/*.java')
// 클로저로 생성하기, 여러개씩 include, exclude
tree = fileTree('src') {
include '**/*.java'
include '**/*.xml'
exclude(['**/Abstract*', '**/*Test.java'])
}
tree.each { File file ->
println file.absolutePath
}
// map으로 생성하기
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')
}
* FileTree 용법 : FileCollection의 모든 용법을 포함한다.
task list << {
def tree = fileTree('src') {
include '**/*.java'
include '**/*.xml'
exclude(['**/Abstract*', '**/*Test.java'])
}
// include에 매칭되는 것만 필터링
FileTree filtered = tree.matching {
include '**/*properties.xml'
}
filtered.each { File file ->
println "Properties : ${file}"
}
FileTree sum = tree + fileTree(dir: 'src/test')
tree.visit { element ->
println "$element.relativePath => $element.file"
}
}
* [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/FileTree.html#matching%28groovy.lang.Closure%29|FileTree.matching(Closure)]]에서 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html|PatternFilterable]]의 메소드들을 사용해 필터링 규칙을 정한다.
* ''zipTree()''와 ''tarTree()''로 압축 파일에 대해 FileTree 객체 생성 가능.
FileTree zip = zipTree('someFile.zip')
FileTree tar = tarTree('someFile.tar')
//tar tree는 압축형식을 파일 확장자로 스스로 판단하지만 (보통 *.tar.gz 형태)
//압축 형태를 파일명으로 알 수 없을 때는 다음과 같이 명시한다.
FileTree someTar = tarTree(resources.gzip('someTar.ext'))
===== 입력 파일 묶음 지정 =====
* 다양한 곳에서 입력 파일 묶음을 지정해야 한다.(예: 자바 클래스 컴파일시 소스 파일 목록 등).
* 이 입력 파일 묶은음 ''files()'' 로 생성가능한 모든 값을 지정할 수 있다. 즉, 다시말해 문자열, file, 컬렉션, FileCollection, FileTree 게다가 클로저를 이용할 수도 있다.
// Use a File object to specify the source directory
compile {
source = file('src/main/java')
}
// Use a String path to specify the source directory
compile {
source = 'src/main/java'
}
// Use a collection to specify multiple source directories
compile {
source = ['src/main/java', '../shared/java']
}
// Use a FileCollection (or FileTree in this case) to specify the source files
compile {
source = fileTree(dir: 'src/main/java').matching { include 'org/gradle/api/**' }
}
// Using a closure to specify the source files.
compile {
source = {
// Use the contents of each zip file in the src dir
file('src').listFiles().findAll {it.name.endsWith('.zip')}.collect { zipTree(it) }
}
}
* 대부분의 경우 입력 파일지정 프라퍼티는 동일한 이름의 메소드로도 호출 가능하다.
compile {
// Add some source directories use String paths
source 'src/main/java', 'src/main/groovy'
// Add a source directory using a File object
source file('../shared/java')
// Add some source directories using a closure
source { file('src/test/').listFiles() }
}
===== 파일/디렉토리 관리 =====
파일/디렉토리 관리는 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/Project.html|Project]] 객체에 내장된 각종 메소드를 직접 호출하거나, Copy 등의 상위 태스크를 상속받아 태스크로 만드는 방식으로 처리할 수도 있다.
===== 파일 복사 =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html|Copy]] 태스크로 파일을 복사한다.
* 복사시 입력 소스와 출력 대상은 [[http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/CopySpec.html|CopySpec]] 인터페이스의 메소드로 표현한다. Copy 태스크는 이 인터페이스를 구현하고 있다.
* 기본 동작
task copyTask(type: Copy) {
from 'src/main/webapp'
into 'build/explodedWar'
}
* ''from()'' 메소드는 ''files()''에서 받을 수 있는 모든 인자를 받는다.
* 인자가 디렉토리이면 그 디렉토리 이하 모든 파일(디렉토리 자체는 제외)을 복사
* 인자파 파일이면 해당 파일만 복사
* 인자가 존재하지 않는 파일이면 무시.
* ''into()'' 메소드는 ''file()''에서 받을 수 있는 모든 인자를 받는다.
* from 예
task anotherCopyTask(type: Copy) {
// Copy everything under src/main/webapp
from 'src/main/webapp'
// Copy a single file
from 'src/staging/index.html'
// copyTask의 outputs
from copyTask
// copyTaskWithPatterns 태스크의 outputs 명시
from copyTaskWithPatterns.outputs
// Zip 파일 내용
from zipTree('src/main/assets.zip')
// Determine the destination directory later
into { getDestDir() }
}
* 복사 대상을 상세히 명시
task copyTaskWithPatterns(type: Copy) {
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
exclude { details -> details.file.name.endsWith('.html') && details.file.text.contains('staging') }
}
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:copy(groovy.lang.Closure)|Project.copy()]] 메소드도 동일하게 사용할 수 있다.
task copyMethod << {
copy {
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
}
}
==== 복사시 이름 변경 ====
task rename(type: Copy) {
from 'src/main/webapp'
into 'build/explodedWar'
// Use a closure to map the file name
rename { String fileName ->
fileName.replace('-staging-', '')
}
// Use a regular expression to map the file name
rename '(.+)-staging-(.+)', '$1$2'
rename(/(.+)-staging-(.+)/, '$1$2')
}
==== 복사시 파일 내용 필터링(내용 변형) ====
import org.apache.tools.ant.filters.FixCrLfFilter
import org.apache.tools.ant.filters.ReplaceTokens
task filter(type: Copy) {
from 'src/main/java'
into 'build/filtered'
// Substitute property references in files
expand(copyright: '2009', version: '2.3.1')
expand(project.properties)
// Ant이용.
filter(FixCrLfFilter)
filter(ReplaceTokens, tokens: [copyright: '2009', version: '2.3.1'])
// 모든 줄을 대괄호로 감싸기
filter { String line ->
"[$line]"
}
}
==== CopySpec ====
* CopySpec은 계층구조이다. 목표 경로, include/exclude 패턴, 복사 행위, 이름 매핑, 필터 등을 모두 상속한다.
* nested copy specs
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.hibernate:hibernate-core:3.6.7.Final'
}
task nestedSpecs(type: Copy) {
into 'build/explodedWar'
exclude '**/*Test.java'
from('src/main') {
include '**/*.java'
}
// 아래 into는 위에서 선언한 build/explodedWar를 상속하여 그에 상대적인 경로이다.
// 즉 buld/explodedWar/libs 를 뜻한다.
into('libs') {
// 모든 의존성 *.jar들을 libs 에 복사한다.
from configurations.runtime
}
}
===== Sync =====
* Copy를 상속한 태스크.
* 두 폴더간의 싱크를 수행한다.
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.hibernate:hibernate-core:3.6.7.Final'
}
// 모든 의존성 *.jar들을 build/libs 에 복사한다.
task libs(type: Sync) {
from configurations.runtime
into "$buildDir/libs"
}
===== 파일 압축 =====
* [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.Jar.html|Jar]], [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.War.html|War]], [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.Zip.html|Zip]], [[http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.Tar.html|Tar]] 등을 지원한다.
* 기본 용법
apply plugin: 'java'
task zip(type: Zip) {
from 'src/dist'
into('libs') { // 압축 파일 안의 libs/ 디렉토리로 파일 넣음
from configurations.runtime
}
}