建立原生軟體
功能
原生軟體外掛程式提供
-
在 Windows、Linux、macOS 和其他平台上建置原生函式庫和應用程式的支援。
-
支援多種原始語言。
-
支援為不同的架構、作業系統或任何目的建立不同變體的相同軟體。
-
遞增式平行編譯、預編譯標頭。
-
原生軟體元件之間的相依性管理。
-
單元測試執行。
-
產生 Visual Studio 解决方案和專案檔案。
-
與各種工具鏈深度整合,包括已安裝工具鏈的偵測。
支援的語言
目前支援下列原始碼語言
-
C
-
C++
-
Objective-C
-
Objective-C++
-
組合語言
-
Windows 資源
工具鏈支援
Gradle 提供使用不同工具鏈執行相同建置的功能。當您建置原生二進位檔案時,Gradle 會嘗試在您的機器上找到可以建置二進位檔案的已安裝工具鏈。您可以微調確切的運作方式,請參閱 工具鏈支援 以取得詳細資料。
支援下列工具鏈
作業系統 | 工具鏈 | 備註 |
---|---|---|
Linux |
||
Linux |
||
macOS |
XCode |
使用與 XCode 捆綁的 Clang 工具鏈。 |
Windows |
Windows XP 及更新版本,Visual C++ 2010/2012/2013/2015/2017/2019。 |
|
Windows |
Windows XP 及更新版本。 |
|
Windows |
Windows XP 及更新版本。 |
下列工具鏈未經官方支援。它們通常運作良好,但未持續測試
作業系統 | 工具鏈 | 備註 |
---|---|---|
macOS |
GCC 來自 Macports |
|
macOS |
Clang 來自 Macports |
|
類 UNIX |
||
類 UNIX |
工具鏈安裝
請注意,如果您使用 GCC,則目前需要安裝 C++ 支援,即使您不是從 C++ 原始碼建置。此限制將在未來的 Gradle 版本中移除。 |
若要建置原生軟體,您需要安裝相容的工具鏈
Windows
若要在 Windows 上建置,請安裝相容版本的 Visual Studio。原生外掛程式會偵測 Visual Studio 安裝並選取最新版本。無需使用環境變數或批次指令碼。這在 Cygwin shell 或 Windows 命令列中都能正常運作。
或者,您可以使用 GCC 或 MinGW 安裝 Cygwin。目前不支援 Clang。
macOS
要在 macOS 上進行建置,您應該安裝 XCode。原生外掛程式會使用系統路徑來偵測 XCode 安裝。
原生外掛程式也可以搭配 Macports 內建的 GCC 和 Clang 使用。若要使用其中一個 Macports 工具鏈,您需要使用 port select
指令將該工具鏈設為預設值,並將 Macports 加入系統路徑。
Linux
要在 Linux 上進行建置,請安裝相容版本的 GCC 或 Clang。原生外掛程式會使用系統路徑來偵測 GCC 或 Clang。
原生軟體模型
原生軟體模型建置於 Gradle 基本軟體模型上。
若要使用 Gradle 建置原生軟體,您的專案應該定義一個或多個原生元件。每個元件代表 Gradle 應該建置的可執行檔或函式庫。一個專案可以定義任意數量的元件。Gradle 預設不會定義任何元件。
對於每個元件,Gradle 會針對元件可以建置的每種語言定義一個來源組。來源組基本上只是一個包含來源檔案的來源目錄集。例如,當您套用 c
外掛程式並定義一個稱為 helloworld
的函式庫時,Gradle 會預設定義一個包含 src/helloworld/c
目錄中的 C 來源檔案的來源組。它會使用這些來源檔案來建置 helloworld
函式庫。這會在下方更詳細地說明。
對於每個元件,Gradle 會定義一個或多個二進位檔作為輸出。若要建置二進位檔,Gradle 會取得為元件定義的來源檔案,針對來源語言適當地編譯它們,並將結果連結到二進位檔檔案中。對於可執行元件,Gradle 可以產生可執行二進位檔檔案。對於函式庫元件,Gradle 可以產生靜態和共用函式庫二進位檔檔案。例如,當您定義一個稱為 helloworld
的函式庫並在 Linux 上建置時,Gradle 會預設產生 libhelloworld.so
和 libhelloworld.a
二進位檔。
在許多情況下,一個元件可以產生多個二進位檔。這些二進位檔可能會根據用於建置的工具鏈、提供的編譯器/連結器旗標、提供的相依性或提供的額外原始檔而有所不同。為元件產生的每個原生二進位檔稱為變體。二進位檔變體在下面詳細討論。
平行編譯
預設情況下,Gradle 使用單一建置工作者池來同時編譯和連結原生元件。不需要任何特殊設定即可啟用同時建置。
預設情況下,工作者池大小由建置機器上的可用處理器數量決定 (如報告給建置 JVM)。若要明確設定工作者數量,請使用 --max-workers
命令列選項或 org.gradle.workers.max
系統屬性。通常不需要變更此設定的預設值。
建置工作者池會在所有建置工作中共享。這表示在使用 平行專案執行 時,同時個別編譯作業的最大數量不會增加。例如,如果建置機器有 4 個處理器核心,且有 10 個專案平行編譯,Gradle 只會使用 4 個工作者,而不是 40 個。
建置函式庫
若要建置靜態或共用原生函式庫,請在 components
容器中定義函式庫元件。下列範例定義一個稱為 hello
的函式庫
範例:定義函式庫元件
model {
components {
hello(NativeLibrarySpec)
}
}
函式庫元件使用 NativeLibrarySpec 表示。每個函式庫元件至少可以產生一個共用函式庫二進位檔 (SharedLibraryBinarySpec) 和至少一個靜態函式庫二進位檔 (StaticLibraryBinarySpec).
建置可執行檔
若要建置原生可執行檔,請在 components
容器中定義可執行檔元件。下列範例定義一個稱為 main
的可執行檔
範例:定義可執行檔元件
model {
components {
main(NativeExecutableSpec) {
sources {
c.lib library: "hello"
}
}
}
}
可執行檔元件使用 NativeExecutableSpec 表示。每個可執行檔元件至少可以產生一個可執行檔二進位檔 (NativeExecutableBinarySpec).
對於定義的每個元件,Gradle 會新增一個具有相同名稱的 FunctionalSourceSet。這些功能性原始檔集中的每個都會包含專案支援的每種語言的語言特定原始檔集。
組裝或建置相依項
有時,您可能需要組建(編譯和連結)或建置(編譯、連結和測試)元件或二進位檔及其依賴項(依賴於元件或二進位檔的事項)。原生軟體模式提供可啟用此功能的任務。首先,依賴元件報告提供有關各元件之間關係的深入資訊。其次,建置和組建依賴項任務讓您能一步驟組建或建置元件及其依賴項。
在以下範例中,建置檔案定義 OpenSSL
為 libUtil
的依賴項,而 libUtil
為 LinuxApp
和 WindowsApp
的依賴項。測試套件的處理方式類似。依賴項可視為反向依賴項。

藉由反向追蹤依賴項,您可以看到 LinuxApp 和 WindowsApp 是 libUtil 的依賴項。當 libUtil 變更時,Gradle 將需要重新編譯或重新連結 LinuxApp 和 WindowsApp 。
|
當您組建元件的依賴項時,將編譯和連結元件及其所有依賴項,包括任何測試套件二進位檔。Gradle 的最新檢查用於僅在有變更時編譯或連結。例如,如果您以不會影響專案標頭的方式變更原始檔,Gradle 將能夠略過依賴元件的編譯,而只需要重新連結新函式庫。組建元件時不會執行測試。
當您建置元件的依賴項時,將編譯、連結並檢查元件及其所有依賴二進位檔。檢查元件表示執行任何檢查任務,包括執行任何測試套件,因此建置元件時會執行測試。
在以下各節中,我們將示範使用 assembleDependents*
、buildDependents*
和 dependentComponents
任務,並使用包含 CUnit 測試套件的範例建置。範例的建置指令碼如下
範例:範例建置
plugins {
id 'c'
id 'cunit-test-suite'
}
model {
flavors {
passing
failing
}
platforms {
x86 {
if (operatingSystem.macOsX) {
architecture "x64"
} else {
architecture "x86"
}
}
}
components {
operators(NativeLibrarySpec) {
targetPlatform "x86"
}
}
testSuites {
operatorsTest(CUnitTestSuiteSpec) {
testing $.components.operators
}
}
}
依賴元件報告
Gradle 提供一份報告,您可以從命令列執行,以顯示專案中元件的圖表,以及依賴於這些元件的元件。以下是對範例專案執行 gradle dependentComponents
的範例
範例:依賴元件報告
gradle dependentComponents
的輸出> gradle dependentComponents > Task :dependentComponents ------------------------------------------------------------ Root project 'cunit' ------------------------------------------------------------ operators - Components that depend on native library 'operators' +--- operators:failingSharedLibrary +--- operators:failingStaticLibrary +--- operators:passingSharedLibrary \--- operators:passingStaticLibrary Some test suites were not shown, use --test-suites or --all to show them. BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
請參閱 DependentComponentsReport API 文件以取得更多詳細資訊。 |
預設情況下,非可建置二進位檔和測試套件會從報告中隱藏。dependentComponents
任務提供選項,讓你能夠使用 --all
選項查看所有依賴項
範例:依賴項組件報告
gradle dependentComponents --all
的輸出> gradle dependentComponents --all > Task :dependentComponents ------------------------------------------------------------ Root project 'cunit' ------------------------------------------------------------ operators - Components that depend on native library 'operators' +--- operators:failingSharedLibrary +--- operators:failingStaticLibrary | \--- operatorsTest:failingCUnitExe (t) +--- operators:passingSharedLibrary \--- operators:passingStaticLibrary \--- operatorsTest:passingCUnitExe (t) operatorsTest - Components that depend on Cunit test suite 'operatorsTest' +--- operatorsTest:failingCUnitExe (t) \--- operatorsTest:passingCUnitExe (t) (t) - Test suite binary BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
以下是 operators
組件的對應報告,顯示其所有二進位檔的依賴項
範例:依賴於 operators 組件的組件報告
gradle dependentComponents --component operators
的輸出> gradle dependentComponents --component operators > Task :dependentComponents ------------------------------------------------------------ Root project 'cunit' ------------------------------------------------------------ operators - Components that depend on native library 'operators' +--- operators:failingSharedLibrary +--- operators:failingStaticLibrary +--- operators:passingSharedLibrary \--- operators:passingStaticLibrary Some test suites were not shown, use --test-suites or --all to show them. BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
以下是 operators
組件的對應報告,顯示其所有二進位檔的依賴項,包括測試套件
範例:依賴於 operators 組件的組件報告,包括測試套件
gradle dependentComponents --test-suites --component operators
的輸出> gradle dependentComponents --test-suites --component operators > Task :dependentComponents ------------------------------------------------------------ Root project 'cunit' ------------------------------------------------------------ operators - Components that depend on native library 'operators' +--- operators:failingSharedLibrary +--- operators:failingStaticLibrary | \--- operatorsTest:failingCUnitExe (t) +--- operators:passingSharedLibrary \--- operators:passingStaticLibrary \--- operatorsTest:passingCUnitExe (t) (t) - Test suite binary BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
此外,--non-binaries
選項會在報告中顯示非可建置二進位檔,--no-non-buildable
則會隱藏它們。類似地,--test-suites
選項會顯示測試套件,而 --no-test-suites
則會隱藏它們。--no-all
選項會從報告中隱藏非可建置二進位檔和測試套件。
組建依賴項
對於每個 NativeBinarySpec,Gradle 會建立一個名為 assembleDependents${component.name}${binary.variant}
的任務,用於組建(編譯和連結)二進位檔及其所有依賴二進位檔。
對於每個 NativeComponentSpec,Gradle 會建立一個名為 assembleDependents${component.name}
的任務,用於組建組件的所有二進位檔及其所有依賴二進位檔。
例如,要組建「operators」組件的「static」函式庫二進位檔的「passing」風味的依賴項,你會執行 assembleDependentsOperatorsPassingStaticLibrary
任務
範例:組建依賴於 operators 組件的 passing/static 二進位檔的組件
gradle assembleDependentsOperatorsPassingStaticLibrary --max-workers=1
的輸出> gradle assembleDependentsOperatorsPassingStaticLibrary --max-workers=1 > Task :compileOperatorsTestPassingCUnitExeOperatorsC > Task :operatorsTestCUnitLauncher > Task :compileOperatorsTestPassingCUnitExeOperatorsTestC > Task :compileOperatorsTestPassingCUnitExeOperatorsTestCunitLauncher > Task :linkOperatorsTestPassingCUnitExe > Task :operatorsTestPassingCUnitExe > Task :assembleDependentsOperatorsTestPassingCUnitExe > Task :compileOperatorsPassingStaticLibraryOperatorsC > Task :createOperatorsPassingStaticLibrary > Task :operatorsPassingStaticLibrary > Task :assembleDependentsOperatorsPassingStaticLibrary BUILD SUCCESSFUL in 0s 7 actionable tasks: 7 executed
在以上的輸出中,目標二進位檔會組建,依賴於它的測試套件二進位檔也會組建。
你也可以使用對應的組件任務(例如 assembleDependentsOperators
)組建組件的所有依賴項(即其所有二進位檔/變異)。如果你有許多建置類型、風味和平台的組合,而且想要組建所有這些組合,這會很有用。
建置依賴項
對於每個 NativeBinarySpec,Gradle 會建立一個名為 buildDependents${component.name}${binary.variant}
的工作,用於建置(編譯、連結和檢查)二進位檔及其所有相依的二進位檔。
對於每個 NativeComponentSpec,Gradle 會建立一個名為 buildDependents${component.name}
的工作,用於建置該元件的所有二進位檔及其所有相依的二進位檔。
例如,若要建置「operators」元件的「static」程式庫二進位檔的「passing」風味的相依項,您會執行 buildDependentsOperatorsPassingStaticLibrary
工作
範例:建置相依於 operators 元件的 passing/static 二進位檔的元件
gradle buildDependentsOperatorsPassingStaticLibrary --max-workers=1
的輸出> gradle buildDependentsOperatorsPassingStaticLibrary --max-workers=1 > Task :compileOperatorsTestPassingCUnitExeOperatorsC > Task :operatorsTestCUnitLauncher > Task :compileOperatorsTestPassingCUnitExeOperatorsTestC > Task :compileOperatorsTestPassingCUnitExeOperatorsTestCunitLauncher > Task :linkOperatorsTestPassingCUnitExe > Task :operatorsTestPassingCUnitExe > Task :installOperatorsTestPassingCUnitExe > Task :runOperatorsTestPassingCUnitExe > Task :checkOperatorsTestPassingCUnitExe > Task :buildDependentsOperatorsTestPassingCUnitExe > Task :compileOperatorsPassingStaticLibraryOperatorsC > Task :createOperatorsPassingStaticLibrary > Task :operatorsPassingStaticLibrary > Task :buildDependentsOperatorsPassingStaticLibrary BUILD SUCCESSFUL in 0s 9 actionable tasks: 9 executed
在以上的輸出中,目標二進位檔以及相依於它的測試套件二進位檔已建置完成,且測試套件已執行完畢。
您也可以使用對應的元件工作建置元件的所有相依項(也就是它的所有二進位檔/變異),例如 buildDependentsOperators
。
工作
對於每個建置中可以產生的 NativeBinarySpec,會建構一個單一的生命週期工作,可用於建立該二進位檔,以及一組執行編譯、連結或組裝二進位檔實際工作的其他工作。
${component.name}Executable
-
- 元件類型
- 原生二進位檔類型
- 已建立二進位檔的位置
-
${project.layout.buildDirectory}/exe/${component.name}/${component.name}
${component.name}SharedLibrary
-
- 元件類型
- 原生二進位檔類型
- 已建立二進位檔的位置
-
${project.layout.buildDirectory}/libs/${component.name}/shared/lib${component.name}.so
${component.name}StaticLibrary
-
- 元件類型
- 原生二進位檔類型
- 已建立二進位檔的位置
-
${project.layout.buildDirectory}/libs/${component.name}/static/${component.name}.a
檢查工作
對於每個建置中可以產生的 NativeBinarySpec,會建構一個單一的檢查工作,可用於組裝和檢查該二進位檔。
check${component.name}Executable
-
- 元件類型
- 原生二進位檔類型
check${component.name}SharedLibrary
-
- 元件類型
- 原生二進位檔類型
check${component.name}StaticLibrary
-
- 元件類型
- 原生二進位檔類型
內建的 check
工作相依於專案中所有二進位檔的檢查工作。在沒有 CUnit 或 GoogleTest 外掛的情況下,二進位檔檢查工作只會相依於組裝二進位檔的生命週期工作,請參閱 原生工作。
當套用 CUnit 或 GoogleTest 外掛程式時,執行元件測試套件的任務會自動連接到適當的檢查任務。
您也可以新增自訂檢查任務,如下所示
範例:新增自訂檢查任務
plugins {
id "cpp"
}
// You don't need to apply the plugin below if you're already using CUnit or GoogleTest support
apply plugin: TestingModelBasePlugin
tasks.register('myCustomCheck') {
doLast {
println 'Executing my custom check'
}
}
model {
components {
hello(NativeLibrarySpec) {
binaries.all {
// Register our custom check task to all binaries of this component
checkedBy $.tasks.myCustomCheck
}
}
}
}
現在,執行 check
或任何 hello
二進位檔的檢查任務,都會執行自訂檢查任務
範例:執行給定二進位檔的檢查
gradle checkHelloSharedLibrary
的輸出> gradle checkHelloSharedLibrary > Task :myCustomCheck Executing my custom check > Task :checkHelloSharedLibrary BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
使用共用函式庫
對於產生的每個可執行二進位檔,cpp
外掛程式會提供一個 install${binary.name}
任務,用來建立可執行檔的開發安裝,以及它需要的共用函式庫。這讓您可以在不需將共用函式庫安裝在最終位置的情況下執行可執行檔。
找出更多關於專案的資訊
Gradle 提供一個報告,您可以從命令列執行,顯示專案產生的元件和二進位檔的一些詳細資訊。若要使用此報告,只要執行 gradle components
。以下是執行此報告的一個範例,針對其中一個範例專案
範例:元件報告
gradle components
的輸出> gradle components > Task :components ------------------------------------------------------------ Root project 'cpp' ------------------------------------------------------------ Native library 'hello' ---------------------- Source sets C++ source 'hello:cpp' srcDir: src/hello/cpp Binaries Shared library 'hello:sharedLibrary' build using task: :helloSharedLibrary build type: build type 'debug' flavor: flavor 'default' target platform: platform 'current' tool chain: Tool chain 'clang' (Clang) shared library file: build/libs/hello/shared/libhello.dylib Static library 'hello:staticLibrary' build using task: :helloStaticLibrary build type: build type 'debug' flavor: flavor 'default' target platform: platform 'current' tool chain: Tool chain 'clang' (Clang) static library file: build/libs/hello/static/libhello.a Native executable 'main' ------------------------ Source sets C++ source 'main:cpp' srcDir: src/main/cpp Binaries Executable 'main:executable' build using task: :mainExecutable install using task: :installMainExecutable build type: build type 'debug' flavor: flavor 'default' target platform: platform 'current' tool chain: Tool chain 'clang' (Clang) executable file: build/exe/main/main Note: currently not all plugins register their components, so some components may not be visible here. BUILD SUCCESSFUL in 0s 1 actionable task: 1 executed
語言支援
目前,Gradle 支援從下列任何組合的原始語言建立原生軟體。原生二進位檔專案將包含一個或多個已命名 FunctionalSourceSet
實例(例如「main」、「test」等),每個實例都可以包含 LanguageSourceSet
,其中包含原始檔案,每個語言一個。
-
C
-
C++
-
Objective-C
-
Objective-C++
-
組合語言
-
Windows 資源
C++ 原始檔
C++ 語言支援是透過 'cpp'
外掛程式提供的。
範例:'cpp' 外掛程式
plugins {
id 'cpp'
}
要包含在原生二進位檔中的 C++ 原始檔是透過 CppSourceSet 提供的,它定義一組 C++ 原始檔,並選擇性地定義一組匯出的標頭檔(用於函式庫)。預設情況下,對於任何已命名元件,CppSourceSet 都包含 src/${name}/cpp
中的 .cpp
原始檔,以及 src/${name}/headers
中的標頭檔。
雖然 cpp
外掛程式為每個 CppSourceSet 定義這些預設位置,但可以延伸或覆寫這些預設值,以允許不同的專案配置。
範例:C++ 原始檔組
sources {
cpp {
source {
srcDir "src/source"
include "**/*.cpp"
}
}
}
對於名為「main」的函式庫,src/main/headers
中的標頭檔會被視為「公開」或「匯出」標頭檔。不應匯出的標頭檔應放置在 src/main/cpp
目錄中(但請注意,此類標頭檔應始終以相對於包含它們的檔案的方式參照)。
C 來源
C 語言支援是透過 'c'
外掛程式來提供的。
範例:'c' 外掛程式
plugins {
id 'c'
}
要包含在原生二進位檔中的 C 來源是透過 CSourceSet 提供的,它定義一組 C 來源檔案,並可選擇一組匯出的標頭檔 (用於函式庫)。預設情況下,對於任何命名元件,CSourceSet 都包含 src/${name}/c
中的 .c
來源檔案,以及 src/${name}/headers
中的標頭檔。
雖然 c
外掛程式為每個 CSourceSet 定義了這些預設位置,但可以延伸或覆寫這些預設值,以允許不同的專案配置。
範例:C 來源組
sources {
c {
source {
srcDir "src/source"
include "**/*.c"
}
exportedHeaders {
srcDir "src/include"
}
}
}
對於名為 'main' 的函式庫,src/main/headers
中的標頭檔會被視為「公開」或「匯出」標頭。不應匯出的標頭檔應放置在 src/main/c
目錄中 (但請注意,此類標頭檔應始終以相對於包含它們的檔案的方式參照)。
組譯器來源
組譯語言支援是透過 'assembler'
外掛程式來提供的。
範例:'assembler' 外掛程式
plugins {
id 'assembler'
}
要包含在原生二進位檔中的組譯器來源是透過 AssemblerSourceSet 提供的,它定義一組組譯器來源檔案。預設情況下,對於任何命名元件,AssemblerSourceSet 都包含 src/${name}/asm
中的 .s
來源檔案。
Objective-C 來源
Objective-C 語言支援是透過 'objective-c'
外掛程式來提供的。
範例:'objective-c' 外掛程式
plugins {
id 'objective-c'
}
要包含在原生二進位檔中的 Objective-C 來源是透過 ObjectiveCSourceSet 提供的,它定義一組 Objective-C 來源檔案。預設情況下,對於任何命名元件,ObjectiveCSourceSet 都包含 src/${name}/objectiveC
中的 .m
來源檔案。
Objective-C++ 來源
Objective-C++ 語言支援是透過 'objective-cpp'
外掛程式提供的。
範例:'objective-cpp' 外掛程式
plugins {
id 'objective-cpp'
}
要包含在原生二進位檔中的 Objective-C++ 來源是透過 ObjectiveCppSourceSet 提供的,它定義了一組 Objective-C++ 來源檔案。預設情況下,對於任何已命名元件,ObjectiveCppSourceSet 都包含 src/${name}/objectiveCpp
底下的 .mm
來源檔案。
設定編譯器、組譯器和連結器
每個要產生的二進位檔都與一組編譯器和連結器設定相關聯,其中包括命令列引數和巨集定義。這些設定可以套用至所有二進位檔、個別二進位檔,或根據某些準則選擇性地套用至一群二進位檔。
範例:套用至所有二進位檔的設定
model {
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"
}
}
}
}
每個二進位檔都與特定的 NativeToolChain 相關聯,允許根據此值設定目標設定。
很容易將設定套用至特定類型的所有二進位檔
範例:套用至所有共用函式庫的設定
// For any shared library binaries built with Visual C++,
// define the DLL_EXPORT macro
model {
binaries {
withType(SharedLibraryBinarySpec) {
if (toolChain in VisualCpp) {
cCompiler.args "/Zi"
cCompiler.define "DLL_EXPORT"
}
}
}
}
此外,可以指定套用至為特定 executable
或 library
元件產生的所有二進位檔的設定
範例:套用至為 'main' 可執行元件產生的所有二進位檔的設定
model {
components {
main(NativeExecutableSpec) {
targetPlatform "x86"
binaries.all {
if (toolChain in VisualCpp) {
sources {
platformAsm(AssemblerSourceSet) {
source.srcDir "src/main/asm_i386_masm"
}
}
assembler.args "/Zi"
} else {
sources {
platformAsm(AssemblerSourceSet) {
source.srcDir "src/main/asm_i386_gcc"
}
}
assembler.args "-g"
}
}
}
}
}
上述範例會將提供的設定套用至所有建置的 executable
二進位檔。
類似地,可以指定設定以鎖定特定類型的元件二進位檔:例如主函式庫元件的所有共用函式庫。
範例:僅套用至為 'main' 函式庫元件產生的共用函式庫的設定
model {
components {
main(NativeLibrarySpec) {
binaries.withType(SharedLibraryBinarySpec) {
// Define a preprocessor macro that only applies to shared libraries
cppCompiler.define "DLL_EXPORT"
}
}
}
}
Windows 資源
在使用 VisualCpp 工具鏈時,Gradle 能夠編譯 Windows 資源 (rc
) 檔案,並將它們連結到原生二進位檔中。此功能是由 'windows-resources'
外掛程式提供的。
範例:'windows-resources' 外掛程式
plugins {
id 'windows-resources'
}
要包含在原生二進位檔中的 Windows 資源是透過 WindowsResourceSet 提供,其中定義一組 Windows 資源原始檔。預設情況下,對於任何命名元件,WindowsResourceSet 會包含在 src/${name}/rc
底下的 .rc
原始檔。
與其他原始檔類型一樣,您可以設定要包含在二進位檔中的 Windows 資源位置。
範例:設定 Windows 資源來源位置
sources {
rc {
source {
srcDirs "src/hello/rc"
}
exportedHeaders {
srcDirs "src/hello/headers"
}
}
}
您可以透過提供沒有其他語言來源的 Windows 資源來源來建構僅資源的程式庫,並適當地設定連結器
範例:建構僅資源的 dll
model {
components {
helloRes(NativeLibrarySpec) {
binaries.all {
rcCompiler.args "/v"
linker.args "/noentry", "/machine:x86"
}
sources {
rc {
source {
srcDirs "src/hello/rc"
}
exportedHeaders {
srcDirs "src/hello/headers"
}
}
}
}
}
}
上述範例也示範傳遞額外命令列引數給資源編譯器的機制。rcCompiler
擴充功能的類型為 PreprocessingTool。
程式庫相依性
原生元件的相依性是匯出標頭檔的二進位程式庫。標頭檔會在編譯期間使用,而編譯的二進位相依性會在連結和執行期間使用。標頭檔應組織成子目錄,以防止常見標頭名稱發生衝突。例如,如果您的 mylib
專案有一個 logging.h
標頭,如果您將它包含為 "mylib/logging.h"
而不是 "logging.h"
,則較不容易使用錯誤的標頭。
同一個專案中的相依性
一組來源可能會相依於同一個專案中另一個二進位元件提供的標頭檔。常見的範例是使用由獨立原生程式庫元件提供的函式的原生可執行元件元件。
此類程式庫相依性可以新增到與 executable
元件關聯的來源組
範例:提供程式庫相依性給來源組
sources {
cpp {
lib library: "hello"
}
}
或者,程式庫相依性可以直接提供給 executable
的 NativeExecutableBinarySpec
。
範例:提供二進位檔的函式庫相依性
model {
components {
hello(NativeLibrarySpec) {
sources {
c {
source {
srcDir "src/source"
include "**/*.c"
}
exportedHeaders {
srcDir "src/include"
}
}
}
}
main(NativeExecutableSpec) {
sources {
cpp {
source {
srcDir "src/source"
include "**/*.cpp"
}
}
}
binaries.all {
// Each executable binary produced uses the 'hello' static library binary
lib library: 'hello', linkage: 'static'
}
}
}
}
專案相依性
對於在不同 Gradle 專案中產生的元件,符號類似。
範例:宣告專案相依性
plugins {
id 'cpp'
}
model {
components {
main(NativeLibrarySpec)
}
// For any shared library binaries built with Visual C++,
// define the DLL_EXPORT macro
binaries {
withType(SharedLibraryBinarySpec) {
if (toolChain in VisualCpp) {
cppCompiler.define "DLL_EXPORT"
}
}
}
}
plugins {
id 'cpp'
}
model {
components {
main(NativeExecutableSpec) {
sources {
cpp {
lib project: ':lib', library: 'main'
}
}
}
}
}
預編譯標頭
預編譯標頭是一種效能最佳化,可降低編譯廣泛使用的標頭多次的成本。此功能會預編譯標頭,讓編譯的物件檔案可在編譯每個原始檔時重複使用,而非每次都重新編譯標頭。此支援適用於 C、C++、Objective-C 和 Objective-C++ 建置。
若要設定預編譯標頭,首先需要定義一個標頭檔案,其中包含所有應該預編譯的標頭。它必須指定為每個原始檔中第一個包含的標頭,其中應該使用預編譯標頭。假設這個標頭檔案和它包含的任何標頭都使用標頭防護,以便它們可以以冪等方式包含。如果標頭檔案中未使用標頭防護,標頭可能會被編譯多次,並可能導致建置中斷。
範例:建立預編譯標頭檔案
#ifndef PCH_H
#define PCH_H
#include <iostream>
#include "hello.h"
#endif
範例:在原始檔中包含預編譯標頭檔案
#include "pch.h"
void LIB_FUNC Greeter::hello () {
std::cout << "Hello world!" << std::endl;
}
預編譯標頭會在原始檔集上指定。只有一個預編譯標頭檔案可以在給定的原始檔集上指定,並會套用至所有宣告它為第一個包含的原始檔。如果原始檔未將這個標頭檔案包含為第一個標頭,檔案將會以一般方式編譯(不使用預編譯標頭物件檔案)。提供的字串應該與原始檔中「#include」指令中使用的相同。
範例:設定預編譯標頭
model {
components {
hello(NativeLibrarySpec) {
sources {
cpp {
preCompiledHeader "pch.h"
}
}
}
}
}
使用預編譯標頭的所有檔案都必須以相同方式包含它。通常,這表示標頭檔案應該存在於原始檔集「標頭」目錄或包含在編譯器包含路徑的目錄中。
原生二進制變體
對於每個已定義的可執行檔或函式庫,Gradle 能夠建置許多不同的原生二進制變體。不同變體的範例包括偵錯與發布二進制檔案、32 位元與 64 位元二進制檔案,以及使用不同自訂預處理器旗標產生的二進制檔案。
Gradle 產生的二進制檔案可以在 建置類型、平台 和 風味 中區分。對於這些「變體維度」中的每一個,都可以指定一組可用值,以及將每個元件鎖定在這些值中的其中一個、幾個或全部。例如,外掛程式可以定義支援平台的範圍,但您可能選擇只針對特定元件鎖定 Windows-x86。
建置類型
build type
決定二進制檔案的各種非功能性面向,例如是否包含偵錯資訊,或二進制檔案編譯時使用哪個最佳化層級。典型的建置類型為「偵錯」和「發布」,但專案可以自由定義任何建置類型組。
範例:定義建置類型
model {
buildTypes {
debug
release
}
}
如果專案中未定義任何建置類型,則會新增一個稱為「偵錯」的單一預設建置類型。
對於建置類型,Gradle 專案通常會針對每個工具鏈定義一組編譯器/連結器旗標。
範例:設定偵錯二進制檔案
model {
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 的未來版本會自動為任何「偵錯」建置類型包含適當的偵錯旗標,並且可能也會知道各種最佳化層級。 |
平台
可建置可執行檔或函式庫在不同的作業系統和 CPU 架構上執行,並為每個平台產生一個變體。Gradle 將每個作業系統/架構組合定義為 NativePlatform,而且專案可以定義任意數量的平台。如果專案中未定義任何平台,則會新增一個稱為「目前」的單一預設平台。
目前, |
範例:定義平台
model {
platforms {
x86 {
architecture "x86"
}
x64 {
architecture "x86_64"
}
itanium {
architecture "ia-64"
}
}
}
對於給定的變體,Gradle 會嘗試尋找一個 NativeToolChain,它能夠針對目標平台進行建置。會依據定義的順序搜尋可用的工具鏈。請參閱下方 工具鏈 區段以取得更多詳細資料。
風味
每個元件都可以有一組名為 flavors
的風味,而且可以針對每個風味產生一個獨立的二進制變體。雖然 build type
和 target platform
變體維度在 Gradle 中有定義的意義,但每個專案都可以自由定義任意數量的風味,並以任何方式套用意義。
元件風味的範例可能是區分元件的「示範」、「付費」和「企業」版本,其中使用同一組來源產生具有不同功能的二進制檔案。
範例:定義風味
model {
flavors {
english
french
}
components {
hello(NativeLibrarySpec) {
binaries.all {
if (flavor == flavors.french) {
cppCompiler.define "FRENCH"
}
}
}
}
}
在上述範例中,定義了一個具有「英文」和「法文」風味的函式庫。在編譯「法文」變體時,會定義一個獨立的巨集,這會產生一個不同的二進制檔案。
如果未針對元件定義風味,則會使用一個名為「預設」的單一預設風味。
選擇元件的建置類型、平台和風味
對於預設元件,Gradle 會嘗試針對專案定義的每個 buildType
和 flavor
組合建立一個原生二進制變體。可以透過指定 targetBuildTypes
和/或 targetFlavors
組合,針對每個元件覆寫這個設定。預設情況下,Gradle 會針對預設平台建置,請參閱 上方,除非透過指定 targetPlatforms
組合針對每個元件明確指定。
範例:將元件鎖定在特定平台
model {
components {
hello(NativeLibrarySpec) {
targetPlatform "x86"
targetPlatform "x64"
}
main(NativeExecutableSpec) {
targetPlatform "x86"
targetPlatform "x64"
sources {
cpp.lib library: 'hello', linkage: 'static'
}
}
}
}
在這裡,您可以看到 TargetedNativeComponent.targetPlatform(java.lang.String) 方法用於指定 NativeExecutableSpec
名為 main
的平台,它應該針對該平台建置。
建置所有可能的變體
當為元件定義一組建置類型、目標平台和風味時,會為這些元素的每一個可能的組合建立一個 NativeBinarySpec 模型元素。然而,在許多案例中,無法建置特定變異,可能是因為沒有工具鏈可用來為特定平台建置。
如果無法基於任何原因建置二進位變異,則與該變異關聯的 NativeBinarySpec 將不會是 buildable
。可以使用此屬性來建立一個任務,以在特定機器上產生所有可能的變異。
範例:建置所有可能的變異
model {
tasks {
buildAllExecutables(Task) {
dependsOn $.binaries.findAll { it.buildable }
}
}
}
工具鏈
單一建置可以使用不同的工具鏈來為不同平台建置變異。為此,核心「原生二進位」外掛程式將嘗試尋找並提供支援的工具鏈。然而,專案的工具鏈組也可以明確定義,允許額外的交叉編譯器進行組態,並允許指定安裝目錄。
範例:定義工具鏈
model {
toolChains {
visualCpp(VisualCpp) {
// Specify the installDir if Visual Studio cannot be located
// 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 將嘗試尋找一個 NativeToolChain,能夠為目標平台建置。可用的工具鏈會按照定義的順序進行搜尋。
當平台未定義架構或作業系統時,會假設工具鏈的預設目標。因此,如果平台未定義 operatingSystem 的值,Gradle 將會尋找第一個可用的工具鏈,該工具鏈能夠為指定的 architecture 建置。
|
核心 Gradle 工具鏈能夠立即針對下列架構。在每種情況下,工具鏈將針對目前的作業系統。請參閱下一節,以取得關於為其他作業系統進行交叉編譯的資訊。
工具鏈 | 架構 |
---|---|
GCC |
x86、x86_64、arm64(僅限 macOS) |
Clang |
x86、x86_64、arm64(僅限 macOS) |
Visual C++ |
x86、x86_64、ia-64 |
因此,對於在 Linux 上執行的 GCC,支援的目標平台為「linux/x86」和「linux/x86_64」。對於透過 Cygwin 在 Windows 上執行的 GCC,支援的平台為「windows/x86」和「windows/x86_64」。(Cygwin POSIX 執行時間目前尚未建模為平台的一部分,但未來會納入。)
如果未針對專案定義任何目標平台,則所有二進位檔都會建置為目標為名為「current」的預設平台。此預設平台未指定任何 architecture
或 operatingSystem
值,因此使用第一個可用工具鏈的預設值。
Gradle 提供一個 hook,讓建置作者可以控制傳遞給工具鏈可執行檔的精確參數組。這讓建置作者可以解決 Gradle 中的任何限制,或 Gradle 做出的任何假設。參數 hook 應視為「最後手段」機制,優先考慮真正建模基礎網域。
範例:重新設定工具參數
model {
toolChains {
visualCpp(VisualCpp) {
eachPlatform {
cppCompiler.withArguments { args ->
args << "-DFRENCH"
}
}
}
clang(Clang) {
eachPlatform {
cCompiler.withArguments { args ->
Collections.replaceAll(args, "CUSTOM", "-DFRENCH")
}
linker.withArguments { args ->
args.remove "CUSTOM"
}
staticLibArchiver.withArguments { args ->
args.remove "CUSTOM"
}
}
}
}
}
範例:定義目標平台
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"
}
}
components {
main(NativeExecutableSpec) {
targetPlatform "arm"
targetPlatform "sparc"
}
}
}
Visual Studio IDE 整合
Gradle 能為建置中定義的原生元件產生 Visual Studio 專案和解決方案檔案。此功能是由 visual-studio
外掛程式新增的。對於多專案建置,所有包含原生元件的專案 (以及根專案) 都應套用此外掛程式。
當 visual-studio
外掛程式套用於根專案時,會建立一個名為 visualStudio
的工作,它會產生一個 Visual Studio 解決方案檔案,其中包含建置中的所有元件。此解決方案會包含每個元件的 Visual Studio 專案,以及設定每個元件使用 Gradle 建置。
當專案為根專案時,visual-studio
外掛程式也會建立一個名為 openVisualStudio
的工作。此工作會產生 Visual Studio 解決方案,然後在 Visual Studio 中開啟解決方案。這表示您可以從根專案執行 gradlew openVisualStudio
,以一個方便的步驟產生並開啟 Visual Studio 解決方案。
可以透過 visualStudio
擴充功能提供的 API hook 修改產生之 Visual Studio 檔案的內容。請參閱「visual-studio」範例,或在 API 文件中參閱 VisualStudioExtension.getProjects() 和 VisualStudioRootExtension.getSolution() 以取得更多詳細資料。
CUnit 支援
Gradle cunit
外掛提供支援,可在原生二進位專案中編譯和執行 CUnit 測試。對於專案中定義的每個 NativeExecutableSpec 和 NativeLibrarySpec,Gradle 會建立一個相符的 CUnitTestSuiteSpec 元件,命名為 ${component.name}Test
。
CUnit 來源
對於專案中的每個 CUnitTestSuiteSpec 元件,Gradle 會建立一個名為「cunit」的 CSourceSet。此來源組應包含受測元件的 CUnit 測試檔案。來源檔案可以位於慣例位置 (src/${component.name}Test/cunit
),或可以像任何其他來源組一樣進行設定。
Gradle 會初始化 CUnit 測試登錄檔並執行測試,利用一些產生的 CUnit 啟動器來源。Gradle 會期待並呼叫具有簽章 void gradle_cunit_register()
的函式,您可以使用此函式來設定要執行的實際 CUnit 組合和測試。
範例:註冊 CUnit 測試
#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 提供的方法發生衝突。
|
建立 CUnit 可執行檔
CUnitTestSuiteSpec 元件具有相關聯的 NativeExecutableSpec 或 NativeLibrarySpec 元件。對於為主要元件設定的每個 NativeBinarySpec,會在測試組件上設定相符的 CUnitTestSuiteBinarySpec。這些測試組件二進位檔可以設定的方式與任何其他二進位檔執行個體類似
範例:設定 CUnit 測試
model {
binaries {
withType(CUnitTestSuiteBinarySpec) {
lib library: "cunit", linkage: "static"
if (flavor == flavors.failing) {
cCompiler.define "PLUS_BROKEN"
}
}
}
}
專案提供的 CUnit 來源和產生的啟動器都需要核心 CUnit 標頭和函式庫。目前,此函式庫相依性必須由您的專案提供,以供每個 CUnitTestSuiteBinarySpec 使用。 |
執行 CUnit 測試
對於每個 CUnitTestSuiteBinarySpec,Gradle 會建立一個任務來執行這個二進位檔,這將執行所有已註冊的 CUnit 測試。測試結果會顯示在 ${build.dir}/test-results
目錄中。
範例:執行 CUnit 測試
plugins {
id 'c'
id 'cunit-test-suite'
}
model {
flavors {
passing
failing
}
platforms {
x86 {
if (operatingSystem.macOsX) {
architecture "x64"
} else {
architecture "x86"
}
}
}
repositories {
libs(PrebuiltLibraries) {
cunit {
headers.srcDir "libs/cunit/2.1-2/include"
binaries.withType(StaticLibraryBinary) {
staticLibraryFile =
file("libs/cunit/2.1-2/lib/" +
findCUnitLibForPlatform(targetPlatform))
}
}
}
}
components {
operators(NativeLibrarySpec) {
targetPlatform "x86"
}
}
testSuites {
operatorsTest(CUnitTestSuiteSpec) {
testing $.components.operators
}
}
}
model {
binaries {
withType(CUnitTestSuiteBinarySpec) {
lib library: "cunit", linkage: "static"
if (flavor == flavors.failing) {
cCompiler.define "PLUS_BROKEN"
}
}
}
}
gradle -q runOperatorsTestFailingCUnitExe
的輸出> gradle -q runOperatorsTestFailingCUnitExe There were test failures: 1. /home/user/gradle/samples/src/operatorsTest/c/test_plus.c:6 - plus(0, -2) == -2 2. /home/user/gradle/samples/src/operatorsTest/c/test_plus.c:7 - plus(2, 2) == 4 FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':runOperatorsTestFailingCUnitExe'. > There were failing tests. See the results at: file:///home/user/gradle/samples/build/test-results/operatorsTest/failing/ * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. BUILD FAILED in 0s
目前對 CUnit 的支援相當基本。未來整合的計畫包括
|
GoogleTest 支援
Gradle google-test
外掛程式提供在原生二進位專案中編譯和執行 GoogleTest 測試的支援。對於專案中定義的每個 NativeExecutableSpec 和 NativeLibrarySpec,Gradle 會建立一個相符的 GoogleTestTestSuiteSpec 元件,命名為 ${component.name}Test
。
GoogleTest 來源
Gradle 會為專案中的每個 GoogleTestTestSuiteSpec 元件建立一個名為「cpp」的 CppSourceSet。這個來源組應該包含要測試元件的 GoogleTest 測試檔案。來源檔案可以放在傳統位置 (src/${component.name}Test/cpp
) 或可以像任何其他來源組一樣進行組態。
建立 GoogleTest 可執行檔
GoogleTestTestSuiteSpec 元件有一個相關聯的 NativeExecutableSpec 或 NativeLibrarySpec 元件。對於為主要元件組態的每個 NativeBinarySpec,會在測試套件元件上組態一個相符的 GoogleTestTestSuiteBinarySpec。這些測試套件二進位檔可以像任何其他二進位執行個體一樣進行組態
範例:註冊 GoogleTest 測試
model {
binaries {
withType(GoogleTestTestSuiteBinarySpec) {
lib library: "googleTest", linkage: "static"
if (flavor == flavors.failing) {
cppCompiler.define "PLUS_BROKEN"
}
if (targetPlatform.operatingSystem.linux) {
cppCompiler.args '-pthread'
linker.args '-pthread'
if (toolChain instanceof Gcc || toolChain instanceof Clang) {
// Use C++03 with the old ABIs, as this is what the googletest binaries were built with
cppCompiler.args '-std=c++03', '-D_GLIBCXX_USE_CXX11_ABI=0'
linker.args '-std=c++03'
}
}
}
}
}
專案提供的 GoogleTest 來源需要核心 GoogleTest 標頭和函式庫。目前,這個函式庫相依性必須由專案提供給每個 GoogleTestTestSuiteBinarySpec。 |
執行 GoogleTest 測試
對於每個 GoogleTestTestSuiteBinarySpec,Gradle 會建立一個任務來執行此二進位檔,這將執行所有已註冊的 GoogleTest 測試。測試結果會出現在 ${build.dir}/test-results
目錄中。
目前對 GoogleTest 的支援相當基本。未來整合計畫包括
|