Android Studio 如何配置 CMake
前面的小節(jié)我們學習了如何安裝 NDK 和 CMake。本小節(jié)學習如何配置 CMake。
1. 概述
CMake 構(gòu)建腳本是一個純文本文件,我們必須將其命名為 CMakeLists.txt,并在其中包含 CMake 構(gòu)建我們的 C/C++ 庫時需要使用的命令。如果我們的原生源代碼文件還沒有 CMake 構(gòu)建腳本,我們需要自行創(chuàng)建一個,并在其中包含適當?shù)?CMake 命令。
下面我們將學習應(yīng)該在構(gòu)建腳本中包含哪些基本命令,以便指示 CMake 在創(chuàng)建原生庫時使用哪些源代碼文件。如需了解詳情,請參閱介紹 CMake 命令的官方文檔。
2. 創(chuàng)建 CMake 構(gòu)建腳本
要創(chuàng)建一個可以用作 CMake 構(gòu)建腳本的純文本文件,請按以下步驟操作:
-
從 IDE 的左側(cè)打開 Project 窗格,然后從下拉菜單中選擇 Project 視圖。
-
右鍵點擊 your-module 的根目錄,然后依次選擇 New > File。
-
輸入 CMakeLists.txt 作為文件名,然后點擊 OK。
現(xiàn)在,我們可以通過添加 CMake 命令來配置我們的構(gòu)建腳本。要指示 CMake 根據(jù)原生源代碼創(chuàng)建原生庫,請向我們的構(gòu)建腳本添加 cmake_minimum_required() 和 add_library() 命令:
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.
cmake_minimum_required(VERSION 3.4.1)
# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.
add_library( # Specifies the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
在使用 add_library() 向 CMake 構(gòu)建腳本添加源代碼文件或庫時,Android Studio 還會在我們同步項目后在 Project 視圖中顯示相關(guān)的頭文件。不過,為了讓 CMake 能夠在編譯時找到頭文件,我們需要向 CMake 構(gòu)建腳本添加 include_directories() 命令,并指定頭文件的路徑:
add_library(...)
# Specifies a path to native header files.
include_directories(src/main/cpp/include/)
CMake 使用 liblibrary-name.so 這樣的規(guī)范來為庫文件命名。例如,如果我們在構(gòu)建腳本中指定 native-lib 作為共享庫的名稱,CMake 就會創(chuàng)建一個名為 libnative-lib.so 的文件。不過,在 Java 代碼中加載此庫時,要使用在 CMake 構(gòu)建腳本中指定的名稱:
static {
System.loadLibrary("native-lib");
}
3. 添加 NDK API
Android NDK 提供了一套我們可能會覺得非常實用的原生 API 和庫。通過在項目的 CMakeLists.txt 腳本文件中包含 NDK 庫,我們可以使用其中任何 API。
Android 平臺上已存在預(yù)構(gòu)建的 NDK 庫,因此我們無需構(gòu)建它們或?qū)⑺鼈兇虬?APK 中。由于這些 NDK 庫已位于 CMake 搜索路徑中,因此我們甚至無需指定本地安裝的 NDK 庫的位置,我們只需為 CMake 提供我們想要使用的庫的名稱,并將其與我們自己的原生庫相關(guān)聯(lián)即可。
向 CMake 構(gòu)建腳本添加 find_library() 命令以找到 NDK 庫并將其路徑存儲為一個變量。我們可以使用此變量在構(gòu)建腳本的其他部分引用 NDK 庫。
以下示例可以找到 Android 專有的日志支持庫,并會將其路徑存儲在 log-lib 中:
find_library( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib
# Specifies the name of the NDK library that
# CMake needs to locate.
log )
為了讓我們的原生庫能夠調(diào)用 log 庫中的函數(shù),我們需要使用 CMake 構(gòu)建腳本中的 target_link_libraries() 命令來關(guān)聯(lián)這些庫:
find_library(...)
# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the log library to the target library.
${log-lib} )
NDK 還以源代碼的形式包含一些庫,我們將需要構(gòu)建這些代碼并將其關(guān)聯(lián)到我們的原生庫。我們可以使用 CMake 構(gòu)建腳本中的 add_library() 命令將源代碼編譯到原生庫中。如需提供本地 NDK 庫的路徑,我們可以使用 Android Studio 自動為我們定義的 ANDROID_NDK 路徑變量。
例如,以下命令告訴 CMake 要構(gòu)建 android_native_app_glue.c(負責管理 NativeActivity 生命周期事件和觸摸輸入),并將其鏈接到靜態(tài)庫 native-lib 中:
add_library( app-glue
STATIC
${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )
# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )
4. 添加其他預(yù)構(gòu)建庫
添加預(yù)構(gòu)建庫的步驟與為 CMake 指定其他要構(gòu)建的原生庫的步驟相似。不過,由于庫已構(gòu)建,因此我們需要使用 IMPORTED 標記告訴 CMake 我們只想要將此庫導(dǎo)入到我們的項目中。
add_library( imported-lib
SHARED
IMPORTED )
然后,我們需要使用 set_target_properties() 命令指定庫的路徑,具體步驟如下所示。
某些庫會針對特定的 CPU 架構(gòu)或應(yīng)用二進制接口 (ABI) 提供單獨的軟件包,并將其整理到單獨的目錄中。此方法既有助于庫充分利用特定的 CPU 架構(gòu),又能讓我們只使用所需的庫版本。要向 CMake 構(gòu)建腳本添加庫的多個 ABI 版本,而不必為庫的每個版本編寫多個命令,我們可以使用 ANDROID_ABI 路徑變量。此變量使用的是 NDK 支持的一組默認 ABI,或者我們手動配置 Gradle 以使用的一組經(jīng)過過濾的 ABI。
例如:
add_library(...)
set_target_properties( # Specifies the target library.
imported-lib
# Specifies the parameter you want to define.
PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
imported-lib/src/${ANDROID_ABI}/libimported-lib.so )
為了讓 CMake 能夠在編譯時定位我們的頭文件,我們需要使用 include_directories() 命令并包含相應(yīng)頭文件的路徑:
include_directories( imported-lib/include/ )
如需將預(yù)構(gòu)建庫關(guān)聯(lián)到我們自己的原生庫,請將其添加到 CMake 構(gòu)建腳本的 target_link_libraries() 命令中:
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )
如需將預(yù)構(gòu)建庫打包到 APK 中,我們需要使用 sourceSets 塊手動配置 Gradle 以包含 .so 文件的路徑。構(gòu)建 APK 后,我們可以使用 APK 分析器驗證 Gradle 會將哪些庫打包到我們的 APK 中。
5. 包含其他 CMake 項目
如果想要構(gòu)建多個 CMake 項目并在 Android 項目中包含它們的輸出,我們可以使用一個 CMakeLists.txt 文件(即為我們關(guān)聯(lián)到 Gradle 的那個文件)作為頂級 CMake 構(gòu)建腳本,并添加其他 CMake 項目作為此構(gòu)建腳本的依賴項。以下頂級 CMake 構(gòu)建腳本會使用 add_subdirectory() 命令將另一個 CMakeLists.txt 文件指定為構(gòu)建依賴項,然后關(guān)聯(lián)其輸出,就像處理任何其他預(yù)構(gòu)建庫一樣。
# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )
# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})
# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
${lib_src_DIR}
# Specifies the directory for the build outputs.
${lib_build_DIR} )
# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )
# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )
6. 小結(jié)
本節(jié)課程我們主要學習了如何配置 CMake。本節(jié)課程的重點如下:
- 掌握如何創(chuàng)建 CMake 構(gòu)建腳本;
- 掌握如何添加 NDK API 和 預(yù)構(gòu)建庫。