美文网首页音视频直播技术DevSupport
深入理解使用CMake编译 NDK 程序

深入理解使用CMake编译 NDK 程序

作者: 音视频直播技术专家 | 来源:发表于2017-07-02 13:24 被阅读535次

    使用 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
    
    Build Arguments Description
    -G < build-system > <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进行交叉编译的。


    微信公众号

    相关文章

      网友评论

      • d6bc66d4ced8:cmake确实跨平台利器,写的简洁明了啊

      本文标题:深入理解使用CMake编译 NDK 程序

      本文链接:https://www.haomeiwen.com/subject/egencxtx.html