CMake

Android NDK 支援使用 CMake 為應用程式編譯 C 和 C++ 程式碼。本頁面說明如何透過 Android Gradle 外掛程式的 ExternalNativeBuild,或透過直接叫用 CMake,將 NDK 與 CMake 搭配使用。

CMake 工具鏈檔案

NDK 透過工具鏈檔案支援 CMake。工具鏈檔案是 CMake 檔案,用於自訂跨平台程式碼編譯的工具鏈行為。用於 NDK 的工具鏈檔案位於 NDK 的 <NDK>/build/cmake/android.toolchain.cmake 中。

叫用 cmake 時,指令列會提供 ABI、minSdkVersion 等建構參數。如需支援的引數清單,請參閱「工具鏈引數」一節。

「全新」工具鍊檔案

先前 NDK 嘗試了新的工具鍊檔案實作, 可減少使用 NDK 工具鍊檔案和 使用內建的 CMake 支援功能。但最後卻需要產生 但並沒有真正改善行為 因此我們不會再處理

「全新」工具鍊檔案的行為迴歸 (與「舊版」相比) 工具鍊檔案。預設行為為建議的工作流程。如果您是 使用 -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF,建議您移除該標記 建構容器新的工具鍊檔案永遠無法與舊版檔案保持一致 工具鍊檔案,因此可能會發生行為迴歸問題。

雖然我們建議不要使用新的工具鍊檔案,但是目前還沒有 並計劃從 NDK 中移除。這樣做會導致依賴該物件的建構作業 新版與舊版工具鍊檔案的行為差異。 重新命名選項將名稱標示為「舊版」其實是 也可能打斷使用者選擇如果您滿意地使用 您不需要遷移的新工具鍊檔案,但請務必留意 可能無法修正新的工具鍊檔案行為, 您需要遷移這些資料

用量

Gradle

使用 externalNativeBuild 時,系統會自動使用 CMake 工具鏈檔案。詳情請參閱 Android Studio 的將 C 和 C++ 程式碼新增至專案指南。

指令列

在 Gradle 之外使用 CMake 進行建構時,必須將工具鏈檔案及其引數傳遞至 CMake,例如:

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

工具鏈引數

下列引數可傳遞至 CMake 工具鏈檔案。如果使用 Gradle 建構,請按照 ExternalNativeBuild 說明文件所述,將引數新增至 android.defaultConfig.externalNativeBuild.cmake.arguments。如果是透過指令列進行建構,請使用 -D 將引數傳遞至 CMake。例如,強迫 armeabi-v7a 不要使用 Neon 進行建構 支援,請傳遞 -DANDROID_ARM_NEON=FALSE

ANDROID_ABI

目標 ABI。如需瞭解支援的 ABI,請參閱 Android ABI

Gradle

Gradle 會自動提供這個引數。請勿在您的 build.gradle 檔案中明確設定這個引數。如要控管 ABI Gradle 的目標,請依照 Android ABI 中所述使用 abiFilters

指令列

CMake 針對每項建構作業的單一目標進行建構。如要設定多個 Android ABI 目標,則必須為每個 ABI 建構一次。建議針對每個 ABI 使用不同的建構目錄,以免建構作業之間發生衝突。

附註
armeabi-v7a
armeabi-v7a with NEON armeabi-v7a 相同。
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

指定是否為 armeabi-v7a 產生 arm 或 thumb 指令。不會影響其他 ABI。詳情請參閱 Android ABI 說明文件。

附註
arm
thumb 預設行為。

ANDROID_NATIVE_API_LEVEL

ANDROID_PLATFORM 的別名。

ANDROID_PLATFORM

指定應用程式或程式庫支援的最低 API 級別。這個值對應應用程式的 minSdkVersion

Gradle

使用 Android Gradle 外掛程式時,系統會自動將這個值設為與應用程式的 minSdkVersion 相符,因此不應手動設定。

指令列

直接叫用 CMake 時,這個值會預設為使用的 NDK 所支援的最低 API 級別。例如,針對 NDK r20,這個值預設為 API 級別 16。

這個參數支援多種格式:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

$API_LETTER 格式可讓您指定 android-N,而無需確定該版本編號。請注意,部分版本增加了 API,但字母沒有增加。您可以透過附加 -MR1 後置字串來指定這些 API。例如,API 級別 25 是 android-N-MR1

ANDROID_STL

指定這個應用程式要使用的 STL。詳情請參閱 C++ 程式庫支援。根據預設,系統會使用 c++_static

附註
c++_shared libc++ 的共用程式庫變化版本。
c++_static libc++ 的靜態程式庫變化版本。
none 不支援 C++ 標準程式庫。
system 系統 STL

管理編譯器標記

如果您需要將特定標記傳遞至版本的編譯器或連結器, 請參閱 set_target_compile_options 的 CMake 說明文件,以及 一起做出選擇「另請參閱」該網頁最下方的部分 一些有用的線索

一般而言,最佳做法是將編譯器標記套用至最小範圍 可用的範圍您要套用至所有指定目標的旗標 (例如 -Werror) 對每個模組重複比較方便,但還是很少見 全域套用 (CMAKE_CXX_FLAGS),因為這些設定可能對 專案中的第三方依附元件在這種情況下,標記可能會 已套用至目錄範圍 (add_compile_options)。

針對少數編譯器旗標,您也可以在 build.gradle 中設定 透過 cppFlags 或類似屬性管理檔案請勿這麼做。旗幟 從 Gradle 傳遞至 CMake 時,會有非預期的優先順序行為。 覆寫被實作的標記, 來建構 Android 程式碼一律偏好處理 CMake 行為 直接整合到 CMake 中如需根據 AGP buildType 控制編譯器標記, 請參閱「在 CMake 中使用 AGP 建構類型」。

在 CMake 中使用 AGP 建構類型

如果您需要為自訂 Gradle buildType 自訂 CMake 行為,請使用該程式碼 建構型別來傳遞另一個 CMake 標記 (而非編譯器標記), 可讀取 CMake 建構指令碼。舉例來說,如果您擁有「免費」以及「Premium」 建構變數,並由 build.gradle.kts 控管,因此您需要傳遞 將資料提供給 CMake:

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

接著在 CMakeLists.txt 中:

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

您可以自行決定變數名稱,但請務必避免使用 ANDROID_APP_CMAKE_ 前置字元,以免發生衝突或混淆 和一些現有標記

如需範例,請參閱 Sanitizers NDK 範例

瞭解 CMake 建構指令

如要對 CMake 建構問題偵錯,建議您先瞭解為 Android 執行跨平台程式碼編譯作業時,Gradle 使用哪些特定建構引數。

Android Gradle 外掛程式會針對每個 ABI 及與 build_command.txt 配對的建構類型的 CMake 建構作業,儲存執行此類作業時使用的建構引數。這些檔案位於以下目錄中:

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/

您可以使用 CMake 引數建構 hello-jni 範例的可偵錯版本並指定 armeabi-v7a 架構,如以下程式碼片段範例所示:

                    Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :


                    Build command args: []
                    Version: 1

使用預先建構的程式庫

如果需要匯入的預先建構程式庫是發布為 AAR,請按照 Studio 依附元件說明文件所述,匯入和使用這類程式庫。如果不是使用 AGP,可以按照 https://google.github.io/prefab/example-workflow.html 的指示操作,但改用 AGP 可能更簡單。

如果程式庫並非發布為 AAR,請參閱 CMake 使用手冊中有關 IMPORTED 目標的 add_library 說明文件,瞭解如何透過 CMake 使用預先建構的程式庫。

建構第三方程式碼

在 CMake 專案中建構第三方程式碼的實用方法有很多,哪種方法最合適需視情況而定,但最佳做法通常是完全不建構第三方程式碼。請改為針對程式庫建構 AAR,並在應用程式中取用。您不必「發布」AAR,AAR 可位於 Gradle 專案內部。

如果不適合這種做法:

  • 將第三方來源提供 (即複製) 到存放區,並使用 add_subdirectory 建構。只有在其他程式庫也使用 CMake 來建構時,才能使用這種做法。
  • 定義 ExternalProject
  • 從專案中單獨建構程式庫,並按照「使用預先建構的程式庫」所述,將其匯入為預先建構程式庫。

CMake 中的 YASM 支援

NDK 針對建構以 YASM 編寫的組譯程式碼提供 CMake 支援,以便在 x86 和 x86-64 架構上執行。YASM 是 x86 和 x86-64 架構的開放原始碼組譯工具,以 NASM 組譯工具為基礎。

如要使用 CMake 建構組譯程式碼,請在專案的 CMakeLists.txt 中進行下列變更:

  1. 呼叫 enable_language,並將值設為 ASM_NASM
  2. 如要建構共用程式庫,請呼叫 add_library;如要建構可執行的二進位檔,則呼叫 add_executable。在引數中傳遞來源檔案清單,當中包含 YASM 組合程式的 .asm 檔案,以及相關 C 程式庫或函式的 .c 檔案。

下列程式碼片段展示如何設定 CMakeLists.txt,以將 YASM 程式建構為共用程式庫。

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

如需查看示範將 YASM 程式建構為執行檔的範例,請參閱 NDK Git 存放區中的 YASM 測試

回報問題

如果遇到 NDK 或其 CMake 工具鏈檔案的相關問題,請透過 GitHub 的 android-ndk/ndk 問題追蹤工具回報這類問題。假如遇到 Gradle 或 Android Gradle 外掛程式的問題,請改為回報 Studio 錯誤