关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qiniudn.com 上的文档为准。如发现翻译有误的地方,将首先在以上两个地方更新。因时间精力问题,博客中发表的译文基本不会同步修改。
另外,目前Gradle1.12版本的文档进入校稿阶段,校稿的方式可以为到该项目https://github.com/msdx/gradledoc 提交issue或是pull request。校稿的结果不只是在此版本更新,也会用于改善Gradle下一版本(2.0)文档的翻译。
Gradle 对构建原生二进制文件的支持目前还是孵化阶段。请务必注意,在以后的 Gradle 版本中,相关的 DSL 和其他配置可能会有所改变。
不同的原生二进制插件添加了对从C++,C,Objective-C,Objective-C++以及汇编源文件构建原生软件组件的支持。虽然对于这种软件开发的空间,已经有许多优秀的构建工具存在,但是Gradle 向开发人员提供的是它的强大特征和灵活性,以及在依赖管理实践上,在JVM开发空间上更为传统的发现。
Gradle 提供了使用不同的工具链执行相同的构建的能力。你可以通过更改操作系统的PATH让其包含所需的工具链编译器,来控制使用哪个工具链。或者,你可以直接配置工具链,就如下面的“原生二进制变种”中描述的。
支持以下的工具链:
操作系统 | 工具链 | 备注 |
Linux | GCC | |
Linux | Clang | |
Mac OS X | GCC | 使用 XCode 中的 GCC。 |
Mac OS X | Clang | 使用 XCode 中的 Clang。 |
Windows | Visual C++ | Windows XP及以上,Visual C++ 2010 及以上版本。 |
Windows | GCC | Windows XP及以上,使用Cygwin的GCC。 |
Windows | MinGW | Windows XP 及以上。 |
一个原生二进制project定义了一组Executable
和Library
组件,每一个的 Gradle 都映射大量的NativeBinary
输出。对于每个定义的executable
或library
,Gradle 添加了具有相同名称的FunctionalSourceSet
。这些功能源码集将为project所支持的每一种语言包含指定语言的源码集。
为构建一个静态或共享的原生库二进制文件,一个Library
组件将添加到libraries
容器中。每个library
组件可以产生至少一个SharedLibraryBinary
和至少一个StaticLibraryBinary
。
示例 54.1. 定义一个库组件
build.gradle
libraries { hello {} }
为构建一个可执行的二进制,Executable
组件添加到executables
容器中,并与源码集相关联。
示例 54.2. 定义可执行组件
build.gradle
executables { main {} }
在许多情况下,一个组件可以产生超过一个的原生二进制文件。基于构建所使用的工具链,提供的编译器/链接器标志,提供的依赖或其他源文件,这些二进制文件可能会发生变化。组件所产生的每个原生二进制称为variant
。下面将详细讨论二进制 variant。
针对由构建所产生的每一个 NativeBinary
,我们构造了一个生命周期任务用于创建二进制,以及一系统的其他做实际工作,如编译,链接或者是装配二进制文件的任务。
组件类型 | 原生二进制文件类型 | 生命周期任务 | 创建二进制文件的位置 |
Executable |
ExecutableBinary |
|
|
Library |
SharedLibraryBinary |
|
|
Library |
StaticLibraryBinary |
|
|
对于每个产生的可执行二进制文件, cpp
插件提供了install${binary.name}
任务,这个任务可以创建该可执行文件及它需要的共享库的开发安装。它允许你运行可执行文件,而无需在其最终的位置安装共享库。
目前,Gradle 支持从C++,C,Assembler,Objective-C 和 Objective-C++源码的任意组合中构建原生二进制文件。一个原生二进制project 将包含一个或多个叫做FunctionalSourceSet
的实例(如"main","test"等),其中每一个都可以包含含有C++,C,Assembler,Objective-C或Objective-C++源代码的LanguageSourceSet
s。
'cpp'
插件提供了C++ 语言的支持。
示例 54.3. cpp 插件
build.gradle
apply plugin: 'cpp'
把 C++ 源码包含到原生二进制文件中,是通过一个 CppSourceSet
来实现的,它定义了一个C++源文件集以及(可选的)一个可导出的头文件(用于library)集默认情况下,对于任何命名的组件,CppSourceSet
包含了在src/${name}/cpp
中的.cpp
源文件,和在src/${name}/headers
中的头文件。
而cpp
插件为每个CppSourceSet
定义了这些默认的位置,且可以扩展或重写这些默认值使能够成为不同的项目布局。
示例 54.4. C++ 源代码集
build.gradle
sources { main { cpp { source { srcDir "src/source" include "**/*.cpp" } } } }
对于名字为“main”的library, src/main/headers
中的文件都被视为"公共"或"导出"的头文件。不应该被导出(而是内部使用)的头文件,应该放在 src/main/cpp
目录(不过要注意,这样的文件应该总是以相对于包含它们的文件这样的一种方式被引用)。
'c'
插件提供了C 语言的支持。
示例 54.5. “C” 插件
build.gradle
apply plugin: 'c'
把 C 源码包含到原生二进制文件中,是通过一个 CSourceSet
来实现的,它定义了一个C源文件集以及(可选的)一个可导出的头文件(用于library)集默认情况下,对于任何命名的组件,CSourceSet
包含了在src/${name}/c
中的.c
源文件,和在src/${name}/headers
中的头文件。
而c
插件为每个CSourceSet
定义了这些默认的位置,且可以扩展或重写这些默认值使能够成为不同的项目布局。
示例 54.4. C 源代码集
build.gradle
sources { hello { c { source { srcDir "src/source" include "**/*.c" } exportedHeaders { srcDir "src/include" } } } }
对于名字为“main”的library, src/main/headers
中的文件都被视为"公共"或"导出"的头文件。不应该被导出(而是内部使用)的头文件,应该放在 src/main/c
目录(不过要注意,这样的文件应该总是以相对于包含它们的文件这样一种方式被引用)。
“assembler”
插件提供了汇编语言的支持。
示例 54.3. assembler 插件
build.gradle
apply plugin: 'assembler'
把汇编源码包含到原生二进制文件中,是通过一个 AssemblerSourceSet
来实现的,它定义了一个汇编源文件集。默认情况下,对于任何命名的组件,AssemblerSourceSet
包含了在src/${name}/asm
中的.s
源文件。
'objective-c'
插件提供了Objective-C 语言的支持。
示例 54.8. “objective-c”插件
build.gradle
apply plugin: 'objective-c'
把Objective-C源码包含到原生二进制文件中,是通过一个 ObjectiveCSourceSet
来实现的,它定义了一个Objective-C源文件集。默认情况下,对于任何命名的组件,ObjectiveCSourceSet
包含了在src/${name}/objectiveC
中的.m
源文件。
'objective-cpp'
插件提供了Objective-C++ 语言的支持。
示例 54.9. “objective-cpp” 插件
build.gradle
apply plugin: 'objective-cpp'
把Objective-C++源码包含到原生二进制文件中,是通过一个 ObjectiveCppSourceSet
来实现的,它定义了一个Objective-C++源文件集。默认情况下,对于任何命名的组件,ObjectiveCppSourceSet
包含了在src/${name}/objectiveCpp
中的.mm
源文件。
每一个产生的二进制文件都和一系列的编译器和链接器设置相关联,这些设置包含了命令行参数,以及宏定义。这些设置可以应用于所有的二进制文件,单个二进制文件,或选择性地应用于基于某些条件的一组二进制文件。
示例 54.10. 应用于所有二进制文件的设置
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
关联,允许设置基于此值进行针对性的配置。
让设置应用于指定类型的所有二进制文件很简单:
示例 54.11. 应用于所有共享库的设置
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" } }
此外,还可以指定设置应用于某个特定的executable
或 library
组件 产生的所有二进制文件:
示例 54.12. 应用于“main”可执行组件所产生的所有二进制文件的设置
build.gradle
executables { main { binaries.all { if (toolChain in VisualCpp) { assembler.args "/Zi" } else { assembler.args "-g" } } } }
上面的例子将会把提供的配置应用到所有构建的 executable
二进制文件。
同样,也可以为某种特定类型的组件,把设置指向目标二进制文件:例如所有main library
组件的shared libraries
。
示例 54.13. 仅应用于“main”library组件所产生的共享库的设置
build.gradle
libraries { main { binaries.withType(SharedLibraryBinary) { // Define a preprocessor macro that only applies to shared libraries cppCompiler.define "DLL_EXPORT" } } }
当使用VisualCpp
工具链时,Gradle 时能够编译Window Resource (rc
) 文件并将它们链接到本地的二进制文件。这个功能是由'windows-resources'
插件所提供的。
示例 54.14. 'windows-resources' 插件
build.gradle
apply plugin: 'windows-resources'
将 Windows 资源包含进本机二进制文件中,是通过一个WindowsResourceSet
提供的,它定义了一组Windows Resource源文件。默认情况下,WindowsResourceSet
为所有的命名组件包含了在src/${name}/rc
下的.rc
源文件。
与其他源文件类型一样,您可以配置把windows 资源的位置包含进二进制文件中。
示例 54.15. 配置 Windows 资源源文件的位置
build-resource-only-dll.gradle
sources { helloRes { rc { source { srcDirs "src/hello/rc" } exportedHeaders { srcDirs "src/hello/headers" } } } }
你能够通过提供没有任何其他语言来源的 Windows Resource 源文件,来构造纯资源库,并适当地配置链接器︰
示例 54.16. 构建一个纯资源 dll
build-resource-only-dll.gradle
libraries { helloRes { binaries.all { rcCompiler.args "/v" linker.args "/noentry", "/machine:x86" } } }
上面的示例还演示了将额外的命令行参数传递给资源编译器的机制。rcCompiler
扩展是PreprocessingTool
类型。
C++ 项目的依赖是导出头文件的二进制库。这些头文件在编译期间使用,而编译的二进制依赖则在链接过程中使用。
一组源文件可能依赖于在同一个项目中由另一个二进制组件提供的头文件。一个常见的例子是一个本地可执行组件,使用了由一个单独的本地库组件提供的功能。
这样的库依赖可以很方便地提供给source set关联上executable
组件。
示例 54.17. 向source set提供库依赖
build.gradle
sources { main { cpp { lib libraries.hello } } }
另外,一个库依赖项可以直接提供给ExecutableBinary
的executable
。
示例 54.18. 向二进制文件提供库依赖
build.gradle
executables { main { binaries.all { // Each executable binary produced uses the 'hello' static library binary lib libraries.hello.static } } }
对于在不同的 Gradle 项目产生的组件,notation是类似的。
示例 54.19. 定义项目依赖
build.gradle
project(":lib") { apply plugin: "cpp" libraries { main {} } } project(":exe") { apply plugin: "cpp" executables { main {} } sources { main { cpp { lib project: ':lib', library: 'main' } } } }
对于每个定义的可执行文件或库,Gradle 能够构建多个不同的本机二进制变种。这样的例子包括debug及release的二进制文件,32位及64位的二进制文件,以及使用不同的自定义预处理标志生成的二进制文件。
Gradle 产生的二进制文件可以区分 构建类型, 平台以及 flavor。对于这里的每一个“变种维度”,它可以指定一组可用的值,并且针对每个组件使用这里的一个或多个或全部的值。例如,一个插件可以定义一系列的支持平台,但你可以选择某个特定组件只构建Windows-x86平台。
一个 build type
确定了一个二进制文件的各种非功能性方面,比如是否包含调试信息,或者使用什么样的优化级别来编译二进制文件。典型的构建类型是“debug”和“release”,但一个project可以自由定义任意的构建类型。
示例 54.20. 定义构建类型
build.gradle
model { buildTypes { debug release } }
如果在project中没有定义任何构建类型,那么会有一个默认的“debug”构建类型被加进去。
对于一个构建类型,Gradle project 通常会定义一组每个工具链的编译器/链接器标志。
示例 54.21. 配置debug二进制文件
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' } }
通过为每个平台生成一个变种,能够使一个可执行文件或库构建为可以运行在不同的操作系统及CPU架构上。Gradle 把每一个系统架构组合定义为一个 Platform
,一个project可以定义多个platforms。如果在project里没有定义任何平台,那么会添加一个默认的“current”平台。
Platform
由一个定义的操作系统和架构构成。随着我们继续开发 Gradle 的本地二进制支持,将扩展Platform的概念,包括 C-runtime版本,Windows SDK,ABI,等等。复杂的构建,可能使用 Gradle 的扩展性来把附加属性应用到每个platform中,然后可以查询为一个本地二进制包含了哪些特别指定的预处理器或者是编译器参数。
示例 54.22. 定义platform
build.gradle
model { platforms { x86 { architecture "x86" } x64 { architecture "x86_64" } itanium { architecture "ia-64" } } }
对于给定的变种,Gradle 将尝试查找能够构建目标平台的ToolChain
。可用的工具链将按照定义的顺序进行查找。更多的细节请参阅下面的工具链部分。
每个组件都可以有一组flavors
,并且能为每一个flavor生成一个单独的二进制变种。在Gradle中 build type
和 target platform
变种维度是有一个确定的定义的,而每一个project都可以自由地定义数量的flavor并且用任何方式去应用它们的意义。
一个关于组件flavor的例子是可以区分组件的“demo”,“paid”和“enterprise”版本,它们都用同样的源码来生成不同功能的二进制文件。
示例 54.23. 定义flavors
build.gradle
model { flavors { english french } } libraries { hello { binaries.all { if (flavor == flavors.french) { cppCompiler.define "FRENCH" } } source sources.lib } }
在上面的示例中,library 定义了“english”和“french”两个flavor。当编译“french”变种时,会定义一个单独的宏,以产生不同的二进制文件。
如果一个组件没有定义任何的flavor,那么会使用一个默认的“default”的flavor。
对于一个默认的组件, Gradle会尝试为这个project所定义的每一个buildType
, platform
和 flavor
,以及它们的每一种组合,创建一个本地二进制变种。通过指定的 targetBuildTypes
, targetPlatforms
或 targetFlavors
,是可以在每一个组件的基础上进行重写的。
示例 54.24. 针对一个组件的特定平台
build.gradle
executables { main { targetPlatforms "x86", "x64" } }
在这里你可以看到 TargetedNativeComponent.targetPlatforms()
方法被用于为 executables.main
选择一组平台。
在选择 TargetedNativeComponent.targetBuildTypes()
and和TargetedNativeComponent.targetFlavors()
上也有类似的机制。
当为一个组件定义了一组构建类型,目标平台,以及flavor时,将会为它们的每一种可能的组合创建一个NativeBinary
模型元素。然而,在许多情况下是不可能构建一个特定的变种的,可能的原因是某个特定的平台没有可用的工具链。
如果一个二进制变种因为某些原因不能构建,那么与之关联的NativeBinary
将不会是buildable
。可以用这个属性来创建一个任务,生成在某一特定计算机上所有可能的变种。
示例 54.25. 构建所有可能的变种
build.gradle
task buildAllExecutables { dependsOn binaries.withType(ExecutableBinary).matching { it.buildable } }
一个构建可以使用不同的工具链来构建不同平台的变种。为此,核心的“native-binary”将尝试查找并使支持的工具链可用。不过,一个项目里的一组工具链也可以被显示地定义,允许配置额外的交叉编译器以及指定安装目录。
支持的工具链类型有︰
Gcc
Clang
VisualCpp
示例 54.26. 定义工具链
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文档)
指定构建所使用的工具链是不必要和可能的。对于给定的变种,Gradle 将尝试查找能够构建目标平台的ToolChain
。可用的工具链将按照定义的顺序进行查找。
operatingSystem
定义一个值的话,Gradle 将会找到第一个可以用来构建这个指定 architecture
的工具链。
核心 Gradle 工具链针对以下的架构能够开箱即用。在每种情况中,工具链将针对当前的操作系统。关于其他操作系统的交叉编译的信息,可以参考下一节。
工具链 | 架构 |
GCC | x86, x86_64 |
Clang | x86, x86_64 |
Visual C++ | x86, x86_64, ia-64 |
所以对于linux上运行的GCC,支持的目标平台是“linux/x86”和“linux/x86_64”。对于通过Cygwin运行在Windows上的GCC,则支持“windows/x86”和“windows/x86_64”。(Cywgin运行时还不能模拟为Platform的一部分,但以后将会实现。)
如果在project里没有定义任何平台,那么所有的二进制文件会针对一个默认平台“current”进行构建。该默认平台不指定任何architecture
或operatingSystem
的值,因此会使用第一个可用的工具链的默认值。
使用 Gcc
和 Clang
工具链,通过 以编程方式添加附加的目标平台的支持,是可以做到交叉编译的。它通过 PlatformConfigurableToolChain
API来完成。每个添加的 TargetPlatformConfiguration
定义了对一个特定的目标平台的支持,并且提供了针对该平台所需要的额外的工具参数。
Gradle 可以为在你的构建中定义的本地组件生成生成 Visual Studio 项目及解决方案文件。这个功能是通过 visual-studio
插件添加的。对于多项目构建,所有带有本地组件的project都应该应用这个插件。
当应用 visual-studio
插件后,会为每一个定义的组件创建一个名为 ${component.name}VisualStudio
的任务。这个任务会为所命名的组件生成一个 Visual Studio Solution 文件。这个方案包含了一个该组件的 Visual Studio Project ,并且为每一个依赖的二进制文件链接到项目文件中。
通过由visualStudio
提供的编程hook,可以修改所生成的visual studio文件的内容, 更详细的信息,可以参考“visual-studio”例子,或者参阅 VisualStudioExtension.getProjects()
及 VisualStudioExtension.getSolutions()
。
Gradle cunit
插件向你的native-binary项目提供了编译及执行CUnit 测试的支持。对于在你的项目中定义的每一个 Executable
和 Library
,Gradle将创建一个匹配的 CUnitTestSuite
组件,名字为${component.name}Test
。
Gradle将为项目中的每一个 CUnitTestSuite
组件创建一个名字为“cunit”的CSourceSet
。这个源码集应包含组件源码的 cunit 测试文件。源文件可以位于约定的位置(src/${component.name}Test/cunit
),或者是像其他源集一样配置到别的地方。
初始化 CUnit 测试注册以及执行这些测试的工作,都由 Gradle 通过一些生成的 CUnit 启动器源码来执行。Gradle 将认定和调用一个void gradle_cunit_register()
函数,这个函数你可以用于配置实际的CUnit套件以及要执行的测试。
示例 54.27. 注册 CUnit 测试
suite_operators.c
#include#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); }
main
方法,因为这会与 Gradle 所提供的方法产生冲突。
一个 CUnitTestSuite
组件会有一个相关联的 Executable
或 Library
组件。对于为main 组件配置的每一个ProjectNativeBinary
,在测试套件组件上都会配置一个匹配的 TestSuiteExecutableBinary
。这些测试套件二进制文件可以以一种类似的方式配置到任何其他二进制的实例︰
示例 54.28. 注册 CUnit 测试
build.gradle
binaries.withType(TestSuiteExecutableBinary) { lib library: "cunit", linkage: "static" if (flavor == flavors.failing) { cCompiler.define "PLUS_BROKEN" } }
TestSuiteExecutableBinary
提供。
对于每个TestSuiteExecutableBinary
,Gradle 将创建一个任务来执行此二进制文件,这项任务将运行所有注册的 CUnit 测试。生成的测试结果将位于
目录。${build.dir}
/test-results
示例 54.29. 运行 CUnit 测试
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 的 binary 或 source 分发包的samples/native-binaries/cunit
中找到。。
> 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 的支持还是相当简陋。未来的集成计划包括:
允许测试声明为 javadoc 风格的注解。
改进 HTML 报告,类似于 JUnit 提供的那样。
测试执行的的实时反馈。
支持另外的测试框架。