- 构建工具
- 开发工具
【免费下载链接】gradle
Adaptable, fast automation for all
本篇技术指南围绕 Gradle 源码仓库中的执行平台核心文档(platforms/core-execution/README.md)展开,系统讲解 Gradle 用于在构建上下文中执行工作(work)的基础设施:执行引擎(Execution Engine)、快照与指纹(Snapshotting & Fingerprinting)以及工作验证(Work Validation)。读者读完本文后,将理解 Gradle 的任务(Task)、构件变换(Artifact Transform)乃至构建脚本编译、访问器生成等内部工作是如何被统一、安全、高效地执行,并掌握 up-to-date 检查、构建缓存、增量执行、输入规范化等核心机制的底层原理,可直接对照源码继续深挖。
一、执行平台总览:三份文档构成的核心骨架
Gradle 仓库将"在构建上下文中执行工作"的基础设施集中放在platforms/core-execution目录下,其顶层 README 以一份**术语表(Glossary)**的形式给出了整个执行平台的核心定义,并链接到三份详细指南:
| 指南 | 相对仓库根的路径 | 主题 |
|---|---|---|
| Execution Engine(执行引擎) | platforms/core-execution/execution/README.md | 执行平台的主组件:如何声明并安全高效地执行"工作单元" |
| Snapshotting and Fingerprinting(快照与指纹) | platforms/core-execution/snapshots/README.md | 如何捕获输入/输出的原始状态与规范化状态 |
| Work Validation(工作验证) | platforms/core-execution/Work Validation.md | 如何检查工作单元及其属性是否被正确定义与接线 |
[!NOTE] 顶层 README 明确说明:执行引擎是 Gradle 中执行工作的主要方式,同时其部分部件也被用于 Develocity 的 Maven 与 sbt 扩展中;执行引擎通过跟踪不同工作单元(如任务与构件变换)的**执行状态(execution state)**来优化执行并保障安全,还用于高效执行构建脚本编译、访问器生成等内部工作。
从目录结构看,platforms/core-execution下还包含了file-watching(文件系统监听)、hashing/hashing-services(哈希)、normalization/normalization-api/normalization-java(输入规范化)、snapshots(快照与虚拟文件系统)、workers/worker-main/worker-process-services等实现子模块,为上述三份文档描述的概念提供了源码级支撑。
二、核心术语:执行引擎、执行状态、快照与指纹
2.1 执行引擎(Execution Engine)
执行引擎用于安全且高效地物化(manifest)工作单元的输出。它是 Gradle 执行工作的主要方式,其核心设计目标包括:
- 提供一种简单、统一的方式来声明工作单元(unit of work),让引擎能够在并发环境下安全高效地产出它们的输出;
- 引擎本身是完全线程安全的,保证并行执行多个工作单元不会相互干扰,即使多个进程同时执行工作单元也是如此;
- 引擎不负责决定哪些工作单元应当执行,也不负责调度执行(那是调度器 Scheduler 的职责)——它只关注"如何执行"与"如何安全复用输出"。
任何具有明确定义的输入与输出、且需要安全执行或输出复用的动作,都可以用执行引擎实现。Gradle 的长期愿景是所有这类工作都经由执行引擎执行,并且执行引擎保持与 Gradle 概念解耦——既有利于未来更广泛的使用,也能保持架构清晰、API 契约简单易懂。
从执行引擎的实现看,其主目录 platforms/core-execution/execution/src/main/java 下有约 200 个 Java 文件,其中包含下文将详细剖析的步骤管线实现(如ValidateStep);同时配套的 执行引擎示意图 以可缩放 SVG 的形式直观呈现了整个引擎的流水线,建议用浏览器新标签页打开查看细节。
Gradle 执行引擎示意图,展示了 Identify、身份缓存、可变/不可变流水线、构建缓存与执行等完整步骤管线
2.2 工作单元(Unit of Work)
工作单元由以下要素定义:
- 一个可识别(identifiable)的动作(action);
- 一组已知的输入(inputs);
- 一组期望的输出(outputs)。
其中,动作是相对于其输入而言的纯函数——等效的输入产生等效的输出。输入和输出可以是标量值,也可以是文件和目录(当前实现仅支持文件类输出,但这属于可以解除的历史限制)。输入在工作开始执行后应保持不可变;输出只能由执行中的动作修改。当工作产出文件时,它在一个仅对该工作动作可见的工作空间(workspace)目录中执行,以此实现沙箱隔离(Gradle 任务目前不在隔离工作空间中执行,这是遗留问题)。
工作单元的典型例子包括:
- Gradle 任务动作(task actions)与构件变换(artifact transforms);
- 构建脚本的编译(build script compilation);
- 构建脚本的依赖访问器生成(generation of dependency accessors);
- Maven goals(执行引擎当前并未直接用于执行 Maven goals,但其本身没有任何 Gradle 特有内容会阻止这种用途)。
2.3 识别工作:本地身份(Identity)与构建缓存键(Build Cache Key)
为支持安全、优化的执行,执行引擎需要识别工作单元。每个单元有两个标识符:
- 身份(Identity):在当前执行作用域(如整个 Gradle 构建树)内本地唯一的工作标识。对于任务,身份就是任务路径(如
:subproject:taskName);对于其他类型的工作,身份是由其不可变输入计算出的哈希。 - 构建缓存键(Build Cache Key):用于跨时间与空间存储和检索工作输出的全局标识符,使用全部输入计算。
2.4 执行状态(Execution State)
工作单元的执行状态由以下部分组成:
- 输入(inputs):
- 实现本身——工作实现的完全限定类名(FQCN,如
org.gradle.api.tasks.compile.JavaCompile)及其 classpath 的 classloader 哈希; - 输入属性值的value snapshot;
- 文件输入的指纹(fingerprint)。
- 实现本身——工作实现的完全限定类名(FQCN,如
- 输出(outputs):
- 输出文件的文件系统快照(file-system snapshot);
- 执行是否成功;
- 输出的来源(origin)(本次构建新产生,或复用了之前构建的结果)。
历史执行状态按工作的身份索引,存储在**执行历史(execution history)**中。执行历史对应示意图中的 "Execution History" 存储节点,其在源码中的序列化实现可参考 platforms/core-execution/execution/src/main/java/org/gradle/internal/execution/history/impl/FileSystemSnapshotSerializer.java。
2.5 快照(Snapshot)与指纹(Fingerprint)
执行状态的捕获通过对工作的输入和输出做**快照(snapshotting)与指纹(fingerprinting)**完成,通常发生在执行前后。
- 快照(Snapshotting):为输入或输出在某个时间点创建其**原样状态(verbatim state)**的不可变记录。快照之后可以用来与同一对象在另一时刻的快照对比,以检测是否发生了修改。
- 指纹(Fingerprinting):捕获输入的规范化状态(normalized state)。
两者本质都是数据的"指纹",区别在于:快照代表数据的原样状态,指纹代表按预期用途规范化后的状态(更精确地说是"未规范化指纹"与"规范化指纹",但文档沿用了这两个更简洁的术语)。
规范化(Normalization)帮助忽略无关信息、提升缓存命中率,它取决于输入的用途(usage):同一批.class文件,作为编译任务的编译 classpath 与作为运行测试的运行时 classpath 时,会被不同地规范化。而输出从不被规范化。
三、执行引擎的流水线:步骤管线(Step Pipeline)
执行引擎采用**嵌套步骤的流水线(pipeline of nested steps)**来处理执行请求,这种结构与 Servlet 应用中的过滤器链(filter chain)类似。单个步骤的执行通常遵循以下"配方":
- 步骤接收被执行的工作单元,以及一个包含前面步骤收集信息的不可变数据对象(context,上下文);
- 步骤可以做一些有意义的工作,例如在外部服务中查找关于工作单元的信息、修改工作空间等;
- 步骤决定是否调用流水线中的下一步,将 context 传递给它——可以原样传递,也可以附加更多数据;步骤也可以短路(short-circuit):不调用下一步而提前返回;
- 步骤返回一个结果(result):描述发生了什么的不变数据对象。它可以是下一步返回的结果本身、附加了新数据的增强版结果,或完全由本步骤创建的新对象。
此外,步骤可以修改工作空间(Can modify workspace),数据既可以附加到 context 上(输入性质),也可以附加到 result 上(输出性质)——这些信息均直接标注在执行引擎示意图中。
四、执行引擎的优化手段
当输入定义良好的工作单元时,执行引擎可以采用以下安全优化来尽可能高效地产出输出:
- 身份缓存(Identity Caching):如果工作在当前工具调用上下文中已用相同输入执行过,则查询内存缓存获取结果。身份缓存仅对构件变换可用(见下文"延迟执行")。
- Up-to-Date 检查(Up-to-Date Checks,即增量构建):如果工作的执行状态是up-to-date,则跳过执行。
- 构建缓存(Build Cache):如果输出在本地不是最新的,引擎先在本地缓存中检索已存储的结果,必要时再查远程缓存。
- 执行(Execution):如果在构建缓存中未找到结果,则真正执行工作。
4.1 来源元数据(Origin Metadata)
每个产生的结果都带有来源元数据:一组键值对,描述该结果由哪个构建创建、耗时多久,以及关联的缓存键。通过查询来源元数据,可以判断结果是本次构建产生的,还是从之前的构建复用的。
4.2 Up-to-Date 检查(增量构建)的实现
通过将工作单元执行前的状态与上一次执行后的状态进行对比:
- 输入值变化:比较输入值属性的
ValueSnapshot; - 输入文件变化:比较输入文件属性的指纹;
- 输出变化:比较输出属性的快照。
如果输入和输出都没有变化,则无需执行,直接跳过。
[!NOTE] 文档特意提示:增量**构建(incremental build)与增量工作(incremental work)**指代不同种类的增量性,术语上应逐步弃用"incremental build"。
4.3 构建缓存的读写流程
如果工作的执行状态已过期,则尝试从构建缓存加载输出:
- 允许使用缓存时,先查本地缓存;本地缓存没有对应缓存键的条目时,再问远程缓存;
- 缓存命中时,删除并解包结果到输出位置;同时清理本地状态;
- 缓存未命中或不允许从缓存加载时,继续执行;
- 执行结束后,检查是否允许存入本地缓存;允许时,同时存入本地与远程缓存。
[!NOTE] 对于任务,存在一个内部机制可以阻止将已执行任务的结果存入构建缓存。这一内部特性仅被 Develocity 的预测性测试选择(predictive test selection)优化所使用。
五、执行模式:可变工作与不可变工作
执行引擎支持两类工作:可变(mutable)与不可变(immutable)。可变工作可根据与上次执行相比哪些输入发生了变化,选择增量或非增量执行。
- 不可变工作(Immutable Work):全部输入都是不可变输入,完整输入集定义了工作。例如访问器生成、非增量构件变换。
- 可变工作(Mutable Work):只有一部分输入是不可变输入,其余是可能在不同执行之间变化的可变输入。例如任务与增量构件变换。
工作在其工作空间中执行(即在其工作空间中产出输出)。工作空间是由身份分配给工作的专用目录,在工作空间内的执行受锁保护,防止多个工作单元相互冲突(Gradle 任务因历史原因没有独占工作空间与互斥,两个 Gradle daemon 在同一构建上运行同一任务时可能冲突)。
关键规则:如果不可变输入相比上一次本地执行发生了变化,引擎会将工作视为一个新的、不同的单元,并分配不同的工作空间;而可变输入的变化不会改变可变工作的身份,因此它会在同一工作空间中执行(任务目前例外:其身份是完整任务路径,计划改为与构件变换一样在工作空间中执行)。
5.1 工作空间分配(Workspace Allocation)
- 可变工作只要只有可变输入在变化,就会在**同一个可变工作空间(mutable workspace)**中执行(因此得名)。
- 不可变工作在执行时被分配一个临时工作空间目录;执行结束(或从缓存加载结果)后,该工作空间目录被原子性地移动到不可变工作空间(immutable workspace),此后不再被修改。
5.2 增量执行(Incremental Execution)
对于可变工作,可变输入有以下三种行为:
- 非增量(Non-incremental):属性值的任何变化都触发工作的完全重建。
- 增量(Incremental,在任务与变换中用
@Incremental标注):属性值的变化可以触发工作的增量执行,由工作负责更新任何先前的输出。为此,执行引擎会向动作提供自上次在同一工作空间执行以来任何增量输入的变化列表。 - 主要(Primary,在任务中用
@SkipWhenEmpty标注):与增量输入相同,额外特性是:如果所有主要输入都为空,则跳过工作执行并删除其输出。
如果选择非增量执行,可变工作空间首先被清理(任务因历史原因不清理:一些增量任务未声明自己是增量的,直接删除输出会是破坏性变更)。
5.3 延迟执行:构件变换(Artifact Transforms)的特例
具有相同身份的工作在单次构建中通常只执行一次。但构件变换有些特殊:它们定义在消费项目中,因此相同身份的变换可能在同一次构建中被多次、且往往并发地调用(例如每个需要 Guava 的子项目都调用"最小化 Guava JAR"这个变换)。
为处理这种场景并避免竞态条件,执行引擎提供了"延迟(deferred)"路径:此模式下引擎立即返回一个Deferrable<T>,它要么已持有缓存结果,要么可以在稍后同步完成。工作完成后,结果被存入身份缓存;通过同步、非延迟方式执行时,身份缓存被忽略。
六、遗留特例:Gradle 任务(Legacy: The Special Case of Gradle Tasks)
文档明确指出:适用于其他工作单元的一些不变量尚未适用于任务,这主要是历史原因,应当被解决。
6.1 身份(Identity)
任务的身份是其在构建中的路径(如:project-name:taskName)。关键点是:该身份与非增量输入无关。对于非增量任务,当某个非增量输入变化时,先前的输出仍会呈现给动作,清理责任由动作承担;对于增量任务,当非增量输入变化时,执行引擎会自动清理输出。计划是将此行为扩展到非增量任务(或让它们通过不可变工作空间执行),但有些任务没有声明自己是增量的却依赖更新输出的能力——典型例子是 Gradle 的 Kotlin 编译任务,简单强制这一变更会让它们失去增量能力。
6.2 无独占工作空间(No Exclusive Workspace)
任务没有自己的工作空间,其文件输出可以指向文件系统上的任意位置(实践中通常只产生单个目录或文件)。未来计划让任务更像构件变换,要求其在工作空间内产出输出。
6.3 重叠输出(Overlapping Outputs)
任务允许在其他任务也产出输出的目录中产出输出,这使确定哪个输出文件由哪次执行产生变得困难,也让并行执行不安全。因此,对于检测到产生重叠输出的任务,执行引擎会禁用执行优化。未来计划禁止任务产生重叠输出(不过为将来支持 Maven goals——其重叠输出很常见——引擎中很可能保留一定程度的支持)。
七、快照与指纹的底层实现
snapshots/README.md 提供了快照与指纹的详细技术说明。
7.1 快照的三种形态
快照是某些数据状态的简洁表示,用于检查对应数据是否发生变化,使用密码学哈希表示数据状态:
- 标量(非文件)输入:由
ValueSnapshotter捕获为ValueSnapshot对象; - 单个文件系统位置:可通过
FileSystemAccess获取为FileSystemLocationSnapshot——包含该文件系统对象的绝对路径与其内容的哈希; - 文件集合:由
FileCollectionSnapshotter捕获为FileSystemSnapshot(可以有多条根(roots),而FileSystemLocationSnapshot只有一条根)。
上述类型在源码中均有对应实现,例如 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/snapshot/FileSystemLocationSnapshot.java 与 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/snapshot/FileSystemSnapshot.java。
7.2 哈希算法与 Merkle 树
- 计算文件内容与标量输入的快照/指纹时使用MD5密码学哈希算法,同一算法也用于计算工作单元的标识符(如构建缓存键)。
- 为什么选 MD5:MD5 在安全层面早已被攻破,但此处目标不是对抗恶意意图,而是避免意外碰撞,MD5 对此足够强;它速度极快、JVM 上普遍可用,且每个哈希仅占 16 字节,相比 SHA1(20 字节)或 SHA256(32 字节)更省内存。
- 文档同时指出:可以使用其他密码学哈希,让哈希可配置是合理的;BLAKE 系列哈希族在性能上很有前景,但仍需更多研究。非密码学哈希如 xxHash、MurmurHash 虽然显著更快,但缺少所需的"天文级"抗碰撞性。
- 复杂输入使用Merkle 树生成单一哈希:将各组成部分的哈希再哈希,得到整体的哈希。对应实现见 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/snapshot/MerkleDirectorySnapshotBuilder.java。
7.3 虚拟文件系统(Virtual File-System,VFS)
执行引擎经常需要以快照形式获取文件系统状态。若每次都重新读取整个文件层级,效率过低,因此文件系统状态被缓存在**虚拟文件系统(VFS)**中:
- VFS只存储正在运行的构建的目录层级信息——不会保留构建根目录(及任何 included build 根目录)之外的文件系统对象信息(之前构建的数据也可保留一段时间)。
- VFS 使用
FileSystemNode组成的高效稀疏树数据结构存储,该结构允许在请求父目录快照时复用已拍摄的快照,还支持存储过滤后的快照(例如只请求目录层级中的*.java文件)。对应实现见 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/snapshot/FileSystemNode.java。 - VFS 不直接暴露,入口是
FileSystemAccess服务(见 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/vfs/FileSystemAccess.java),它有多个read()方法获取文件与目录快照。VFS 的整体结构可参考 虚拟文件系统示意图。
Gradle 虚拟文件系统(VFS)结构示意图
变更跟踪(Change Tracking)
为保证 VFS 正确缓存文件系统状态,必须在文件系统被修改之前使 VFS 的相关部分失效。假设在构建运行期间,VFS 跟踪区域的文件系统变更总是会被失效处理,因此修改文件系统的代码应包裹在FileSystemAccess.write()中,或在变更生效前调用FileSystemAccess.invalidate()。
文件系统监听(File-System Watching)
- 为了跟踪构建之间的修改,VFS 在支持的平台上监听文件系统;发生修改时,VFS 中任何可能过期的数据部分都会被丢弃。对应机制见 文件系统监听示意图。
- 当文件系统监听不可用时,构建期间收集的所有 VFS 数据都会被丢弃。
- VFS 与执行引擎整体上不区分直接访问的内容与通过符号链接(symlink)访问的内容;但文件系统监听无法可靠地通知符号链接内容的变更,因此在构建结束时,即使启用了文件系统监听,也会将通过符号链接访问的内容从 VFS 中丢弃。
缓存文件哈希(Caching File Hashes)
实际的文件内容哈希由FileHasher处理,文件哈希在内存与磁盘上都有缓存。这些缓存使用文件修改日期与大小作为启发式信号判断文件是否被修改。这意味着即使由于某种原因使某文件的 VFS 快照失效,只要文件在真实文件系统中未变,仍可通过缓存的哈希廉价地重新创建快照。
八、输入规范化(Input Normalization)
并非所有输入都与工作相关,利用这一点可获得更好的性能。文档给出的经典例子是Java 编译任务的换行符:源文件中的换行符无关紧要,无论使用\n还是\r\n都不影响编译结果;因此换行符的变化不应触发重新编译,之前的.class文件应被视为 up-to-date。忽略此类差异还允许通过远程构建缓存跨操作系统复用缓存编译结果。
除了源文件的换行符规范化,Java 编译还可声明编译 classpath 通过ABI 提取(ABI extraction)进行规范化——输入哈希基于 classpath 提取出的 ABI 而非原始文件内容计算,从而只要依赖差异不影响 ABI,就能复用已有编译结果。
[!NOTE] 输入规范化目前仅对文件输入可用,但如需也可引入标量输入规范化。
8.1 指纹(Fingerprints)
规范化后的输入被捕获为指纹(FileSystemLocationFingerprint,见 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/fingerprint/FileSystemLocationFingerprint.java)。这些对象与快照类似,但:
- 携带的是规范化路径而非绝对路径;
- 捕获的是规范化内容的哈希而非原样内容的哈希。
例如计算 Java 源文件指纹时,先把所有换行符替换为\n再哈希内容,并且只捕获从源目录起的相对路径。对于FileCollection,逐文件指纹存储在以文件绝对路径为索引的Map中——即指纹不保留快照那样的文件系统层级结构(这是历史选择,可以改变)。
8.2 规范化策略(Normalization Strategies)
文件集合指纹通过**指纹策略(fingerprinting strategy)**对文件系统快照中的文件与路径做指纹化生成。一个指纹策略可以多种方式规范化文件输入:
- 归档理解(archive comprehension):将归档视为与目录等价,可遍历其元素,忽略文件顺序、时间戳与权限等元数据;
- 模式过滤(pattern filtering):将范围限制到某些模式的文件,如 Java 编译 classpath 规范化中的
*.class(注意此过滤是对输入FileCollection本身已有过滤的补充,FileCollection级过滤已体现在文件集合快照中); - 空目录过滤(empty directory filtering):用
@IgnoreEmptyDirectories忽略指纹中的空目录; - 路径规范化(path normalization):可忽略每个文件路径的部分或全部,如任务属性上使用的
@PathSensitive(RELATIVE); - 顺序规范化(order normalization):通过按某种可复现顺序排序来忽略文件顺序;根元素的顺序可以与后代元素的顺序不同地处理,这由
FingerprintHashingStrategy负责; - 内容规范化(content normalization):每个文件可单独规范化,例如 JVM 运行时 classpath 上的
.properties文件可被解析以忽略注释等。
8.3 路径规范化(Path Normalization)
大多数输入文件属性使用路径规范化并完全忽略条目顺序,通过输入属性上的@PathSensitive注解实现,选项包括:
| 选项 | 含义 |
|---|---|
ABSOLUTE | 不忽略路径,使用每个条目的完整绝对路径 |
RELATIVE | 忽略从根开始的路径 |
NAME_ONLY | 只考虑文件名 |
IGNORE | 完全忽略路径 |
8.4 内容规范化(Content Normalization)
Gradle 对Java 编译 classpath 与 JVM 运行时 classpath特殊处理:对它们规范内容和路径;根元素顺序不规范化,但JAR 与类目录内部条目的顺序被忽略。
@CompileClasspath:- 假设文件是 ZIP,将其视为目录层级;
- 保留根元素顺序,忽略子树中条目的顺序;
- 过滤
.class文件; - 使用每个
.class文件提取出的 ABI计算内容哈希。
@Classpath(即运行时 classpath):- 假设文件是 ZIP 并将其视为目录层级,递归进行;
- 保留根元素顺序,忽略子树中条目的顺序;
- 应用来自
project.normalization.runtimeClasspath的过滤器; - 根据
project.normalization.runtimeClasspath规范化.properties与META-INF内容。
另外,标记了@NormalizeLineEndings(通常是源代码)的输入会做换行符规范化。
8.5 比较指纹(Comparing Fingerprints)
指纹策略还会决定结果文件集合指纹的指纹比较策略(fingerprint compare strategy),该比较策略用于up-to-date 检查时比较两个文件集合指纹。
九、工作验证(Work Validation)
Work Validation.md 讲解的工作验证是检查工作单元(任务、变换)在构建逻辑中是否被正确定义的过程。它专门关注工作如何被定义、其输入与输出是否被正确接线;执行期间发生的运行时错误不在此处处理。
9.1 验证的两种形式
- 静态验证(Static validation):发生在插件开发期间,通过 ASM 访问构建逻辑编译后的
.class文件进行,由ValidatePlugins任务执行。 - 运行时验证(Runtime validation):在工作单元执行期间发生,使用反射信息。执行引擎在
ValidateStep中调用运行时验证。
对应实现可参考执行引擎步骤实现 platforms/core-execution/execution/src/main/java/org/gradle/internal/execution/steps/ValidateStep.java 及其测试 platforms/core-execution/execution/src/test/groovy/org/gradle/internal/execution/steps/ValidateStepTest.groovy。
两种形式都关心工作及其属性是否具有正确的注解与方法签名,但可用数据的来源与详细程度不同。
9.2 嵌套属性验证的关键差异
两者最大的区别在于嵌套属性的类型如何被验证。考虑任务的如下属性:
@Nested Property<AnInterfaceType> getValue();- 静态验证只能知道
value属性将是AnInterfaceType的某个实现,因此只能验证AnInterfaceType本身; - 运行时验证则知道
value属性中存储的对象的实际类型(例如SomeImplementationType)。该类型实现了AnInterfaceType,但它可能还定义了自己的更多属性,这些属性也需要被验证。因此,运行时验证可以产生静态验证已通过的工作类型的警告与错误。
[!NOTE] 运行时验证还可以验证通过运行时 API为任务注册的属性。
9.3 验证内容
文档同时指出:由于历史原因,目前只对任务实现了验证,但所有用户定义的工作类型都可受益于验证;运行时任务验证实现在TaskExecution.validate()中。
十、总结与深入阅读路径
Gradle 的 Core Execution 平台用一套统一模型解决了"如何在并发、可缓存、可增量的环境下安全高效地执行工作"这一核心问题:
- 执行引擎(platforms/core-execution/execution/README.md)负责以嵌套步骤流水线执行工作单元,组合身份缓存、up-to-date 检查、构建缓存与增量执行等优化;
- 快照与指纹(platforms/core-execution/snapshots/README.md)负责通过 MD5 + Merkle 树、VFS 与规范化策略,为执行状态捕获可靠且可复用的"证据";
- 工作验证(platforms/core-execution/Work Validation.md)通过静态(
ValidatePlugins+ ASM)与运行时(ValidateStep+ 反射)两种途径,保证工作定义的正确性。
若想继续深入,推荐按以下顺序阅读源码:
- 执行引擎实现与步骤管线:platforms/core-execution/execution/src/main/java/org/gradle/internal/execution/steps;
- 快照与虚拟文件系统:platforms/core-execution/snapshots/src/main/java/org/gradle/internal/snapshot 与 platforms/core-execution/snapshots/src/main/java/org/gradle/internal/vfs;
- 输入规范化策略:platforms/core-execution/normalization-api 与 platforms/core-execution/normalization-java;
- 哈希与文件监听:platforms/core-execution/hashing、platforms/core-execution/file-watching。
理解这些机制,是深入理解 Gradle 增量构建、远程构建缓存与构件变换调优的基础。
- 构建工具
- 开发工具
【免费下载链接】gradle
Adaptable, fast automation for all
相关推荐
Nacos 任务执行引擎(Task Execution)深度解析:延迟任务、执行任务与领域调度机制
Nacos 任务执行引擎(Task Execution)深度解析:延迟任务、执行任务与领域调度机制 本文以 Nacos 官方设计规范 Foundation Ta
后端微服务配置中心服务注册发现云原生Gradle 快照与指纹识别(Snapshotting & Fingerprinting)原理与实践深度解析
Gradle 快照与指纹识别(Snapshotting & Fingerprinting)原理与实践深度解析 本文围绕 Gradle 执行平台(Core Exe
构建工具开发工具EIP-7886 延迟执行(Delayed Execution)深度解析:将区块验证与执行解耦,解锁异步验证与更高吞吐
EIP 7886 延迟执行(Delayed Execution)深度解析:将区块验证与执行解耦,解锁异步验证与更高吞吐 导读 EIP 7886(Delayed
区块链文档Web3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考