写点什么

Android NDK 开发之 CMake 必知必会,后台开发 Android 岗

发布于: 23 小时前

注释与大小写

在前面就已经用到了 CMake 注释了,每一行的开头 # 代表注释。


另外,CMake 的所有语法指令是不区分大小写的。

变量定义与消息打印

通过 set 来定义变量:

变量名为 var,值为 hello

set(var hello)


当需要引用变量时,在变量名外面加上 ${} 符合来引用变量。

引用 var 变量

${var}


还可以通过 message 在命令行中输出打印内容。


set(var hello)message(${var})

数学和字符串操作

数学操作

CMake 中通过 math 来实现数学操作。

math 使用,EXPR 为大小

math(EXPR <output-variable> <math-expression>)


math(EXPR var "1+1")

输出结果为 2

message(${var})


math 支持 +, -, *, /, %, |, &, ^, ~, <<, >> 等操作,和 C 语言中大致相同。

字符串操作

CMake 通过 string 来实现字符串的操作,这波操作有很多,包括将字符串全部大写、全部小写、求字符串长度、查找与替换等操作。


具体查看 官方文档


set(var "this is string")set(sub "this")set(sub1 "that")

字符串的查找,结果保存在 result 变量中

string(FIND {sub1} result )

找到了输出 0 ,否则为 -1

message(${result})

将字符串全部大写

string(TOUPPER {result})

求字符串的长度

string(LENGTH {num})


另外,通过空白或者分隔符号可以表示字符串序列。


set(foo this is a list) // 实际内容为字符串序列 message(${foo})


当字符串中需要用到空白或者分隔符时,再用双括号""表示为同一个字符串内容。


set(foo "this is a list") // 实际内容为一个字符串 message(${foo})

文件操作

CMake 中通过 file 来实现文件操作,包括文件读写、下载文件、文件重命名等。


具体查看 官方文档

文件重命名

file(RENAME "test.txt" "new.txt")

文件下载

把文件 URL 设定为变量

set(var "http://img.zcool.cn/community/0117e2571b8b246ac72538120dd8a4.jpg")

使用 DOWNLOAD 下载

file(DOWNLOAD ${var} "/Users/glumes/CLionProjects/HelloCMake/image.jpg")


在文件的操作中,还有两个很重要的指令 GLOBGLOB_RECURSE

GLOB 的使用

file(GLOB ROOT_SOURCE *.cpp)

GLOB_RECURSE 的使用

file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp)


其中,GLOB 指令会将所有匹配 *.cpp 表达式的文件组成一个列表,并保存在 ROOT_SOURCE 变量中。


GLOB_RECURSE 指令和 GLOB 类似,但是它会遍历匹配目录的所有文件以及子目录下面的文件。


使用 GLOBGLOB_RECURSE 有好处,就是当添加需要编译的文件时,不用再一个一个手动添加了,同一目录下的内容都被包含在对应变量中了,但也有弊端,就是新建了文件,但是 CMake 并没有改变,导致在编译时也会重新产生构建文件,要解决这个问题,就是动一动 CMake,让编译器检测到它有改变就好了。

预定义的常量

在 CMake 中有许多预定义的常量,使用好这些常量能起到事半功倍的效果。


  • CMAKE_CURRENT_SOURCE_DIR

  • 指当前 CMake 文件所在的文件夹路径

  • CMAKE_SOURCE_DIR

  • 指当前工程的 CMake 文件所在路径

  • CMAKE_CURRENT_LIST_FILE

  • 指当前 CMake 文件的完整路径

  • PROJECT_SOURCE_DIR

  • 指当前工程的路径


比如,在 add_library 中需要指定 cpp 文件的路径,以 CMAKE_CURRENT_SOURCE_DIR 为基准,指定 cpp 相对它的路径就好了。

利用预定义的常量来指定文件路径

add_library( # Sets the name of the library.openglutil

Sets the library as a shared library.

SHARED

Provides a relative path to your source file(s).

${CMAKE_CURRENT_SOURCE_DIR}/opengl_util.cpp)

平台相关的常量

CMake 能够用来在 Window、Linux、Mac 平台下进行编译,在它的内部也定义了和这些平台相关的变量。


具体查看 官方文档


列举一些常见的:


  • WIN32

  • 如果编译的目标系统是 Window,那么 WIN32 为 True 。

  • UNIX

  • 如果编译的目标系统是 Unix 或者类 Unix 也就是 Linux ,那么 UNIX 为 True 。

  • MSVC

  • 如果编译器是 Window 上的 Visual C++ 之类的,那么 MSVC 为 True 。

  • ANDROID

  • 如果目标系统是 Android ,那么 ANDROID 为 1 。

  • APPLE

  • 如果目标系统是 APPLE ,那么 APPLE 为 1 。


有了这些常量做区分,就可以在一份 CMake 文件中编写不同平台的编译选项。


if(WIN32){

do something

}elseif(UNIX){

do something

}

函数、宏、流程控制和选项 等命令

具体参考cmake-commands ,这里面包括了很多重要且常见的指令。


简单示例 CMake 中的函数操作:


function(add a b)message("this is function call")math(EXPR num "{b}" )message("result is ${aa}")endfunction()


add(1 2)


其中,function 为定义函数,第一个参数为函数名称,后面为函数参数。


在调用函数时,参数之间用空格隔开,不要用逗号。


宏的使用与函数使用有点类似:


macro(del a b)message("this is macro call")math(EXPR num "{b}")message("num is ${num}")endmacro()


del(1 2)


在流程控制方面,CMake 也提供了 if、el


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


se 这样的操作:


set(num 0)if (1 AND {num})message("or operation")else ()message("not reach")endif ()


其中,CMake 提供了 ANDORNOTLESSEQUAL 等等这样的操作来对数据进行判断,比如 AND 就是要求两边同为 True 才行。


另外 CMake 还提供了循环迭代的操作:


set(stringList this is string list)foreach (str {str}")endforeach ()


CMake 还提供了一个 option 指令。


可以通过它来给 CMake 定义一些全局选项:


option(ENABLE_SHARED "Build shared libraries" TRUE)


if(ENABLE_SHARED)

do something

else()

do something

endif()


可能会觉得 option 无非就是一个 True or False 的标志位,可以用变量来代替,但使用变量的话,还得添加 ${} 来表示变量,而使用 option 直接引用名称就好了。

CMake 阅读实践

明白了上述的 CMake 语法以及从官网去查找陌生的指令意思,就基本上可以看懂大部分的 CMake 文件了。


这里举两个开源库的例子:



这两个例子中大量用到了前面所讲的内容,可以试着读一读增加熟练度。

为编译的库设置属性

接下来再回到用 CMake 编译动态库的话题上,毕竟 Android NDK 开发也主要是用来编译库了,当编译完 so 之后,我们可以对它做一些操作。


通过 set_target_properties 来给编译的库设定相关属性内容,函数原型如下:


set_target_properties(target1 target2 ...PROPERTIES prop1 value1prop2 value2 ...)


比如,要将编译的库改个名称:


set_target_properties(native-lib PROPERTIES OUTPUT_NAME "testlib" )


更多的属性内容可以参考 官方文档


不过,这里面有一些属性设定无效,在 Android Studio 上试了无效,在 CLion 上反而可以,当然也可能是我使用姿势不对。


比如,实现动态库的版本号:


set_target_properties(native-lib PROPERTIES VERSION 1.2 SOVERSION 1 )


对于已经编译好的动态库,想要把它导入进来,也需要用到一个属性。


比如编译的 FFmpeg 动态库,

使用 IMPORTED 表示导入库

add_library(avcodec-57_lib SHARED IMPORTED)

使用 IMPORTED_LOCATION 属性指定库的路径

set_target_properties(avcodec-57_lib PROPERTIES IMPORTED_LOCATION${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/armeabi/libavcodec-57.so )

链接到其他的库

如果编译了多个库,并且想库与库之间进行链接,那么就要通过 target_link_libraries

评论

发布
暂无评论
Android NDK 开发之 CMake 必知必会,后台开发Android岗