使用 CMake 進行Android NDK編譯的原理
介紹
Android Studio 2.2 及以後的版本默認使用CMake進行 NDK 編譯, 其中最吸引人的地方是,在開發NDK程序時可以進行聯機調試,這真是大在的方便了開發者開發NDK程序的效率了。 那麼使用CMake編譯NDK程序是否與我們之前介紹的使用ndk-build編譯有很大的不同呢?下面我們就來一窺它的原理。
前面我給大家介紹了兩種交叉編譯的方式,沒看過的同學可以瀏覽一下( Linux/Mac 交叉編譯 Android 程序 和 深入理解Android NDK編譯(一) )
什麼是CMake
CMake是個開源的跨平臺自動化建構系統,它用配置文件控制建構過程(build process)的方式和Unix的Make相似,只是CMake的配置文件取名爲CMakeLists.txt。Cmake並不直接建構出最終的軟件,而是產生標準的建構檔(如Unix的Makefile或Windows Visual C++的projects/workspaces),然後再依一般的建構方式使用。“CMake”這個名字是"cross platform make"的縮寫。雖然名字中含有"make",但是CMake和Unix上常見的“make”系統是分開的,而且更爲高級。
Android Studio 如何使用 CMake
其實通過 CMake 進行 NDK 交叉編譯的方式與我們之前介紹的兩種方式的原理是相同的。 都是要先設定交叉編譯各種工具的環境, 包括編譯器、鏈接器等。 然後再通過自動化構建工具進行編譯。
Android Studio在執行 CMake build 之前,會將需要的參數存放在 cmake_build_command.txt 文件中,針對每種ABI(arm, mips, x86等)及每種build類型(debug, release),Android Studio都會拷貝一份 cmake_build_command.txt 到//.externalNativeBuild/cmake///目錄下。
我們來看一下在 cmake_build_command.txt 裏都是些什麼內容:
Executable : ~/Library/Android/sdk/cmake/3.6.3155560/bin/cmake
arguments :
-H~/mytest/MyApplication/app
-B~/mytest/MyApplication/app/.externalNativeBuild/cmake/debug/arm64-v8a
-GAndroid Gradle - Ninja
-DANDROID_ABI=arm64-v8a
-DANDROID_NDK=~/Library/Android/sdk/ndk-bundle
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=~/mytest/MyApplication/app/build/intermediates/cmake/debug/obj/arm64-v8a
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_MAKE_PROGRAM=~/Library/Android/sdk/cmake/3.6.3155560/bin/ninja
#下面這個參數特別重要
-DCMAKE_TOOLCHAIN_FILE=~/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DANDROID_PLATFORM=android-21
-DCMAKE_CXX_FLAGS=-std=c++11 -frtti -fexceptions
Build Arguments | Description |
---|---|
-G < build-system > | 一般設置爲 “Android Gradle - Ninja” 它指明 CMake 使用 ninja build system 編譯並鏈接C/C++ ,同時 CMake 還會產生android_gradle_build.json 文件,該文件包含了Gradle CMake 插件使用的信息,如編譯參數,產生的目標名等。 |
-DANDROID_ABI < abi > | NDK 支持的 ABIs, 如 armeabi,armeabi-v7a,armeabi-v7a with NEON,arm64-v8a等。 |
-DANDROID_NDK < path > | NDK安裝路徑 |
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY < path > | 輸出庫的位置 |
-DCMAKE_BUILD_TYPE < type > | build 類型, release 或 debug |
-DCMAKE_MAKE_PROGRAM < program-name > | 啓動 native build 系統的工具 |
-DCMAKE_TOOLCHAIN_FILE < path > | CMake 用於交叉編譯 Andriod配置文件的路徑。它位於 $NDK/build/cmake/ directory 目錄下。 |
-DANDROID_PLATFORM < level > | CMake 編譯 Android API 級別 |
在這些參數裏,-DCMAKE_TOOLCHAIN_FILE 這個參數特別重要,因爲 Android Stuido 在這個參數指定的文件裏設置了交叉編譯工具的環境變量,下面我們來大體看一下它流程:
207 ......
208 # ABI.
209 set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
210 if(ANDROID_ABI MATCHES "^armeabi(-v7a)?$")
211 set(ANDROID_SYSROOT_ABI arm)
212 set(ANDROID_TOOLCHAIN_NAME arm-linux-androideabi)
213 set(ANDROID_TOOLCHAIN_ROOT ${ANDROID_TOOLCHAIN_NAME})
214 set(ANDROID_HEADER_TRIPLE arm-linux-androideabi)
215 if(ANDROID_ABI STREQUAL armeabi)
216 set(CMAKE_SYSTEM_PROCESSOR armv5te)
217 set(ANDROID_LLVM_TRIPLE armv5te-none-linux-androideabi)
218 elseif(ANDROID_ABI STREQUAL armeabi-v7a)
219 set(CMAKE_SYSTEM_PROCESSOR armv7-a)
220 set(ANDROID_LLVM_TRIPLE armv7-none-linux-androideabi)
221 endif()
222
223 ......
android.toolchain.cmake 在第 208 行根據 cmake_build_command.txt 文件中ABI的值,設置 ANDROID_SYSROOT_ABI、ANDROID_TOOLCHAIN_NAME、ANDROID_TOOLCHAIN_ROOT等參數。 然後走到 318 行,設置 CMAKE_SYSROOT 值如下:
317 ......
318 # Sysroot.
319 if(ANDROID_DEPRECATED_HEADERS)
320 set(CMAKE_SYSROOT
321 "${ANDROID_NDK}/platforms/${ANDROID_PLATFORM}/arch-${ANDROID_SYSROOT_ABI}")
322 else()
323 set(CMAKE_SYSROOT "${ANDROID_NDK}/sysroot")
324 ......
設置完 CMAKE_SYSROOT 走到 355 行,設置ANDROID_TOOLCHAIN_ROOT 和 C/C++ 編譯器,代碼如下:
354 ......
355 # Toolchain.
355 # 首先判斷運行的宿主機是什麼
356 if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
357 set(ANDROID_HOST_TAG linux-x86_64)
358 elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
359 set(ANDROID_HOST_TAG darwin-x86_64)
360 elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
361 set(ANDROID_HOST_TAG windows-x86_64)
362 endif()
362 # 設置 ANDROID_TOOLCHAIN_ROOT
363 set(ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_ROOT}-4.9/prebuilt/${ANDROID_HOST_TAG}")
364 set(ANDROID_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_NAME}-")
365 ......
368
369 set(ANDROID_HOST_PREBUILTS "${ANDROID_NDK}/prebuilt/${ANDROID_HOST_TAG}")
370 # 如果編譯器是 clang
371 if(ANDROID_TOOLCHAIN STREQUAL clang)
372 set(ANDROID_LLVM_TOOLCHAIN_PREFIX "${ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}/bin/")
373 set(ANDROID_C_COMPILER "${ANDROID_LLVM_TOOLCHAIN_PREFIX}clang${ANDROID_TOOLCHAIN_SUFFIX}")
374 set(ANDROID_CXX_COMPILER "${ANDROID_LLVM_TOOLCHAIN_PREFIX}clang++${ANDROID_TOOLCHAIN_SUFFIX}")
375 set(ANDROID_ASM_COMPILER "${ANDROID_LLVM_TOOLCHAIN_PREFIX}clang${ANDROID_TOOLCHAIN_SUFFIX}")
376 ......
396 # 如果編譯器是 gcc
397 elseif(ANDROID_TOOLCHAIN STREQUAL gcc)
398 set(ANDROID_C_COMPILER "${ANDROID_TOOLCHAIN_PREFIX}gcc${ANDROID_TOOLCHAIN_SUFFIX}")
399 set(ANDROID_CXX_COMPILER "${ANDROID_TOOLCHAIN_PREFIX}g++${ANDROID_TOOLCHAIN_SUFFIX}")
400 set(ANDROID_ASM_COMPILER "${ANDROID_TOOLCHAIN_PREFIX}gcc${ANDROID_TOOLCHAIN_SUFFIX}")
所以通過上面的分析我們可以瞭解到,Android Studio 通過cmake_build_command.txt指定的 android.toolchain.cmake 文件就把交叉編譯的環境設置好了。
CMake NDK 編譯過程
當我們在Android Studio中build我們的NDK工程時,AS會通過上面的步驟爲我們設置好交叉編譯環境,然後再將CMakelists.txt文件傳給 CMake, CMake解析裏面的內容,並最終調用不同平臺的工具,編譯出我們需要的目標環境程序。
小結
通過上面的分析,我們知道了 Android Studio 在開發 NDK 程序時,是如何使用 CMake Gradle plugin 設置交叉編譯環境的,也基本瞭解了 CMake 編譯 NDK 程序的基本流程。希望本篇文章可以幫助大家理解最新的 Andriod Studio 是如何使用 CMake進行交叉編譯的。
csdn地址:http://blog.csdn.net/u012534831
github地址:https://github.com/qht1003077897 \
如有幫助,請多多點贊支持哦。