group, name, version 순서로 써줄 수 있다.
dependencies { compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final' } // 짧게 쓰면 "group:name:version" dependencies { compile 'org.hibernate:hibernate-core:3.6.7.Final' }
Maven과 Ivy 리포지토리를 지원한다.
repositories { mavenCentral() }
repositories { maven { url "http://repo.mycompany.com/maven2" } }
maven {}
을 여러번 사용하는 것이 가능하다.repositories { maven { credentials { username 'user' password 'password' } url "http://repo.mycompany.com/maven2" } }
2.+
latest.integration
changing = true
옵션 추가compile ('com.some:library:0.1') { changing = true }
changing = true
설정이 필요하다.
경험상으로 볼 때 failOnVersionConflict()
를 설정하고 의존성을 관리하는 것이 좋아보인다. 나도 모르는 사이에 이행성에 의해 버전이 변하는 것을 방지할 수 있다.
configurations.all { resolutionStrategy { // 동일 모듈에 대한 버전 충돌시 즉시 오류 발생하고 실패. failOnVersionConflict() // 특정 모듈의 버전을 강제 지정(최상위건 이행적 의존성이건 무관함) force 'asm:asm-all:3.3.1', 'commons-io:commons-io:1.4' // 이미 강제 지정된 모듈 버전을 대체함 forcedModules = ['asm:asm-all:3.3.1'] // 동적 버전 탐색을 10분 캐시 cacheDynamicVersionsFor 10, 'minutes' // 변하는 모듈(Changing Module)을 캐시하지 않음 cacheChangingModulesFor 0, 'seconds' } }
configurations
로 그룹화 된다. 구성의 각 그룹은 클래스패스를 의미한다.configurations
객체로 관리한다. configurations 객체에 클로저를 전달하면 이 클래스의 API가 호출된다.configurations
의 각 항목은 Configuration 객체이다.compile
: 프로젝트를 컴파일할 때 필요한 의존 라이브러리들runtime
: 프로젝트를 실행할 때 필요한 의존 라이브러리들. 기본적으로 compile을 모두 포함한다.testCompile
: 프로젝트의 테스트를 컴파일할 때 필요한 라이브러리들. 기본적으로 프로젝트의 컴파일된 클래스들과 compile 의존성을 포함한다.testRuntime
: 프로젝트의 테스트를 실행할 때 필요한 라이브러리들. 기본적으로 compile, runtime, testCompile 의존성을 포함한다.configurations { compile }
configurations
의 항목은 Configuration 객체이다.configurations { compile { description = 'compile classpath' transitive = true } runtime { extendsFrom compile } } configurations.compile { description = 'compile classpath' }
리포지토리에 있는 외부 모듈에 대한 의존성. 가장 일반적인 방식이다.
dependencies { runtime group: 'org.springframework', name: 'spring-core', version: '2.5' runtime 'org.springframework:spring-core:2.5', 'org.springframework:spring-aop:2.5' runtime( [group: 'org.springframework', name: 'spring-core', version: '2.5'], [group: 'org.springframework', name: 'spring-aop', version: '2.5'] ) runtime('org.hibernate:hibernate:3.0.5') { transitive = true } runtime group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true runtime(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') { transitive = true } }
default
구성에 의존성을 지정한 것이다.default
구성에 첨부된 모든 artifact들이다.default
구성에 원치않는 artifact가 있을 경우에 해당 모듈을 빼도록 의존성을 구성해야 한다.default
가 아닌 다른 구성에 들어있을 경우 어떤 구성의 artifact를 가져올지 명시해줘야 한다.@jar
가 된다. (artifact 파일이 zip 일수도 있다)ext
키에 확장자를 지정한다.pom.xml
이나 ivy.xml
같은 모듈 기술자 파일은 다운로드하지 말라는 뜻이기 때문이다. 모듈 기술자 파일을 받지 않았으므로 의존성을 분석할 수 없기 때문에 해당 모듈의 해당 확장자 파일만 받게 된다. 이미 다운로드 받은 모듈 기술자는 무시한다.dependencies { runtime "org.groovy:groovy:1.8.7@jar" runtime group: 'org.groovy', name: 'groovy', version: '1.8.7', ext: 'jar' }
compile "org.gradle.test.classifiers:service:1.0:jdk15@jar" otherConf group: 'org.gradle.test.classifiers', name: 'service', version: '1.0', classifier: 'jdk15'
다음 태스크를 만들고 gradle -q listJars
로 실행한다. 태스크 없이 gradle dependencies
만 해도 된다.
task listJars << { configurations.compile.each { File file -> println file.name } }
configurations.compile
객체 같은 것)configurations.[configurationName].resolvedConfiguration.resolvedArtifacts
를 이터레이션 돌면 된다. ResolvedArtifactresolvedArtifact.getModuleVersion().getId()
는 ModuleVersionIdentifier 객체이며 여기에 모듈에 관한 group, name, version 정보가 들어있다.dependencies { runtime module("org.codehaus.groovy:groovy-all:1.8.7") { dependency("commons-cli:commons-cli:1.0") { transitive = false } module(group: 'org.apache.ant', name: 'ant', version: '1.8.4') { dependencies "org.apache.ant:ant-launcher:1.8.4@jar", "org.apache.ant:ant-junit:1.8.4" } } }
dependencies { compile project(':shared') // shared 프로젝트에 의존 }
dependencies { runtime files('libs/a.jar', 'libs/b.jar') runtime files('dir/to/classes') // 디렉토리 자체를 클래스패스에 추가 runtime rootProject.files('root-project-dir') // rootProject의 디렉토리를 클래스패스에 추가 runtime fileTree(dir: 'libs', include: '*.jar') }
dependencies { compile files("$buildDir/classes") { builtBy 'compile' // 'compile' 태스크에 의해 클래스 파일들 생성됨. } } task compile << { println 'compiling classes' } task list(dependsOn: configurations.compile) << { println "classpath = ${configurations.compile.collect {File file -> file.name}}" }
Gradle 태스크나 플러그인을 만들 경우에 현재 Gradle API(DependencyHandler.gradleApi()에 대해 의존성을 지정할 수 있다.
dependencies { compile gradleApi() }
Gradle 과 함께 배포되는 Groovy에 대한 의존성(DependencyHandler.localGroovy())을 지정할 수 있다. 마찬가지로 Gradle 태스크나 플러그인을 만들때 사용할 수 있다.
dependencies { groovy localGroovy() }
이행적 의존성 중에서 일부는 제외하도록 설정할 수 있다.
// 구성 단위 제외 configurations { compile.exclude module: 'commons' // compile configuration에서 특정 모듈 제외 all*.exclude group: 'org.gradle.test.excludes', module: 'reports' // 모든 configuration에서 특정 모듈 제외 } // 의존성 단위 제외 dependencies { compile("org.gradle.test.excludes:api:1.0") { exclude module: 'shared' // 특정 의존성에 대해선만 모듈 제외. exclude group: 'groupId', module: 'artifactId' } }
all*
으로 Groovy의 spread-dot 연산자를 사용한다.module: '이름
')하거나 그룹 이름(group: '그룹이름
')만 지정하거나 혹은 둘다 함께 지정할 수 있다.name
을 제외한 모든 속성은 선택적이다. 리토지토리 타입에 따라 어떤 속성은 꼭 필요하기도 하고 그렇지 않기도 한다.dependencies { runtime ":junit:4.10", ":testng" runtime name: 'testng' }
dependencies { runtime group: 'org.somegroup', name: 'somedependency', version: '1.0', configuration: 'someConfiguration' }
dependencies { compile project(path: ':api', configuration: 'spi') }
gradle -q dependencies 서브프로젝트:dependencies
configurations { all*.exclude group: 'xml-apis', module: 'xmlParserAPIs' } // Equivalent to: configurations { all.collect { configuration -> configuration.exclude group: 'xml-apis', module: 'xmlParserAPIs' } }
configurations { sealife alllife } dependencies { sealife "sea.mammals:orca:1.0", "sea.fish:shark:1.0", "sea.fish:tuna:1.0" alllife configurations.sealife // sealife에서 상속 받음 alllife "air.birds:albatros:1.0" }
task dependencies << { configurations.alllife.dependencies.each { dep -> println dep.name } println() configurations.alllife.allDependencies.each { dep -> println dep.name } println() configurations.alllife.allDependencies.findAll { dep -> dep.name != 'orca' }.each { dep -> println dep.name } }
files()
메소드로 접근할 수 있다.task allFiles << { configurations.sealife.files.each { file -> println file.name } }
Configuration.files
메소드는 해당 구성의 모든 artifact를 가져온다.task copy << { configurations.alllife.copyRecursive { dep -> dep.name != 'orca' }.allDependencies.each { dep -> println dep.name } println() configurations.alllife.copy().allDependencies.each { dep -> println dep.name } }
copy()
메소드는 해당 구성에 속한 의존성만 복사한다.copyRecursive()
메소드는 상속한 부모의 구성의 의존성까지 복사한다.task copyVsFiles << { configurations.sealife.copyRecursive { dep -> dep.name == 'orca' }.each { file -> println file.name } println() // 위와는 다른 내용 출력 configurations.sealife.files { dep -> dep.name == 'orca' }.each { file -> println file.name } } // 결과 > gradle -q copyVsFiles orca-1.0.jar seal-1.0.jar orca-1.0.jar seal-2.0.jar
orca
는 seal-1.0
에 의존하고 있는 반면, shark
는 seal-2.0
에 의존하고 있는 상태.seal-2.0
을 사용한다. 따라서 원본을 사용하는 files()
메소드는 orca
에 대한 이행적 의존성 판단 결과로 seal-2.0
을 선택한다.orca
의 의존성만 복제했으며 따라서 버전 충돌이 없고 seal-1.0
이 선택된다.repositories { mavenCentral() }
repositories { mavenLocal() }
settings.xml
이 존재하면 이에 따라 로컬 리포지토리를 판단한다.$USER_HOME/.m2/repository
기본값$USER_HOME/.m2/settings.xml
우선$M2_HOME/conf/settings.xml
차선repositories { maven { url "http://repo.mycompany.com/maven2" } }
artifactUrls
를 지정해 JAR를 찾을 수 있게 해준다.repositories { maven { // POM과 jar등의 artifact 탐색 url "http://repo2.mycompany.com/maven2" // 위에서 artifact가 없으면 다음에서 검색 artifactUrls "http://repo.mycompany.com/jars" artifactUrls "http://repo.mycompany.com/jars2" } }
repositories { maven { credentials { username 'user' password 'password' } url "http://repo.mycompany.com/maven2" } }
repositories { flatDir { dirs 'lib' } flatDir { dirs 'lib1', 'lib2' } }
name
만 지정해주면 된다.repositories { mavenCentral() flatDir { dirs 'libs' } } /* libs 디렉토리에 imap.jar, smtp.jar, hibernate-jpa-2.0-api-1.0.0.Final.jar 가 있을 때 */ dependencies { compile name: 'imap' // 혹은 compile ':imap' compile name: 'smtp' // 혹은 compile ':smtp' // 혹은 compile ':hibernate-jpa-2.0-api:1.0.0.Final' compile name: 'hibernate-jpa-2.0-api', version: '1.0.0.Final' } task listJars << { configurations.compile.each { File file -> println file.name} }
gradle -q listJars
실행 결과imap.jar smtp.jar hibernate-jpa-2.0-api-1.0.0.Final.jar
repositories { ivy { url "http://repo.mycompany.com/repo" layout "maven" } }
repositories { ivy { url "http://repo.mycompany.com/repo" layout 'pattern', { artifact "[module]/[revision]/[artifact].[ext]" ivy "[module]/[revision]/ivy.xml" } } }
repositories { ivy { artifactPattern "http://repo.mycompany.com/3rd-party-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" artifactPattern "http://repo.mycompany.com/company-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" ivyPattern "http://repo.mycompany.com/ivy-files/[organisation]/[module]/[revision]/ivy.xml" } }
repositories { ivy { credentials { username 'user' password 'password' } artifactPattern "http://repo.mycompany.com/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" } }
repositories { flatDir { name 'localRepository' } } repositories { localRepository { dirs 'lib' } } repositories.localRepository { dirs 'lib' }
Gradle은 의존성과 선언과 리포지토리 정의를 읽어서 의존성 결정 과정(dependency resolution)을 통해 모든 의존하는 파일을 다운로드한다. 그 과정은 다음과 같다.
1.+
형태)으로 지정돼 있다면, Gradle이 가장 최신의 정적 버전을 리토지토리에서 찾아서 정한다. Maven에서는 maven-metadata.xml
파일로, Ivy에서는 디렉토리의 파일 목록을 확인해서 결정한다.pom
파일이고 부모 pom이 선언돼 있다면, Gradle은 해당 pom의 부모까지 재귀적으로 탐색하여 정하게 된다.Gradle의 의존성 캐시는 두가지 키 타입의 스토리지로 이뤄져 있다.
--offline
스위치는 재검사할 필요가 있는지 여부와 무관하게 항상 캐시를 사용해 의존성을 결정하도록 한다. Gradle이 의존성 결정때문에 네트워크에 접근하는 일은 생기지 않는다. 필요한 모듈이 캐시에 없으면 빌드가 실패한다.
리포지토리 설정 상태에 따라 캐시의 싱크가 안 맞을 수도 있다. 리포지토리를 잘못 설정했거나, 변하지 않는 모듈을 잘못 배포했을 수 있다.
--refresh-dependencies
옵션을 사용하면 모든 의존성 캐시를 갱신한다. 이 옵션을 사용하면 결정된 모듈과 artifact의 모든 캐시된 항목들을 무시한다. 모든 리포지토리에 대해 의존성 결정을 다시 수행하고 다운로드한다.
ResolutionStrategy를 통해 캐시 세부 설정이 가능하다.
configurations.all { resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes' // 10분 }
configurations.all { resolutionStrategy.cacheChangingModulesFor 30, 'days' // 30일 }
compileOnly
Scope가 생겼다.compileOnly
를 사용한다.compileOnly
는 의존성이 test 로 이행되지 않기 때문에 test 에는 testCompile
의존성으로 지정하거나 혹은 아래와 같이 강제로 이행처리를 해야한다. compileOnly dependencies are not available in testssourceSets { test.compileClasspath += configurations.compileOnly test.runtimeClasspath += configurations.compileOnly } // 혹은 configurations { testImplementation.extendsFrom compileOnly }
아래 모든 방법들을 사용하기 보다는 Gradle Web(War) Plugin 에 나오는 exclude 방식을 추천.
컴파일시에는 클래스패스에 넣지만, 실행/배포시에는 빠져야 하는 의존성이 있을 수 있다. 예를 들면 Servlet API 같은 것들이 그렇다. Servlet API는 Tomcat등의 WAS에 내장되어 있으므로 배포는 할 필요가 없다.
Gradle 1.2는 현재 Gradle Web(War) Plugin이 아닐경우에 provided
를 제공해주고 있지 않다. 하지만 이를 흉내낼 수 있다. [#GRADLE-784] Provide a 'provided' configuration 참조.
configurations { provided } /* 배포시에는 빠지고, 컴파일시에는 들어가는 의존성을 provided에 지정한다. */ sourceSets { main { compileClasspath += configurations.provided } test { compileClasspath += configurations.provided } } // war 프로젝트일 경우 war { classpath -= configurations.provided }
Gradle Ecplise Plugin 사용시 조정이 필요하다.
eclipse { classpath { // 클래스패스에는 넣지만... plusConfigurations += configurations.provided // .classpath 파일에서 해당 jar의 classpathentry@export를 false로 변경 noExportConfigurations += configurations.provided // 현재 Gradle 1.2는 noExportConfigurations를 설정해도 WTP에서 export되는 문제가 있다. // 이 문제는 멀티 프로젝트일 경우에만 발생한다. 멀티 프로젝트 아니라며 다음은 불필요함. // .classpath XML에서 @exported == false|null인 classpathentry의 // "org.eclipse.jst.component.dependency" <attribute> 삭제해야 한다 file.withXml { provider -> provider.asNode().classpathentry.findAll { it.@kind == 'lib' && (it.@exported == null || it.@exported == 'false') }.each { cpe -> def attributes = cpe.children()[0]; if (attributes == null) { return } def componentDependency = attributes.attribute.find { it.@name == 'org.eclipse.jst.component.dependency'} if (componentDependency == null) { return } attributes.remove(componentDependency) } } } wtp { component { // WTP Deployment Assembly component 에서는 뺀다. 'war' 플러그인이 적용되지 않은 상태에서는 사용 할 수 없다. minusConfigurations += configurations.provided } } }
Gradle Community Forums - How can I gather all my project's dependencies into a folder?
task copyToLib(type: Copy) { into "$buildDir/output/lib" from configurations.runtime }
./gradlew dependencyInsight --configuration testCompile --dependency junit
settings.gradle
에서 includeBuild '../another_project''