Android Studio 如何配置 CMake
前面的小節(jié)我們學(xué)習(xí)了如何安裝 NDK 和 CMake。本小節(jié)學(xué)習(xí)如何配置 CMake。
1. 概述
CMake 構(gòu)建腳本是一個(gè)純文本文件,我們必須將其命名為 CMakeLists.txt,并在其中包含 CMake 構(gòu)建我們的 C/C++ 庫(kù)時(shí)需要使用的命令。如果我們的原生源代碼文件還沒(méi)有 CMake 構(gòu)建腳本,我們需要自行創(chuàng)建一個(gè),并在其中包含適當(dāng)?shù)?CMake 命令。
下面我們將學(xué)習(xí)應(yīng)該在構(gòu)建腳本中包含哪些基本命令,以便指示 CMake 在創(chuàng)建原生庫(kù)時(shí)使用哪些源代碼文件。如需了解詳情,請(qǐng)參閱介紹 CMake 命令的官方文檔。
2. 創(chuàng)建 CMake 構(gòu)建腳本
要?jiǎng)?chuàng)建一個(gè)可以用作 CMake 構(gòu)建腳本的純文本文件,請(qǐng)按以下步驟操作:
-
從 IDE 的左側(cè)打開(kāi) Project 窗格,然后從下拉菜單中選擇 Project 視圖。
-
右鍵點(diǎn)擊 your-module 的根目錄,然后依次選擇 New > File。
-
輸入 CMakeLists.txt 作為文件名,然后點(diǎn)擊 OK。
現(xiàn)在,我們可以通過(guò)添加 CMake 命令來(lái)配置我們的構(gòu)建腳本。要指示 CMake 根據(jù)原生源代碼創(chuàng)建原生庫(kù),請(qǐ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)建腳本添加源代碼文件或庫(kù)時(shí),Android Studio 還會(huì)在我們同步項(xiàng)目后在 Project 視圖中顯示相關(guān)的頭文件。不過(guò),為了讓 CMake 能夠在編譯時(shí)找到頭文件,我們需要向 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ī)范來(lái)為庫(kù)文件命名。例如,如果我們?cè)跇?gòu)建腳本中指定 native-lib 作為共享庫(kù)的名稱,CMake 就會(huì)創(chuàng)建一個(gè)名為 libnative-lib.so 的文件。不過(guò),在 Java 代碼中加載此庫(kù)時(shí),要使用在 CMake 構(gòu)建腳本中指定的名稱:
static {
System.loadLibrary("native-lib");
}
3. 添加 NDK API
Android NDK 提供了一套我們可能會(huì)覺(jué)得非常實(shí)用的原生 API 和庫(kù)。通過(guò)在項(xiàng)目的 CMakeLists.txt 腳本文件中包含 NDK 庫(kù),我們可以使用其中任何 API。
Android 平臺(tái)上已存在預(yù)構(gòu)建的 NDK 庫(kù),因此我們無(wú)需構(gòu)建它們或?qū)⑺鼈兇虬?APK 中。由于這些 NDK 庫(kù)已位于 CMake 搜索路徑中,因此我們甚至無(wú)需指定本地安裝的 NDK 庫(kù)的位置,我們只需為 CMake 提供我們想要使用的庫(kù)的名稱,并將其與我們自己的原生庫(kù)相關(guān)聯(lián)即可。
向 CMake 構(gòu)建腳本添加 find_library() 命令以找到 NDK 庫(kù)并將其路徑存儲(chǔ)為一個(gè)變量。我們可以使用此變量在構(gòu)建腳本的其他部分引用 NDK 庫(kù)。
以下示例可以找到 Android 專有的日志支持庫(kù),并會(huì)將其路徑存儲(chǔ)在 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 )
為了讓我們的原生庫(kù)能夠調(diào)用 log 庫(kù)中的函數(shù),我們需要使用 CMake 構(gòu)建腳本中的 target_link_libraries() 命令來(lái)關(guān)聯(lián)這些庫(kù):
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 還以源代碼的形式包含一些庫(kù),我們將需要構(gòu)建這些代碼并將其關(guān)聯(lián)到我們的原生庫(kù)。我們可以使用 CMake 構(gòu)建腳本中的 add_library() 命令將源代碼編譯到原生庫(kù)中。如需提供本地 NDK 庫(kù)的路徑,我們可以使用 Android Studio 自動(dòng)為我們定義的 ANDROID_NDK 路徑變量。
例如,以下命令告訴 CMake 要構(gòu)建 android_native_app_glue.c(負(fù)責(zé)管理 NativeActivity 生命周期事件和觸摸輸入),并將其鏈接到靜態(tài)庫(kù) 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)建庫(kù)
添加預(yù)構(gòu)建庫(kù)的步驟與為 CMake 指定其他要構(gòu)建的原生庫(kù)的步驟相似。不過(guò),由于庫(kù)已構(gòu)建,因此我們需要使用 IMPORTED 標(biāo)記告訴 CMake 我們只想要將此庫(kù)導(dǎo)入到我們的項(xiàng)目中。
add_library( imported-lib
SHARED
IMPORTED )
然后,我們需要使用 set_target_properties() 命令指定庫(kù)的路徑,具體步驟如下所示。
某些庫(kù)會(huì)針對(duì)特定的 CPU 架構(gòu)或應(yīng)用二進(jìn)制接口 (ABI) 提供單獨(dú)的軟件包,并將其整理到單獨(dú)的目錄中。此方法既有助于庫(kù)充分利用特定的 CPU 架構(gòu),又能讓我們只使用所需的庫(kù)版本。要向 CMake 構(gòu)建腳本添加庫(kù)的多個(gè) ABI 版本,而不必為庫(kù)的每個(gè)版本編寫(xiě)多個(gè)命令,我們可以使用 ANDROID_ABI 路徑變量。此變量使用的是 NDK 支持的一組默認(rèn) ABI,或者我們手動(dòng)配置 Gradle 以使用的一組經(jīng)過(guò)過(guò)濾的 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 能夠在編譯時(shí)定位我們的頭文件,我們需要使用 include_directories() 命令并包含相應(yīng)頭文件的路徑:
include_directories( imported-lib/include/ )
如需將預(yù)構(gòu)建庫(kù)關(guān)聯(lián)到我們自己的原生庫(kù),請(qǐng)將其添加到 CMake 構(gòu)建腳本的 target_link_libraries() 命令中:
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )
如需將預(yù)構(gòu)建庫(kù)打包到 APK 中,我們需要使用 sourceSets 塊手動(dòng)配置 Gradle 以包含 .so 文件的路徑。構(gòu)建 APK 后,我們可以使用 APK 分析器驗(yàn)證 Gradle 會(huì)將哪些庫(kù)打包到我們的 APK 中。
5. 包含其他 CMake 項(xiàng)目
如果想要構(gòu)建多個(gè) CMake 項(xiàng)目并在 Android 項(xiàng)目中包含它們的輸出,我們可以使用一個(gè) CMakeLists.txt 文件(即為我們關(guān)聯(lián)到 Gradle 的那個(gè)文件)作為頂級(jí) CMake 構(gòu)建腳本,并添加其他 CMake 項(xiàng)目作為此構(gòu)建腳本的依賴項(xiàng)。以下頂級(jí) CMake 構(gòu)建腳本會(huì)使用 add_subdirectory() 命令將另一個(gè) CMakeLists.txt 文件指定為構(gòu)建依賴項(xiàng),然后關(guān)聯(lián)其輸出,就像處理任何其他預(yù)構(gòu)建庫(kù)一樣。
# 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é)課程我們主要學(xué)習(xí)了如何配置 CMake。本節(jié)課程的重點(diǎn)如下:
- 掌握如何創(chuàng)建 CMake 構(gòu)建腳本;
- 掌握如何添加 NDK API 和 預(yù)構(gòu)建庫(kù)。