Apache Ant 是 Java 领域中历史悠久的构建工具,尽管团队数量在减少,但它仍然被广泛使用。虽然灵活,但它缺乏惯例和 Gradle 提供的许多强大功能。迁移到 Gradle 是值得的,这样您的构建可以变得更精简、更简单、更快,同时仍保留您在 Ant 中享有的灵活性。您还将受益于对多项目构建的强大支持以及易于使用且灵活的依赖项管理。

从 Ant 迁移到 Gradle 的最大挑战是没有标准的 Ant 构建。这使得难以提供具体说明。幸运的是,Gradle 具有与 Ant 的一些出色的集成功能,可以使该过程相对顺利。从基于 Ivy 的依赖项管理迁移并不困难,因为 Gradle 具有基于 依赖项配置 的类似模型,该模型可与兼容 Ivy 的存储库配合使用。

我们将首先概述在将构建从 Ant 迁移到 Gradle 时应考虑的事项,并提供一些有关如何继续进行的一般准则。

通用指南

当您将构建从 Ant 迁移到 Gradle 时,您应该记住您已有的内容的本质以及您希望最终得到什么。您想要一个反映现有 Ant 构建结构的 Gradle 构建吗?或者您想要迁移到更符合 Gradle 习惯用语的内容?您正在寻找的主要好处是什么?

为了更好地理解,请考虑以下相反的场景

  • 通过 ant.importBuild() 导入的构建

    这种方法快速、简单,并且适用于许多基于 Ant 的构建。您最终得到一个与原始 Ant 构建有效相同的构建,除了您的 Ant 目标变成 Gradle 任务。甚至目标之间的依赖关系也得以保留。

    缺点是您仍在使用 Ant 构建,您必须继续维护它。您还失去了 Gradle 惯例、其许多插件、其依赖关系管理等的优势。您仍然可以使用 增量构建信息 增强构建,但它比普通 Gradle 构建所需的工作量更大。

  • 一个符合 Gradle 习惯用语的构建

    如果您想为您的构建提供未来保障,那么这就是您想要达到的目标。利用 Gradle 的惯例和插件将产生一个更小、更容易维护的构建,其结构为许多 Java 开发人员所熟悉。您还将发现更容易利用 Gradle 的强大功能来提高构建性能。

    主要的缺点是执行迁移所需额外的工作,特别是如果现有构建很复杂并且有许多项目间依赖关系。然而,这些构建通常从切换到符合 Gradle 习惯用语中受益最多。此外,Gradle 提供了许多可以简化迁移的功能,例如 直接从 Gradle 构建中使用核心和自定义 Ant 任务 的能力。

从长远来看,您理想情况下希望最终接近第二个选项,但您不必一下子就达到那里。

以下是帮助您决定要采取的方法以及如何进行的一系列步骤

  1. 并排保留旧的 Ant 构建和新的 Gradle 构建。

    您知道 Ant 构建有效,因此您应该保留它,直到您确信 Gradle 构建生成了所有相同的工件,并且以其他方式完成了您需要的内容。这也意味着用户可以在不创建源树的新副本的情况下尝试 Gradle 构建。

    在准备切换之前,不要尝试更改构建的目录和文件结构。

  2. 开发一种机制来验证两个构建是否生成相同的工件。

    这是确保部署和测试不会中断的重要步骤。即使是小的更改,例如 JAR 中清单文件的内容,也可能导致问题。如果 Gradle 构建生成与 Ant 构建相同的输出,这将使您和其他人员对切换充满信心,并使实施将带来最大收益的重大更改变得更加容易。

  3. 确定您是否具有多项目构建。

    多项目构建通常更难迁移,并且比单项目构建需要更多工作。我们在 迁移多项目构建 部分中提供了一些专门的建议来帮助完成此过程。

  4. 确定为每个项目使用哪些插件。

    我们预计绝大多数 Ant 构建都适用于 基于 JVM 的项目,为此提供了大量插件,可提供您所需的大量功能。Gradle 插件包括 核心插件,这些插件与 Gradle 一起打包,以及 插件门户 上有用的社区插件。

    即使 Java 插件 或其派生插件(例如 Java 库插件)不适合您的构建,您至少应考虑 基础插件 及其生命周期任务。

  5. 导入 Ant 构建或从头创建 Gradle 构建。

    此步骤很大程度上取决于构建的要求。如果一系列 Gradle 插件可以完成 Ant 构建的大部分工作,那么创建不依赖于 Ant 构建的新 Gradle 构建脚本可能是有意义的。您可以自己实现缺失的部分,也可以 使用现有的 Ant 任务

    另一种方法是将 Ant 构建导入 Gradle 构建脚本并逐步替换 Ant 构建功能。这使您可以在每个阶段进行有效的 Gradle 构建,但需要一些工作才能使 Gradle 任务与 Ant 任务正常配合。您可以在 使用导入的构建 中了解有关此内容的更多信息。

  6. 为现有目录和文件结构配置构建

    Gradle 利用约定来消除与旧构建相关的大量样板代码,并使用户在熟悉这些约定后更轻松地使用新构建。但这并不意味着您必须遵循它们。

    Gradle 提供了许多配置选项,允许进行高度自定义。这些选项通常通过提供约定的插件提供。例如,标准生产 Java 代码的源目录结构 — src/main/java — 由 Java 插件提供,它允许您配置不同的源路径。可以通过 Project 对象上的属性修改许多路径。

  7. 如果您愿意,可以迁移到标准 Gradle 约定

    一旦您确信 Gradle 构建产生的工件和其他资源与 Ant 构建相同,就可以考虑迁移到标准约定,例如源目录路径。这样做将允许您删除覆盖这些约定的额外配置。新团队成员在更改后也会发现构建更容易使用。

    是否值得为此步骤付出努力和潜在的干扰取决于您,而这又取决于您的具体构建和团队。

本章的其余部分涵盖了您在迁移期间可能遇到的常见场景,例如依赖管理和使用 Ant 任务。

使用导入的构建

使用 配置缓存不支持导入 Ant 构建。您需要完成向 Gradle 的转换才能获得缓存的好处。

许多迁移的第一步将涉及使用 ant.importBuild()导入 Ant 构建。那么,如何在不一次替换所有内容的情况下向标准 Gradle 构建迁移?

要记住的重要一点是,Ant 目标变成了真正的 Gradle 任务,这意味着您可以执行修改任务依赖关系、附加额外任务操作等操作。这允许您用本机 Gradle 任务替换等效的 Ant 任务,同时保持与其他现有任务的任何链接。

例如,假设您有一个 Java 库项目,您希望将其从 Ant 迁移到 Gradle。Gradle 构建脚本包含导入 Ant 构建的行,现在希望使用标准 Gradle 机制编译 Java 源文件。但是,您希望继续使用创建库 JAR 文件的现有 package 任务。

在图表形式中,场景如下所示,其中每个框表示一个目标/任务

ant task migration

这个想法是用 Ant build 任务替换标准 Gradle compileJava 任务。此替换涉及几个步骤

  1. 应用 Java 库插件

    这提供了图表中所示的 compileJava 任务。

  2. 重命名旧 build 任务。

    名称 build基本插件(通过 Java 库插件)提供的标准 build 任务冲突。

  3. 配置编译以使用现有目录结构。

    Ant 构建很可能不符合标准 Gradle 目录结构,因此您需要告诉 Gradle 在哪里查找源文件以及在何处放置已编译的类,以便 package 可以找到它们。

  4. 更新任务依赖项。

    compileJava 必须依赖于 preparepackage 必须依赖于 compileJava 而不是 ant_build,并且 assemble 必须依赖于 package 而不是标准 Gradle jar 任务。

应用插件就像在 Gradle 构建脚本的开头插入一个 plugins {} 块一样简单,即在 ant.importBuild() 之前。以下是应用 Java 库插件的方法

build.gradle.kts
plugins {
    `java-library`
}
build.gradle
plugins {
    id 'java-library'
}

要重命名 build 任务,请使用 AntBuilder.importBuild() 的变体,该变体接受转换器,如下所示

build.gradle.kts
ant.importBuild("build.xml") { oldTargetName ->
    if (oldTargetName == "build") "ant_build" else oldTargetName  (1)
}
build.gradle
ant.importBuild('build.xml') { String oldTargetName ->
    return oldTargetName == 'build' ? 'ant_build' : oldTargetName  (1)
}
1 build 目标重命名为 ant_build,并保留所有其他目标不变

构建 Java 和 JVM 项目 中描述了为源配置不同路径。您可以以类似的方式更改已编译类的输出目录。

例如,如果原始 Ant 构建将这些路径存储在 Ant 属性中;src.dir 用于 Java 源文件,classes.dir 用于输出。以下是配置 Gradle 以使用这些路径的方法

示例 3. 配置源集
build.gradle.kts
sourceSets {
    main {
        java.setSrcDirs(listOf(ant.properties["src.dir"]))
        java.destinationDirectory = file(ant.properties["classes.dir"] ?: layout.buildDirectory.dir("classes"))
    }
}
build.gradle
sourceSets {
    main {
        java {
            srcDirs = [ ant.properties['src.dir'] ]
            destinationDirectory = file(ant.properties['classes.dir'])
        }
    }
}

最终,您应该切换到适合您项目类型的标准目录结构,以便能够移除此自定义。

最后一步很简单,涉及使用 Task.dependsOn 属性和 Task.dependsOn() 方法来分离和链接任务。该属性适合于替换依赖项,而该方法是添加到现有依赖项的首选方式。

以下是示例场景所需的必需任务依赖项配置,应在 Ant 构建导入之后

build.gradle.kts
tasks {
    compileJava {
        dependsOn("prepare")  (1)
    }
    named("package") {
        setDependsOn(listOf(compileJava))  (2)
    }
    assemble {
        setDependsOn(listOf("package"))  (3)
    }
}
build.gradle
compileJava.dependsOn 'prepare'  (1)
tasks.named('package') { dependsOn = [ 'compileJava' ] }  (2)
assemble.dependsOn = [ 'package' ]  (3)
1 使编译依赖于 prepare 任务
2 ant_build 任务分离 package,并使其依赖于 compileJava
3 从标准 Gradle jar 任务分离 assemble,并使其依赖于 package

这四个步骤将成功地用 Gradle 实现替换旧的 Ant 编译。即使是这种小型的迁移,您也将获得 Gradle 的 增量 Java 编译 的优势,以实现更快的构建。

这是分阶段迁移的一个示例。在这个阶段,将资源处理(例如属性文件)和打包与编译一起包含在内可能更有意义。

您必须问自己的一个重要问题是每个阶段要迁移多少任务。一次迁移得越多越好,但风险在于受更改影响的 Ant 构建中的自定义步骤的数量。

例如,如果 Ant 构建遵循编译、静态资源、打包和单元测试的相当标准的方法,那么可能值得将所有这些一起迁移。但是,如果构建对已编译的类执行一些额外的处理,或者在处理静态资源时执行一些独特操作,那么可能值得将这些任务拆分为单独的阶段。

管理依赖项

Ant 构建通常采用两种方法之一来处理二进制 依赖项(例如库)

  • 将它们与项目一起存储在本地“lib”目录中

  • 使用 Apache Ivy 来管理它们

它们各自需要不同的技术来迁移到 Gradle,但在任何一种情况下,你都会发现这个过程很简单。让我们在以下部分详细了解每种情况。

从目录提供依赖项

当你尝试迁移在文件系统(本地或网络上)中存储其依赖项的构建时,你应该考虑是否最终要使用远程存储库迁移到受管依赖项。这是因为你可以通过以下两种方式之一将文件系统依赖项合并到 Gradle 构建中

如果你采用第一种方法,则更容易迁移到从 Maven 或与 Ivy 兼容的存储库提供的受管依赖项,但这样做要求你的所有文件都符合命名约定“<moduleName>-<version>.<extension>”。

如果你将依赖项存储在标准 Maven 存储库布局中 — <repoDir>/<group>/<module>/<version> — 那么你可以使用 file:// URL 定义一个 自定义 Maven 存储库

为了演示这两种技术,请考虑一个在 libs 目录中具有以下库 JAR 的项目

libs
├── our-custom.jar
├── awesome-framework-2.0.jar
└── utility-library-1.0.jar

文件 our-custom.jar 没有版本号,因此必须将其添加为文件依赖项。其他两个 JAR 符合所需的命名约定,可以声明为从平面目录存储库检索的普通 模块依赖项

以下示例构建脚本演示了如何将所有这些库合并到构建中

build.gradle.kts
repositories {
    flatDir {
        name = "libs dir"
        dir(file("libs"))  (1)
    }
}

dependencies {
    implementation(files("libs/our-custom.jar"))  (2)
    implementation(":awesome-framework:2.0")     (3)
    implementation(":utility-library:1.0")  (3)
}
build.gradle
repositories {
    flatDir {
        name = 'libs dir'
        dir file('libs')  (1)
    }
}

dependencies {
    implementation files('libs/our-custom.jar')  (2)
    implementation ':awesome-framework:2.0'  (3)
    implementation ':utility-library:1.0'  (3)
}
1 指定包含 JAR 文件的目录的路径
2 为未版本化的 JAR 声明一个文件依赖项
3 使用标准依赖项坐标声明依赖项 — 请注意,未指定组,但每个标识符都有一个前导 :,表示一个空组

以上示例会将 our-custom.jarawesome-framework-2.0.jarutility-library-1.0.jar 添加到 implementation 配置,该配置用于编译项目的代码。

你还可以指定这些模块依赖项中的组,即使它们实际上没有组。这是因为平面目录存储库只是忽略此信息。然后,如果你稍后添加一个正常的 Maven 或与 Ivy 兼容的存储库,Gradle 将从该存储库(而不是平面目录存储库)下载声明有组的模块依赖项。

迁移 Ivy 依赖项

Apache Ivy 是一个独立的依赖项管理工具,与 Ant 广泛配合使用。它的工作方式类似于 Gradle。事实上,它们都允许你

  • 定义您自己的配置

  • 从彼此扩展配置

  • 将依赖项附加到配置

  • 从兼容 Ivy 的存储库中解析依赖项

  • 将工件发布到兼容 Ivy 的存储库

最显着的差异是 Gradle 针对特定类型的项目具有标准配置。例如,Java 插件定义了诸如 implementationtestImplementationruntimeOnly 之类的配置。如果需要,您可以定义您自己的依赖项配置

因此,通常可以轻松地从 Ivy 迁移到 Gradle

  • 将模块描述符中的依赖项声明转录到 Gradle 构建脚本的 dependencies {} 块中,理想情况下使用您应用的任何插件提供的标准配置。

  • 将模块描述符中的任何配置声明转录到构建脚本的 configurations {} 块中,以获取无法由 Gradle 的标准配置替换的任何自定义配置。

  • 将 Ivy 设置文件中的解析器转录到构建脚本的 repositories {} 块中。

有关更多信息,请参阅有关管理依赖项配置声明依赖项声明存储库的章节。

Ivy 提供了几个 Ant 任务来处理 Ivy 提取依赖项的过程。该过程的基本步骤包括

  1. 配置 — 应用 Ivy 设置文件中定义的配置

  2. 解析 — 找到已声明的依赖项,并在必要时将它们下载到缓存中

  3. 检索 — 将缓存的依赖项复制到另一个目录

Gradle 的过程类似,但您不必显式调用前两个步骤,因为它会自动执行这些步骤。第三步根本不会发生——除非您创建一个任务来执行它——因为 Gradle 通常直接在类路径中使用依赖项缓存中的文件,并将其作为组装应用程序包的源。

让我们更详细地了解 Ivy 的步骤如何映射到 Gradle

配置

Gradle 的大多数依赖相关配置都已烘焙到构建脚本中,正如您在 dependencies {} 块等元素中所看到的那样。另一个特别重要的配置元素是 resolutionStrategy,它可以从依赖配置中访问。这提供了 Ivy 的冲突管理器可能具有的许多功能,并且是控制传递依赖项和缓存的强大方式。

某些 Ivy 配置选项在 Gradle 中没有等效项。例如,没有锁定策略,因为 Gradle 保证其依赖项缓存是并发安全的。没有“最新策略”方法,因为拥有一个可靠的单一冲突解决策略更为简单。如果选择了“错误”版本,则可以使用强制版本或其他解决选项来覆盖它。

有关更多信息,请参阅有关 控制传递依赖项 的章节。

解决

在构建开始时,Gradle 将自动解析您已声明的任何依赖项并将它们下载到其缓存中。Gradle 使用 声明存储库的顺序 定义的搜索顺序在存储库中搜索这些依赖项。

值得注意的是,Gradle 支持与 Ivy 相同的动态版本语法,因此您仍然可以使用 1.0.+ 等约定。您还可以使用特殊的 latest.integrationlatest.release 标签。如果您决定使用此类 动态更改 依赖项,则可以通过 resolutionStrategy 配置它们的缓存行为。

如果您使用动态和/或更改依赖项,您可能还想考虑 依赖项锁定。这是一种使构建更可靠并确保 可重现性 的方法。

检索

如前所述,Gradle 不会自动从依赖项缓存中复制文件。其标准任务通常直接使用这些文件。如果您想将依赖项复制到本地目录,您可以在构建脚本中使用 Copy 任务,如下所示

build.gradle.kts
tasks.register<Copy>("retrieveRuntimeDependencies") {
    into(layout.buildDirectory.dir("libs"))
    from(configurations.runtimeClasspath)
}
build.gradle
tasks.register('retrieveRuntimeDependencies', Copy) {
    into layout.buildDirectory.dir('libs')
    from configurations.runtimeClasspath
}

配置也是一个文件集合,因此可以在 from() 配置中使用它。你可以使用类似的技术将配置附加到编译任务或生成文档的任务。有关更多示例和 Gradle 文件 API 的信息,请参阅 使用文件 一章。

发布工件

使用 Ivy 管理依赖项的项目通常还使用它将 JAR 和其他工件发布到存储库。如果你要迁移此类构建,那么你会很高兴得知 Gradle 具有内置支持,可将工件发布到与 Ivy 兼容的存储库。

在尝试迁移构建的这一特定方面之前,请阅读 发布 一章,以了解 Gradle 的发布模型。该章节示例基于 Maven 存储库,但与 Ivy 存储库使用相同的模型。

基本的迁移过程如下所示

一旦完成所有这些,你将能够为每个发布生成一个 Ivy 模块描述符,并将它们发布到一个或多个存储库。

假设你定义了一个名为“myLibrary”的发布和一个名为“myRepo”的存储库。那么 Ivy 的 Ant 任务将映射到 Gradle 任务,如下所示

  • <deliver>generateDescriptorFileForMyLibraryPublication

  • <publish>publishMyLibraryPublicationToMyRepoRepository

还有一个方便的 publish 任务,它将 所有 发布发布到 所有 存储库。如果你想将发布限制到特定存储库,请查看 发布章节的相关部分

关于依赖版本

默认情况下,Ivy 在生成模块描述符时会自动将依赖项的动态版本替换为已解析的“静态”版本。Gradle 不会 模仿此行为,声明的依赖项版本保持不变。

您可以使用 Nebula Ivy Resolved 插件 复制默认 Ivy 行为。或者,您可以 自定义描述符文件,以便其包含您想要的版本。

处理自定义 Ant 任务

Ant 的一个优点是,创建自定义任务并将其纳入构建非常容易。如果您有此类任务,那么将它们迁移到 Gradle 构建有两个主要选项

第一个选项通常快速且容易。如果您想将任务集成到增量构建中,则必须使用 增量构建运行时 API。您还经常必须使用 Ant 路径和文件集,这可能不方便。

从长远来看,第二个选项更可取。Gradle 任务类型往往比 Ant 任务更简单,因为它们不必使用基于 XML 的界面。您还可以获得 Gradle 丰富的 API 的好处。此方法支持基于类型化属性的 类型安全增量构建 API

使用文件

Ant 有许多用于处理文件的任务,其中大多数都有 Gradle 等效项。与 Ant 到 Gradle 迁移的其他领域一样,您可以在 Gradle 构建中 使用那些 Ant 任务。但是,我们强烈建议尽可能迁移到本机 Gradle 构造,以便构建受益于

  • 增量构建

  • 与构建的其他部分(例如依赖项配置)更轻松地集成

  • 更多惯用构建脚本

使用没有直接等效项的 Ant 任务(例如 <checksum><chown>)可能会很方便。但是,从长远来看,最好将它们转换为使用标准 Java API 或第三方库的原生 Gradle 任务类型。

以下是 Ant 构建使用最频繁的文件相关元素,以及 Gradle 等效项

  • <copy> — 首选 Gradle Copy 任务类型

  • <zip>(以及 Java 变体)— 首选 Zip 任务类型(以及 JarWarEar

  • <unzip> — 首选使用 Project.zipTree() 方法和 Copy 任务

您可以在 使用文件 章节中看到 Gradle 文件 API 的几个示例,并了解更多相关信息。

关于路径和文件集

Ant 使用路径样结构和文件集的概念,使用户能够处理文件和目录集合。Gradle 具有更简单、更强大的模型,基于 FileCollectionFileTree,可以将其视为构建中的对象。这两种类型都允许基于 Ant 的 glob 语法进行筛选,例如 **/books_*。您可以在 使用文件 章节中了解有关这些类型和 Gradle 文件 API 其他方面的更多信息。

如果您需要与需要它们的 Ant 任务进行交互,则可以通过 ant 对象从构建中构建 Ant 路径和文件集。有关 Ant 集成 的章节中有使用 <path><fileset> 的示例。在 FileCollection 上还有一个 方法,该方法会将文件集合转换为文件集或类似的 Ant 类型。

迁移 Ant 属性

Ant 使用属性映射来存储可以在整个构建中重用的值。这种方法最大的缺点是属性值都是字符串,并且属性本身的行为就像全局变量。

在 Gradle 中与 Ant 属性进行交互

有时您会希望直接从 Gradle 构建中使用 Ant 任务,并且该任务需要设置一个或多个 Ant 属性。

如果是这种情况,您可以通过 ant 对象轻松设置这些属性,如 从 Gradle 使用 Ant 章节中所述。

Gradle 使用类似的形式,即 项目属性,这是一种合理的方式来参数化构建。这些属性可以从命令行、gradle.properties 文件 中或通过特殊命名的系统属性和环境变量进行设置。

如果您有现有的 Ant 属性文件,您可以将它们的内容复制到项目的 gradle.properties 文件中。但请注意

  • gradle.properties 中设置的属性不会覆盖构建脚本中使用相同名称定义的 额外项目属性

  • 导入的 Ant 任务不会自动“看到”Gradle 项目属性——您必须将它们复制到 Ant 属性映射中才能实现这一点

另一个需要理解的重要因素是,Gradle 构建脚本使用面向对象 API,并且在可能的情况下,最好使用任务、源集和其他对象的属性。例如,此构建脚本片段创建了将 Javadoc 文档打包为 JAR 并解包它的任务,通过它们的属性链接任务

build.gradle.kts
val tmpDistDir = layout.buildDirectory.dir("dist")

tasks.register<Jar>("javadocJarArchive") {
    from(tasks.javadoc)  (1)
    archiveClassifier = "javadoc"
}

tasks.register<Copy>("unpackJavadocs") {
    from(zipTree(tasks.named<Jar>("javadocJarArchive").get().archiveFile))  (2)
    into(tmpDistDir)  (3)
}
build.gradle
def tmpDistDir = layout.buildDirectory.dir('dist')

tasks.register('javadocJarArchive', Jar) {
    from javadoc  (1)
    archiveClassifier = 'javadoc'
}

tasks.register('unpackJavadocs', Copy) {
    from zipTree(javadocJarArchive.archiveFile)  (2)
    into tmpDistDir  (3)
}
1 打包所有 javadoc 的输出文件——相当于 from javadoc.destinationDir
2 使用 javadocJar 任务保存的 Javadoc JAR 的位置
3 使用名为 tmpDistDir 的项目属性来定义“dist”目录的位置

正如您从 tmpDistDir 的示例中看到的那样,通常需要通过属性定义路径,这就是 Gradle 还提供 额外属性 的原因,这些属性可以附加到项目、任务和某些其他类型的对象上。

迁移多项目构建

多项目构建是迁移的一个特殊挑战,因为在 Ant 中没有用于构建它们或处理项目间依赖关系的标准方法。

幸运的是,Gradle 的多项目支持可以处理相当多样化的项目结构,并且它为构建和维护多项目构建提供了比 Ant 更强大、更有帮助的支持。ant.importBuild() 方法还透明地处理 <ant><antcall> 任务,这允许分阶段迁移。

以下步骤重点介绍了一种建议的多项目构建迁移方法

  1. 首先学习 Gradle 如何配置多项目构建

  2. 在构建的每个项目中创建一个 Gradle 构建脚本,将其内容设置为此行

    ant.importBuild 'build.xml'
    ant.importBuild("build.xml")

    build.xml 替换为对应于项目的实际 Ant 构建文件的路径。如果没有相应的 Ant 构建文件,则将 Gradle 构建脚本留空。即使您的构建不适合这种迁移方法,也请继续执行这些步骤,看看是否还有办法进行分阶段迁移。

  3. 创建一个设置文件,包含所有项目,现在这些项目都有一个 Gradle 构建脚本。

  4. 实现项目间依赖项。

    多项目构建中的某些项目将依赖于该构建中一个或多个其他项目生成的产品。此类项目需要确保它们依赖的项目已生成其产品,并且已知这些产品的路径。

    确保生成所需产品通常意味着通过 <ant> 任务调用其他项目的构建。不幸的是,这会绕过 Gradle 构建,从而否定您对 Gradle 构建脚本所做的任何更改。您需要使用 Gradle 任务依赖项替换使用 <ant> 任务的目标。

    例如,您的 Web 项目依赖于同一构建中的一部分“util”库。用于“web”的 Ant 构建文件可能具有如下目标

    web/build.xml
    <target name="buildRequiredProjects">
        <ant dir="${root.dir}/util" target="build"/>  (1)
    </target>
    1 root.dir 必须由构建定义

    这可以通过相应 Gradle 构建脚本中的项目间任务依赖项替换,如下例所示,该示例假定“web”项目的“compile”任务要求事先构建“util”

    web/build.gradle.kts
    ant.importBuild("build.xml")
    
    tasks {
        named<Task>("compile") {
            setDependsOn(listOf(":util:build"))
        }
    }
    web/build.gradle
    ant.importBuild 'build.xml'
    
    compile.dependsOn = [ ':util:build' ]

    这不如 Gradle 的项目依赖项那么健壮或强大,但它在不大幅更改构建的情况下解决了眼前的问题。只要小心删除或覆盖对委托给其他子项目的任务的任何依赖项,例如 buildRequiredProjects 任务。

  5. 识别没有依赖于其他项目的项目,并将它们迁移到惯用的 Gradle 构建脚本。

    按照本指南的其余部分中的建议迁移各个项目构建。如前所述,您应尽可能使用 Gradle 标准插件。这可能意味着您需要向每个构建添加一个额外的复制任务,该任务将生成的产品复制到 Ant 构建的其余部分预期的位置。

  6. 当项目仅依赖于具有完全迁移的 Gradle 构建的项目时,迁移项目。

    此时,您应该能够切换到使用附加到适当依赖项配置的正确项目依赖项。

  7. 一旦 Ant 构建的任何部分不再依赖于项目,就清理项目。

    我们在步骤 5 中提到,您可能需要添加复制任务以满足依赖 Ant 构建的要求。一旦这些构建迁移完毕,就不再需要此类构建逻辑,应将其删除。

在流程结束时,您应该拥有一个 Gradle 构建,您确信它可以按预期工作,并且构建逻辑比之前少得多。

延伸阅读

本章涵盖了将 Ant 构建迁移到 Gradle 的主要主题。剩下的只是在迁移后可能会有用的其他几个方面

最后需要注意的是,本指南只涉及 Gradle 的一些功能,我们鼓励您从用户手册的其他章节中了解其余功能。