Gradle uses a convention-over-configuration approach to building native projects. If you are coming from another native build system, these concepts may be unfamiliar at first, but they serve a purpose to simplify build script authoring.

我们将在本章中详细介绍C ++项目,但是大多数主题也将适用于其他受支持的本地语言. 如果您没有使用Gradle构建本机项目的丰富经验,请查看C ++教程,以获取有关如何构建各种类型的基本C ++项目以及一些常见用例的分步说明.

本章介绍的C ++插件于2018年引入 ,我们建议用户在您可能会参考的旧版Native插件上使用这些插件.

Introduction

C ++项目的最简单构建脚本将应用C ++应用程序插件或C ++库插件,并可以选择设置项目版本:

Example 1. Applying the C++ Plugin
build.gradle
plugins {
    id 'cpp-application' // or 'cpp-library'
}

version = '1.2.1'
build.gradle.kts
plugins {
    `cpp-application` // or `cpp-library`
}

version = "1.2.1"

通过应用这两个C ++插件,您可以获得许多功能:

  • compileDebugCppcompileReleaseCpp任务分别编译src / main / cpp下的C ++源文件,以分别用于著名的调试和发布构建类型.

  • linkDebuglinkRelease任务, linkRelease任务将已编译的C ++对象文件链接到应用程序的可执行文件中,或者将库的共享库链接到具有调试和发布构建类型的共享链接的库中.

  • createDebugcreateRelease任务将已编译的C ++对象文件组合到一个静态库中,这些库具有与调试和发布构建类型具有静态链接的库.

对于任何不平凡的C ++项目,你可能有一些文件相关的和额外的配置具体到项目中.

C ++插件还将上述任务集成到标准生命周期任务中 . 产生开发二进制文件的任务被附加到assemble . 默认情况下,开发二进制文件是调试变量.

本章的其余部分介绍了在构建库和应用程序时根据需要自定义构建的不同方法.

Introducing build variants

本机项目通常可以产生几种不同的二进制文件,例如调试或发布二进制文件,或针对特定平台和处理器体系结构的二进制文件. Gradle通过维度变体的概念来进行管理.

维度只是一个类别,其中每个类别都与其他维度正交. 例如,"构建类型"维是包括调试和发布的类别. "架构"维度涵盖了x86-64和PowerPC之类的处理器架构.

变体是这些维度的值的组合,每个维度恰好由一个值组成. 您可能具有" debug x86-64"或" release PowerPC"变体.

Gradle内置了对多个维度和每个维度内多个值的支持. 您可以在本机插件参考章节中找到它们的列表.

Declaring your source files

Gradle的C ++支持直接从应用程序脚本块中使用ConfigurableFileCollection来配置要编译的源集.

库区分私有(实现细节)和公共(导出到消费者)头.

对于仅在特定目标计算机上编译源的情况,您还可以为每个二进制版本配置源.

cpp sourcesets compilation
图1.源代码和C ++编译

在每个测试套件脚本块上配置测试源. 请参阅测试C ++项目一章.

Managing your dependencies

绝大多数项目都依赖于其他项目,因此管理项目的依存关系是构建任何项目的重要组成部分. 依赖管理是一个大话题,因此我们在这里仅关注C ++项目的基础知识. 如果您想深入了解这些细节,请查看依赖管理的简介 .

Gradle为使用Gradle [ 1 ]发布的Maven存储库中的预构建二进制文件提供支持.

我们将介绍如何在多构建项目中的项目之间添加依赖项.

为您的C ++项目指定依赖项需要两条信息:

  • 标识依赖项的信息(项目路径,Maven GAV)

  • 它需要什么,例如编译,链接,运行时或以上所有.

该信息在C ++ applicationdependencies {}块或library脚本块中指定. 例如,要告诉Gradle您的项目需要common库来编译和链接生产代码,可以使用以下片段:

例子2.声明依赖
build.gradle
application {
    dependencies {
        implementation project(':common')
    }
}
build.gradle.kts
application {
    dependencies {
        implementation(project(":common"))
    }
}

三个元素的Gradle术语如下:

  • 配置 (例如: implementation )-命名的依赖项集合,针对特定目标(如编译或链接模块)分组在一起

  • 项目引用 (例如: project(':common') )-指定路径引用的项目

您可以在此处找到更全面的依赖项管理术语表.

就配置而言,主要感兴趣的是:

  • implementation -用于编译,链接和运行时

  • cppCompile Variant -依赖关系所必需的编译你的产品代码,但不应该是链接或运行过程的一部分

  • nativeLink Variant -用于链接代码所必需的依赖性,但不应该包含在编译或运行时过程中

  • nativeRuntime Variant -依赖关系所必需的运行您的组件,但不应该是编译或链接过程的一部分

您可以在本机插件参考章节中了解有关它们的更多信息以及它们之间的关系.

请注意, C ++库插件为编译和链接模块以及依赖该模块的任何模块所需的依赖项创建了一个附加配置api .

我们仅在此处进行了介绍,因此,一旦您熟悉使用Gradle构建C ++项目的基础知识,我们建议您阅读专用的依赖管理章节 .

需要进一步阅读的一些常见方案包括:

You’ll discover that Gradle has a rich API for working with dependencies - one that takes time to master, but is straightforward to use for common scenarios.

如果遵循以下约定,则编译两个代码都非常容易:

  1. 将您的源代码放在src / main / cpp目录下

  2. implementation配置中声明您的编译依赖性(请参阅上一节)

  3. 运行assemble任务

我们建议您尽可能遵循这些约定,但不必这样做.

有几种自定义选项,您将在下面看到.

所有CppCompile任务都是增量的并且可以缓存.

Supported tool chain

Gradle提供了使用不同工具链执行相同构建的功能. 当您构建本机二进制文件时,Gradle会尝试找到您的计算机上安装的可以构建二进制文件的工具链. Gradle选择可以为目标操作系统和体系结构构建的第一个工具链. 将来,Gradle在选择工具链时将考虑源和ABI兼容性.

Gradle对主要操作系统上的三个主要工具链具有普遍支持:Clang [ 2 ] ,GCC [ 3 ]和Visual C ++ [ 4 ] (仅Windows). 据报道,使用Macports和Homebrew安装的GCC和Clang可以正常工作,但是尚未对此进行连续测试.

Windows

要在Windows上构建,请安装兼容版本的Visual Studio. C ++插件将发现Visual Studio安装并选择最新版本. 无需弄乱环境变量或批处理脚本. 在Cygwin Shell或Windows命令行中,此方法工作正常.

或者,您可以将Cygwin或MinGW与GCC一起安装. 目前不支持Clang.

macOS

要在macOS上构建,您应该安装Xcode. C ++插件将使用系统PATH发现Xcode的安装.

C ++插件还可以与Macports或Homebrew [ 5 ]一起安装的GCC和Clang一起使用. 要使用Macports或Homebrew之一,您需要将Macports / Homebrew添加到系统PATH.

Linux

要在Linux上构建,请安装兼容版本的GCC或Clang. C ++插件将使用系统PATH发现GCC或Clang.

Customizing file and directory locations

假设您有一个旧版库项目,该项目将src目录用于生产代码和专用标头,并包含用于导出标头的目录. 传统的目录结构不起作用,因此您需要告诉Gradle在哪里可以找到源文件和头文件. 您可以通过applicationlibrary脚本块来执行此操作.

每个组件脚本块以及每个二进制文件都定义其源代码所在的位置. 您可以使用以下语法覆盖约定值:

例子3.设置C ++源集
build.gradle
    library {
        source.from file('src')
        privateHeaders.from file('src')
        publicHeaders.from file('include')
    }
build.gradle.kts
    extensions.configure<CppLibrary> {
        source.from(file("src"))
        privateHeaders.from(file("src"))
        publicHeaders.from(file("include"))
    }

现在Gradle将只在src中直接搜索源和私有头,在include中搜索公共头.

大多数编译器和链接器选项可通过相应的任务访问,例如compile Variant Cpplink Variantcreate Variant . 这些任务分别为CppCompileLinkSharedLibraryCreateStaticLibrary类型. 阅读任务参考以获取最新,最全面的选项列表.

例如,如果要更改编译器为所有变体生成的警告级别,则可以使用以下配置:

示例4.为所有变体设置C ++编译器选项
build.gradle
tasks.withType(CppCompile).configureEach {
    // Define a preprocessor macro for every binary
    macros.put("NDEBUG", null)

    // Define a compiler options
    compilerArgs.add '-W3'

    // Define toolchain-specific compiler options
    compilerArgs.addAll toolChain.map { toolChain ->
        if (toolChain in [ Gcc, Clang ]) {
            return ['-O2', '-fno-access-control']
        } else if (toolChain in VisualCpp) {
            return ['/Zi']
        }
        return []
    }
}
build.gradle.kts
tasks.withType(CppCompile::class.java).configureEach {
    // Define a preprocessor macro for every binary
    macros.put("NDEBUG", null)

    // Define a compiler options
    compilerArgs.add("-W3")

    // Define toolchain-specific compiler options
    compilerArgs.addAll(toolChain.map { toolChain ->
        when (toolChain) {
            is Gcc, is Clang -> listOf("-O2", "-fno-access-control")
            is VisualCpp -> listOf("/Zi")
            else -> listOf()
        }
    })
}

也可以通过applicationlibrary脚本块上的BinaryCollection查找特定变体的实例:

例子5.为每个变量设置C ++编译器选项
build.gradle
application {
    binaries.configureEach(CppStaticLibrary) {
        // Define a preprocessor macro for every binary
        compileTask.get().macros.put("NDEBUG", null)

        // Define a compiler options
        compileTask.get().compilerArgs.add '-W3'

        // Define toolchain-specific compiler options
        if (toolChain in [ Gcc, Clang ]) {
            compileTask.get().compilerArgs.addAll(['-O2', '-fno-access-control'])
        } else if (toolChain in VisualCpp) {
            compileTask.get().compilerArgs.add('/Zi')
        }
    }
}
build.gradle.kts
application {
    binaries.configureEach(CppStaticLibrary::class.java) {
        // Define a preprocessor macro for every binary
        compileTask.get().macros.put("NDEBUG", null)

        // Define a compiler options
        compileTask.get().compilerArgs.add("-W3")

        // Define toolchain-specific compiler options
        when (toolChain) {
            is Gcc, is Clang -> compileTask.get().compilerArgs.addAll(listOf("-O2", "-fno-access-control"))
            is VisualCpp -> compileTask.get().compilerArgs.add("/Zi")
        }
    }
}

Selecting target machines

默认情况下,Gradle将尝试为主机操作系统和体系结构创建C ++二进制变体. 通过在applicationlibrary脚本块上指定TargetMachine的集合,可以覆盖此设置:

例子6.设置目标机器
build.gradle
application {
    targetMachines = [
        machines.linux.x86_64,
        machines.windows.x86, machines.windows.x86_64,
        machines.macOS.x86_64
    ]
}
build.gradle.kts
application {
    targetMachines.set(listOf(machines.windows.x86, machines.windows.x86_64, machines.macOS.x86_64, machines.linux.x86_64))
}

Packaging and publishing

在本地环境中,打包和潜在发布C ++项目的方式差异很大. Gradle具有默认值,但是自定义打包可以实现而没有任何问题.

  • 可执行文件直接发布到Maven存储库.

  • 共享库和静态库文件以及公共标头的压缩文件直接发布到Maven存储库.

  • 对于应用程序,Gradle还支持在已知位置安装和运行具有所有共享库依赖项的可执行文件.

Cleaning the build

C ++应用程序和库插件通过使用基本插件clean任务添加到您的项目中. 此任务只是删除$buildDir目录中的所有内容,因此为什么要始终将构建生成的文件放在其中. 该任务是Delete的一个实例,您可以通过设置其dir属性来更改其删除的dir .

Building C++ libraries

库项目的独特之处在于它们被其他C ++项目使用(或"消耗"). 这意味着与二进制文件和标头一起发布的依赖项元数据(以Gradle模块元数据的形式)至关重要. 特别是,库的使用者应能够区分两种不同类型的依赖关系:仅依赖于编译库的依赖关系和也依赖于编译使用者的依赖关系.

Gradle通过C ++库插件来管理这种区别,该插件除了本章介绍的实现之外,还引入了api配置. 如果依赖项的类型显示为静态库的未解析符号或在公共头文件中,则该依赖项通过库的公共API公开,因此应将其添加到api配置中. 否则,依赖项是内部实现细节,应将其添加到Implementation中 .

如果不确定API和实现依赖项之间的区别,请参阅C ++库插件一章的详细说明. 此外,您可以在相应的指南中看到构建C ++库的基本,实际示例.

Building C++ applications

有关更多详细信息,请参见" C ++应用程序插件"一章,但这是您所获得的快速摘要:

  • install创建一个目录,其中包含运行该目录所需的一切

  • Shell和Windows Batch脚本启动应用程序

您可以在相应的指南中看到构建C ++应用程序的基本示例.


1 . 不幸的是,Conan和Nuget存储库尚不支持作为核心功能
2 . 在MacOS上随Xcode一起安装
3 . 通过Cygwin和MinGW在Windows上安装了32位和64位体系结构
4 . 与Visual Studio 2010一起安装到2019
5 . GCC和Clang的Macports和Homebrew安装不受官方支持