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 Library 插件)不适合您的构建,您也应该至少考虑 Base 插件 及其生命周期任务。

  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 源文件。但是,您想继续使用现有的 package 任务来创建库的 JAR 文件。

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

ant task migration

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

  1. 应用 Java Library 插件

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

  2. 重命名旧的 build 任务。

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

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

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

  4. 更新任务依赖项。

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

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

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 属性中;Java 源文件的 src.dir 和输出的 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 packageant_build 任务分离,并使其依赖于 compileJava
3 assemble 从标准的 Gradle jar 任务分离,并使其改为依赖于 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 {} 块中,理想情况下使用您应用的任何插件提供的标准配置。

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

  • 将解析器从 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 Plugin 插件来复制 Ivy 的默认行为。或者,您可以自定义描述符文件,使其包含您想要的版本。

处理自定义 Ant 任务

Ant 的优势之一是它可以非常容易地创建自定义任务并将其集成到构建中。如果您有这样的任务,那么将它们迁移到 Gradle 构建主要有两种选择

第一种选择通常快速而简单。如果您想将任务集成到增量构建中,则必须使用增量构建运行时 API。您通常还需要处理 Ant 路径和 fileset,这可能不太方便。

第二种选择是更长远的选择。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 的几个示例并了解更多信息。

关于路径和 fileset

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

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

迁移 Ant 属性

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

在 Gradle 中与 Ant 属性交互

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

如果是这种情况,您可以像从 Gradle 使用 Ant 章节中所述的那样,通过 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. 创建一个 settings 文件,该文件包含所有现在都有 Gradle 构建脚本的项目

  4. 实现项目间依赖项。

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

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

    例如,您的 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 的一些功能,我们鼓励您从用户手册的其他章节中了解其余功能。