第五十四章. 构建本机二进制文件

Chapter 54. Building native binaries

Gradle 对构建本机二进制文件的支持目前为 试验性阶段。请注意,DSL 和其他配置可能在以后的 Gradle 版本中有所变化。
The Gradle support for building native binaries is currently incubating. Please be aware that the DSL and other configuration may change in later Gradle versions.

各种本机二进制插件添加了对从 C++,C 和其他语言构建本机软件组件(如可执行文件或共享库)的支持。 尽管在软件开发方面已经有许多出色的构建工具,但是 Gradle 向开发者提供了它的强大特征和灵活性,以及在 JVM 开发上更传统的依赖管理实践。
The various native binary plugins add support for building native software components, such as executables or shared libraries, from C++, C and other languages. While many excellent build tools exist for this space of software development, Gradle offers developers it's trademark power and flexibility together with the dependency management practices more traditionally found in the JVM development space.

54.1. 受支持的语言

54.1. Supported languages

当前支持以下源语言:
The following source languages are currently supported:

  • C

  • C++

  • Objective-C

  • Objective-C++

  • Assembly

  • Windows resources

54.2. 工具链支持

54.2. Tool chain support

Gradle 提供了使用不同工具链执行相同构建的能力。你可以通过将操作系统的 PATH 更改为包含所需的工具链编译器,来控制使用哪个工具链进行构建。或者,你可以直接配置工具链,如第 54.10 节,《工具链》中所述。
Gradle offers the ability to execute the same build using different tool chains. You can control which tool chain will be used to build by changing the operating system PATH to include the desired tool chain compiler. Alternatively, you can configure the tool chains directly, as described in Section 54.10, “Tool chains”.

支持以下的工具链:
The following tool chains are supported:

操作系统
Operating System
工具链
Tool Chain
备注
Notes
Linux GCC
Linux Clang
Mac OS X XCode 使用 XCode 捆绑的 Clang 工具链。
Uses the Clang tool chain bundled with XCode.
Mac OS X GCC
Mac OS X Clang
Windows Visual C++ Windows XP 及更高版本,Visual C++ 2010 及以上版本。
Windows XP and later, Visual C++ 2010 and later.
Windows GCC with Cywin 32 Windows XP 和更高版本。当前不支持 Cygwin 64。
Windows XP and later. Cygwin 64 is currently not supported.
Windows GCC with MinGW Windows XP 和更高版本。当前不支持 Mingw-w64
Windows XP and later. Mingw-w64 is currently not supported.

本机插件通常在其他类 UNIX 平台上应该都可以用,但目前仅在上述平台上正式支持。
The native plugins should generally work on other UNIX-like platforms, but are currently officially supported on the above platforms only.

54.3. 组件模型

54.3. Component model

本机二进制项目定义了一组 NativeExecutableNativeLibrary 组件,每个组件 Gradle 都映射到大量的 NativeBinary 输出。对于定义的每个 executablelibrary ,Gradle 会添加有相同名称的 FunctionalSourceSet 。每一个功能源码集都将针对项目所支持的每一种语言包含特定语言的源码集。
A native binary project defines a set of NativeExecutable and NativeLibrary components, each of which Gradle maps to a number of NativeBinary outputs. For each executable or library defined, Gradle adds a FunctionalSourceSet with the same name. Each of these functional source sets will contain a language-specific source set for each of the languages supported by the project.

要构建静态或共享本机库二进制文件,请将 NativeLibrary 组件添加到 libraries 容器中。每个 library 组件都可以生成至少一个 SharedLibraryBinary 和至少一个 StaticLibraryBinary
To build either a static or shared native library binary, a NativeLibrary component is added to the libraries container. Each library component can produce at least one SharedLibraryBinary and at least one StaticLibraryBinary.

示例 54.1. 定义库组件 - Example 54.1. Defining a library component

build.gradle

libraries {
    hello {}
}

要构建可执行的二进制文件,请将 Executable 组件添加到 executables 容器中,并关联到一组源码上。
To build an executable binary, an NativeExecutable component is added to the executables container and associated with a set of sources.

示例 54.2. 定义可执行组件 - Example 54.2. Defining executable components

build.gradle

executables {
    main {}
}

在许多情况下,一个组件可以生成多个本机二进制文件。基于用于构建的工具链,提供的编译器或链接器标志,提供的依赖或其他源文件,这些二进制文件可能会有变化。一个组件生成的每个本机二进制都称为 variant。二进制文件变体下面再进行详细讨论。
In many cases, more than one native binary can be produced for a component. These binaries may vary based on the tool chain used to build, the compiler/linker flags supplied, the dependencies provided, or additional source files provided. Each native binary produced for a component is referred to as variant. Binary variants are discussed in detail below.

54.4. 任务

54.4. Tasks

对于由构建产生的每一个 NativeBinary,我们构造了一个生命周期任务,以及一组其他任务用于创建该二进制文件。这组任务执行编译,链接或组装二进制文件的实际工作。
For each NativeBinary that can be produced by a build, a single lifecycle task is constructed that can be used to create that binary, together with a set of other tasks that do the actual work of compiling, linking or assembling the binary.

组件类型
Component Type
本机二进制类型
Native Binary Type
生命周期任务
Lifecycle task
已创建二进制文件的位置
Location of created binary
NativeExecutable ExecutableBinary $component.nameExecutable $buildDir/binaries/$binary.name/$component.name
NativeLibrary SharedLibraryBinary $component.nameSharedLibrary $buildDir/binaries/$binary.name/lib$component.name.so
NativeLibrary StaticLibraryBinary $component.nameStaticLibrary $buildDir/binaries/$binary.name/$component.name.a

54.4.1. 使用共享库

54.4.1. Working with shared libraries

对于生成的每个可执行二进制文件,cpp 插件提供了 install${binary.name} 任务,该任务将创建可执行文件的开发安装,以及它所需要的共享库。 它允许你在不需要在其最终位置安装共享库的情况下运行可执行文件。
For each executable binary produced, the cpp plugin provides an install${binary.name} task, which creates a development install of the executable, along with the shared libraries it requires. This allows you to run the executable without needing to install the shared libraries in their final locations.

54.5. 语言支持

54.5. Language support

目前,Gradle 支持从下面列出的源码语言的任意组合中构建本机二进制文件。本机二进制项目将包含一个或多个名为 FunctionalSourceSet 的实例(如“main”,“test”等等),每个实例都可以包含多个只包含一种语言的 LanguageSourceSet
Presently, Gradle supports building native binaries from any combination of source languages listed below. A native binary project will contain one or more named FunctionalSourceSet instances (eg 'main', 'test', etc), each of which can contain LanguageSourceSets containing source files, one for each language.

  • C

  • C++

  • Objective-C

  • Objective-C++

  • Assembly

  • Windows resources

54.4.1. C++ 源码

54.5.1. C++ sources

C++ 语言支持通过 'cpp' 插件提供。
C++ language support is provided by means of the 'cpp' plugin.

示例 54.3. 'cpp' 插件 - Example 54.3. The 'cpp' plugin

build.gradle

apply plugin: 'cpp'

把 C++ 源码包含到本机二进制文件中,是通过 CppSourceSet 来实现的,它定义了一组 C++ 源文件以及可选的一组导出的头文件(用于库)。默认情况下,对于任何命名的组件,CppSourceSet 包含了在 src/${name}/cpp 中的 .cpp 源文件,以及在 src/${name}/headers 中的头文件。
C++ sources to be included in a native binary are provided via a CppSourceSet, which defines a set of C++ source files and optionally a set of exported header files (for a library). By default, for any named component the CppSourceSet contains .cpp source files in src/${name}/cpp, and header files in src/${name}/headers.

cpp 插件为每个 CppSourceSet 定义了这些默认的位置时,可以扩展或重写这些默认值实现不同的项目布局。
While the cpp plugin defines these default locations for each CppSourceSet, it is possible to extend or override these defaults to allow for a different project layout.

示例 54.4. C++ 源码集 - Example 54.4. C++ source set

build.gradle

sources {
    main {
        cpp {
            source {
                srcDir "src/source"
                include "**/*.cpp"
            }
        }
    }
}

对于名为“main”的库,src/main/headers 中的文件都被视为“public”或“exported”头。不应导出(但内部使用)的头文件应放在 src/main/cpp 目录内(不过请注意,应该始终以包含它们的文件的相对路径这样一种方式来引用这些头文件)。
For a library named 'main', files in src/main/headers are considered the “public” or “exported” headers. Header files that should not be exported (but are used internally) should be placed inside the src/main/cpp directory (though be aware that such header files should always be referenced in a manner relative to the file including them).

54.5.2. C 源码

54.5.2. C sources

C 语言支持是通过 'c' 插件来提供的。
C language support is provided by means of the 'c' plugin.

示例 54.5. 'C' 插件 - Example 54.5. The 'c' plugin

build.gradle

apply plugin: 'c'

把 C 源码包含到本机二进制文件中,是通过 CSourceSet 来实现的,它定义了一组 C 源文件以及可选的一组导出的头文件(用于库)。默认情况下,对于任何命名的组件,CSourceSet 包含了在 src/${name}/c 中的 .c 源文件,以及在 src/${name}/headers 中的头文件。
C sources to be included in a native binary are provided via a CSourceSet, which defines a set of C source files and optionally a set of exported header files (for a library). By default, for any named component the CSourceSet contains .c source files in src/${name}/c, and header files in src/${name}/headers.

c 插件为每个 CSourceSet 定义了这些默认的位置时,可以扩展或重写这些默认值实现不同的项目布局。
While the c plugin defines these default locations for each CSourceSet, it is possible to extend or override these defaults to allow for a different project layout.

示例 54.4. C 源码集 - Example 54.6. C source set

build.gradle

sources {
    hello {
        c {
            source {
                srcDir "src/source"
                include "**/*.c"
            }
            exportedHeaders {
                srcDir "src/include"
            }
        }
    }
}

对于名为“main”的库,src/main/headers 中的文件都被视为“public”或“exported”头。不应导出(但内部使用)的头文件应放在 src/main/c 目录内(不过请注意,应该始终以包含它们的文件的相对路径这样一种方式来引用这些头文件)。
For a library named 'main', files in src/main/headers are considered the “public” or “exported” headers. Header files that should not be exported (but are used internally) should be placed inside the src/main/c directory (though be aware that such header files should always be referenced in a manner relative to the file including them).

54.5.3. 汇编源码

54.5.3. Assembler sources

汇编语言的支持是通过 “assembler” 插件提供的。
Assembly language support is provided by means of the 'assembler' plugin.

示例 54.7. 'assembler' 插件 - Example 54.7. The 'assembler' plugin

build.gradle

apply plugin: 'assembler'

把汇编源码包含到本机二进制文件中,是通过 AssemblerSourceSet 来实现的,它定义了一组 Assembler 源文件。默认情况下,对于任何命名的组件,AssemblerSourceSet 包含了在 src/${name}/asm 中的 .s 源文件。
Assembler sources to be included in a native binary are provided via a AssemblerSourceSet, which defines a set of Assembler source files. By default, for any named component the AssemblerSourceSet contains .s source files under src/${name}/asm.

54.5.4. Objective-C 源码

54.5.4. Objective-C sources

Objective-C 语言的支持是通过 'objective-c' 插件提供的。
Objective-C language support is provided by means of the 'objective-c' plugin.

示例 54.8. 'objective-c' 插件 - Example 54.8. The 'objective-c' plugin

build.gradle

apply plugin: 'objective-c'

把 Objective-C 源码包含到本机二进制文件中,是通过 ObjectiveCSourceSet 来实现的,它定义了一组 Objective-C 源文件。默认情况下,对于任何命名的组件,ObjectiveCSourceSet 包含了在 src/${name}/objectiveC 中的 .m 源文件。
Objective-C sources to be included in a native binary are provided via a ObjectiveCSourceSet, which defines a set of Objective-C source files. By default, for any named component the ObjectiveCSourceSet contains .m source files under src/${name}/objectiveC.

54.5.5. Objective-C++ 源码

54.5.5. Objective-C++ sources

Objective-C++ 语言的支持是通过 'objective-cpp' 插件提供的。
Objective-C++ language support is provided by means of the 'objective-cpp' plugin.

示例 54.9. 'objective-cpp' 插件 - Example 54.9. The 'objective-cpp' plugin

build.gradle

apply plugin: 'objective-cpp'

把 Objective-C++ 源码包含到本机二进制文件中,是通过 ObjectiveCppSourceSet 来实现的,其定义了一组 Objective-C++ 源文件。默认情况下,对于任何命名的组件,ObjectiveCppSourceSet 包含了在 src/${name}/objectiveCpp 中的 .mm 源文件。
Objective-C++ sources to be included in a native binary are provided via a ObjectiveCppSourceSet, which defines a set of Objective-C++ source files. By default, for any named component the ObjectiveCppSourceSet contains .mm source files under src/${name}/objectiveCpp.

54.6. 配置编译器、汇编器和连接器

54.6. Configuring the compiler, assembler and linker

要生成的每个二进制文件都与一组编译器和链接器设置相关联,这些设置包括命令行参数以及宏定义。这些设置可以应用于所有的二进制文件,单个二进制文件,或者根据某些条件有选择地应用于一组二进制文件。
Each binary to be produced is associated with a set of compiler and linker settings, which include command-line arguments as well as macro definitions. These settings can be applied to all binaries, an individual binary, or selectively to a group of binaries based on some criteria.

示例 54.10. 应用于所有二进制文件的设置 - Example 54.10. Settings that apply to all binaries

build.gradle

binaries.all {
    // Define a preprocessor macro for every binary
    cppCompiler.define "NDEBUG"

    // Define toolchain-specific compiler and linker options
    if (toolChain in Gcc) {
        cppCompiler.args "-O2", "-fno-access-control"
        linker.args "-Xlinker", "-S"
    }
    if (toolChain in VisualCpp) {
        cppCompiler.args "/Zi"
        linker.args "/DEBUG"
    }
}

每个二进制文件都与特定的 ToolChain 关联,允许设置基于此值进行针对性的配置。
Each binary is associated with a particular ToolChain, allowing settings to be targeted based on this value.

让设置应用于特定类型的所有二进制文件很容易:
It is easy to apply settings to all binaries of a particular type:

示例 54.11. 应用于所有共享库的设置 - Example 54.11. Settings that apply to all shared libraries

build.gradle

// For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
binaries.withType(SharedLibraryBinary) {
    if (toolChain in VisualCpp) {
        cCompiler.args "/Zi"
        cCompiler.define "DLL_EXPORT"
    }
}

此外,还可以指定设置应用于某个特定的 executablelibrary 组件产生的所有二进制文件:
Furthermore, it is possible to specify settings that apply to all binaries produces for a particular executable or library component:

示例 54.12. 应用于“main”可执行组件所产生的所有二进制文件的设置 - Example 54.12. Settings that apply to all binaries produced for the 'main' executable component

build.gradle

executables {
    main {
        binaries.all {
            if (toolChain in VisualCpp) {
                assembler.args "/Zi"
            } else {
                assembler.args "-g"
            }
        }
    }
}

上面的例子将会把提供的配置应用到所有构建的 executable 二进制文件。
The example above will apply the supplied configuration to all executable binaries built.

同样,也可以把设置指定为某种特定类型的组件的目标二进制文件:例如所有 main library 组件的 shared libraries
Similarly, settings can be specified to target binaries for a component that are of a particular type: eg all shared libraries for the main library component.

示例 54.13. 仅应用于“main” library 组件所产生的共享库的设置 - Example 54.13. Settings that apply only to shared libraries produced for the 'main' library component

build.gradle

libraries {
    main {
        binaries.withType(SharedLibraryBinary) {
            // Define a preprocessor macro that only applies to shared libraries
            cppCompiler.define "DLL_EXPORT"
        }
    }
}

54.7. Windows Resources

54.7. Windows Resources

使用 VisualCpp 工具链时, Gradle 能够编译 Window Resource(rc)文件,并将它们链接到本机二进制文件。 这个功能是由 'windows-resources' 插件提供的。
When using the VisualCpp tool chain, Gradle is able to compile Window Resource (rc) files and link them into a native binary. This functionality is provided by the 'windows-resources' plugin.

示例 54.14. 'windows-resources' 插件 - Example 54.14. The 'windows-resources' plugin

build.gradle

apply plugin: 'windows-resources'

把 Windows 资源包含到本机二进制文件中,是通过 WindowsResourceSet 来实现的,其定义了一组 Windows Resource 源文件。默认情况下,对于任何已命名的组件,WindowsResourceSet 包含了在 src/${name}/rc 中的 .rc 源文件。
Windows resources to be included in a native binary are provided via a WindowsResourceSet, which defines a set of Windows Resource source files. By default, for any named component the WindowsResourceSet contains .rc source files under src/${name}/rc.

与其他源码类型一样,你可以配置应该包含在二进制文件中的 windows 资源的位置。
As with other source types, you can configure the location of the windows resources that should in included in the binary.

示例 54.15. 配置 Windows 资源源文件的位置 - Example 54.15. Configuring the location of Windows resource sources

build-resource-only-dll.gradle

sources {
    helloRes {
        rc {
            source {
                srcDirs "src/hello/rc"
            }
            exportedHeaders {
                srcDirs "src/hello/headers"
            }
        }
    }
}

你能够通过提供不包含其他语言源码的 Windows Resource 源文件,来构造纯资源库,并根据需要配置链接器︰
You are able to construct a resource-only library by providing Windows Resource sources with no other language sources, and configure the linker as appropriate:

示例 54.16. 构建纯资源 dll - Example 54.16. Building a resource-only dll

build-resource-only-dll.gradle

libraries {
    helloRes {
        binaries.all {
            rcCompiler.args "/v"
            linker.args "/noentry", "/machine:x86"
        }
    }
}

上面的示例还说明了将额外的命令行参数传给资源编译器的机制。rcCompiler 扩展的类型为 PreprocessingTool
The example above also demonstrates the mechanism of passing extra command-line arguments to the resource compiler. The rcCompiler extension is of type PreprocessingTool.

54.8. 库依赖

54.8. Library Dependencies

本机组件的依赖是导出头文件的二进制库。这些头文件在编译期间使用,而已编译的二进制依赖则是在链接和执行的过程中使用。
Dependencies for native components are binary libraries that export header files. The header files are used during compilation, with the compiled binary dependency being used during linking and execution.

54.8.1. 同一项目的依赖

54.8.1. Dependencies within the same project

一组源可能依赖于同一项目中另一个二进制组件提供的头文件。一个常见的示例是,一个本机可执行组件使用了单独的本机库组件提供的功能。
A set of sources may depend on header files provided by another binary component within the same project. A common example is a native executable component that uses functions provided by a separate native library component.

这样的库依赖可以很方便地提供给与 executable 组件相关联的源集:
Such a library dependency can be easily provided to source set associated with the executable component:

示例 54.17. 向源集提供库依赖 - Example 54.17. Providing a library dependency to the source set

build.gradle

sources {
    main {
        cpp {
            lib libraries.hello
        }
    }
}

另外,一个库依赖可以直接提供给 executableExecutableBinary
Alternatively, a library dependency can be provided directly to the ExecutableBinary for the executable.

示例 54.18. 向二进制文件提供库依赖 - Example 54.18. Providing a library dependency to the binary

build.gradle

executables {
    main {
        binaries.all {
            // Each executable binary produced uses the 'hello' static library binary
            lib libraries.hello.static
        }
    }
}

54.8.2. 项目依赖

54.8.2. Project Dependencies

对于在不同的 Gradle 项目产生的组件,表示方法是类似的。
For a component produced in a different Gradle project, the notation is similar.

示例 54.19. 定义项目依赖 - Example 54.19. Declaring project dependencies

build.gradle

project(":lib") {
    apply plugin: "cpp"
    libraries {
        main {}
    }
    // For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
    binaries.withType(SharedLibraryBinary) {
        if (toolChain in VisualCpp) {
            cppCompiler.define "DLL_EXPORT"
        }
    }
}

project(":exe") {
    apply plugin: "cpp"

    executables {
        main {}
    }

    sources {
        main {
            cpp {
                lib project: ':lib', library: 'main'
            }
        }
    }
}

54.9. 本地二进制变体

54.9. Native Binary Variants

对于每个定义的可执行文件或库,Gradle 能够构建多个不同的本机二进制变体。这样的示例包括调试与发布的二进制文件,32位与64位的二进制文件,以及使用不同的自定义预处理器标志生成的二进制文件。
For each executable or library defined, Gradle is able to build a number of different native binary variants. Examples of different variants include debug vs release binaries, 32-bit vs 64-bit binaries, and binaries produced with different custom preprocessor flags.

由 Gradle 生成的二进制文件可以在构建类型平台风格上区分。对于这些“变体维度”中的每个维度,可以指定一组可用值,也可以针对每一个组件指定这里的一个、多个或全部的值。比如,插件可能定义了一系列支持的平台,但你可以对某个特定组件选择只针对 Windows-x86 构建。
Binaries produced by Gradle can be differentiated on build type, platform and flavor. For each of these 'variant dimensions', it is possible to specify a set of available values as well as target each component at one, some or all of these. For example, a plugin may define a range of support platforms, but you may choose to only target Windows-x86 for a particular component.

54.9.1. 构建类型

54.9.1. Build types

build type 决定了二进制文件的各种非功能性方面,例如是否包含调试信息,或使用什么样的优化级别来编译。典型的构建类型为“debug”和“release”,但项目可以自由定义任意的一组构建类型。
A build type determines various non-functional aspects of a binary, such as whether debug information is included, or what optimisation level the binary is compiled with. Typical build types are 'debug' and 'release', but a project is free to define any set of build types.

示例 54.20. 定义构建类型 - Example 54.20. Defining build types

build.gradle

model {
    buildTypes {
        debug
        release
    }
}

如果在项目中未定义任何构建类型,那么会添加一个默认的“debug”构建类型。
If no build types are defined in a project, then a single, default build type called 'debug' is added.

对于一个构建类型,一个 Gradle 项目通常会为每个工具链定义一组编译器或链接器标志。
For a build type, a Gradle project will typically define a set of compiler/linker flags per tool chain.

示例 54.21. 配置调试的二进制文件 - Example 54.21. Configuring debug binaries

build.gradle

binaries.all {
    if (toolChain in Gcc && buildType == buildTypes.debug) {
        cppCompiler.args "-g"
    }
    if (toolChain in VisualCpp && buildType == buildTypes.debug) {
        cppCompiler.args '/Zi'
        cppCompiler.define 'DEBUG'
        linker.args '/DEBUG'
    }
}

在这个阶段,完全由构建脚本来为每个构建类型配置相关的编译器或链接器标志。未来版本的 Gradle 将自动为任何 'debug' 构建类型包含相应的调试标志,并且还可能了解各个优化级别。

54.9.2. 平台

54.9.2. Platform

通过为每个平台生成一个变体,能够使可执行文件或库构建为可以运行在不同的操作系统及 CPU 架构上。Gradle 把每个操作系统/架构的组合定义为一个 Platform,并且一个项目可以定义任意多个平台。如果在项目中没有定义平台,则会添加一个默认的 'current' 平台。
An executable or library can be built to run on different operating systems and cpu architectures, with a variant being produced for each platform. Gradle defines each OS/architecture combination as a Platform, and a project may define any number of platforms. If no platforms are defined in a project, then a single, default platform 'current' is added.

目前,一个 Platform 由一个定义的操作系统和架构组成。随着我们继续开发 Gradle 的本机二进制支持,将扩展平台的概念,包含诸如 C-runtime 版本,Windows SDK,ABI 等等之类的内容。复杂的构建可能会使用 Gradle 的可扩展性把附加属性应用于每个平台,然后可以查询这些属性,以便指定一个本机二进制文件的特定包含,预处理器宏或编译器参数。

示例 54.22. 定义平台 - Example 54.22. Defining platforms

build.gradle

model {
    platforms {
        x86 {
            architecture "x86"
        }
        x64 {
            architecture "x86_64"
        }
        itanium {
            architecture "ia-64"
        }
    }
}

对于给定的变体,Gradle 将尝试查找能够为目标平台构建的 ToolChain 。所有可用的工具链将按照定义的顺序进行查找。更多细节请参阅下面的工具链部分。
For a given variant, Gradle will attempt to find a ToolChain that is able to build for the target platform. Available tool chains are searched in the order defined. See the tool chain section below for more details.

54.9.3. 风格

54.9.3. Flavor

每个组件都可以有一组命名的 flavor,并且可以为每种风格生成单独的二进制变体。虽然 build typetarget platform 变体维度在 Gradle 中是有定义的含义的,但每个项目都可以自由地定义任意数量的风格,并以任意的方式对它们应用含义。
Each component can have a set of named flavors, and a separate binary variant can be produced for each flavor. While the build type and target platform variant dimensions have a defined meaning in Gradle, each project is free to define any number of flavors and apply meaning to them in any way.

有关组件风格的一个例子是,可以区分组件的“demo”,“paid”和“enterprise”版本,它们是用同样的源码来生成不同功能的二进制文件的。
An example of component flavors might differentiate between 'demo', 'paid' and 'enterprise' editions of the component, where the same set of sources is used to produce binaries with different functions.

示例 54.23. 定义风格 - Example 54.23. Defining flavors

build.gradle

model {
    flavors {
        english
        french
    }
}

libraries {
    hello {
        binaries.all {
            if (flavor == flavors.french) {
                cppCompiler.define "FRENCH"
            }
        }
        source sources.lib
    }
}

在上面的示例中,这个库定义了 'english' 和 'french' 两个风格。当编译 'french' 变体时,定义了一个单独的宏,使其生成不同的二进制文件。
In the example above, a library is defined with a 'english' and 'french' flavor. When compiling the 'french' variant, a separate macro is defined which leads to a different binary being produced.

如果一个组件没有定义任何的风格,那么会使用一个叫“default”的默认风格。
If no flavor is defined for a component, then a single default flavor named 'default' is used.

54.9.4. 为组件选择构建类型,平台和风格

54.9.4. Selecting the build types, platforms and flavors for a component

对于一个默认组件,Gradle 将尝试为项目定义的每个 buildTypeplatformflavor 及它们的每一种组合创建本机二进制变体。通过指定 targetBuildTypestargetPlatformstargetFlavor的集合,是可以在项目的每个组件的基础上重写的。
For a default component, Gradle will attempt to create a native binary variant for each and every combination of buildType, platform and flavor defined for the project. It is possible to override this on a per-component basis, by specifying the set of targetBuildTypes, targetPlatforms and/or targetFlavors.

示例 54.24. 针对特定平台上的组件 - Example 54.24. Targeting a component at particular platforms

build.gradle

executables {
    main {
        targetPlatforms "x86", "x64"
    }
}

在这里你可以看到 TargetedNativeComponent.targetPlatforms() 方法被用于为 executables.main选择一组目标平台。
Here you can see that the TargetedNativeComponent.targetPlatforms() method is used to select the set of platforms to target for executables.main.

在选择 TargetedNativeComponent.targetBuildTypes()TargetedNativeComponent.targetFlavors()上也存在着类似的机制。
A similar mechanism exists for selecting TargetedNativeComponent.targetBuildTypes() and TargetedNativeComponent.targetFlavors().

54.9.5. 构建所有可能的变体

54.9.5. Building all possible variants

当为一个组件定义了一组构建类型,目标平台及风格时,将会为它们的每种可能的组合创建一个 NativeBinary 模型元素。然而,在许多情况下无法构建特定的变体,可能的原因是没有可用于某个特定平台的构建的工具链。
When a set of build types, target platforms, and flavors is defined for a component, a NativeBinary model element is created for every possible combination of these. However, in many cases it is not possible to build a particular variant, perhaps because no tool chain is available to build for a particular platform.

如果一个二进制变体因为某种原因而不能构建,那么与之关联的 NativeBinary 将不再是 buildable。可以使用这个属性来创建一个任务,生成在特定机器上所有可能的变体。
If a binary variant cannot be built for any reason, then the NativeBinary associated with that variant will not be buildable. It is possible to use this property to create a task to generate all possible variants on a particular machine.

示例 54.25. 构建所有可能的变体 - Example 54.25. Building all possible variants

build.gradle

task buildAllExecutables {
    dependsOn binaries.withType(ExecutableBinary).matching {
        it.buildable
    }
}

54.10. 工具链

54.10. Tool chains

单个构建可以利用不同的工具链来构建不同平台的变体。为此,核心的“本机二进制”插件将尝试查找并提供受支持的工具链。不过,一个项目里的工具链集也可以被显式定义,允许配置其他交叉编译器以及指定安装目录。
A single build may utilize different tool chains to build variants for different platforms. To this end, the core 'native-binary' plugins will attempt to locate and make available supported tool chains. However, the set of tool chains for a project may also be explicitly defined, allowing additional cross-compilers to be configured as well as allowing the install directories to be specified.

54.10.1. 定义工具链

54.10.1. Defining tool chains

支持的工具链类型有︰
The supported tool chain types are:

示例 54.26. 定义工具链 - Example 54.26. Defining tool chains

build.gradle

model {
    toolChains {
        visualCpp(VisualCpp) {
            // Specify the installDir if Visual Studio cannot be located by default
            // installDir "C:/Apps/Microsoft Visual Studio 10.0"
        }
        gcc(Gcc) {
            // Uncomment to use a GCC install that is not in the PATH
            // path "/usr/bin/gcc"
        }
        clang(Clang)
    }
}

每个工具链的实现都允许一定程度的配置(更多细节请参阅 API 文档)。
Each tool chain implementation allows for a certain degree of configuration (see the API documentation for more details).

54.10.2. 使用工具链

54.10.2. Using tool chains

我们没有必要也不可能指定应该用于构建的工具链。对于给定的变体,Gradle 将尝试查找能够为目标平台构建的 ToolChain 。可用的工具链是按定义的顺序来查找的。
It is not necessary or possible to specify the tool chain that should be used to build. For a given variant, Gradle will attempt to locate a ToolChain that is able to build for the target platform. Available tool chains are searched in the order defined.

当一个平台没有定义架构或操作系统时,会假定工具链的默认目标。所以如果平台没有为 operatingSystem 定义值的话,Gradle 将找到第一个可以为指定的 architecture 构建的可用工具链。

核心 Gradle 工具链对以下的架构能够开箱即用。在每种情况中,工具链都将针对当前的操作系统。有关其他操作系统交叉编译的信息,请参阅下一节。
The core Gradle tool chains are able to target the following architectures out of the box. In each case, the tool chain will target the current operating system. See the next section for information on cross-compiling for other operating systems.

工具链
Tool Chain
架构
Architectures
GCC x86, x86_64
Clang x86, x86_64
Visual C++ x86, x86_64, ia-64

所以对于在 linux 上运行的 GCC,支持的目标平台为“linux/x86”和“linux/x86_64”;而对于在 Windows 上通过 Cygwin 运行的 GCC,支持的平台则为“windows/x86”和“windows/x86_64”。(Cygwin POSIX 运行时还不能模块为平台的一部分,但以后会实现。)
So for GCC running on linux, the supported target platforms are 'linux/x86' and 'linux/x86_64'. For GCC running on Windows via Cygwin, platforms 'windows/x86' and 'windows/x86_64' are supported. (The Cygwin POSIX runtime is not yet modelled as part of the platform, but will be in the future.)

如果没有为项目定义目标平台,那么所有的二进制文件将针对默认平台“current”进行构建。这个默认平台不指定任何的 architectureoperatingSystem 值,因此会使用第一个可用工具链的默认值。
If no target platforms are defined for a project, then all binaries are built to target a default platform named 'current'. This default platform does not specify any architecture or operatingSystem value, hence using the default values of the first available tool chain.

Gradle 提供了一个钩子,允许构建者控制传给可执行的工具链的确切参数集。这使得构建者能够围绕 Gradle 中的任何限制或做出的假定去使用。这个参数钩子应该被当作是一种“最后手段”的机制,我们更倾向于对基本领域真正地进行建模。
Gradle provides a hook that allows the build author to control the exact set of arguments passed to a tool chain executable. This enables the build author to work around any limitations in Gradle, or assumptions that Gradle makes. The arguments hook should be seen as a 'last-resort' mechanism, with preference given to truly modelling the underlying domain.

示例 54.27. 重新配置工具参数 - Example 54.27. Reconfigure tool arguments

build.gradle

model {
    toolChains {
        visualCpp(VisualCpp) {
            cppCompiler.withArguments { args ->
                args << "-DFRENCH"
            }
        }
        clang(Clang){
            cCompiler.withArguments { args ->
                Collections.replaceAll(args, "CUSTOM", "-DFRENCH")
            }
            linker.withArguments { args ->
                args.remove "CUSTOM"
            }
            staticLibArchiver.withArguments { args ->
                args.remove "CUSTOM"
            }
        }

    }
}

54.10.3. 使用 GCC 进行交叉编译

54.10.3. Cross-compiling with GCC

通过添加对其他目标平台的支持,可以做到使用 GccClang 工具链进行交叉编译。这是通过为工具链指定目标平台来完成的。 对于每个目标平台,都可以指定一个自定义的配置。
Cross-compiling is possible with the Gcc and Clang tool chains, by adding support for additional target platforms. This is done by specifying a target platform for a toolchain. For each targetted platform a custom configuration can be specified.

示例54.28. 定义目标平台 - Example 54.28. Defining target platforms

build.gradle

model {
    toolChains {
        gcc(Gcc) {
            target("arm"){
                cppCompiler.withArguments { args ->
                    args << "-m32"
                }
                linker.withArguments { args ->
                    args << "-m32"
                }
            }
            target("sparc")
        }
    }
    platforms {
        arm {
            architecture "arm"
        }
        sparc {
            architecture "sparc"
        }
    }
}

54.11. Visual Studio IDE 集成

54.11. Visual Studio IDE integration

Gradle 能够为构建中定义的本机组件生成 Visual Studio 项目和解决方案文件。这个功能是通过 visual-studio 插件添加的。对于多项目构建,所有有本机组件的项目都应该应用这个插件。
Gradle has the ability to generate Visual Studio project and solution files for the native components defined in your build. This ability is added by the visual-studio plugin. For a multi-project build, all projects with native components should have this plugin applied.

应用 visual-studio 插件时,会为每个定义的组件创建名称为 ${component.name}VisualStudio 的任务。这个任务将为所命名的组件一个 Visual Studio Solution 文件。这个解决方案将包含该组件的 Visual Studio Project,以及链接至每个依赖二进制文件的项目文件。
When the visual-studio plugin is applied, a task name ${component.name}VisualStudio is created for each defined component. This task will generate a Visual Studio Solution file for the named component. This solution will include a Visual Studio Project for that component, as well as linking to project files for each depended-on binary.

通过由visualStudio 扩展提供的程序化钩子,可以修改所生成的visual studio 文件的内容。更详细的信息,请参考“visual-studio”示例,或参阅 VisualStudioExtension.getProjects()VisualStudioExtension.getSolutions()
The content of the generated visual studio files can be modified via programmatic hooks, provided by the visualStudio extension. Take a look at the 'visual-studio' sample, or see VisualStudioExtension.getProjects() and VisualStudioExtension.getSolutions() for more details.

54.12. CUnit 支持

54.12. CUnit support

Gradle cunit 插件支持在本机二进制项目中编译和执行 CUnit 测试。对于项目中定义的每个 NativeExecutableNativeLibrary ,Gradle 将创建一个匹配的 CUnitTestSuite 组件,名为 ${component.name}Test
The Gradle cunit plugin provides support for compiling and executing CUnit tests in your native-binary project. For each NativeExecutable and NativeLibrary defined in your project, Gradle will create a matching CUnitTestSuite component, named ${component.name}Test.

54.12.1. CUnit 源码

54.12.1. CUnit sources

Gradle 将为项目中的每个 CUnitTestSuite 组件创建名为“cunit”的 CSourceSet。这个源码集应该包含组件源码的 cunit 测试文件。源码文件可以位于约定位置(src/${component.name}Test/cunit),也可以像其他源集一样进行配置。
Gradle will create a CSourceSet named 'cunit' for each CUnitTestSuite component in the project. This source set should contain the cunit test files for the component sources. Source files can be located in the conventional location (src/${component.name}Test/cunit) or can be configured like any other source set.

初始化 CUnit 测试注册和执行测试的作业都由 Gradle 通过某些生成的 CUnit 启动源码来执行。Gradle 将认定并调用一个 void gradle_cunit_register() 函数,这个函数你可以用于配置要执行的实际的 CUnit 套件及测试。
The job of initialising the CUnit test registry and executing the tests is performed by Gradle, via some generated CUnit launcher sources. Gradle will expect and call a function with the signature void gradle_cunit_register() that you can use to configure the actual CUnit suites and tests to execute.

示例 54.29. 注册 CUnit 测试 - Example 54.29. Registering CUnit tests

suite_operators.c

#include <CUnit/Basic.h>
#include "gradle_cunit_register.h"
#include "test_operators.h"

int suite_init(void) {
    return 0;
}

int suite_clean(void) {
    return 0;
}

void gradle_cunit_register() {
    CU_pSuite pSuiteMath = CU_add_suite("operator tests", suite_init, suite_clean);
    CU_add_test(pSuiteMath, "test_plus", test_plus);
    CU_add_test(pSuiteMath, "test_minus", test_minus);
}

由于这一机制,你的 CUnit 源码可能不包含 main方法,因为这会与 Gradle 所提供的方法冲突。

54.12.2. 构建 CUnit 可执行文件

54.12.2. Building CUnit executables

一个 CUnitTestSuite 组件有一个相关联的 NativeExecutableNativeLibrary 组件。对于为主组件配置的每一个 ProjectNativeBinary,在测试套件组件上都会配置一个匹配的 TestSuiteExecutableBinary 。这些测试套件二进制文件可以使用一种与其他二进制实例类似的方式来配置:
A CUnitTestSuite component has an associated NativeExecutable or NativeLibrary component. For each ProjectNativeBinary configured for the main component, a matching TestSuiteExecutableBinary will be configured on the test suite component. These test suite binaries can be configured in a similar way to any other binary instance:

示例 54.30. 注册 CUnit 测试 - Example 54.30. Registering CUnit tests

build.gradle

binaries.withType(TestSuiteExecutableBinary) {
    lib library: "cunit", linkage: "static"

    if (flavor == flavors.failing) {
        cCompiler.define "PLUS_BROKEN"
    }
}

你的项目及生成的启动器所提供的两种 CUnit 源码,都需要核心的 CUnit 头文件和库。目前,这个库依赖项必须由你的项目为每个 TestSuiteExecutableBinary 提供。

54.12.3. 运行 CUnit 测试

54.12.3. Running CUnit tests

对于每个 TestSuiteExecutableBinary, Gradle 将创建一个任务来执行此二进制文件,这个任务将运行所有已注册的 CUnit 测试。 生成的测试结果将位于 {build.dir}/test-results 目录中。
For each TestSuiteExecutableBinary, Gradle will create a task to execute this binary, which will run all of the registered CUnit tests. The generated test results will be located in the ${build.dir}/test-results directory.

示例 54.31. 运行 CUnit 测试 - Example 54.31. Running CUnit tests

build.gradle

apply plugin: "c"
apply plugin: "cunit"

model {
    flavors {
        passing
        failing
    }
    repositories {
        libs(PrebuiltLibraries) {
            cunit {
                headers.srcDir "lib/cunit/2.1-2/include"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("lib/cunit/2.1-2/lib/" + findCUnitLibForPlatform(targetPlatform))
                }
            }
        }
    }
}

libraries {
    operators {}
}
binaries.withType(TestSuiteExecutableBinary) {
    lib library: "cunit", linkage: "static"

    if (flavor == flavors.failing) {
        cCompiler.define "PLUS_BROKEN"
    }
}

注意︰ 此示例的代码可以在 Gradle 的二进制分发包或源码分发包中的 samples/native-binaries/cunit 内找到。
Note: The code for this example can be found at samples/native-binaries/cunit which is in both the binary and source distributions of Gradle.


> gradle -q runFailingOperatorsTestCUnitExe

There were test failures:
  1. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:6  - plus(0, -2) == -2
  2. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:7  - plus(2, 2) == 4

:runFailingOperatorsTestCUnitExe FAILED

BUILD FAILED

Total time: 1 secs

当前对 CUnit 的支持还是相当基本。未来的集成计划包括:
The current support for CUnit is quite rudimentary. Plans for future integration include:

  • 允许使用 javadoc 风格的注解声明测试。

    Allow tests to be declared with javadoc-style annotations.

  • 改进 HTML 报告,类似于 JUnit 那样。

    Improved HTML reporting, similar to that available for JUnit.

  • 实时反馈测试的执行。

    Real-time feedback for test execution.

  • 支持其他测试框架。

    Support for additional test frameworks.