====== 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 } }