基础知识
1.cmake是什么?
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
谷歌从AndroidStudio2.2以上就添加了Cmake方式来编译NDK代码,并从NDK例子看出,默认编译的方式就是cmake方式。
创建Android studio ndk工程
傻瓜式操作
- 新建一个工程点击如下:然后把C/Cpp代码复制到
src/main/cpp
下,配置 CMakeLists.txt
手动配置
- 在
src/main
下新建jni
或者cpp
目录, - 新建CMakeLists.txt,推荐目录
/src/main/jni/CMakeLists.txt
, 如下为默认生成的格式:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets 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 )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
并在build.gradle下关联CMakeLists.txt
externalNativeBuild { cmake { //默认和build.gradle同一目录: path "CMakeLists.txt" path "/src/main/jni/CMakeLists.txt" } }
配置gradle脚本
android { compileSdkVersion 28 defaultConfig { ... externalNativeBuild { cmake { // Passes optional arguments to CMake. arguments "-DCMAKE_VERBOSE_MAKEFILE=TRUE" // Sets optional flags for the C compiler. cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2" // Sets a flag to enable format macro constants for the C++ compiler. // -frtti: Runtime Type Information Support // -fexceptions : Exception Support //-std=c++14 : C++ Library Support cppFlags "-D__STDC_FORMAT_MACROS" //更多 arguments https://developer.android.com/ndk/guides/cmake } } ndk { // 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', //指定生成的CPU架构 Specifies the ABI configurations of your native abiFilters 'armeabi-v7a' } } externalNativeBuild { cmake { //默认和build.gradle同一目录: path "CMakeLists.txt" path "/src/main/jni/CMakeLists.txt" } } buildTypes { ... } }
配置 CMake 构建脚本
如果您的原生源文件还没有 CMake 构建脚本,则您需要自行创建一个并包含适当的 CMake 命令。CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt
。本部分介绍了您应包含到构建脚本中的一些基本命令,用于在创建原生库时指示 CMake 应使用哪些源文件。
注:如果您的项目使用 ndk-build,则不需要创建 CMake 构建脚本。提供一个指向您的 Android.mk
文件的路径,将 Gradle 关联到您的原生库。
要创建一个可以用作 CMake 构建脚本的纯文本文件,请按以下步骤操作:
从 IDE 的左侧打开 Project 窗格并从下拉菜单中选择 Project 视图。
右键点击您的模块的根目录并选择 New > File。
注:您可以在所需的任意位置创建构建脚本。不过,在配置构建脚本时,原生源文件和库的路径将与构建脚本的位置相关。
输入“CMakeLists.txt”作为文件名并点击 OK。
add_library
现在,您可以添加 CMake 命令,对您的构建脚本进行配置。要指示 CMake 从原生源代码创建一个原生库,请将 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 构建脚本添加源文件或库时,Android Studio 还会在您同步项目后在 Project 视图下显示关联的标头文件。不过,为了确保 CMake 可以在编译时定位您的标头文件,您需要将 include_directories()
命令添加到 CMake 构建脚本中并指定标头的路径:
add_library(...)
\# Specifies a path to native header files.
include_directories(src/main/cpp/include/)
CMake 使用以下规范来为库文件命名:
lib库名称.so
例如,如果您在构建脚本中指定“native-lib”作为共享库的名称,CMake 将创建一个名称为 libnative-lib.so
的文件。不过,在 Java 代码中加载此库时,请使用您在 CMake 构建脚本中指定的名称:
static { System.loadLibrary(“native-lib”); }
注:如果您在 CMake 构建脚本中重命名或移除某个库,您需要先清理项目,Gradle 随后才会应用更改或者从 APK 中移除旧版本的库。要清理项目,请从菜单栏中选择 Build > Clean Project。
Android Studio 会自动将源文件和标头添加到 Project 窗格的 cpp 组中。使用多个 add_library()
命令,您可以为 CMake 定义要从其他源文件构建的更多库。
添加 NDK API
Android NDK 提供了一套实用的原生 API 和库。通过将 NDK 库包含到项目的 CMakeLists.txt
脚本文件中,您可以使用这些 API 中的任意一种。
预构建的 NDK 库已经存在于 Android 平台上,因此,您无需再构建或将其封装到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,您甚至不需要在您的本地 NDK 安装中指定库的位置 - 只需要向 CMake 提供您希望使用的库的名称,并将其关联到您自己的原生库。
将 find_library()
命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。您可以使用此变量在构建脚本的其他部分引用 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 )
为了确保您的原生库可以在 log
库中调用函数,您需要使用 CMake 构建脚本中的 target_link_libraries()
命令关联库:
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 还以源代码的形式包含一些库,您在构建和关联到您的原生库时需要使用这些代码。您可以使用 CMake 构建脚本中的 add_library()
命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可以使用 ANDROID_NDK
路径变量,Android Studio 会自动为您定义此变量。
以下命令可以指示 CMake 构建 android_native_app_glue.c
,后者会将 NativeActivity
生命周期事件和触摸输入置于静态库中并将静态库关联到 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} )
添加其他预构建库
导入so库
添加预构建库与为 CMake 指定要构建的另一个原生库类似。不过,由于库已经预先构建,您需要使用 IMPORTED
标志告知 CMake 您只希望将库导入到项目中:
add_library( imported-lib
SHARED
IMPORTED )
然后,您需要使用 set_target_properties() 命令指定库的路径,如下所示。
某些库为特定的 CPU 架构(或应用二进制接口 (ABI))提供了单独的软件包,并将其组织到单独的目录中。此方法既有助于库充分利用特定的 CPU 架构,又能让您仅使用所需的库版本。要向 CMake 构建脚本中添加库的多个 ABI 版本,而不必为库的每个版本编写多个命令,您可以使用 ANDROID_ABI
路径变量。此变量使用 NDK 支持的一组默认 ABI,或者您手动配置 Gradle 而让其使用的一组经过筛选的 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()
命令,并包含标头文件的路径:
include_directories( imported-lib/include/ )
注:如果您希望封装一个并不是构建时依赖项的预构建库(例如在添加属于
imported-lib
依赖项的预构建库时),则不需要执行以下说明来关联库。
要将预构建库关联到您自己的原生库,请将其添加到 CMake 构建脚本的 target_link_libraries()
命令中:
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )
在您构建应用时,Gradle 会自动将导入的库封装到 APK 中。您可以使用 APK 分析器验证 Gradle 将哪些库封装到您的 APK 中。如需了解有关 CMake 命令的详细信息,请参阅 CMake 文档。
导入.a静态库
android studio cmake 配置.a连接库
实战小案例
指定 C++标准
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions -std=c++14"
arguments '-DANDROID_STL=c++_shared'
}
}
编译一个目录中所有源文件
# aux_source_directory 方法将路径列表全部放到一个变量中
aux_source_directory(${CMAKE_HOME_DIRECTORY}/src/api SRC_LIST)
aux_source_directory(${CMAKE_HOME_DIRECTORY}/src/core CORE_SRC_LIST)
# 拼接到路径列表
list(APPEND SRC_LIST ${CORE_SRC_LIST})
add_library(native-lib SHARED ${SRC_LIST})
调试
- message 方法打印
cmake_minimum_required(VERSION 3.4.1) message(STATUS "execute CMakeLists") # 日志输出==> .externalNativeBuild/cmake/debug/{abi}/cmake_build_output.txt
CMakeLists.txt 改动什么时候执行
sync
其他的时候只是出来缓存测试了下,好像在 sync 的时候会执行。执行一次后会生成 makefile 的文件缓存之类的东西放在 externalNativeBuild 中。所以如果 CMakeLists.txt 中没有修改的话再次同步好像是不会重新执行的。(或者删除 .externalNativeBuild 目录)
真正编译的时候好像只是读取.externalNativeBuild 目录中已经解析好的 makefile 去编译。不会再去执行 CMakeLists.txt
教程
- 官方7step入门教程: cmake-tutorial 【英文】
翻译: CMAKE官网教程 并有相关方法的详解
进阶
Book: Mastering CMake
资料文献
-
- Introduction
- Building
- Architectures and CPUs
- Debugging and Profiling
- Libraries 安卓各版本提供可调用ndk库
- High-Performance Audio
- 一些流行的底层的第三方库Simpleperf/ OpenSL ES/Vulkan/Machine Learning