依赖关系管理是每个构建的一个关键特性,Gradle的重点在于提供易于理解且与各种方法兼容的一流的依赖管理。 如果您熟悉Maven或Ivy所使用的方法,那么你会很高兴知道Gradle完全兼容这两种方法,此外,它还具有足够的灵活性以支持完全自定义的方法。
Dependency management is a critical feature of every build, and Gradle has placed an emphasis on offering first-class dependency management that is both easy-to-understand and compatible with a wide variety of approaches. If you are familiar with the approach used by either Maven or Ivy you will be delighted to learn that Gradle is fully compatible with both approaches in addition to being flexible enough to support fully-customized approaches.
以下是 Gradle 支持的依赖管理的主要亮点:
Here are the major highlights of Gradle's support for dependency management:
依赖管理传递:Gradle 使你可以完全控制项目的依赖树。
Transitive dependency management: Gradle gives you full control of your project's dependency tree.
对非管理依赖的支持:如果你的依赖只是版本控制下或共享驱动器中的文件,Gradle 也提供了强大的功能来支持这种情况。
Support for non-managed dependencies: If your dependencies are simply files in version control or a shared drive, Gradle provides powerful functionality to support this.
对自定义依赖定义的支持:Gradle 的模块依赖使你能够描述构建脚本中的依赖层次结构。
Support for custom dependency definitions.: Gradle's Module Dependencies give you the ability to describe the dependency hierarchy in the build script.
完全可自定义的依赖解析方法:Gradle 使你能够自定义使依赖替换变得简单的解析规则。
A fully customizable approach to Dependency Resolution: Gradle provides you with the ability to customize resolution rules making dependency substitution easy.
完全兼容 Maven 和 Ivy :如果你已经在 Maven POM 文件或 Ivy 文件中定义了依赖,Gradle 有提供一系列常用的构建工具来进行无缝集成。
Full Compatibility with Maven and Ivy: If you have defined dependencies in a Maven POM or an Ivy file, Gradle provide seamless integration with a range of popular build tools.
与现有依赖管理基础结构的集成:Gradle兼容Maven和Ivy仓库。 如果你是使用Archiva, Nexus或Artifactory,Gradle与所有仓库格式100%兼容。
Integration with existing dependency management infrastructure: Gradle is compatible with both Maven and Ivy repositories. If you use Archiva, Nexus, or Artifactory, Gradle is 100% compatible with all repository formats.
由于成千上万相互依赖的开源组件各有一系列版本和不兼容性,依赖管理常常导致问题的复杂性增加。 当一个构建的依赖树变得笨拙时,你的构建工具不应强制你对依赖管理采取单一、不灵活的方法。 一个正确的构建系统必须设计得灵活,而Gradle可以处理任何情况。
With hundreds of thousands of interdependent open source components each with a range of versions and incompatibilities, dependency management has a habit of causing problems as builds grow in complexity. When a build's dependency tree becomes unwieldy, your build tool shouldn't force you to adopt a single, inflexible approach to dependency management. A proper build system has to be designed to be flexible, and Gradle can handle any situation.
在从一个构建系统迁移到另一个构建系统的过程当中,对于依赖管理而方,可能特别具有挑战性。 如果你要从Ant或Maven之类的工具迁移到Gradle,那么可能会面临一些困难的情况。 例如一个常见的模式是Ant项目,其中包含了存储在文件系统中的无版本jar文件。 其他构建系统需要在迁移之前批量替换此方法。而使用Gradle,你可以让新构建调整为任何现有的依赖源或依赖元数据。这使得增量迁移到Gradle的难度比其他的规范方案要容易得多。 在大多数大型项目上,构建迁移以及对开发流程的任何更改都是增量进行的,因为大多数组织都无法停止所有的一切,并迁移到一个构建工具的依赖管理概念中。
Dependency management can be particularly challenging during a migration from one build system to another. If you are migrating from a tool like Ant or Maven to Gradle, you may be faced with some difficult situations. For example, one common pattern is an Ant project with version-less jar files stored in the filesystem. Other build systems require a wholesale replacement of this approach before migrating. With Gradle, you can adapt your new build to any existing source of dependencies or dependency metadata. This makes incremental migration to Gradle much easier than the alternative. On most large projects, build migrations and any change to development process is incremental because most organizations can't afford to stop everything and migrate to a build tool's idea of dependency management.
即使你的项目正在使用自定义的依赖管理系统,或或是一些像Eclipse.classpath文件作为依赖管理的主数据,也很容易编写Gradle插件在Gradle中使用此数据。 出于迁移目的,这是Gradle中的常见技术。(但是,如果你已经迁移,那么可以从.classpath文件中移出并直接使用Gradle的依赖管理功能可能是个好主意。)
Even if your project is using a custom dependency management system or something like an Eclipse .classpath file as master data for dependency management, it is very easy to write a Gradle plugin to use this data in Gradle. For migration purposes this is a common technique with Gradle. (But, once you've migrated, it might be a good idea to move away from a .classpath file and use Gradle's dependency management features directly.)
具有讽刺意味的是,以丰富的开源组件库著称的语言,Java竟然没有库或者版本的概念。在Java中,没有标准的方法来告知JVM你正在使用Hibernate V3.0.5,也没有标准的方法来表示 foo-1.0.jar
依赖于 bar-2.0.jar
。 这导致了外部的解决方案通常都会基于构建工具。 目前最受欢迎的解决方案是Maven和Ivy。 Maven提供了完整的构建系统,而Ivy则只着眼于依赖管理。
It is ironic that in a language known for its rich library of open source components that Java has no concept of libraries or versions. In Java, there is no standard way to tell the JVM that you are using version 3.0.5 of Hibernate, and there is no standard way to say that foo-1.0.jar
depends on bar-2.0.jar
. This has led to external solutions often based on build tools. The most popular ones at the moment are Maven and Ivy. While Maven provides a complete build system, Ivy focuses solely on dependency management.
这两种工具都依赖于描述符XML文件,这些文件包含有关特定jar的依赖信息。 它们还使用仓库,在这些仓库中,实际的jar与它们的描述符文件放在一起;并且这两者都以一种形式或其他形式提供了jar版本冲突的解决方案。它们都成为解决依赖冲突的标准,而Gradle对于依赖管理从一开始在底层上使用的是Ivy。Gradle取代了对Ivy的直接依赖,采用了本地Gradle语解决引擎,该引擎支持一系列依赖解决方案的方法,包括POM和Ivy描述符文件。
Both tools rely on descriptor XML files, which contain information about the dependencies of a particular jar. Both also use repositories where the actual jars are placed together with their descriptor files, and both offer resolution for conflicting jar versions in one form or the other. Both have emerged as standards for solving dependency conflicts, and while Gradle originally used Ivy under the hood for its dependency management. Gradle has replaced this direct dependency on Ivy with a native Gradle dependency resolution engine which supports a range of approaches to dependency resolution including both POM and Ivy descriptor files.
由于Gradle在依赖管理方面有强烈的主张,该工具提供了两个选项让你从中选择:遵循推荐的最佳实践,或支持你可以想到的任何类型的模式。 本节概述Gradle项目建议的用于管理依赖的最佳实践。
While Gradle has strong opinions on dependency management, the tool gives you a choice between two options: follow recommended best practices or support any kind of pattern you can think of. This section outlines the Gradle project's recommended best practices for managing dependencies.
无论哪种语言,适当的依赖管理对于每个项目都很重要。从一个由Java编写的依赖数百个开源库的复杂企业应用,到依赖少数几个库的最简单的Clojure应用,依赖管理的方法大不相同,并且可能取决于目标技术、应用程序部署的方法以及项目的性质。多上项目捆绑为可复用的库,比起企业应用集成到更大规模的软件和基础结构系统中,可能有不同的需求。尽管需求的差异很大,Gradle项目建议所有项目都遵循这组核心规则:
No matter what the language, proper dependency management is important for every project. From a complex enterprise application written in Java depending on hundreds of open source libraries to the simplest Clojure application depending on a handful of libraries, approaches to dependency management vary widely and can depend on the target technology, the method of application deployment, and the nature of the project. Projects bundled as reusable libraries may have different requirements than enterprise applications integrated into much larger systems of software and infrastructure. Despite this wide variation of requirements, the Gradle project recommends that all projects follow this set of core rules:
在文件名中库的版本必须是容易辨认的。虽然jar的版本通常在Manifest文件中,但当你要检查项目时它并不显而易见。如果有人让你看20个jar文件,你更愿意哪一种?名字像 commons-beanutils-1.3.jar
的文件集还是名字像 spring.jar
的文件集?如果依赖的文件名称带有版本号,那么将更容易快速确定依赖的版本。
The version of a library must be easy to recognize in the filename. While the version of a jar is usually in the Manifest file, it isn't readily apparent when you are inspecting a project. If someone asks you to look at a collection of 20 jar files, which would you prefer? A collection of files with names like commons-beanutils-1.3.jar
or a collection of files with names like spring.jar
? If dependencies have file names with version numbers it is much easier to quickly identify the versions of your dependencies.
如果版本不清楚,你可能会引入一些很难找以的微妙错误。例如可能有一个项目使用 Hibernate 2.5,想一下一个开发者决定在她的机器上安装 3.0.5 的版本,以修复一个关键的安全 bug,但她忘记通知其他团队这个变化。她可能成功地解决了这个安全 bug,但她也可能引入一些 bug 到代码库中,如项目用到了 Hibernate 现在弃用的功能。一周后,在集成的机器上可能会有一个异常,而这个异常无法在任何人的机器上复现。然后多个开发者花了数天的时间去查这个问题,最终才意识到,如果他们知道 Hibernate 已经从 2.5 升级到 3.0.5,这个错误会很容易发现。
If versions are unclear you can introduce subtle bugs which are very hard to find. For example there might be a project which uses Hibernate 2.5. Think about a developer who decides to install version 3.0.5 of Hibernate on her machine to fix a critical security bug but forgets to notify others in the team of this change. She may address the security bug successfully, but she also may have introduced subtle bugs into a codebase that was using a now-deprecated feature from Hibernate. Weeks later there is an exception on the integration machine which can't be reproduced on anyone's machine. Multiple developers then spend days on this issue only finally realising that the error would have easy to uncover if they knew that Hibernate had been upgraded from 2.5 to 3.0.5.
在 jar 名称中的版本增强了项目的表现性,并使其更易于维护。这种做法也减少了发生错误的可能。
Versions in jar names increase the expressiveness of your project and make them easier to maintain. This practice also reduces the potential for error.
传递依赖管理是一种使你的项目依赖于一些库,而这些库又依赖于其他库技术。这种传递依赖的递归模式导致的结果说,在依赖树中,会包含项目的第一级依赖,第二级依赖,等等。如果你不按层级树的第一级和二级依赖对你的依赖建模,那么就会很容易失去对组合的非结构化依赖的控制。请考虑Gradle项目本身,而Gradle仅具有几个直接的第一级依赖,当编译Gradle时,在它的类路径上会需要超过一百个依赖。在规模更大的范围内,使用Spring、Hibernate和其他库,以及成百上千的内部项目的企业项目,可能有非常大的依赖树。
Transitive dependency management is a technique that enables your project to depend on libraries which, in turn, depend on other libraries. This recursive pattern of transitive dependencies results in a tree of dependencies including your project's first-level dependencies, second-level dependencies, and so on. If you don't model your dependencies as a hierarchical tree of first-level and second-level dependencies it is very easy to quickly lose control over an assembled mess of unstructured dependencies. Consider the Gradle project itself, while Gradle only has a few direct, first-level dependencies, when Gradle is compiled it needs more that one hundred dependencies on the classpath. On a far larger scale, Enterprise projects using Spring, Hibernate, and other libraries, alongside hundreds or thousands of internal projects can have very large dependency trees.
当这些大的依赖树需要更改时,你通常需要解决某些依赖的版本冲突。比如说某个开源代码库需要一个版本的日志记录库,而另一个需要另一个版本。 Gradle和其他构建工具都能够解决这种依赖树并解决冲突,但不同的是,Gradle使你可以控制传递依赖冲突解决。
When these large dependency trees need to change, you'll often have to solve some dependency version conflicts. Say one open source library needs one version of a logging library and a another uses an alternative version. Gradle and other build tools all have the ability to solve this dependency tree and resolve conflicts, but what differentiates Gradle is the control it gives you over transitive dependencies and conflict resolution.
虽然你可以尝试手动管理此问题,但你很快就会发现此方法不能扩展。 如果你要去掉第一级依赖,你确实不能确定还有其他哪些 jar 是你需要移除的。第一级依赖的依赖本身也可能是第一级依赖,或者是另一个第一级依赖的传递依赖。 如果你想自己管理传递依赖,最终的结局是你的构建会变得很脆弱:没人敢更改你的依赖,因为破坏构建的风险太高了。 项目类路径将变得完全混乱,如果发生类路径问题,那简直就是人间地狱。
While you could try to manage this problem manually, you will quickly find that this approach doesn't scale. If you want to get rid of a first level dependency you really can't be sure which other jars you should remove. A dependency of a first level dependency might also be a first level dependency itself, or it might be a transitive dependency of yet another first level dependency. If you try to manage transitive dependencies yourself, the end of the story is that your build becomes brittle: no one dares to change your dependencies because the risk of breaking the build is too high. The project classpath becomes a complete mess, and, if a classpath problem arises, hell on earth invites you for a ride.
Gradle为你提供了不同的表达第一级和传递依赖的方法。 通过Gradle,你可以混合使用和适配一些方法;例如,你可以在 SCM 中存储 jar而不需要 XML描述符文件,并且仍然使用传递依赖管理。
Gradle offers you different ways to express first-level and transitive dependencies. With Gradle you can mix and match approaches; for example, you could store your jars in an SCM without XML descriptor files and still use transitive dependency management.
相同的jar的冲突版本应该被检测到,并且要么解决要么抛出异常。 如果不使用传递依赖管理,版本冲突没被检测到,那以在类路径中的无法预测的顺序将决定最终使用哪个版本的依赖。在有许多开发者更改依赖的大型项目上,成功的构建将会少之又少,因为依赖的顺序可能直接影响构建是否成功(或者在生产中是否出现错误)。
Conflicting versions of the same jar should be detected and either resolved or cause an exception. If you don't use transitive dependency management, version conflicts are undetected and the often accidental order of the classpath will determine what version of a dependency will win. On a large project with many developers changing dependencies, successful builds will be few and far between as the order of dependencies may directly affect whether a build succeeds or fails (or whether a bug appears or disappears in production).
如果你还没有处理过在类路径中 jar 包版本冲突的麻烦,这里有一个小趣闻等着你。在一个有30个子模块的大型项目中,向子项目添加的一个依赖改变了类路径的顺序,Spring 2.5与老的2.4版本 顺序被调换了。虽然可以继续构建,开发者会开始注意到在生产中出现了各种令人惊讶(和惊人可怕)的bug。然而,更糟糕的是,无意降低版本的Spring向系统引入了几个安全缺陷,现在需要整个组织进行全面的安全审计。
If you haven't had to deal with the curse of conflicting versions of jars on a classpath, here is a small anecdote of the fun that awaits you. In a large project with 30 submodules, adding a dependency to a subproject changed the order of a classpath, swapping Spring 2.5 for an older 2.4 version. While the build continued to work, developers were starting to notice all sorts of surprising (and surprisingly awful) bugs in production. Worse yet, this unintentional downgrade of Spring introduced several security vulnerabilities into the system, which now required a full security audit throughout the organization.
简而言之,版本冲突是很不好的,你应该管理您的传递依赖以避免版本冲突。你可能还希望了解使用冲突版本的位置,并在整个组织中统一依赖的指定版本。有了类似于Gradle的良好冲突报告工具,这些信息可 用于与整个组织进行通信,并在单个版本上实现标准化。如果你觉得你不会发生版本冲突,那就再想想。不同的第一级依赖,依赖于其他依赖的不同重叠范围的版本非常常见,并且JVM还不能提供简单的方法,使是能在类路径中让相同的 jar 包可以有不同的版本(请参阅 《第50.1.2节,“依赖管理与Java”)。
In short, version conflicts are bad, and you should manage your transitive dependencies to avoid them. You might also want to learn where conflicting versions are used and consolidate on a particular version of a dependency across your organization. With a good conflict reporting tool like Gradle, that information can be used to communicate with the entire organization and standardize on a single version. If you think version conflicts don't happen to you, think again. It is very common for different first-level dependencies to rely on a range of different overlapping versions for other dependencies, and the JVM doesn't yet offer an easy way to have different versions of the same jar in the classpath (see Section 50.1.2, “Dependency management and Java”).
Gradle 提供了以下的冲突解决策略:
Gradle offers the following conflict resolution strategies:
ResolutionStrategy
。 ResolutionStrategy
for details on how to explicitly choose a particular version.虽然上面介绍的策略通常足够解决大部分的冲突,但是 Gradle 也提供了更细粒度的机制来解决版本冲突:
While the strategies introduced above are usually enough to solve most conflicts, Gradle provides more fine-grained mechanisms to resolve version conflicts:
DependencyHandler
中的示例。 DependencyHandler
.ResolutionStrategy
中的示例。ResolutionStrategy
为了解决版本冲突的问题,带有依赖关系图的报告也是很有帮助的。这种报告是依赖管理的另一个功能。
To deal with problems due to version conflicts, reports with dependency graphs are also very helpful. Such reports are another feature of dependency management.
当您想要使用特定依赖的最新版本或某个版本范围内的最新版本时,有许多情况。这可能是开发期中需要,或者可能你正在开发一个库,它被设计为使用某个范围内的依赖版本。你可以通过使用动态版本很容易地依赖这些不断变化的依赖。动态版本可以是一个版本范围(例如2.+
),也可以是最新版本的占位符(比如latest.integration
)。
There are many situation when you want to use the latest version of a particular dependency, or the latest in a range of versions. This can be a requirement during development, or you may be developing a library that is designed to work with a range of dependency versions. You can easily depend on these constantly changing dependencies by using a dynamic version. A dynamic version can be either a version range (e.g. 2.+
) or it can be a placeholder for the latest version available (e.g. latest.integration
).
或者,有时你请求的模块可能会随着时间的推移而变化,即使是版本相同。这种变化模块类型的一个例子是Maven的SNAPSHOT
模块,它始终指向最新发布的工件。换句话说,一个标准的Maven快照是一个永远不会不变的模块,可以这样说,它是一个“变化模块”。
Alternatively, sometimes the module you request can change over time, even for the same version. An example of this type of changing module is a Maven SNAPSHOT
module, which always points at the latest artifact published. In other words, a standard Maven snapshot is a module that never stands still so to speak, it is a "changing module".
动态版本 和 变化模块 之间的主要差别是,当你解析一个 动态版本时,你会得到真实的、静态的版本作为模块名称。当你解析一个 变化模块时,将使用你请求的版本来命名工件,但底层工件可能会随时间而变化。
The main difference between a dynamic version and a changing module is that when you resolve a dynamic version, you'll get the real, static version as the module name. When you resolve a changing module, the artifacts are named using the version you requested, but the underlying artifacts may change over time.
默认情况下,Grdale 对动态版本和变化模块的缓存时间是24小时。你可能使用命令行选项重写默认的缓存模式。你可以通过 resolution strategy
修改你的构建的缓存到期时间(见第 50.9.3 节,《调整控制依赖缓存》)。
By default, Gradle caches dynamic versions and changing modules for 24 hours. You can override the default cache modes using command line options. You can change the cache expiry times in your build using the resolution strategy
(see Section 50.9.3, “Fine-tuned control over dependency caching”).
在Gradle中,依赖将被分组到配置。配置有一个名称和许多其他属性,并且可以相互扩展。许多Gradle插件会向项目添加了预定义的配置。例如,Java插件添加了一些配置来表示它需要的各种类路径。有关详细信息,请参阅 第23.5节,《依赖管理》 。当然,你可以在此基础上添加自定义配置。有关自定义配置的用例很多。这是非常方便的,例如添加依赖时不需要再去构建或测试你的软件(比如,要随分发一起提供的其他JDBC驱动程序)。
In Gradle dependencies are grouped into configurations. Configurations have a name, a number of other properties, and they can extend each other. Many Gradle plugin add pre-defined configurations to your project. The Java plugin, for example, adds some configurations to represent the various classpaths it needs. see Section 23.5, “Dependency management” for details. Of course you can add custom configurations on top of that. There are many use cases for custom configurations. This is very handy for example for adding dependencies not needed for building or testing your software (e.g. additional JDBC drivers to be shipped with your distribution).
项目的配置由 configurations
对象管理。你传给这个配置对象的闭包将通过它对应的 API 被应用。要了解有关此 API 的更多信息,请查看 ConfigurationContainer
。
A project's configurations are managed by a configurations
object. The closure you pass to the configurations object is applied against its API. To learn more about this API have a look at ConfigurationContainer
.
如果要定义配置:
To define a configuration:
示例 50.1. 配置的定义 - Example 50.1. Definition of a configuration
build.gradle
configurations { compile }
如果要访问配置:
To access a configuration:
示例 50.2. 访问配置 - Example 50.2. Accessing a configuration
build.gradle
println configurations.compile.name
println configurations['compile'].name
如果要配置一个配置:
To configure a configuration:
示例 50.3. 配置的配置 - Example 50.3. Configuration of a configuration
build.gradle
configurations { compile { description = 'compile classpath' transitive = true } runtime { extendsFrom compile } } configurations.compile { description = 'compile classpath' }
你可以声明几种不同类型的依赖:
There are several different types of dependencies that you can declare:
表 50.1. 依赖类型 - Table 50.1. Dependency types
类型 Type |
描述 Description |
外部模块依赖 External module dependency |
对一些仓库中的外部模块的依赖。 A dependency on an external module in some repository. |
项目依赖 Project dependency |
对同一个构建中另一个项目的依赖。 A dependency on another project in the same build. |
文件依赖 File dependency |
对本地文件系统上一组文件的依赖 A dependency on a set of files on the local filesystem. |
客户端模块依赖 Client module dependency |
对外部模块的依赖,该外部模块的工件位于某仓库中,但模块元数据由本地构建指定。当你要覆盖模块的元数据时,可以使用这种类型的依赖。 A dependency on an external module, where the artifacts are located in some repository but the module meta-data is specified by the local build. You use this kind of dependency when you want to override the meta-data for the module. |
Gradle API 依赖 Gradle API dependency |
对当前 Gradle 版本的 API 的依赖。在开发自定义 Gradle 插件和任务类型时,使用这种依赖。 A dependency on the API of the current Gradle version. You use this kind of dependency when you are developing custom Gradle plugins and task types. |
本地 Groovy 依赖 Local Groovy dependency |
对当前 Gradle 版本所使用的 Groovy 版本的依赖。在开发自定义 Gradle 插件和任务类型时,使用这种依赖。 A dependency on the Groovy version used by the current Gradle version. You use this kind of dependency when you are developing custom Gradle plugins and task types. |
外部模块依赖是最常见的依赖。它们引用外部仓库中的模块。
External module dependencies are the most common dependencies. They refer to a module in an external repository.
示例 50.4. 模块依赖 - Example 50.4. Module dependencies
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
。
See DependencyHandler
for more examples and a complete reference.
Gradle 为模块依赖提供了不同的标记法,有字符串表示法和映射表示法。模块依赖有 API 可以让你进一步配置,请查看 ExternalModuleDependency
了解有关该 API 的所有内容。该 API 提供了属性和配置方法。通过字符串表示法,你可以定义一个属性子集。而通过映射表示法,你可以定义所有属性。要访问完整的 API(包括映射或字符串表示法),你可以通过一个闭包将一个依赖指定给配置。
Gradle provides different notations for module dependencies. There is a string notation and a map notation. A module dependency has an API which allows for further configuration. Have a look at ExternalModuleDependency
to learn all about the API. This API provides properties and configuration methods. Via the string notation you can define a subset of the properties. With the map notation you can define all properties. To have access to the complete API, either with the map or with the string notation, you can assign a single dependency to a configuration together with a closure.
如果你定义了一个模块依赖,Gradle 将在仓库中查找相应的模块描述符文件(pom.xml
或 ivy.xml
)。如果存在这样的模块描述符文件,它会进行解析,并下载此模块的工件(如 hibernate-3.0.5.jar
)及其依赖(如 cglib)。如果不存在这样的模块描述符文件,Gradle将查找名为 hibernate-3.0.5.jar
的文件。在 Maven 中,一个模块只能有一个工件。在 Gradle 和 Ivy 中,一个模块可以有多个工件。每个工件都可以有一组不同的依赖。
If you declare a module dependency, Gradle looks for a corresponding module descriptor file (pom.xml
or ivy.xml
) in the repositories. If such a module descriptor file exists, it is parsed and the artifacts of this module (e.g. hibernate-3.0.5.jar
) as well as its dependencies (e.g. cglib) are downloaded. If no such module descriptor file exists, Gradle looks for a file called hibernate-3.0.5.jar
to retrieve. In Maven, a module can have one and only one artifact. In Gradle and Ivy, a module can have multiple artifacts. Each artifact can have a different set of dependencies.
ivy.xml
)可以声明多个工件。关于这一点的更多信息请参阅关于
ivy.xml
的 Ivy 参考文档。在 Gradle 中,当你在一个 Ivy 模块上声明依赖时,实际上是在该模块的
default
配置上声明了一个依赖。所以你实际上依赖的工件集(通常是一些 jar 包),是与该模块的
default
配置相关联的工件集。以下是这个问题上的一些情况:
default
配置包含了不需要的工件。你应该仅声明对所需工件的依赖,而不是依赖于整个配置。 default
configuration of a module contains undesired artifacts. Rather than depending on the whole configuration, a dependency on just the desired artifacts is declared.default
配置。该配置被显式地命名为依赖声明的一部分。 default
. That configuration is explicitly named as part of the dependency declaration.DependencyHandler
。
如上所述,如果找不到模块描述符文件,那么默认情况下 Gradle 会下载一个与模块名称相同的 jar。但有时候,即便仓库中包含了模块描述符,你也希望只下载工件 jar 而不下载它的依赖。[14] 有时候你希望从仓库中下载没有模块描述符的 zip。 Gradle 提供了 仅工件 表示法,用于这些案例——只需要对你想要下载的扩展名前加个 '@'
符号:
As said above, if no module descriptor file can be found, Gradle by default downloads a jar with the name of the module. But sometimes, even if the repository contains module descriptors, you want to download only the artifact jar, without the dependencies. [14] And sometimes you want to download a zip from a repository, that does not have module descriptors. Gradle provides an artifact only notation for those use cases - simply prefix the extension that you want to be downloaded with '@'
sign:
示例50.5. 仅工件表示法 - Example 50.5. 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' }
仅工件表示法将创建只下载具有指定扩展名的工件文件的模块依赖,而忽略现有的模块描述符。
An artifact only notation creates a module dependency which downloads only the artifact file with the specified extension. Existing module descriptors are ignored.
Maven 依赖管理有分类器的概念, [15] Gradle 支持这一点。如果你想从 Maven 仓库中获取分类的依赖,可以这样写:
The Maven dependency management has the notion of classifiers. [15] Gradle supports this. To retrieve classified dependencies from a Maven repository you can write:
示例 50.6. 使用分类器的依赖 - Example 50.6. Dependency with 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'
如上面的第一行所示,分类器可以和仅工件表示法一起用。
As can be seen in the first line above, classifiers can be used together with artifact only notation.
要遍历一个配置的依赖工件很容易:
It is easy to iterate over the dependency artifacts of a configuration:
示例 50.7. 遍历一个配置 - Example 50.7. Iterating over a configuration
build.gradle
task listJars << { configurations.compile.each { File file -> println file.name } }
gradle -q listJars
的输出结果
Output of 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
客户端模块依赖允许直接在构建脚本中声明 传递 依赖,它们是外部仓库中模块描述符的替代者。
Client module dependencies allow to declare transitive dependencies directly in the build script. They are a replacement for a module descriptor in an external repository.
示例 50.8. 客户端模块依赖——传递依赖 - Example 50.8. Client module dependencies - transitive dependencies
build.gradle
dependencies { runtime module("org.codehaus.groovy:groovy:2.3.3") { 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 描述符来找出它的依赖,而是从构建文件中获取信息。客户端模块的依赖可以是正常模块依赖或工件依赖,或者是另一个客户端模块。还请查看 API 文档:ClientModule
This declares a dependency on Groovy. Groovy itself has dependencies. But Gradle does not look for an XML descriptor to figure them out but gets the information from the build file. The dependencies of a client module can be normal module dependencies or artifact dependencies or another client module. Have also a look at the API documentation: ClientModule
当前版本的客户端模块中有一个限制。假设你的项目是一个库,你想要将这个库上传到公司的 Maven 或 Ivy 仓库,Gradle 会将项目的 jar 包与依赖的 XML 描述符文件一起上传到公司仓库中。如果你使用了客户端模块,那么 XML 描述符文件中的依赖声明就会不正确。我们会在 Gradle 的未来版本中改善这一点。
In the current release client modules have one limitation. Let's say your project is a library and you want this library to be uploaded to your company's Maven or Ivy repository. Gradle uploads the jars of your project to the company repository together with the XML descriptor file of the dependencies. If you use client modules the dependency declaration in the XML descriptor file is not correct. We will improve this in a future release of Gradle.
对于多项目构建,Gradle 能区分外部依赖与作为多项目构建的一部分的某个子项目上的依赖。对于后者,你可以声明项目依赖。
Gradle distinguishes between external dependencies and dependencies on projects which are part of the same multi-project build. For the latter you can declare Project Dependencies.
示例 50.9. 项目依赖 - Example 50.9. Project dependencies
build.gradle
dependencies {
compile project(':shared')
}
详细信息请参阅ProjectDependency
的 API 文档。
For more information see the API documentation for ProjectDependency
多项目构建将在第 56 章,《多项目构建》中进行详述。
Multi-project builds are discussed inChapter 56, Multi-project Builds.
文件依赖可以让你直接将一组文件添加到配置中,而不首先把它们添加到仓库。如果你无法或不想将某些文件放在仓库里,或者是如果你不想使用任何仓库来存储依赖的话,这会很有用。
File dependencies allow you to directly add a set of files to a configuration, without first adding them to a repository. This can be useful if you cannot, or do not want to, place certain files in a repository. Or if you do not want to use any repositories at all for storing your dependencies.
要将一些文件添加为配置的依赖,你只需要把文件集合作为依赖传入:
To add some files as a dependency for a configuration, you simply pass a file collection as a dependency:
示例 50.10. 文件依赖 - Example 50.10. File dependencies
build.gradle
dependencies { runtime files('libs/a.jar', 'libs/b.jar') runtime fileTree(dir: 'libs', include: '*.jar') }
项目发布的依赖描述符不包含文件依赖,但同一个构建内的传递项目依赖会包含它。这意味着,文件依赖可以在同一个构建内使用,但不能在当前构建之外使用。
File dependencies are not included in the published dependency descriptor for your project. However, file dependencies are included in transitive project dependencies within the same build. This means they cannot be used outside the current build, but they can be used with the same build.
你可以声明哪些任务将产生文件依赖的文件。例如,你可以在通过构建生成文件的时候这样做。
You can declare which tasks produce the files for a file dependency. You might do this when, for example, the files are generated by the build.
示例 50.11. 生成文件依赖 - Example 50.11. Generated file dependencies
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
的输出结果
Output of gradle -q list
> gradle -q list compiling classes classpath = [classes]
你可以通过使用 DependencyHandler.gradleApi()
方法,来声明一个当前版本的 Gradle API 的依赖。
You can declare a dependency on the API of the current version of Gradle by using the DependencyHandler.gradleApi()
method. This is useful when you are developing custom Gradle tasks or plugins.
示例 50.12. Gradle API 依赖 - Example 50.12. Gradle API dependencies
build.gradle
dependencies { compile gradleApi() }
你可以通过使用 DependencyHandler.localGroovy()
方法,来声明对与 Gradle 一起分发的 Groovy 的依赖。当你使用 Groovy 开发自定义 Gradle 任务或插件时,这会很有用。
You can declare a dependency on the Groovy that is distributed with Gradle by using the DependencyHandler.localGroovy()
method. This is useful when you are developing custom Gradle tasks or plugins in Groovy.
示例 50.13. Gradle 的 Groovy 依赖 - Example 50.13. Gradle's Groovy dependencies
build.gradle
dependencies { compile localGroovy() }
通过配置或是依赖,你可以排除掉传递依赖:
You can exclude a transitive dependency either by configuration or by dependency:
示例 50.14. 排除传递依赖 - Example 50.14. Excluding transitive dependencies
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' } }
如果你为一个特定配置定义了排除,那么解析这个配置或任何继承的配置时,所有依赖的被排除的传递依赖都会被过滤掉。如果你要从所有配置中排除传递依赖,那么可以使用一种简明的方式,使用 Groovy 的 spread-dot 运算符来表示此操作,如示例中所示。定义排除时,你可以只指定组织或只指定模块名称,或者两者都指定。另请查看 Dependency
和 Configuration
的 API 文档。
If you define an exclude for a particular configuration, the excluded transitive dependency will be filtered for all dependencies when resolving this configuration or any inheriting configuration. If you want to exclude a transitive dependency from all your configurations you can use the Groovy spread-dot operator to express this in a concise way, as shown in the example. When defining an exclude, you can specify either only the organization or only the module name or both. Have also a look at the API documentation of Dependency
and Configuration
.
并非每个传递依赖都可以排除_某些传递依赖可能对于入驻正确的运行时行为至关重要。通常,可以排除的传递依赖,是在运行时不需要,或者是保证在目标环境或平台上可用。
Not every transitive dependency can be excluded - some transitive dependencies might be essential for correct runtime behavior of the application. Generally, one can exclude transitive dependencies that are either not required by runtime or that are guaranteed to be available on the target environment/platform.
你的排除应该是对依赖还是配置?事实表明,在大多数情况下,你希望使用的是对配置的排除。以下是为什么想要排除传递依赖的一些示例原因。记住,对于某些用例,存在比排除更有效的解决方案!
Should you exclude per-dependency or per-configuration? It turns out that in majority of cases you want to use the per-configuration exclusion. Here are the some exemplary reasons why one might want to exclude a transitive dependency. Bear in mind that for some of those use cases there are better solutions than exclusions!
ResolutionStrategy
的文档,以了解这个问题的可能更好的解决方案。 ResolutionStrategy
for a potentially better solution to the problem. 基本上,大多数情况下,应该对配置排除依赖,这样依赖声明就更加明确。它也更加准确,因为对依赖的排除规则不保证给定的传递依赖不会显示在配置中。例如,其他某些不含任何排除规则的依赖可能会带上那个不需要的传递依赖。
Basically, in most of the cases excluding the transitive dependency should be done per configuration. This way the dependency declaration is more explicit. It is also more accurate because a per-dependency exclude rule does not guarantee the given transitive dependency does not show up in the configuration. For example, some other dependency, which does not have any exclude rules, might pull in that unwanted transitive dependency.
其他有关依赖排除的示例,可以参考 ModuleDependency
或 DependencyHandler
。
Other examples of the dependency exclusions can be found in the reference for ModuleDependency
or DependencyHandler
.
依赖的所有属性除了名称之外都是可选的。这取决于仓库类型,这些信息实际上用于查找仓库中的依赖。请参阅 第50.6节,《存储库》。 比如如果你使用 Maven 仓库,那么就需要定义组,名称及版本。如果你使用文件系统仓库,那么可能只需要名称或名称及版本。
All attributes for a dependency are optional, except the name. It depends on the repository type, which information is need for actually finding the dependencies in the repository. See Section 50.6, “Repositories”. If you work for example with Maven repositories, you need to define the group, name and version. If you work with filesystem repositories you might only need the name or the name and the version.
示例 50.15. 依赖的可选属性 - Example 50.15. Optional attributes of dependencies
build.gradle
dependencies { runtime ":junit:4.10", ":testng" runtime name: 'testng' }
你也可以将依赖的集合或数组指定给配置:
You can also assign collections or arrays of dependency notations to a configuration:
示例 50.16. 依赖的集合和数组 - Example 50.16. Collections and arrays of dependencies
build.gradle
List groovy = ["org.codehaus.groovy:groovy-all:2.3.3@jar", "commons-cli:commons-cli:1.0@jar", "org.apache.ant:ant:1.9.3@jar"] List hibernate = ['org.hibernate:hibernate:3.0.5@jar', 'somegroup:someorg:1.0@jar'] dependencies { runtime groovy, hibernate }
在 Gradle 中,一个依赖可以有不同的配置(就像你的项目可以有不同的配置)。如果未显式指定任何内容,那么 Gradle 会使用依赖的默认配置。对于 Maven 仓库的依赖,默认配置是唯一可用的配置。 如果你使用 Ivy 仓库并且想为依赖声明一个非默认配置,那么必须使用映射表示法并声明:
In Gradle a dependency can have different configurations (as your project can have different configurations). If you don't specify anything explicitly, Gradle uses the default configuration of the dependency. For dependencies from a Maven repository, the default configuration is the only available one anyway. If you work with Ivy repositories and want to declare a non-default configuration for your dependency you have to use the map notation and declare:
示例 50.17. 依赖配置 - Example 50.17. Dependency configurations
build.gradle
dependencies { runtime group: 'org.somegroup', name: 'somedependency', version: '1.0', configuration: 'someConfiguration' }
要对项目依赖执行同样操作,你需要声明:
To do the same for project dependencies you need to declare:
示例 50.18. 项目的依赖配置 - Example 50.18. Dependency configurations for project
build.gradle
dependencies { compile project(path: ':api', configuration: 'spi') }
你可以从命令行生成依赖报告(请参阅 第11.6.4节,《列示项目依赖》)。在项目报告插件的帮助下(请参阅 第41章,《项目报告插件》) ,你的构建能够创建这样一个报告。
You can generate dependency reports from the command line (see Section 11.6.4, “Listing project dependencies”). With the help of the Project report plugin (see Chapter 41, The Project Report Plugin) such a report can be created by your build.
从 Gradle 1.2 起,还有一个新的程序化 API 来访问已解析的依赖信息。这个依赖报告(请参阅上一段)正是使用这个API。 这个 API 可以让你查看解析后的依赖图,并提供依赖的有关信息。在即将到来的发行版中,API 将提供更多有关解析结果的信息。有关此 API 的更多信息,请参阅 ResolvableDependences.getResolutionResult()
上的 javadocs。 ResolutionResult
API 的可能用法:
Since Gradle 1.2 there is also a new programmatic API to access the resolved dependency information. The dependency reports (see the previous paragraph) are using this API under the covers. The API lets you to walk the resolved dependency graph and provides information about the dependencies. With the coming releases the API will grow to provide more information about the resolution result. For more information about the API please refer to the javadocs on ResolvableDependencies.getResolutionResult()
. Potential usages of the ResolutionResult
API:
下面的示例我们使用以下的依赖设置:
For the examples below we have the following dependencies setup:
示例 50.19. Configuration.copy - Example 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" }
这些依赖有以下的传递依赖:
The dependencies have the following transitive dependencies:
shark-1.0 -> seal-2.0, tuna-1.0
orca-1.0 -> seal-1.0
tuna-1.0 -> herring-1.0
你可以使用配置来访问声明的依赖或其中的子集:
You can use the configuration to access the declared dependencies or a subset of those:
示例 50.20. 访问已声明的依赖 - Example 50.20. Accessing declared dependencies
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
的输出结果
Output of gradle -q dependencies
> gradle -q dependencies albatros albatros orca shark tuna albatros shark tuna
dependencies
只返回了明确属于配置的依赖,allDependencies
则包括了扩展配置的依赖。 dependencies
returns only the dependencies belonging explicitly to the configuration. allDependencies
includes the dependencies from extended configurations.
要获得配置依赖的库文件,你可以执行如下操作:
To get the library files of the configuration dependencies you can do:
示例 50.21. Configuration.files - Example 50.21. Configuration.files
build.gradle
task allFiles << { configurations.sealife.files.each { file -> println file.name } }
gradle -q allFiles
的输出结果
Output of 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
有时你想一些配置依赖的子集(例如单个依赖)的库文件。
Sometimes you want the library files of a subset of the configuration dependencies (e.g. of a single dependency).
示例 50.22. 特殊的 Configuration.files - Example 50.22. Configuration.files with spec
build.gradle
task files << {
configurations.sealife.files { dep -> dep.name == 'orca' }.each { file ->
println file.name
}
}
gradle -q files
的输出结果
Output of gradle -q files
> gradle -q files orca-1.0.jar seal-2.0.jar
Configuration.files
方法总是获取 整个 配置的所有工件,然后它通过指定的依赖对检索到的文件进行过滤。正如你在示例中所看到的,传递依赖会被包含在内。
The Configuration.files
method always retrieves all artifacts of the whole configuration. It then filters the retrieved files by specified dependencies. As you can see in the example, transitive dependencies are included.
你也可以复制一个配置。你可以选择仅复制原配置中的一部分依赖。复制方法有两种:copy
方法仅复制显式属于配置的依赖; copyRecursive
方法会复制所有的依赖,包括扩展配置中的依赖。
You can also copy a configuration. You can optionally specify that only a subset of dependencies from the original configuration should be copied. The copying methods come in two flavors. The copy
method copies only the dependencies belonging explicitly to the configuration. The copyRecursive
method copies all the dependencies, including the dependencies from extended configurations.
示例 50.23. Configuration.copy - Example 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
的输出结果
Output of gradle -q copy
> gradle -q copy albatros shark tuna albatros
请务必注意,复制的配置所返回的文件,通常并不总是与原配置的依赖子集所返回的文件相同。 如果子集的依赖与不属于子集的依赖发生版本冲突,那么解析结果可能会不同。
It is important to note that the returned files of the copied configuration are often but not always the same than the returned files of the dependency subset of the original configuration. In case of version conflicts between dependencies of the subset and dependencies not belonging to the subset the resolve result might be different.
示例 50.24. Configuration.copy 与 Configuration.files - Example 50.24. Configuration.copy vs. 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
的输出结果
Output of 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
版本。 方法也因此返回
seal-2.0
作为 orca
的传递依赖。而复制的配置只有 orca
依赖,故而不存在版本冲突,所以 seal-1.0
作为传递依赖被返回。
In the example above, orca
has a dependency on seal-1.0
whereas shark
has a dependency onseal-2.0
. The original configuration has therefore a version conflict which is resolved to the newer seal-2.0
version. The files
method therefore returns seal-2.0
as a transitive dependency oforca
. The copied configuration only has orca
as a dependency and therefore there is no version conflict and seal-1.0
is returned as a transitive dependency.
一旦一个配置被配置,那它就是不可变的了。如果去修改它或它的某个依赖的状态就会引发异常。你始终可以复制已经解析的配置,这个复制的配置处于未解析状态,并且可以重新解析。
Once a configuration is resolved it is immutable. Changing its state or the state of one of its dependencies will cause an exception. You can always copy a resolved configuration. The copied configuration is in the unresolved state and can be freshly resolved.
想了解更多关于这个配置类的 API,请参阅 API 文档:Configuration
。
To learn more about the API of the configuration class see the API documentation: Configuration
.
基于Apache Ivy 的 Gradle 仓库管理对仓库布局和检索策略的有很大的灵活性。 此外,Gradle 还提供了多种便捷的方法来添加预配置的仓库。
Gradle repository management, based on Apache Ivy, gives you a lot of freedom regarding repository layout and retrieval policies. Additionally Gradle provides various convenience method to add pre-configured repositories.
你可以配置任意数量的仓库,每个仓库都会被 Gradle 独立处理。如果 Gradle 在一个特定的仓库中找到模块描述符,那么它将尝试从 同一仓库中下载该模块的所有工件。虽然模块元数据和模块工件必须位于同一仓库中,但也可能单个仓库由多个 URL 组成,同时提供多个位置来搜索元数据文件和 jar 文件。
You may configure any number of repositories, each of which is treated independently by Gradle. If Gradle finds a module descriptor in a particular repository, it will attempt to download all of the artifacts for that module from the same repository. Although module meta-data and module artifacts must be located in the same repository, it is possible to compose a single repository of multiple URLs, giving multiple locations to search for meta-data files and jar files.
可以声明几种不同类型的仓库:
There are several different types of repositories you can declare:
表 50.2. 仓库类型 - Table 50.2. Repository types
类型 Type |
描述 Description |
Maven 中央仓库 Maven central repository |
一个在 Maven Cemtral 中查找依赖的预配置仓库。 A pre-configured repository that looks for dependencies in Maven Central. |
Maven JCenter 仓库 Maven JCenter repository |
一个在 Bintray 的 Jcenter 中查找依赖的预配置仓库。 A pre-configured repository that looks for dependencies in Bintray's JCenter. |
Maven 本地仓库 Maven local repository |
一个在本地 Maven 仓库中查找依赖的预配置仓库。 A pre-configured repository that looks for dependencies in the local Maven repository. |
Maven 仓库 Maven repository |
一个 Maven 仓库。 可以位于本地文件系统上,或在某个远程位置。 A Maven repository. Can be located on the local filesystem or at some remote location. |
Ivy 仓库 Ivy repository |
一个 Ivy 仓库。 可以位于本地文件系统上,或在某个远程位置。 An Ivy repository. Can be located on the local filesystem or at some remote location. |
Flat 目录仓库 Flat directory repository |
一个在本地文件系统上的简单仓库。不支持任何元数据格式。 A simple repository on the local filesystem. Does not support any meta-data formats. |
Maven 和 Ivy 仓库支持使用各种传输协议,当前支持以下协议:
Maven and Ivy repositories support the use of various transport protocols. At the moment the following protocols are supported:
表50.3 仓库传输协议 - Table 50.3. Repository transport protocols
类型 Type |
认证方案 Authentication schemes |
file |
无 none |
http |
用户名/密码 username/password |
https |
用户名/密码 username/password |
sftp |
用户名/密码 username/password |
要定义仓库,请使用 repositories
配置块。在 repositories
闭包中,使用 maven
声明 Maven 仓库,使用 ivy
声明Ivy 仓库。传输协议是仓库 URL 定义的一部分。以下构建脚本演示了如何创建基于 HTTP 的 Maven 和 Ivy 仓库:
To define a repository use the repositories
configuration block. Within the repositories
closure, a Maven repository is declared with maven
. An Ivy repository is declared with ivy
. The transport protocol is part of the URL definition for a repository. The following build script demonstrates how to create a HTTP-based Maven and Ivy repository:
示例50.25. 声明 Maven 和 Ivy 仓库 - Example 50.25. Declaring a Maven and Ivy repository
build.gradle
repositories { maven { url "http://repo.mycompany.com/maven2" } ivy { url "http://repo.mycompany.com/repo" } }
如果仓库需要认证,那么可以提供相关的凭证。以下示例展示了如何为 SFTP 仓库提供基于用户名/密码的认证:
If authentication is required for a repository, the relevant credentials can be provided. The following example shows how to provide username/password-based authentication for SFTP repositories:
示例50.26. 向 Maven 和 Ivy 仓库提供凭证 - Example 50.26. Providing credentials to a Maven and Ivy repository
build.gradle
repositories { maven { url "sftp://repo.mycompany.com:22/maven2" credentials { username 'user' password 'password' } } ivy { url "sftp://repo.mycompany.com:22/repo" credentials { username 'user' password 'password' } } }
若要添加中央 Maven 2 仓库(http://repo1.maven.org/maven2),只需添加下面的代码到你的构建脚本中:
To add the central Maven 2 repository (http://repo1.maven.org/maven2) simply add this to your build script:
示例 50.27. 添加 Maven 中央仓库 - Example 50.27. Adding central Maven repository
build.gradle
repositories { mavenCentral() }
现在 Gradle 将会在此仓库中查找你的依赖。
Now Gradle will look for your dependencies in this repository.
Bintray 的 JCenter 是所有流行的 Maven OSS 工件(包括直接发布到 Bintray 的工件)的最新集合。
Bintray's JCenter is an up-to-date collection of all popular Maven OSS artifacts, including artifacts published directly to Bintray.
若要添加 JCenter Maven 仓库(http://jcenter.bintray.com),只需添加下面的内容到你的构建脚本中:
To add the JCenter Maven repository (http://jcenter.bintray.com) simply add this to your build script:
示例 50.28. 添加 Bintray 的 Jcenter Maven 仓库 - Example 50.28. Adding Bintray's JCenter Maven repository
build.gradle
repositories { jcenter() }
现在 Gradle 将会在 JCenter 仓库中查找你的依赖。
Now Gradle will look for your dependencies in the JCenter repository.
要把本地的 Maven 缓存用作仓库,你可以这样操作:
To use the local Maven cache as a repository you can do:
示例 50.29. 将本地 Maven 缓存添加为仓库: - Example 50.29. Adding the local Maven cache as a repository
build.gradle
repositories { mavenLocal() }
Gradle 使用与 Maven 相同的逻辑来标识本地 Maven 缓存的位置。如果在 settings.xml
中定义了本地仓库位置,那么将使用此位置。
中的 USER_HOME
/.m2settings.xml
优先级高于
中的 M2_HOME
/confsettings.xml
。如果没有 settings.xml
可用,Gradle将使用默认位置
。 USER_HOME
/.m2/repository
Gradle uses the same logic as Maven to identify the location of your local Maven cache. If a local repository location is defined in a settings.xml
, this location will be used. The settings.xml
in
takes precedence over the USER_HOME
/.m2settings.xml
in
. If no M2_HOME
/confsettings.xml
is available, Gradle uses the default location
.USER_HOME
/.m2/repository
要添加自定义 Maven 仓库,你可以如下操作:
For adding a custom Maven repository you can do:
示例 50.30. 添加一个自定义的 Maven 仓库 - Example 50.30. Adding custom Maven repository
build.gradle
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}
有时候一个仓库会将 POM 发布到一个位置,而 JAR 和其他工件发布在另一个位置。要定义这样的一个仓库,你可以这样:
Sometimes a repository will have the POMs published to one location, and the JARs and other artifacts published at another location. To define such a repository, you can do:
示例 50.31. 为 JAR 文件添加额外的 Maven 仓库 - Example 50.31. Adding additional Maven repositories for JAR files
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,那么会使用工件 URL 来查找 JAR。
Gradle will look at the first URL for the POM and the JAR. If the JAR can't be found there, the artifact URLs are used to look for JARs.
要访问一个使用基本认证的 Maven 仓库,请在定义该仓库时指定要使用的用户名和密码:
To access a Maven repository which uses basic authentication, you specify the username and password to use when you define the repository:
示例 50.32. 访问受密码保护的 Maven 仓库 - Example 50.32. Accessing password protected Maven repository
build.gradle
repositories { maven { credentials { username 'user' password 'password' } url "http://repo.mycompany.com/maven2" } }
建议把你的用户名和密码写在 gradle.properties
中,而不是直接写在构建文件上。
It is advisable to keep your username and password in gradle.properties
rather than directly in the build file.
如果想要把一个(平面)文件系统目录用作仓库,只需简单输入:
If you want to use a (flat) filesystem directory as a repository, simply type:
示例 50.33. 平面仓库解析器 - Example 50.33. Flat repository resolver
build.gradle
repositories { flatDir { dirs 'lib' } flatDir { dirs 'lib1', 'lib2' } }
这将添加一些在一个或多个目录查找依赖的仓库。如果你只使用平面目录解析器,那么不需要设置依赖的所有属性。请参阅 第 50.4.8 节,《可选属性》
This adds repositories which look into one or more directories for finding dependencies. If you only work with flat directory resolvers you don't need to set all attributes of a dependency. See Section 50.4.8, “Optional attributes”
示例 50.34. Ivy 仓库 - Example 50.34. Ivy repository
build.gradle
repositories {
ivy {
url "http://repo.mycompany.com/repo"
}
}
通过使用指定名称的布局,可以指定仓库遵循 Ivy 或 Maven 的默认布局。
You can specify that your repository conforms to the Ivy or Maven default layout by using a named layout.
示例50.35. 使用指定名称的布局的 Ivy 仓库 - Example 50.35. Ivy repository with named layout
build.gradle
repositories { ivy { url "http://repo.mycompany.com/repo" layout "maven" } }
布局名称的有效值为 'gradle'
(默认值),'maven'
和 'ivy'
。有关这些指定的布局,请参阅 IvyArtifactRepository.layout()
。
Valid named layout values are 'gradle'
(the default), 'maven'
and 'ivy'
. See IvyArtifactRepository.layout()
for details of these named layouts.
要定义非标准布局的 Ivy 仓库,你可以为这个仓库定义一个“模式”布局:
To define an Ivy repository with a non-standard layout, you can define a 'pattern' layout for the repository:
示例 50.36. 使用模式而已的 Ivy 仓库 - Example 50.36. Ivy repository with pattern layout
build.gradle
repositories { ivy { url "http://repo.mycompany.com/repo" layout "pattern", { artifact "[module]/[revision]/[type]/[artifact].[ext]" } } }
要定义一个从不同的位置获取 Ivy 文件和工件的 Ivy 仓库,你可以定义用于查找 Ivy 文件和工件的单独模式:
To define an Ivy repository which fetches Ivy files and artifacts from different locations, you can define separate patterns to use to locate the Ivy files and artifacts:
一个仓库指定的每个 artifact
或 ivy
都添加了一个要使用的 其他 模式。这些模式按照它们被定义的顺序使用。
Each artifact
or ivy
specified for a repository adds an additional pattern to use. The patterns are used in the order that they are defined.
示例50.37. 使用多个定制模式的 Ivy 仓库 - Example 50.37. Ivy repository with multiple custom patterns
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" } } }
作为可选的功能,一个使用模式布局的仓库可以让它的“组织”部分使用是Maven 风格,以斜杠替换点作为分隔符。例如,组织 my.company
将表示为 my/company
。
Optionally, a repository with pattern layout can have its 'organisation' part laid out in Maven style, with forward slashes replacing dots as separators. For example, the organisation my.company
would then be represented as my/company
.
示例50.38. 使用 Maven 兼容布局的 Ivy 仓库 - Example 50.38. Ivy repository with Maven compatible layout
build.gradle
repositories { ivy { url "http://repo.mycompany.com/repo" layout "pattern", { artifact "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" m2compatible = true } } }
要访问一个使用基本认证的 Ivy 仓库,请在定义该仓库时指定要使用的用户名和密码:
To access an Ivy repository which uses basic authentication, you specify the username and password to use when you define the repository:
示例 50.39. Ivy 仓库 - Example 50.39. Ivy repository
build.gradle
repositories { ivy { url 'http://repo.mycompany.com' credentials { username 'user' password 'password' } } }
要访问一个仓库:
To access a repository:
示例 50.40. 访问一个仓库: - Example 50.40. Accessing a repository
build.gradle
println repositories.localRepository.name
println repositories['localRepository'].name
要配置一个仓库:
To configure a repository:
示例 50.41. 仓库配置 - Example 50.41. Configuration of a repository
build.gradle
repositories { flatDir { name 'localRepository' } } repositories { localRepository { dirs 'lib' } } repositories.localRepository { dirs 'lib' }
Gradle 得益于其幕后的 Ivy,它在仓库方面非常灵活。
Gradle, thanks to Ivy under its hood, is extremely flexible regarding repositories:
与仓库通信的协议有许多选择(比如文件系统,http,ssh,sftp……)
There are many options for the protocol to communicate with the repository (e.g. filesystem, http, ssh, sftp ...)
sftp 协议当前仅支持基于用户名/密码的认证。
The protocol sftp currently only supports username/password-based authentication.
每个仓库都可以有其自己的布局。
Each repository can have its own layout.
比如说,你声明了一个对 junit:jun:3.8.2
库的依赖。现在 Gradle 是怎么在仓库中找到它的呢?依赖信息是必须映射到路径上的。相比于固定路径的 Maven,Gradle 可以定义一个模式,这个模式定义了路径的表示形式。这里有一些例子:[16]
Let's say, you declare a dependency on the junit:junit:3.8.2
library. Now how does Gradle find it in the repositories? Somehow the dependency information has to be mapped to a path. In contrast to Maven, where this path is fixed, with Gradle you can define a pattern that defines what the path will look like. Here are some examples: [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]
要添加任何一种仓库(你可以很方便地编写自己的仓库),你可以这样做:
To add any kind of repository (you can pretty easy write your own ones) you can do:
示例 50.42. 定制仓库的定义 - Example 50.42. Definition of a custom repository
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。
An overview of which Resolvers are offered by Ivy and thus also by Gradle can be found here. With Gradle you just don't configure them via XML but directly via their API.
Gradle 将获取你的依赖声明和仓库定义,并尝试通过称为 依赖解析的过程来下载所有依赖。以下是该过程如何工作的简要概述。
Gradle takes your dependency declarations and repository definitions and attempts to download all of your dependencies by a process called dependency resolution. Below is a brief outline of how this process works.
给定一个所需要的依赖,Gradle 首先尝试解析该依赖的模块。每个仓库都按顺序进行检查,首先搜索指示该模块存在的 模块描述符 文件(POM 或 Ivy 文件)。如果没找到模块描述符,Gradle 将搜索表示模块存在于仓库中的主要 模块工件 文件。
如果依赖被声明为一个动态版本(如 1.+
),Gradle 将会把它解析为仓库中最新的静态版本(如 1.2
)。对于 Maven 存储库,它是通过 maven-metadata.xml
文件来实现的;而对于 Ivy 存储库,则是通过目录列表来实现。
如果模块描述符是一个有父 POM 声明的 POM 文件,Gradle 将以递归方式尝试解析该 POM 文件的每个父模块。
Given a required dependency, Gradle first attempts to resolve the module for that dependency. Each repository is inspected in order, searching first for a module descriptor file (POM or Ivy file) that indicates the presence of that module. If no module descriptor is found, Gradle will search for the presence of the primary module artifact file indicating that the module exists in the repository.
If the dependency is declared as a dynamic version (like 1.+
), Gradle will resolve this to the newest available static version (like 1.2
) in the repository. For Maven repositories, this is done using the maven-metadata.xml
file, while for Ivy repositories this is done by directory listing.
If the module descriptor is a POM file that has a parent POM declared, Gradle will recursively attempt to resolve each of the parent modules for the POM.
在对每个仓库进行模块检查后,Gradle 会选择使用“最好的”那个仓库。它使用以下标准完成的:
当依赖声明为静态版本,并且在仓库中找到模块描述符文件时,将不再需要继续搜索后面的仓库,这个过程的其余部分是短路的。
Once each repository has been inspected for the module, Gradle will choose the 'best' one to use. This is done using the following criteria:
When the dependency is declared by a static version and a module descriptor file is found in a repository, there is no need to continue searching later repositories and the remainder of the process is short-circuited.
然后将从上面的过程所选择的 同一个仓库 中请求这个模块的所有工件。
All of the artifacts for the module are then requested from the same repository that was chosen in the process above.
在大多数情况下,Gradle 默认的依赖管理会解析你构建中所需要的依赖。但是,在某些情况下,可能需要轻微调整依赖解析,以确保你的构建能精确得到正确的依赖。
In most cases, Gradle's default dependency management will resolve the dependencies that you want in your build. In some cases, however, it can be necessary to tweak dependency resolution to ensure that your build receives exactly the right dependencies.
有许多种方式可以影响到 Gradle 的依赖解析。
There are a number of ways that you can influence how Gradle resolves dependencies.
强制使用一个模块版本,是指示 Gradle 对于给定的依赖(传递或非传递),始终使用一个特定的版本,并覆盖在发布的模块描述符中所指定的任何版本。当处理版本冲突时,这非常有用——有关详细信息,请参阅 第 50.2.3 节,《解决版本冲突》。
Forcing a module version tells Gradle to always use a specific version for given dependency (transitive or not), overriding any version specified in a published module descriptor. This can be very useful when tackling version conflicts - for more information see Section 50.2.3, “Resolve version conflicts”.
强制版本也可用于处理传递依赖的恶意元数据。如果传递依赖的元数据质量有问题,导致依赖解析时出现问题,那么可以强制 Gradle 使用这个依赖较新的修复版本。有关示例,请参见 ResolutionStrategy
。注意,“依赖解析规则”(见下文)提供了一种更强大的机制来取代已损坏的模块依赖。请参阅 第 50.8.2.3 节,《将特定版本列入黑名单并替换》。
Force versions can also be used to deal with rogue metadata of transitive dependencies. If a transitive dependency has poor quality metadata that leads to problems at dependency resolution time, you can force Gradle to use a newer, fixed version of this dependency. For an example, see ResolutionStrategy
. Note that 'dependency resolve rules' (outlined below) provide a more powerful mechanism for replacing a broken module dependency. See Section 50.8.2.3, “Blacklisting a particular version with a replacement”.
一个依赖解析规则针对每个已解析的依赖执行,并提供了一个功能强大的 api,用于在该依赖解析之前处理所请求的依赖。这个功能目前还是实验性功能,但当前提供了对一个所请求的依赖更改它的组,名称及版本的功能,从而允许在解析期间将一个依赖替换为一个完全不同的模块。
A dependency resolve rule is executed for each resolved dependency, and offers a powerful api for manipulating a requested dependency prior to that dependency being resolved. This feature is incubating, but currently offers the ability to change the group, name and/or version of a requested dependency, allowing a dependency to be substituted with a completely different module during resolution.
依赖解析规则提供了一种非常强大的方法来控制依赖解析过程,并且可以用于实现依赖管理中的所有高级模式。下面是其中一些模式的概述。有关更多信息和代码示例,请参阅 ResolutionStrategy
。
Dependency resolve rules provide a very powerful way to control the dependency resolution process, and can be used to implement all sorts of advanced patterns in dependency management. Some of these patterns are outlined below. For more information and code samples see ResolutionStrategy
.
通常一个组织会发布一组同一个版本的库;这些库一起构建,测试和发布。这些库形成了一个“可发布单元”,被设计并打算作为一个整体使用。而使用来自不同的可发布单元的库,也不会有意义。
Often an organisation publishes a set of libraries with a single version; where the libraries are built, tested and published together. These libraries form a 'releasable unit', designed and intended to be used as a whole. It does not make sense to use libraries from different releasable units together.
但传递依赖的解析会很容易破坏这种协议。举个例子:
But it is easy for transitive dependency resolution to violate this contract. For example:
module-a
依赖于 releasable-unit:part-one:1.0
module-a
depends on releasable-unit:part-one:1.0
module-a
依赖于 releasable-unit:part-one:1.0
module-b
depends on releasable-unit:part-two:1.1
一个同时依赖于 module-a
和 module-b
的构建,将在可发布单元内获得这个库的不同版本。
A build depending on both module-a
and module-b
will obtain different versions of libraries within the releasable unit.
依赖解析规则使你能够在构建中强制指定可发布单元。设想一下,一个可发布单元,由有 “org.gradle” 组的所有库定义。我们可以强制所有这些库使用一致的版本:
Dependency resolve rules give you the power to enforce releasable units in your build. Imagine a releasable unit defined by all libraries that have 'org.gradle' group. We can force all of these libraries to use a consistent version:
示例 50.43. 强制一个组的所有库使用一致的版本 - Example 50.43. Forcing consistent version for a group of libraries
build.gradle
configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'org.gradle') { details.useVersion '1.4' } } }
在某些企业环境中,可以在 Gradle 构建中声明的模块版本列表,是在外部进行维护和审计的。依赖解析规则提供了这种模式的整洁实现:
In some corporate environments, the list of module versions that can be declared in Gradle builds is maintained and audited externally. Dependency resolve rules provide a neat implementation of this pattern:
default
”。default
'. 该规则的实现可以灵活地封装在一个公司插件中,并在组织内的所有构建中共享。
This rule implementation can be neatly encapsulated in a corporate plugin, and shared across all builds within the organisation.
示例 50.44. 使用自定义的版本方案 - Example 50.44. Using a custom versioning scheme
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" }
依赖解析规则提供了一种机制,用于将特定版本的依赖列入黑名单并提供替换版本。 如果某个依赖版本已损坏且不应使用,那么这就会很有用,因为依赖解析规则可以将这个版本替换为已知的好的版本。有关损坏模块的例子有,在一个库上所声明的依赖在任何公共仓库中都找不到。但是为什么不能使用特定的模块版本,而更希望另一个不同的版本,还有许多其他原因。
Dependency resolve rules provide a mechanism for blacklisting a particular version of a dependency and providing a replacement version. This can be useful if a certain dependency version is broken and should not be used, where a dependency resolve rule causes this version to be replaced with a known good version. One example of a broken module is one that declares a dependency on a library that cannot be found in any of the public repositories, but there are many other reasons why a particular module version is unwanted and a different version is preferred.
在下面的示例中,假设版本 1.2.1
包含了重要的修复,并且应该始终优先于 1.2
使用。提供的规则将仅强制执行如下操作:在任何时间,遇到了版本 1.2
时,都把它替换为 1.2.1
。请注意,这与上面描述的强制版本有所区别,因为这个模块的其他版本都不受影响。这意味着,如果版本 1.3
也由于依赖传递被获取到,那么"最新的"冲突解决策略还是会选择这个新的版本。
In example below, imagine that version 1.2.1
contains important fixes and should always be used in preference to 1.2
. The rule provided will enforce just this: any time version 1.2
is encountered it will be replaced with 1.2.1
. Note that this is different from a forced version as described above, in that any other versions of this module would not be affected. This means that the 'newest' conflict resolution strategy would still select version 1.3
if this version was also pulled transitively.
示例 50.45. 通过替换把版本列入黑名单 - Example 50.45. Blacklisting a version with a replacement
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 起,你可以使用依赖解析规则来进行这些替换:
At times a completely different module can serve as a replacement for a requested module dependency. Examples include using 'groovy
' in place of 'groovy-all
', or using 'log4j-over-slf4j
' instead of 'log4j
'. Starting with Gradle 1.5 you can make these substitutions using dependency resolve rules:
示例 50.46. 在解析中更改依赖组及名称 - Example 50.46. Changing dependency group and/or name at the resolution
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's Ivy repository implementations support the equivalent to Ivy's dynamic resolve mode. Normally, Gradle will use the rev
attribute for each dependency definition included in an ivy.xml
file. In dynamic resolve mode, Gradle will instead prefer the revConstraint
attribute over the rev
attribute for a given dependency definition. If the revConstraint
attribute is not present, the rev
attribute is used instead.
要启用动态解析模式,需要在仓库定义上设置相应的选项,示例如下。注意,动态解析方式只可用于 Gradle 的 Ivy 仓库,不能用于 Maven 仓库或自定义的 Ivy DependencyResolver
实现。
To enable dynamic resolve mode, you need to set the appropriate option on the repository definition. A couple of examples are shown below. Note that dynamic resolve mode is only available for Gradle's Ivy repositories. It is not available for Maven repositories, or custom Ivy DependencyResolver
implementations.
示例 50.47. 启用动态解析模式 - Example 50.47. Enabling dynamic resolve mode
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 }
每个模块(也称为组件),都有与其相关的元数据,比如说组、名称、版本、依赖,等等。这些元数据通常来源于模块的描述符。元数据规则能够在构建脚本中处理模块元数据的某些部分。它们在模块的描述符下载之后,但在所有候选版本之间选择它之前生效。这使得元数据规则成为自定义依赖解析方案的另一种手段。
Each module (also called component) has metadata associated with it, such as its group, name, version, dependencies, and so on. This metadata typically originates in the module's descriptor. Metadata rules allow certain parts of a module's metadata to be manipulated from within the build script. They take effect after a module's descriptor has been downloaded, but before it has been selected among all candidate versions. This makes metadata rules another instrument for customizing dependency resolution.
Gradle 理解的其中一个模块元数据是模块的 状态方案。 这个概念也可以从 Ivy 中了解到,对随时间推移模块的不同成熟度级别进行建模。默认的状态方案,按从最低到最高的成熟度排序,分别为 integration
,milestone
和 release
。 除状态方案外,模块还有一个(当前)状态,它必须是状态方案中的值之一。如果没有在(Ivy)描述符中指定, Ivy 模块和 Maven 快照模块的状态默认是 integration
,而不是快照的 Maven 模块则默认为 release
。
One piece of module metadata that Gradle understands is a module's status scheme. This concept, also known from Ivy, models the different levels of maturity that a module transitions through over time. The default status scheme, ordered from least to most mature status, is integration
, milestone
, release
. Apart from a status scheme, a module also has a (current) status, which must be one of the values in its status scheme. If not specified in the (Ivy) descriptor, the status defaults to integration
for Ivy modules and Maven snapshot modules, and release
for Maven modules that aren't snapshots.
一个模块的状态及状态模式,会在解析 latest
版本选择器时考虑到。具体而言,latest.someStatus
会解析成有着 someStatus
或更成熟状态的最高模块版本。例如,如果其他地方存在默认的状态模式,那么 latest.integration
将选择最高的模块版本,而不考虑其状态(因为 integration
是成熟度最低的状态),而 latest.release
将选择 release
状态的最高模块版本。以下是在代码中的表现:
A module's status and status scheme are taken into consideration when a latest
version selector is resolved. Specifically, latest.someStatus
will resolve to the highest module version that has status someStatus
or a more mature status. For example, with the default status scheme in place, latest.integration
will select the highest module version regardless of its status (because integration
is the least mature status), whereas latest.release
will select the highest module version with status release
. Here is what this looks like in code:
示例 50.48,“最新的”版本选择器 - Example 50.48. 'Latest' version selector
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
的输出结果
Output of gradle -q listFish
> gradle -q listFish tuna-1.5.jar tuna-1.4.jar
下一个示例基于模块的元数据规则中声明的自定义状态模式,来演示 latest
选择器:
The next example demonstrates latest
selectors based on a custom status scheme declared in a module metadata rule:
示例 50.49. 自定义状态模式 - Example 50.49. Custom status scheme
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
的输出结果
Output of gradle -q listBirds
> gradle -q listBirds albatros-2.0.jar
Gradle 包含了一种高度复杂的依赖缓存机制,它力求减少依赖解析中的远程请求数,同时努力保证依赖解析结果正确且可重现。
Gradle contains a highly sophisticated dependency caching mechanism, which seeks to minimise the number of remote requests made in dependency resolution, while striving to guarantee that the results of dependency resolution are correct and reproducible.
Gradle 依赖缓存由两种关键类型的存储组成:
The Gradle dependency cache consists of 2 key types of storage:
基于文件的下载工件存储,包括了像 jar 这样的二进制文件,以及像 POM 文件及 Ivy 文件这样的原始下载的元数据。下载的工件的存储路径包含了 SHA1 校验和,它意味着可以很容易地缓存两个名称相同但内容不同的工件。
A file-based store of downloaded artifacts, including binaries like jars as well as raw downloaded meta-data like POM files and Ivy files. The storage path for a downloaded artifact includes the SHA1 checksum, meaning that 2 artifacts with the same name but different content can easily be cached.
解析的模块元数据的二进制存储,包括解析动态版本、 模块描述符和工件的结果。
A binary store of resolved module meta-data, including the results of resolving dynamic versions, module descriptors, and artifacts.
从下载的工件的存储中分离出缓存的元数据,使我们能够对比缓存执行一些非常强大的操作,而如果使用一个透明的只有文件的缓存布局则会很困难。
Separating the storage of downloaded artifacts from the cache metadata permits us to do some very powerful things with our cache that would be difficult with a transparent, file-only cache layout.
Gradle 缓存不允许本地缓存隐藏问题以及创建神秘且难以调试的行为,这些行为是许多构建工具所面临的挑战。这一新行为通过带宽和存储的有效途径实现。在这个过程中,Gradle 支持可靠且可复制的企业构建。
The Gradle cache does not allow the local cache to hide problems and creating mysterious and difficult to debug behavior that has been a challenge with many build tools. This new behavior is implemented in a bandwidth and storage efficient way. In doing so, Gradle enables reliable and reproducible enterprise builds.
Gradle 在元数据缓存中以二进制格式保留了依赖解析各方面的记录。存在元数据缓存的信息包括:
Gradle keeps a record of various aspects of dependency resolution in binary format in the metadata cache. The information stored in the metadata cache includes:
1.+
)解析为一个具体版本(如1.2
)的结果。1.+
) to a concrete version (e.g. 1.2
). 元数据缓存中的每个条目都包括了一条仓库记录,它提供了信息以及可用于缓存到期的时间戳。
Every entry in the metadata cache includes a record of the repository that provided the information as well as a timestamp that can be used for cache expiry.
如上文所述,每个仓库都有一个单独的元数据缓存。一个仓库由它的 URL、类型和布局来进行标识。如果一个模块或工件在之前从没有在 这个仓库中解析过,那么 Gradle 将尝试针对这个仓库来解析这个模块。这将始终涉及仓库的远程查找,但是在许多情况下并不需要下载(请参阅下面的第 50.9.1.3 节,《工件复用》)。
As described above, for each repository there is a separate metadata cache. A repository is identified by its URL, type and layout. If a module or artifact has not been previously resolved from this repository, Gradle will attempt to resolve the module against the repository. This will always involve a remote lookup on the repository, however in many cases no download will be required (seeSection 50.9.1.3, “Artifact reuse”, below).
如果所需的构件,在构建所指定的任何仓库中都不可用,无论本地缓存是否已经从其他的仓库中获取到这个构件,依赖解析都会失败。仓库独立能够使构建之间用一种先进的方法彼此隔离,以前从没有构建工具能够做到。这是一个关键的功能,使得能够在任何环境中创建可靠且可复制的构建。
Dependency resolution will fail if the required artifacts are not available in any repository specified by the build, regardless whether the local cache has retrieved this artifact from a different repository. Repository independence allows builds to be isolated from each other in an advanced way that no build tool has done before. This is a key feature to create builds that are reliable and reproducible in any environment.
在下载工件前,Gradle 试图通过下载与该工件关联的 sha 文件来确定所需工件的校验和。如果校验和可以获取得到,并且已经存在有相同 id 和校验和的工件,就不会再去下载为个工件。如果无法从远程服务器中获取校验和,则下载这个工件(并且如果它与现有工件相匹配就忽略掉)。
Before downloading an artifact, Gradle tries to determine the checksum of the required artifact by downloading the sha file associated with that artifact. If the checksum can be retrieved, an artifact is not downloaded if an artifact already exists with the same id and checksum. If the checksum cannot be retrieved from the remote server, the artifact will be downloaded (and ignored if it matches an existing artifact).
在考虑从其他仓库下载的工件时,Gradle 还将尝试复用在本地 Maven 仓库中找到的工件。如果某个候选工件已经通过 Maven 下载,并且它可以验证和远程服务器定义的校验和相匹配,Gradle 就会使用此工件。
As well as considering artifacts downloaded from a different repository, Gradle will also attempt to reuse artifacts found in the local Maven Repository. If a candidate artifact has been downloaded by Maven, Gradle will use this artifact if it can be verified to match the checksum declared by the remote server.
对于相同的工件标识,不同的仓库可能会提供不同的二进制工件。这种情况常见的是 Maven SNAPSHOT 工件,但是对于任何工件,都可以不更改其标识而重新发布。通过缓存基于 SHA1 校验和的工件,Gradle 能够维护同一工件的多个版本。意味着,当解析一个仓库时,Gradle 不会从其他仓库中覆盖已缓存的工件文件。它不需要一个单独的工件文件在每个仓库都存储就可以做到。
It is possible for different repositories to provide a different binary artifact in response to the same artifact identifier. This is often the case with Maven SNAPSHOT artifacts, but can also be true for any artifact which is republished without changing it's identifier. By caching artifacts based on their SHA1 checksum, Gradle is able to maintain multiple versions of the same artifact. This means that when resolving against one repository Gradle will never overwrite the cached artifact file from a different repository. This is done without requiring a separate artifact file store per repository.
Gradle 依赖缓存使用基于文件的锁,以确保可安全地由多个 Gradle 进程并行使用。每当正在读取或写入二进制元数据存储时,就会持有锁,但是会对诸如下载远程工件之类的慢操作时会释放锁。
The Gradle dependency cache uses file-based locking to ensure that it can safely be used by multiple Gradle processes concurrently. The lock is held whenever the binary meta-data store is being read or written, but is released for slow operations such as downloading remote artifacts.
--offline
命令行开关告诉 Gradle 始终使用缓存中的依赖模块,而不考虑是否要再检查。在使用离线模式运行时,Gradle 将不会尝试访问网络来执行依赖解析。如果所需的模块在依赖缓存中不存在,构建执行就会失败。
The --offline
command line switch tells Gradle to always use dependency modules from the cache, regardless if they are due to be checked again. When running with offline, Gradle will never attempt to access the network to perform dependency resolution. If required modules are not present in the dependency cache, build execution will fail.
有时,Gradle 依赖缓存可能会与已配置仓库的实际状态不同步。也许一个仓库的最初配置有误,或者是没有正确发布“无更改”模块。要刷新依赖缓存中的所有依赖,请在命令行上使用 --refresh-dependencies
选项。
At times, the Gradle Dependency Cache can be out of sync with the actual state of the configured repositories. Perhaps a repository was initially misconfigured, or perhaps a "non-changing" module was published incorrectly. To refresh all dependencies in the dependency cache, use the --refresh-dependencies
option on the command line.
--refresh-dependencies
选项告诉 Gradle 忽略已解析的模块和工件的所有缓存条目。所有已配置的仓库都会执行新的解析,并重新计算动态版本,刷新模块及下载工件。但是,在可能的情况下,Gradle 会检查之前下载的工件是否有效,然后再下载。这是通过比较仓库中已发布的 SHA1 值和现在已下载好的工件的 SHA1 值来完成的。
The --refresh-dependencies
option tells Gradle to ignore all cached entries for resolved modules and artifacts. A fresh resolve will be performed against all configured repositories, with dynamic versions recalculated, modules refreshed, and artifacts downloaded. However, where possible Gradle will check if the previously downloaded artifacts are valid before downloading again. This is done by comparing published SHA1 values in the repository with the SHA1 values for existing downloaded artifacts.
你可以在配置中使用 ResolutionStrategy
对缓存的某些方面进行微调。
You can fine-tune certain aspects of caching using the ResolutionStrategy
for a configuration.
默认情况下,Gradle 缓存动态版本的时间为 24 小时。如果要更改 Gradle 对动态版本解析到的版本的缓存时间,请使用:
By default, Gradle caches dynamic versions for 24 hours. To change how long Gradle will cache the resolved version for a dynamic version, use:
示例 50.50. 动态版本缓存控制 - Example 50.50. Dynamic version cache control
build.gradle
configurations.all { resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes' }
默认情况下, Gradle缓存变化模块的时间为 24 小时。如果要更改 Gradle 缓存变化模块的元数据和工件的时间,请使用:
By default, Gradle caches changing modules for 24 hours. To change how long Gradle will cache the meta-data and artifacts for a changing module, use:
示例 50.51. 变化模块缓存控制 - Example 50.51. Changing module cache control
build.gradle
configurations.all { resolutionStrategy.cacheChangingModulesFor 4, 'hours' }
更多详细信息请参阅 ResolutionStrategy
的 API 文档。
For more details, take a look at the API documentation forResolutionStrategy
.
许多项目依赖于 Maven 中央仓库,这不是没有问题的。
Many projects rely on the Maven Central repository. This is not without problems.
Maven 中央仓库可能会下线,或者响应时间很长。
The Maven Central repository can be down or has a very long response time.
许多项目的 POM 文件会有错误的信息(比如,commons-httpclient-3.0
的 POM 文件声明了 JUnit 是运行时依赖)。
The POM files of many projects have wrong information (as one example, the POM of commons-httpclient-3.0
declares JUnit as a runtime dependency).
对于许多项目而言,没有一组正确依赖(因 POM 格式的影响会多或少)。
For many projects there is not one right set of dependencies (as more or less imposed by the POM format).
如果你的项目依赖于 Maven 中央仓库,你很可能需要额外的自定义仓库,因为:
If your project relies on the Maven Central repository you are likely to need an additional custom repository, because:
你可能需要还没有上传到 Maven 中央仓的依赖。
You might need dependencies that are not uploaded to Maven Central yet.
你希望正确地处理 Maven 中央仓 POM 文件中错误的元数据。
You want to deal properly with wrong metadata in a Maven Central POM file.
你不希望 Maven 中央仓库故障停机或者有时候响应时间太长时,想要构建你的项目的人直接受到影响。
You don't want to expose people who want to build your project, to the downtimes or sometimes very long response times of Maven Central.
设置一个自定义的仓库并不算什么,[17] 但想让这个仓库保持最新的态可能就会很烦人。对于一个新的版本,总是要创建新的 XML 描述符和目录。你的自定义仓库是另一个基础结构元素,可能会宕机,并且需要进行更新。 要启用历史构建,你需要保留所有之前的库,并且需要备份。这是另一个间接层,你必须查找的另一个信息源。所有这些都不是大问题,但累加起来就有影响了。仓库管理器(如 Artifactory 或 Nexus )把这些事情变得轻松些。但是,举个例子,开源项目通常没有主机用于这些产品。而通过一些新服务,这种状况也改了,比如 Bintray ,它可以让开发者使用自助服务仓库平台来托管和分发他们发布的二进制文件。通过 JCenter 公共仓库,Bintray 还支持共享通过他们审核的工件,为所有常用的 OSS Java 工件提供一个单一的解析地址(请参阅 第 50.6.3 节,《Maven JCenter 仓库》)。
It is not a big deal to set-up a custom repository. [17] But it can be tedious, to keep it up to date. For a new version, you have always to create the new XML descriptor and the directories. And your custom repository is another infrastructure element which might have downtimes and needs to be updated. To enable historical builds, you need to keep all the past libraries and you need a backup. It is another layer of indirection. Another source of information you have to lookup. All this is not really a big deal but in its sum it has an impact. Repository Manager like Artifactory or Nexus make this easier. But for example open source projects don't usually have a host for those products. This is changing with new services like Bintray that let developers host and distribute their release binaries using a self-service repository platform. Bintray also supports sharing approved artifacts though the JCenter public repository to provide a single resolution address for all popular OSS java artifacts (see Section 50.6.3, “Maven JCenter repository”).
这也是为什么一些项目希望将他们的库存在其版本控制系统中的原因。这种做法 Gradle 完全支持。库可以存在没有任何 XML 模块描述符文件的平面目录中,但 Gradle 提供了完整的传递依赖管理。你可以使用客户端模块依赖来表达依赖关系,或者是使用工件依赖,以防第一级依赖没有传递依赖。人们可以从 svn 中检出这样的项目,并具有构建它所需的的一切。
This is a reason why some projects prefer to store their libraries in their version control system. This approach is fully supported by Gradle. The libraries can be stored in a flat directory without any XML module descriptor files. Yet Gradle offers complete transitive dependency management. You can use either client module dependencies to express the dependency relations, or artifact dependencies in case a first level dependency has no transitive dependencies. People can check out such a project from svn and have everything necessary to build it.
如果你正在使用像 Git 一样的分布式版本控制系统,由于人们会检出整个历史,那么你可能不希望使用版本控制系统来保存这些库。但即使是这样,Gradle 的灵活性也可以让你更轻松。例如,你可以使用不带 XML 描述符的共享平面目录,但是却能如上所述有完整的传递依赖管理。
If you are working with a distributed version control system like Git you probably don't want to use the version control system to store libraries as people check out the whole history. But even here the flexibility of Gradle can make your life easier. For example you can use a shared flat directory without XML descriptors and yet you can have full transitive dependency management as described above.
你也可以使用混合策略。如果你主要关注的是 POM 文件中的错误元数据以及维护自定义的 XML 描述符,客户端模块提供了一种替代方案。但你还是可以将 Maven2 仓库和自定义仓库用作 只放 jar 的仓库,并且仍然可以使用传递依赖管理。或者,你也可以只为带有错误元数据的 POM 提供客户端模块,而对于 jar 和正确的 POM 依然使用远程仓库。
You could also have a mixed strategy. If your main concern is bad metadata in the POM file and maintaining custom XML descriptors, Client Modules offer an alternative. But you can of course still use Maven2 repo and your custom repository as a repository for jars only and still enjoy transitive dependency management. Or you can only provide client modules for POMs with bad metadata. For the jars and the correct POMs you still use the remote repository.
还有一种处理传递依赖 不带 XML 描述符文件的方法。你可以用 Gradle 来做,但是我们不推荐。之所以提到它,是为了完整性以及与其他构建工具作比较。
There is another way to deal with transitive dependencies without XML descriptor files. You can do this with Gradle, but we don't recommend it. We mention it for the sake of completeness and comparison with other build tools.
这个诀窍是只使用工件依赖,并在列表中对它们分组。使用这种方法,你要以某种方式来表达你的第一级依赖是什么,传递依赖又是什么(请参阅第 50.4.8 节,《可选属性》)。而缺点是,对于 Gradle 依赖管理而言,所有依赖都会被当作是第一级依赖。依赖报告不会显示你实际上的依赖关系图,并且 compile
任务会使用所有依赖,而不仅仅是第一级的。总之,比起使用客户端模块,你的构建可维护性和可靠性都很差,而不会有其他好处。
The trick is to use only artifact dependencies and group them in lists. That way you have somehow expressed, what are your first level dependencies and what are transitive dependencies (see Section 50.4.8, “Optional attributes”). But the draw-back is, that for the Gradle dependency management all dependencies are considered first level dependencies. The dependency reports don't show your real dependency graph and the compile
task uses all dependencies, not just the first level dependencies. All in all, your build is less maintainable and reliable than it could be when using client modules. And you don't gain anything.
[14] Gradle 支持部分多项目构建(参见第 56 章,《多项目构建》)。
[14] Gradle supports partial multiproject builds (see Chapter 56, Multi-project Builds).
[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 模式的内容。
[16] At http://ant.apache.org/ivy/history/latest-milestone/concept.html you can learn more about ivy patterns.
[17] 如果你想在 Maven 中央仓库宕机时保护你的项目,事情会变得更复杂些。你可能想要为此设置一个仓库代理。在企业环境中,这种情况相当普遍。而对于开源项目,看起来就有些小题大做了。
[17] If you want to shield your project from the downtimes of Maven Central things get more complicated. You probably want to set-up a repository proxy for this. In an enterprise environment this is rather common. For an open source project it looks like overkill.