关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qiniudn.com 上的文档为准。如发现翻译有误的地方,将首先在以上两个地方更新。因时间精力问题,博客中发表的译文基本不会同步修改。html
依赖管理是每一个构建的关键特性,而且 Gradle 强调提供最好的依赖管理,容易理解以及使用各类各样的方法来兼容。若是您熟悉使用 Maven 或 Ivy 的方法,那么你会很高兴地知道,Gradle 彻底兼容这两种方法,除此以外,它还有足够的灵活性,以支持彻底自定义的方法。java
这里是 Gradle 支持的依赖管理的主要亮点:git
依赖管理传递: Gradle 赋予你对你的项目依赖项树的彻底控制。github
对非管理依赖的支持:若是你的依赖只是版本控制下或共享的驱动器中的文件,Gradle 也提供了强大的功能以支持这种状况。spring
支持依赖定义的自定义:Gradle 的模块依赖给你在构建脚本中描述依赖项层次结构的能力。apache
一个彻底可自定义的依赖解析的方法:Gradle 为你提供了自定义解析规则的能力,让你能够轻松替换依赖。编程
彻底兼容 Maven 和 Ivy :若是你已经在 Maven POM 文件或 Ivy 文件中定义了依赖,Gradle 提供了一系列受欢迎的构建工具能够进行无缝集成。api
与现有依赖管理基础结构的集成:Gradle 兼容 Maven 和 Ivy 仓库。若是你使用 Archiva, Nexus,或者是 Artifactory, Gradle 100% 兼容全部仓库的格式。数组
成千上万的开源组件互相依赖,每一个组件都有一系列的版本及不兼容性,随着构建变得愈来愈复杂,依赖管理经常出现各类问题。当一个构建的依赖树变得笨拙时,你的构建工具不该该强迫你对依赖管理采起单1、不灵活的作法。一个正确的构建系统必须被设计得很灵活,而 Gradle 能够应付任何状况。缓存
依赖管理在从一个构建系统迁移到另外一个的过程当中尤为具备挑战性。若是你从一个像 Ant 或 Maven 这样的工具迁移到 Gradle,你可能会面临一些困难的状况。例如,一个常见的模式是,有一个 Ant 项目,而它保存在文件系统中的一些 jar 文件缺乏版本号。其余的构建系统在迁移以前须要对这种方法进行大量的替换。使用 Gradle,你可让你的新构建适配任何现有的依赖来源或依赖元数据。这使得增量迁移到 Gradle 比其余的要容易得多。在大多数的大项目中,构建迁移和对发展过程的任何更改都是增量的,由于大多数组织不能负担得起中止一切事情并迁移到依赖管理的构建工具的想法。
即便你的项目使用一个自定义的依赖管理系统,或者是一些像 Eclipse 做为依赖管理的主数据的 .classpath 文件,那么能够很容易地写一个 Gradle 插件在 Gradle 中使用此数据。出于迁移的目的,这是在 Gradle 中的一种经常使用技术。(可是,一旦你已经迁移,远离 .classpath 文件,直接使用 Gradle 的依赖管理功能会是一个好主意。)
使人哭笑不得的是,以其丰富的开源组件库著称的语言,Java 居然没有库或者版本的概念。在 Java 中,没有标准的方法来告诉 JVM 你使用的是 3.0.5 版本的 Hibernate,也没有标准的方法来代表 foo-1.0.jar
依赖于 bar-2.0.jar
。这致使了外部的解决方案一般都会基于构建工具。目前最受欢迎的解决方案是 Maven 和 Ivy。Maven 提供了一个完整的构建系统,而 Ivy 则彻底着眼于依赖管理。
这两种工具都依赖于描述符 XML 文件,包含有关具体某个 jar 的依赖的信息。这二者都使用了存储库,在存储库中实际的 jar 文件和它们的描述文件都放在一块儿,并且这二者经过窗口或其余的方式都提供了 jar 版本冲突的解决方案。这二者都已成为解决依赖冲突的标准,而 Gradle 最初使用的是 Ivy 引擎的依赖管理。Gradle 已经取代了 Ivy 上的直接依赖,而采用了原生的 Gradle 依赖解决引擎,它支持许多的依赖解决方案的方法,包括 POM 文件和 Ivy 描述文件。
因为 Gradle 对依赖管理有强烈的主张,该工具使你能够选择两个选项之间:遵循推荐的最佳实践或支持任何你能想到的类型的模式。本节概述 Gradle 项目推荐的管理依赖的最佳实践。
无论用什么语言,对每一个项目而言,适当的依赖管理是很重要的。从一个由 Java 编写的依赖了数以百计开源库的复杂企业应用,再到依赖少许库的最简单的 Clojure 应用,依赖管理的办法大不相同,而且能够依赖于目标的技术、应用程序部署的方法和项目的性质。项目捆绑做为可重用的库,比起企业应用集成到更大的软件和基础设施的系统中,可能有不一样的要求。尽管这一要求变化很大,Gradle 项目建议全部项目都遵循这套核心规则:
在文件名中库的版本必须是容易辨认的。Jar 的版本一般是在清单文件中,当你要检查一个项目时它并不显而易见。若是有人让你看 20 个 jar 文件,你更喜欢哪种?一个文件命名为 beanutils-beanutils-1.3.jar
的集合,仍是文件命名为 spring.jar
的集合?若是依赖的文件名称带有版本号,那么将更容易快速肯定依赖的版本。
若是版本不清楚,你可能引入一些很难找到的微妙的错误。例如可能有一个项目使用 Hibernate 2.5。想一下一个开发人员决定在她的机器安装 3.0.5 的版本,以修复一个关键的安全 bug,但她忘记通知其余团队这个变化。她可能成功地解决了这个安全 bug,但她也可能有引入一些 bug 到代码库中,由于项目用到了 Hibernate 如今弃用的功能。一周后在集成的机器上可能会有一个异常,而这个异常没法在任何人的机器上复现。而后多个开发人员花了数天的时间在这个问题上,终于意识到若是他们知道 Hibernate 已经从 2.5 升级到 3.0.5,这个错误会很容易发现。
在 jar 文件中包含版本号提升了在你的项目中的表现,而且让它们更易于维护。这种作法也减小了潜在的错误。
传递依赖管理是一种技术,让你的项目可以依赖那些反过来依赖其余库的库。这种递归模式传递依赖的结果是,在一个依赖树中,会包含你的项目的第一级依赖、第二级依赖,等等。若是你不把你的依赖做为层次结构树的第一级和第二级依赖的模型,那么在对未组织的依赖进行混乱的组装以后,就会很容易地失去控制。考虑 Gradle 项目自己,虽然 Gradle 仅有几个直接的第一级依赖,当 编译 Gradle 时在它的类路径上会须要超过一百个的依赖。在更大的规模上,使用 Spring,Hibernate和其余的库,旁边数百或数千个内部项目的企业应用,也有很是大的依赖树。
当这些大的依赖树须要更改时,你常常得解决一些依赖的版本冲突。好比说一个开源库须要一个日志库的一个版本,而另外一个库须要日志库的另外一个版本。Gradle 和其余的构建工具都可以处理这种关系树和解决冲突问题,但不一样的是,Gradle 让你能够控制传递依赖和冲突的解决。
虽然你能够尝试手动管理这个问题,你很快就会发现这种方法不能扩展。若是你想要摆脱第一级的依赖,你不能真正肯定还有哪些 jar 文件你是须要删除的。第一级依赖的依赖项也多是第一级依赖自己,或者也多是另外一个第一级依赖的传递依赖。若是你尝试本身管理传递依赖,最终的结果是你的构建会变得很脆弱:没有人敢去改变你的依赖,由于破坏构建的风险过高。项目的类路径会变得一片狼藉,而且,若是类路径出现问题时,那简直就是人间地狱。
Gradle 向你提供了不一样的方式来表达第一级的和传递的依赖。经过 Gradle 你能够混合使用和适配一些方法;例如,你能够在 SCM 中存储你的 jar 包,而不须要 XML 描述符文件,而且仍然使用传递依赖管理。
相同的 jar 包的冲突版本应该被检测到,而且要么解决,要么抛出异常。若是你不使用传递依赖管理,版本冲突没被发现,那么在类路径中没法预测的顺序,将致使不知道会使用哪个版本的依赖。对于许多开发人员都会更改依赖的大型项目,成功的构建将会少之又少,由于依赖的顺序可能会直接影响到构建是否成功(或者在产品中是否会出现一个 bug)。
若是你尚未处理过在类路径中 jar 包版本冲突的麻烦,这里有一个小趣闻等着你。在一个有30个子模块的大型项目中,向子项目添加的一个依赖改变了类路径的顺序, Spring 2.5 与老的 2.4 版本的顺序被交换。虽然能够继续构建,开发者已经开始注意到在生产中出现了各类使人惊讶(和惊人可怕)的 bug。然而,更糟糕的是,无心下降版本的 Spring 向系统引入了几个安全漏洞,如今须要在整个组织中进行全面的安全审核。
总之,版本冲突是很很差的,你应该管理你的传递依赖,以免它们。你也可能想要了解版本冲突用到的地方,而且在你的整个组织中统一一个指定版本的依赖。经过一个好的冲突报告工具,像 Gradle,这些信息能够用于与整个组织沟通,并在一个单一的版本上实现标准化。若是你以为你不会发生版本冲突,再想一想。 不一样的第一级依赖,依赖于一系列不一样的重叠版本的其余依赖,这种状况是很常见的,而 JVM 还不能提供简单的方法,使得能在类路径中让相同的 jar 包能够有不一样的版本(请参阅第 50.1.2 节,“依赖管理和 Java”)。
Gradle 提供了如下的冲突解决策略:
ResolutionStrategy
。虽然上面介绍的策略一般足够解决大部分的冲突,可是 Gradle 也提供更细粒度的机制,以解决版本冲突:
DependencyHandler
中的示例。ResolutionStrategy
中的示例。为了解决版本冲突问题,报告依赖关系图也是颇有帮助的。这些报告是依赖管理的另外一个功能。
有许多状况,是你想要使用一个特定依赖的最新版本,或者是某个版本范围内的最新版。这能够是在开发中须要,或者你可能正在开发一个库,它被设计为使用一个范围内的依赖版本。你能够经过使用动态版本很容易地依赖这些不断变化的依赖。一个动态的版本能够是一个版本范围(例如2.+
),也能够是表示可用的最新版本的占位符(例如latest.integration
)。
另外,你请求的模块随着时间推移,即便是同一版本,有时也可能改变了。这种变化模块的类型的一个例子是 MavenSNAPSHOT
模块,它老是指向最新发布的构件。换句话说,一个标准的 Maven snapshot 是一个这样的模块,它永远不会不变,能够说,它是“不断变化的模块”。
动态版本和变化模块的主要区别是,当你解析一个动态版本时,你会获得真正的、 静态的版本做为模块名称。当你解析一个变化模块时,这个 artifacts 使用你请求的版本进行命名,但下层的 artifacts 可能随时会有变化。
默认状况下,Gradle 对动态版本和变化模块的缓存时间是24小时。你可使用命令行选项重写默认的缓存模式。你能够经过resolution strategy
修改你的构建的缓存到期时间(见第 50.9.3 节,“调整控制依赖缓存”)。
在 Gradle 中,依赖被分组到配置中。配置有一个名字和许多属性,而且它们可以互相继承。许多 Gradle 插件会向你的 project 添加预约义的配置。例如,Java 插件会添加一些配置来表示它所须要的不一样的类路径。详细信息请参阅第 23.5 节,“依赖管理” 。固然,你能够添加自定义配置到这上面。关于自定义配置,有许多的用例。这是很是方便的,例如添加依赖时不须要构建或测试你的软件(好比,将会与发布的软件一块儿的额外的 JDBC 驱动程序)。
一个项目的配置被一个 configurations
对象所管理。你传给这个 configurations 对象的闭包会经过它对应的 API 被应用。要了解更多关于此 API 的内容,能够看看ConfigurationContainer
。
若是要定义配置:
若是要访问配置:
示例 50.2. 访问配置
build.gradle
println configurations.compile.name
println configurations['compile'].name
配置一个配置:
你能够声明几种不一样类型的依赖:
表 50.1. 依赖类型
类型 | 描述 |
外部模块依赖 | 对一些仓库中的外部模块的依赖 |
项目依赖 | 在同一个构建中对另外一个项目的依赖 |
文件依赖 | 对本地文件系统中的一些文件的依赖 |
客户端模块依赖 | 对外部模块的依赖,该块部模块的 artifacts 存储于一些仓库中,可是模块的元数据由本地构建指定。当你想要重写模块的元数据时,你可使用这种类型的依赖。 |
Gradle API 依赖 | 对当前的 Gradle 版本的 API 的依赖当你正在开发自定义的 Gradle 插件和任务类型时,你可使用这种类型的依赖。 |
本地的 Groovy 依赖 | 对当前的 Gradle 所使用的 Groovy 版本的依赖。当你正在开发自定义的 Gradle 插件和任务类型时,你可使用这种类型的依赖。 |
外部模块依赖是最多见的依赖。它们引用外部仓库中的模块。
示例 50.4. 模块依赖
build.gradle
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 } }
有关更多的例子和完整的参考,请参阅DependencyHandler
。
Gradle 为模块依赖提供了不一样的标记法。有 string 标记法和 map 标记法。模块依赖有一个 API,用于进行进一步的配置。要了解全部该 API 的内容,能够参阅ExternalModuleDependency
。该 API 提供了一些属性和配置方法。经过 string 标记法,你能够定义一个属性的子集。而经过使用 map 标记法,你能够定义全部的属性。要访问完整的 API,使用 map 或 string 标记法,你能够把单个的依赖与一个闭包一块儿指定给一个配置。
若是你定义了一个模块依赖,Gradle 会在仓库中查找相应的模块描述符文件(pom.xml
或ivy.xml
)。若是存在此类模块描述符文件,它会进行分析,并下载此模块的 artifacts (例如hibernate-3.0.5.jar
)以及其依赖项(例如 cglib)。若是不存在这样的模块描述符文件,Gradle 会查找一个hibernate-3.0.5.jar
文件。在 Maven 中,一个模块仅能有一个 artifact。在 Gradle 和 Ivy 中,一个模块能够具备多个 artifacts。每一个 artifact 能够有一组不一样的依赖。
ivy.xml
)能够定义多个 artifact。有关更多的信息,请参阅
ivy.xml
引用的Ivy。在 Gradle 中,当你声明一个对 Ivy 模块的依赖时,实际上你是在那个模块的
default
配置上声明了一个依赖。因此实际上,你依赖的 artifacts 集(一般是一些 jar 包) 是与该模块的
default
配置相关联的 artifacts 集。如下是一些比较重要的状况:
default
配置包含了不但愿有的 artifacts。一个依赖只是声明了所需的 artifacts ,而不是依赖整个配置。default
的配置。这个配置被显式地命名为这个依赖声明的一部分。DependencyHandler
的例子和声明依赖的完整参考。
如上所述,若是找不到模块的描述符文件,默认状况下 Gradle 会下载一个与模块的名称相同的 jar 文件。但有时候,即便存储库包含了模块描述符,而你只是想下载 artifact jar而不下载它的依赖项。[14] 而有时候你想要从一个仓库中下载一个 zip,而它没有模块描述符。Gradle 提供了一个 artifact only notation 用于这些案例状况——只是对你想要下载的扩展前加个 '@'
标志:
示例 505. Artifact only notation
build.gradle
dependencies { runtime "org.groovy:groovy:2.2.0@jar" runtime group: 'org.groovy', name: 'groovy', version: '2.2.0', ext: 'jar' }
一个 artifact only notation 建立了一个模块依赖,它只下载指定扩展名的 artifact 文件。现有的模块描述符将被忽略。
Maven 依赖管理有 classifier 的概念。[15] 而 Gradle 支持这一点。若是你想从一个 Maven 仓库中获取 classified 依赖项,你能够这样写:
示例 50.6. 使用 classifier 的依赖
build.gradle
compile "org.gradle.test.classifiers:service:1.0:jdk15@jar" otherConf group: 'org.gradle.test.classifiers', name: 'service', version: '1.0', classifier: 'jdk14'
如上面的第一行所示,classifiers 能够与artifact only notation 一块儿使用。
它能够轻松地遍历一个配置的依赖 artifacts:
示例 50.7. 遍历一个配置
build.gradle
task listJars << { configurations.compile.each { File file -> println file.name } }
gradle -q listJars
的输出结果
> gradle -q listJars hibernate-core-3.6.7.Final.jar antlr-2.7.6.jar commons-collections-3.1.jar dom4j-1.6.1.jar hibernate-commons-annotations-3.2.0.Final.jar hibernate-jpa-2.0-api-1.0.1.Final.jar jta-1.1.jar slf4j-api-1.6.1.jar
客户端模块依赖容许直接在构建脚本中声明传递依赖。它们是外部库的模块描述符的替代者。
示例 50.8. 客户端模块依赖 - 传递依赖
build.gradle
dependencies { runtime module("org.codehaus.groovy:groovy-all:2.2.0") { dependency("commons-cli:commons-cli:1.0") { transitive = false } module(group: 'org.apache.ant', name: 'ant', version: '1.9.3') { dependencies "org.apache.ant:ant-launcher:1.9.3@jar", "org.apache.ant:ant-junit:1.9.3" } } }
这里定义了一个对 Groovy 的依赖。Groovy 自己具备依赖。但 Gradle 不会去查找一个 XML 描述符来找出它的依赖,而是从构建文件中获取信息。一个客户端模块的依赖能够是正常的模块依赖,或者是 artifact 依赖项或是另外一个客户端模块。能够看一看 API 文档: ClientModule
在当前版本客户端模块有一个缺陷。假设你的项目是一个库,你想要这个库上传到你公司的 Maven 或Ivy 仓库。Gradle 会将你的项目的 jar 包以及这仆依赖的 XML 描述文件上传到公司仓库。若是你使用了客户端模块,在 XML 描述符文件中的依赖声明就会不正确。咱们将在将来版本的 Gradle 修正这一点。
对于多项目构建,Gradle 能区分外部依赖与做为多项目构建的一部分的某个项目上的依赖。对于后者,你能够声明项目依赖。
详细信息请参阅ProjectDependency
的 API 文档
多项目构建将在第 56 章,多项目生成中进行详述。
文件依赖容许你直接将一组文件添加到配置中,而不用先将它们添加到存储库。这将会很是有用,好比你没法,或者是不想要把某些文件放到仓库。或者是你若是不想使用任何仓库来存储你的依赖。
若是想添加一些文件做为配置的依赖,你只须要传一个文件集合做为依赖:
示例 50.10. 文件依赖
build.gradle
dependencies { runtime files('libs/a.jar', 'libs/b.jar') runtime fileTree(dir: 'libs', include: '*.jar') }
文件依赖项不会包含在你的项目的发布的依赖描述中。然而,文件依赖会被包含在同一个构建的传递项目依赖里。这意味着它们不能在当前的构建外使用,但它们能够在同一个构建中使用。
你能够声明哪些任务将产生做为文件依赖的文件。例如,你能够在经过构建生成文件的时候这样作。
示例 50.11. 生成文件依赖
build.gradle
dependencies { compile files("$buildDir/classes") { builtBy 'compile' } } task compile << { println 'compiling classes' } task list(dependsOn: configurations.compile) << { println "classpath = ${configurations.compile.collect {File file -> file.name}}" }
gradle -q list
的输出结果
> gradle -q list compiling classes classpath = [classes]
你能够经过使用DependencyHandler.gradleApi()
方法,来声明一个当前的 Gradle 版本的 API 上的依赖。当你在开发自定义 Gradle 任务或插件时将会颇有用。
能够经过使用DependencyHandler.localGroovy()
方法,来声明对与 Gradle 一块儿发布的 Groovy 的依赖。当你在开发自定义 Gradle 任务或在 Groovy 中的插件时将会颇有用。
经过配置或者是经过依赖,你能够排除一个传递依赖:
示例 50.14. 排除传递依赖
build.gradle
configurations { compile.exclude module: 'commons' all*.exclude group: 'org.gradle.test.excludes', module: 'reports' } dependencies { compile("org.gradle.test.excludes:api:1.0") { exclude module: 'shared' } }
若是你为一个特定的配置定义一个 exclude,则解析此配置或任何继承的配置时,对于全部的依赖,所排除的传递依赖将会被过滤掉。若是你想要从你的全部配置中排除传递依赖,你能够用简明的方式,使用 Groovy 的 spread-dot 运算符来表示,如这个例子所示例。在定义一个 exclude 时,你能够只指定 organization 或者 module 名称,或者是二者都指定。能够看看 Dependency
和 Configuration
的 API 文档。
不是每一个传递依赖均可以被排除 — — 一些传递依赖多是应用程序能正确运行的必要条件。通常来讲,能够被排除的传递依赖,在运行时并不须要,或者是保证在目标环境或平台上可用。
你应排除每一个依赖或每一个配置吗?事实证实,在大多数状况下你想要排除每个配置。下面是为何可能想要排除传递依赖的一些缘由。记住,对于其中一些用例,有着比排除更好的解决方案!
ResolutionStrategy
文档,以了解这个问题潜在的更好的解决方案。基本上,在大多数状况下对每个配置都是排除传递依赖。这种依赖声明的方式更加明确。它也更准确,由于每一个依赖排除规则并不能保证给定的传递依赖不会显示在配置中。例如,某些其余的依赖,并无任何排除规则,可能会带上那个多余的传递依赖。
其余相关依赖关系排除的示例,能够参考 ModuleDependency
或DependencyHandler
。
一个依赖的全部属性都是可选的,除了 name。这取决于仓库类型,这些信息实际上须要用于在仓库中找到这个依赖。请参阅 50.6 节,“仓库”。例如,若是你使用 Maven 仓库,你须要定义group,name 和 version。若是你使用文件系统仓库,你可能只须要 name 或 name 和 version。
在 Gradle ,一个依赖能够有不一样的配置 (就像你的项目能够有不一样的配置)。若是你不显式指定任何东西,Gradle 会使用依赖的默认配置。对于 Maven 存储库的依赖,至少默认配置是惟一可用的一个。若是你使用 Ivy 存储库,而且想要为你的依赖定义一个非默认配置,就要使用 map 标记法而且声明:
示例 50.17. 依赖配置
build.gradle
dependencies { runtime group: 'org.somegroup', name: 'somedependency', version: '1.0', configuration: 'someConfiguration' }
一样的项目依赖,你须要声明:
你能够从命令行生成依赖报告 (参阅 第11.6.4节,“列出项目依赖”)。经过使用 Project report 插件(参阅 第 41 章, Project Report 插件),在构建中能够建立一个这样的报告。
从 Gradle 1.2 起,有一个新的编程 API 用于访问解析的依赖信息。依赖报告(见前面一段)正是使用此 API。这个 API 可让你查看解析的依赖图,并提供有关依赖的信息。在将来的版本,这个API 将提供更多有关解析结果的详细信息。关于这个 API 的更多信息,请参考ResolvableDependencies.getResolutionResult()
上的文档。ResolutionResult
API的可能用法:
下面的示例咱们使用如下依赖设置:
示例 50.19. Configuration.copy
build.gradle
configurations { sealife alllife } dependencies { sealife "sea.mammals:orca:1.0", "sea.fish:shark:1.0", "sea.fish:tuna:1.0" alllife configurations.sealife alllife "air.birds:albatros:1.0" }
这些依赖有如下的传递依赖:
shark-1.0 -> seal-2.0, tuna-1.0
orca-1.0 -> seal-1.0
tuna-1.0 -> herring-1.0
你可使用配置来访问它们的声明依赖或其中一个子集:
示例 50.20. 访问声明依赖
build.gradle
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 }
}
gradle -q dependencies
的输出结果
> gradle -q dependencies albatros albatros orca shark tuna albatros shark tuna
dependencies
返回只明确属于配置的依赖。allDependencies
包括了扩展配置的依赖。
要得到配置依赖的library文件,你能够这样:
示例 50.21. Configuration.files
build.gradle
task allFiles << { configurations.sealife.files.each { file -> println file.name } }
gradle -q allFiles
的输出结果
> gradle -q allFiles orca-1.0.jar shark-1.0.jar tuna-1.0.jar herring-1.0.jar seal-2.0.jar
有时你想要配置依赖的某个子集(例如单个依赖)的 library 文件。
示例 50.22. 指定的 Configuration.files
build.gradle
task files << {
configurations.sealife.files { dep -> dep.name == 'orca' }.each { file ->
println file.name
}
}
gradle -q files
的输出结果
> gradle -q files orca-1.0.jar seal-2.0.jar
Configuration.files
方法老是获取整个配置的全部 artifacts。而后,由指定的依赖筛选获取的文件。正如你在示例中所看到的,传递依赖都被包括在内。
你还能够复制配置。你能够选择指定只复制原始配置里的一个子集的依赖。复制的方法有两种。copy
方法只复制明确属于配置的依赖。copyRecursive
方法将复制全部依赖,包括扩展配置的依赖。
示例 50.23. Configuration.copy
build.gradle
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
}
}
gradle -q copy
的输出结果
> gradle -q copy albatros shark tuna albatros
要重点注意,复制的配置所返回的文件,每每并不老是与原始配置的依赖子集所返回的文件同样。在子集的依赖和不属于子集的依赖之间,存在版本冲突的状况下,解析的结果可能会有所不一样。
示例 50.24. Configuration.copy 与 Configuration.files
build.gradle
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
的输出结果
> 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
方法所以返回 seal-2.0
做为 orca
的传递依赖。复制的配置只有 orca
一个依赖,所以没有版本冲突,seal-1.0
做为传递依赖被返回。
一旦一个配置被解析,那它就是不可变的了。若是去修改它的状态,或者是它的某个依赖的状态,将会引起一个异常。你能够永远复制一个解析了的配置。这个复制的配置处于未解析的状态,而且能够被刷新解析。
想了解更多关于这个配置类的API,能够参阅它的 API 文档:Configuration
.
Gradle 仓库管理,基于 Apache Ivy,为你提供了有关仓库布局和获取策略的许多自由。另外,Gradle 提供了各类方便的方法来添加预配置的仓库。
您能够配置任意数量的仓库,每个都会被Gradle 独立处理。若是 Gradle 在特定仓库中查找模块描述符,它将尝试从同一仓库中下载该模块的全部 artifacts。虽然模块的元数据和模块 artifacts 必须位于同一仓库,但也有可能单个仓库有多个 Url,给多个位置去搜索元数据文件和 jar 文件。
有几种不一样类型的存储库能够声明:
表 50.2. 仓库类型
类型 | 描述 |
Maven 中央仓库 | 一个会在 Maven 中央仓中查找依赖的预配置仓库。 |
Maven JCenter 仓库 | 一个会在 Bintray 的 Jcenter 查找依赖的预配置仓库。 |
Maven 本地仓库 | 一个会在本地 Maven 仓库中查找依赖的预配置仓库。 |
Maven 仓库 | 一个 Maven 仓库。能够位于本地文件系统上,或在某个远程的位置。 |
Ivy 仓库 | 一个 Ivy 仓库能够位于本地文件系统上,或在某个远程的位置。 |
Flat 目录仓库 | 一个在本地文件系统上的简单的仓库。不支持任何元数据格式。 |
若要添加中央 Maven 2 仓库(http://repo1.maven.org/maven2),只需添加下面的代码到你的构建脚本中:
如今 Gradle 将会在此仓库中查找你的依赖。
BintrayJCenter 是全部流行的 Maven OSS artifacts 的up-to-date 集合,包括直接发布到 Bintray 的 artifacts。
若要添加 JCenter Maven 仓库(http://jcenter.bintray.com),只需添加下面的内容到你的构建脚本中:
如今 Gradle 将会在 JCenter 仓库中查找你的依赖。
你能够这样把本地的 Maven 缓存做为仓库使用:
Gradle 使用与 Maven 相同的逻辑来标识你本地的 Maven 缓存的位置。若是在settings.xml
中定义一个本地仓库的位置,那么这个位置将会被使用。在
的USER_HOME
/.m2settings.xml
比在
的M2_HOME
/confsettings.xml
优先。若是没有settings.xml
可用,Gradle 将使用默认的位置
。USER_HOME
/.m2/repository
要添加一个自定义的 Maven 仓库,你能够以下操做:
示例 50.28. 添加一个自定义的 Maven 仓库
build.gradle
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}
有时候,一个仓库会出现 POM 文件发布在一个地方,而 JAR 文件和其余构件发布在另外一个地方。要定义一个这样的仓库,你能够这样:
示例 50.29. 为 JAR 文件添加额外的 Maven 仓库
build.gradle
repositories { maven { // Look for POMs and artifacts, such as JARs, here url "http://repo2.mycompany.com/maven2" // Look for artifacts here if not found at the above location artifactUrls "http://repo.mycompany.com/jars" artifactUrls "http://repo.mycompany.com/jars2" } }
Gradle 将会在第一个URL中查找 POM 和 JAR 文件。若是那里找不到 JAR,就会用 artifact URLs 来查找 JAR 文件。
若是你想要把一个(平面)文件系统目录做为仓库使用,只需输入:
示例 50.31. 平面仓库的解决
build.gradle
repositories { flatDir { dirs 'lib' } flatDir { dirs 'lib1', 'lib2' } }
这将会添加一些用于查找依赖的仓库,它会在一个或多个目录中寻找。若是你只使用平面目录解析器,那么你不须要去设置一个依赖的全部属性。请参阅 第50.4.8 节,“可选属性”
使用一个标准布局的 Ivy 存储库:
示例 50.32. Ivy存储库
build.gradle
repositories { ivy { url "http://repo.mycompany.com/repo" layout "maven" } }
详细信息请参阅IvyArtifactRepository
。
若要定义非标准布局的 Ivy 仓库,你能够定义一个仓库模式布局:
做为可选的功能,一个使用模式布局的仓库能够有它本身的以Maven 风格奠基的“组织”部分,该部分使用斜杠替换点做为分隔符。例如,组织my.company
将表示为my/company
。
若要定义一个从不一样的位置获取 Ivy 文件和 artifacts 的 Ivy 仓库,您可使用模式布局,每一个单独的模式用于定位到 Ivy 文件和 artifacts:
示例 50.35. 自定义模式的 Ivy 仓库
build.gradle
repositories { ivy { url "http://repo.mycompany.com/repo" layout "pattern", { artifact "3rd-party-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" artifact "company-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" ivy "ivy-files/[organisation]/[module]/[revision]/ivy.xml" } } }
每一个artifact
或ivy
都指定了一个仓库,添加一个额外的的模式来使用。这些模式以定义它们的顺序来使用。
若要访问一个仓库:
示例 50.37. 访问一个仓库:
build.gradle
println repositories.localRepository.name
println repositories['localRepository'].name
要配置一个仓库:
Gradle因为 Ivy 在它的 hood 之下,对仓库很是灵活。
对于与仓库通讯的协议,有不少的选项(好比文件系统,http, ssh……)
每一个仓库均可以有其本身的布局。
比方说,你能够声明一个junit:junit:3.8.2
库的依赖。如今 Gradle 是如何发现它在存储库中的?某种程度上依赖信息必须映射到一个路径上。相比于固定路径的 Maven,使用 Gradle 你能够定义一个模式,该模式定义了路径的样子。这里有一些例子:[16]
// Maven2 layout (if a repository is marked as Maven2 compatible, the organization (group) is split into subfolders according to the dots.) someroot/[organisation]/[module]/[revision]/[module]-[revision].[ext] // Typical layout for an Ivy repository (the organization is not split into subfolder) someroot/[organisation]/[module]/[revision]/[type]s/[artifact].[ext] // Simple layout (the organization is not used, no nested folders.) someroot/[artifact]-[revision].[ext]
要添加任何一种仓库 (你能够很简单地编写你本身的) ,你能够:
示例 50.39. 自定义仓库的定义
build.gradle
repositories { ivy { ivyPattern "$projectDir/repo/[organisation]/[module]-ivy-[revision].xml" artifactPattern "$projectDir/repo/[organisation]/[module]-[revision](-[classifier]).[ext]" } }
其中由 Ivy (也所以由 Gradle )提供解析器的,它的概述能够在这里找到。经过Gradle,你只是不用经过XML来配置它们,而是经过它们的API。
Gradle 将获取你的依赖声明和仓库定义,并经过一个称为依赖项解析的过程尝试下载全部依赖项。下面是这个过程的工做原理的简要概述。
给出所需的依赖项,Gradle 首先尝试为该依赖解析模块。每一个仓库按顺序进行检查,首先查找指示该模块存在的模块描述符文件(POM 或 Ivy 文件)。若是没有找到模块描述符,Gradle 将搜索表示模块存在于存储库中的主要模块 artifact文件。
若是依赖被声明为一个动态版本(像1.+
),Gradle 将会把它解析到在仓库中的最新的可用的静态版本(如1.2
)。对于 Maven 仓库,是经过maven metadata.xml
文件来实现,而对于 Ivy 存储库,则是经过目录列表。
若是模块描述符是一个具备的父 POM 文件声明的 POM 文件,Gradle 将以递归方式尝试为该 POM 文件解析每一个父模块。
一旦已为该模块检查了每一个仓库,Gradle 将会选择使用“最好”的一个。它使用如下标准来完成:
当依赖由一个静态版原本声明,而且在仓库中找到模块描述符文件时,将不会再继续搜索后面的仓库,这个过程的其他部分是短路的。
而后这个模块的全部artifact将从上面的过程所选择的 同一个仓库 中请求。
在大多数状况下,Gradle 的默认依赖管理将会在你的构建中按你所想的解析依赖关系。然而,在某些状况下,会有必要调整依赖解析,以确保你的构建能获得正确的依赖关系。
有许多种方式均可以影响到Gradle解析依赖。
强制一个模块的版本,是告诉 Gradle 对于指定的依赖(无论是否传递依赖),始终使用一个特定的版本,而覆盖在发布的模块描述符中所指定的任何版本。当解决版本冲突时,这可能很是有用——有关详细信息请参阅第 50.2.3 节,“解决版本冲突”。
强制版本也能够用于处理传递依赖所带来的流氓元数据。若是传递依赖有质量较差的元数据,致使了依赖解析时的问题时,你能够强制 Gradle 对于这一依赖使用一个较新的,固定的版本。有关示例,请参见ResolutionStrategy
。请注意,“依赖解析规则”(见下文),提供了一种更强大的机制,来取代一个损坏的模块依赖。请参阅50.8.2.3 节,“黑名单替换指定版本”。
一个依赖解析规则为每个解析依赖执行,并提供功能强大的 api 用于在解析依赖以前处理这个依赖的请求。此功能还在孵化中,但目前提供了对于一个请求的依赖更改组、 名称及版本的功能,容许在解析过程当中,把一个依赖替换为另外一个彻底不一样的模块。
依赖解析规则提供了一种很是强大的方式来控制依赖解析过程,并能够用于实如今依赖管理中各类高级模式的排序。下面将概述其中的某些模式。更多的信息和代码示例请参阅ResolutionStrategy
。
一般一个组织会使用一个版本发布一组库;这些库将在一块儿构建,测试以及发布。这些库造成一个“可发布的单位”,被设计并打算做为一个总体使用。而一块儿使用来自不一样的可发布单位的库,也不会有意义。
但传递依赖解析会很容易破坏这种协议。举个例子:
module-a
依赖于 releasable-unit:part-one:1.0
module-a
依赖于 releasable-unit:part-one:1.0
一个依赖于module-a
和module-b
的构建,将在可发布单位内得到这个库的不一样版本。
依赖解析规则使你可以在构建中强制指定可发布的单位。想象一下,一个可发布的单位,由有“org.gradle” 组的全部库定义。咱们能够强制全部这些库使用一致的版本:
在一些企业的环境中,能够声明在 gradle 构建中的模块版本列表,是在外部维护和审核的。依赖解析规则提供了这种模式的整洁的实现:
default
”。该规则实现能够整齐地封装在一个公司的插件中,并在组织内和全部构建共享。
示例 50.41. 使用自定义的版本方案
build.gradle
configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.version == 'default') { def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name) details.useVersion version } } } def findDefaultVersionInCatalog(String group, String name) { //some custom logic that resolves the default version into a specific version "1.0" }
依赖解析规则提供了一个机制,用于把一个依赖的指定版本列入黑名单,并提供一个替代的版本。若是某个依赖的版本坏了,而且不该该被使用,这个机制将很是有用。一个依赖解析规则将会使这个版本被替换为一个已知的好的版本。一个坏的模块的例子是,在一个库上声明的一个依赖没法在任何公共仓库中找到,但为何不能使用一个特定的模块版本,而更但愿要另外一个版本,还有不少其余缘由。
在下面的示例中,想象版本1.2.1
包含了重要的修复程序,并应始终优先于1.2
使用。提供的规则将强制执行:在任什么时候间遇到了1.2
版本,都将会替换为1.2.1
。注意,这与上面描述的强制使用一个版本不一样,这个模块的其余版本将不受影响。这意味着,若是这个版本也因依赖传递被获取到,“使用最新”的冲突解决策略仍然会选择 1.3
版本。
示例 50.42. 黑名单替换特定版本
build.gradle
configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'org.software' && details.requested.name == 'some-library' && details.requested.version == '1.2') { //prefer different version which contains some necessary fixes details.useVersion '1.2.1' } } }
有时一个彻底不一样的模块能够做为请求的模块依赖的替代者。示例包括,使用“groovy
”来代替“groovy-all
”,或者使用“log4j-over-slf4j
”来代替“log4j
”。从 Gradle 1.5 开始,你可使用依赖解析规则来进行这些替换:
示例 50.43. 在解析中更改依赖组及名称
build.gradle
configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.name == 'groovy-all') { //prefer 'groovy' over 'groovy-all': details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version } if (details.requested.name == 'log4j') { //prefer 'log4j-over-slf4j' over 'log4j', with fixed version: details.useTarget "org.slf4j:log4j-over-slf4j:1.7.5" } } }
Gradle 的Ivy 仓库实现支持至关于 Ivy 的动态解析的模式。一般状况下,Gradle 将rev
属性用于在ivy.xml
文件中包含的每一个依赖定义。在动态解析模式中,Gradle 将优先使用revConstraint
属性来代替rev
属性,用于一个给定的依赖定义。若是不存在revConstraint
属性,则使用rev
属性。
若要启用动态解析模式,你须要在仓库定义进行合适的设置。下面展现了几个例子。注意,动态解析模式只对 Gradle 的 Ivy 仓库有效。它不能用于 Maven 仓库,或自定义的 Ivy DependencyResolver
实现。
示例 50.44. 启用动态解析模式
build.gradle
// Can enable dynamic resolve mode when you define the repository repositories { ivy { url "http://repo.mycompany.com/repo" resolve.dynamicMode = true } } // Can use a rule instead to enable (or disable) dynamic resolve mode for all repositories repositories.withType(IvyArtifactRepository) { resolve.dynamicMode = true }
每一个模块(也称为组件)都有相关的元数据上,像它的组,名称,版本,依赖,等等。这个元数据一般来源于模块的描述符。元数据规则容许模块的元数据在构建脚本中被操纵。它们在模块描述符下载以后,被全部候选版本之间选择以前生效。这使得元数据成为自定义依赖解析的另外一种手段。
其中一个 Gradle 能理解的模块元数据是模块的状态模式。这一律念,也能够从 Ivy 中,随着时间推移,一个模块发展的成熟水平能够得知。默认状态模式,按状态的成熟程度排序,分别是integration
、milestone
、release
。除了状态模式之外,模块也有一个(当前) 的状态,这个状态必须是它的状态模式中的值之一。若是没有在(Ivy)描述符中指定,Ivy 模块和 Maven snapshot模块的状态默认为integration
,不是snapshot 的 Maven 模块则默认为release
。
一个模块的状态以及状态模式,会在解析 latest
版本选择器时考虑到。具体而言,latest.someStatus
将会解析成有着someStatus
或更成熟状态的最高的模块版本。例如,在适当的位置使用默认的状态模式,latest.integration
将选择最高的模块版本,不论其状态(由于integration
是成熟度最低的状态),而latest.release
将选择release
状态的最高模块版本。这里是在代码中的表现:
示例 50.45,“Latest”版本选择器
build.gradle
dependencies { config1 "sea.fish:tuna:latest.integration" config2 "sea.fish:tuna:latest.release" } task listFish << { configurations.config1.each { println it.name } println() configurations.config2.each { println it.name} }
gradle -q listFish
的输出结果
> gradle -q listFish tuna-1.5.jar tuna-1.4.jar
下一个示例演示了基于在一个模块的元数据规则中声明的自定义状态模式的latest
选择器:
示例 50.46. 自定义状态模式
build.gradle
dependencies { config3 "air.birds:albatros:latest.silver" components { eachComponent { ComponentMetadataDetails details -> if (details.id.group == "air.birds") { details.statusScheme = ["bronze", "silver", "gold", "platinum"] } } } } task listBirds << { configurations.config3.each { println it.name } }
gradle -q listBirds
的输出结果
> gradle -q listBirds albatros-2.0.jar
Gradle 包含了一个高度复杂的依赖缓存机制,该机制力求减小依赖解析中的远程请求,同时努力保证依赖解析结果的正确及可再生性。
Gradle 依赖缓存包含 2 个主要类型的存储:
一个基于文件的下载构件存储,包括像jars这样的二进制文件,以及像 POM 文件以及 Ivy 文件这样的原始下载的元数据。下载的构件的存储路径包括了 SHA1 校验和,意味着 2 个具备相同名称但内容不一样的构件能够很容易地被缓存。
解析的模块元数据的二进制存储,包括解析动态版本、 模块描述符和构件的结果。
从下载的构件的存储中分享出缓存的元数据,将容许咱们使用缓存作一些很是有用的东西,而若是使用一个透明的只有文件的缓存布局则会很困难。
Gradle 缓存中不容许本地缓存隐藏问题和,以及一直是许多构建工具的挑战的创造神秘及难以调试的行为。这一新行为经过带宽和存储空间的有效途径被实现。在此过程当中,Gradle 实现了可靠和可重复的的企业构建。
Gradle 以二进制格式在元数据缓存中保留了依赖解析的各方面的记录。存储在元数据缓存中的信息包括:
1.+
)到一个具体的版本(例如1.2
)的结果。每一个元数据缓存中的条目包括了一条存储库中提供的信息,以及可用于缓存到期的时间戳的记录。
如上文所述,每一个仓库是一个单独的元数据缓存。一个仓库由它的 URL、类型和布局来区分。若是一个模块或构件以以前没有从这个存储库中解析过,Gradle 将尝试在这个存储库中解析这个模块。这将始终涉及到存储库中的远程查找,然而,在许多状况下都没有下载的须要(见第 50.9.1.3 节,“构件重用”下文)、。
若是所需的构件,在构建所指定的任何仓库中都没有找到的话,依赖解析将会失败,不管本地缓存是否从一个其余的仓库中取回这个构件。仓库独立容许构建之间用一种先进方法的方法彼此隔离,之前没有构建工具能作到这样。这是一个关键的功能,能在任何环境中建立可靠,重复性好的构建。
在下载构件前,Gradle 试图经过下载与该项目关联的sha文件以肯定所需的构件的校验和。若是校验和能够获取到,而且若是已经存在具备相同 id 和校验和的构件,则不会再去下载这个构件。若是校验和不能从远程服务器检索,构件将被下载(而且忽略它所匹配的现有的构件)。
同时考虑构件从不一样的仓库下载,Gradle 还将试图重用本地 Maven 库中发现的构件。若是一个候选的构件已经经过 Maven 下载,而且若是它能够和远程服务器定义的校验和匹配,Gradle 将使用这个构件。
不一样的仓库提供不一样的二进制构件以响应相同的构件标识符是可能的。这种常见的状况是 Maven 的快照构件,但任何构件也均可以不改变它的标识符而从新发布。经过基于其 SHA1 校验和缓存构件,Gradle 是可以保持同一构件的多个版本。这意味着,当对一个存储库解析时,Gradle 将永远不会覆盖从一个不一样的仓库中缓存的构件文件。这不须要让一个单独的文件在每个仓库中存储就能够作到。
--offline
命令行开机告诉 Gradle 老是从缓存中使用依赖模块,不管它们是否被再次检查。在使用离线运行时,Gradle 将不会尝试访问网络来执行依赖解析。若是所需的模块在依赖缓存中不存在,构建执行将会失败。
有时,Gradle 依赖缓存可能与已配置的仓库的实际状态不一样步。也许一个存储库的最初配置不正确,或许是一个“无改变”的模块被不正确地发布。要刷新依赖缓存中的全部依赖项,请使用--refresh-dependencies
命令行选项。
--refresh-dependencies
选项告诉 Gradle 在解析模块和构件时忽略全部缓存条目。对全部已配置的仓库执行新的解析,经过从新计算动态版本,模块刷新,以及下载构件。然而,在再次下载以前Gradle 可能将会检查前一个下载的构件是否有效。这是经过比较发布在仓库中的 SHA1 值和如今已经下载好的工件的 SHA1 值来完成的。
你能够在一个配置中经过使用ResolutionStrategy
对缓存的某些方面进行微调。
默认状况下,Gradle 缓存动态版本的时间为 24 小时。若是要改变Gradle对解析一个动态版本的缓存时间,可使用:
示例 50.47. 动态版本缓存控制
build.gradle
configurations.all { resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes' }
默认状况下,Gradle 的变化模块将缓存 24 小时。要修改 Gradle 对变化模块的元数据和构件的缓存时间,请使用:
示例 50.48. 变化模块的缓存控制
build.gradle
configurations.all { resolutionStrategy.cacheChangingModulesFor 4, 'hours' }
更多详细信息请参阅ResolutionStrategy
的 API 文档。
许多项目依赖于Maven 中央仓库。这不是没有问题的。
Maven 中央存储库可能会下线,或者响应时间很长。
许多项目的 POM 文件会有错误的信息(好比,commons-httpclient-3.0
的POM文件声明了 JUnit 是运行时依赖)。
对于许多项目而言,可能不是只有正确的一组依赖(因POM格式的影响会多或少)。
若是您的项目依赖于 Maven 中央仓,你极可能须要额外的自定义仓库,由于:
你可能须要尚未上传到Maven 中央仓的依赖。
你想要正确地处理 Maven 中央仓 POM 文件中错误的元数据。
你不想曝光给想要对你的项目进行构建的人,Maven 中央仓停机或者有时候响应时间太长。
想在设置一个自定义的仓库并不算什么。[17]但想让这个仓库保持最新的状态,可能会很乏味。对一个新的版本,你老是要建立新的 XML 描述符和目录。你的自定义存仓库是另外一个基础结构元素,它可能有停机时间而且须要更新。要启用历史版本,你须要保留全部过去的库,而且须要备份。它是一个间接层。你还要查找另一个信息源。尽管这一切真的不是大问题,但累加起来就有影响了。仓库管理器,好比像 Artifactory 或 Nexus 则会使这些工做变得轻松。可是好比开源项目一般没有主机用于这些产品。这种情况经过一些新的服务也改变了,好比Bintray ,它可让开发者使用自助服务的仓库平台托管和分发他们发布的二进制文件。Bintray 还支持共享通过他们审核的构件,经过JCenter公共仓库,为全部普通的OSS java 构件提供一个单一的解析地址(见第 50.6.2 节,“Maven JCenter 存储库”)。
这也是为何一些项目更愿意把他们的库存储于他们的版本控件系统的缘由。这种作法 Gradle 也彻底支持。库能够存在一个平面目录中,而没有任何 XML 模块描述符文件。然而 Gradle 能提供完整的传递依赖管理。您可使用客户端模块依赖,或者是工件依赖来表达依赖关系,后者的状况中第一级依赖是没有传递依赖的。人们能够从 svn 检出这样一个项目,而且具体必要的一切,来构建它。
若是您是使用像 Git 同样的分布式版本控制系统,因为人们会检出整个历史,你可能不想使用版本控制系统来保存这些库。但即便是这样, Gradle 的灵活性也可使你的生活更轻松。例如你可使用一个共享平面目录,而不包括如上所述能够有彻底的传递依赖管理的XML 描述符。
你也可使用混合策略。若是你主要关心的是 POM 文件和维护的自定义 XML 描述符中的元数据不正确,客户端模块提供了一种替代方案。但你固然也能够仍然使用 Maven2 仓库和你自定义的存储库,做为只放 jars和依然使用传递依赖管理的仓库。或者,你能够只为元数字不正确的 POMs 提供客户端模块。对于这些jar 和不正确的 POMs,你依然要使用远程仓库。
还有另一种方法,用于处理没有XML 描述符文件的传递依赖。你可使用 Gradle 来作,但咱们不推荐它。咱们提到它是为了完整性,以及和其余构建工具进行比较。
诀窍是只使用构件依赖,并在列表中对它们进行分组。对于这种方法,你将用某种方式表达,你第一级的依赖项和传递依赖是什么(见第 50.4.8 条,“可选属性”)。但缺点是,对于 Gradle 依赖管理而言,全部的依赖都被认为是第一级依赖。依赖报告不会显示你真正的依赖关系图,而且compile
任务会使用全部的依赖,而不仅是第一级依赖。总之,比起使用客户端模块,你的构建再也不那么容易维护和可靠。而你不会有其余收获。
[14] Gradle 支持部分多项目构建(参见 第 56 章, 多项目构建)。
[15] http://books.sonatype.com/mvnref-book/reference/pom-relationships-sect-project-relationships.html
[16] 在http://ant.apache.org/ivy/history/latest-milestone/concept.html,你能够了解到更多关于 ivy 模式的内容。
[17] 若是你想要从 Maven 中央仓库停机保护你的项目不变得更加复杂时,你可能须要设置一个仓库代理。在企业环境中,这是至关广泛的。而对于一个开放源码项目而言,看起来则有点小题大作了。