深入理解 Android NDK 编译

本文详细解析了Android Studio如何利用CMake进行NDK编译,重点介绍了CMake的工作原理及其配置文件的作用,帮助读者理解现代Android开发中的交叉编译流程。

使用 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 到< project-root >/< module-root >/.externalNativeBuild/cmake/< build-type >/< ABI >/目录下。

我们来看一下在 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
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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   ......
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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 ......
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

设置完 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}")
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

所以通过上面的分析我们可以了解到,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进行交叉编译的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值