数学操作
CMake 中通过 math
来实现数学操作。
math 使用,EXPR 为大小
math(EXPR )
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 ${var} ${sub1} result )
找到了输出 0 ,否则为 -1
message(${result})
将字符串全部大写
string(TOUPPER v a r r e s u l t ) m e s s a g e ( {var} result) message( varresult)message({result})
求字符串的长度
string(LENGTH v a r n u m ) m e s s a g e ( {var} num) message( varnum)message({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”)
在文件的操作中,还有两个很重要的指令 GLOB
和 GLOB_RECURSE
。
GLOB 的使用
file(GLOB ROOT_SOURCE *.cpp)
GLOB_RECURSE 的使用
file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp)
其中,GLOB
指令会将所有匹配 *.cpp
表达式的文件组成一个列表,并保存在 ROOT_SOURCE
变量中。
而 GLOB_RECURSE
指令和 GLOB
类似,但是它会遍历匹配目录的所有文件以及子目录下面的文件。
使用 GLOB
和 GLOB_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 “${a} + ${b}” )
message(“result is ${aa}”)
endfunction()
add(1 2)
其中,function 为定义函数,第一个参数为函数名称,后面为函数参数。
在调用函数时,参数之间用空格隔开,不要用逗号。
宏的使用与函数使用有点类似:
macro(del a b)
message(“this is macro call”)
math(EXPR num “${a} - ${b}”)
message(“num is ${num}”)
endmacro()
del(1 2)
在流程控制方面,CMake 也提供了 if、else 这样的操作:
set(num 0)
if (1 AND ${num})
message(“and operation”)
elseif (1 OR ${num})
message(“or operation”)
else ()
message(“not reach”)
endif ()
其中,CMake 提供了 AND
、OR
、NOT
、LESS
、EQUAL
等等这样的操作来对数据进行判断,比如 AND
就是要求两边同为 True 才行。
另外 CMake 还提供了循环迭代的操作:
set(stringList this is string list)
foreach (str ${stringList})
message(“str is ${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 文件了。
这里举两个开源库的例子:
- github.com/g-truc/glm
glm
是一个用来实现矩阵计算的,在 OpenGL 的开发中会用到。CMakeLists.txt
地址在 这里- github.com/libjpeg-tur…
libjpeg-turbo
是用来进行图片压缩的,在 Android 底层就是用的它。CMakeLists.txt
地址在 这里
这两个例子中大量用到了前面所讲的内容,可以试着读一读增加熟练度。
为编译的库设置属性
接下来再回到用 CMake 编译动态库的话题上,毕竟 Android NDK 开发也主要是用来编译库了,当编译完 so 之后,我们可以对它做一些操作。
通过 set_target_properties
来给编译的库设定相关属性内容,函数原型如下:
set_target_properties(target1 target2 …
PROPERTIES prop1 value1
prop2 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
。
target_link_libraries( native-lib
glm
turbojpeg
log )
在 Android 底层也提供了一些 so 库供上层链接使用,也要通过上面的方式来链接,比如最常见的就是 log
库打印日志。
如果要链接自己编译的多个库文件,首先要保证每个库的代码都对应一个 CMakeLists.txt
文件,这个 CMakeLists.txt
文件指定当前要编译的库的信息。
然后在当前库的 CMakeLists.txt
文件中通过 ADD_SUBDIRECTORY
将其他库的目录添加进来,这样才能够链接到。
ADD_SUBDIRECTORY(src/main/cpp/turbojpeg)
ADD_SUBDIRECTORY(src/main/cpp/glm)
添加头文件
在使用的时候有一个容易忽略的步骤就是添加头文件,通过 include_directories
指令把头文件目录包含进来。
这样就可以直接使用 #include "header.h"
的方式包含头文件,而不用 #include "path/path/header.h"
这样添加路径的方式来包含。
小结
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
学习,对自己的提升是很慢的。
我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
[外链图片转存中…(img-74Wsi5aP-1713086225546)]
我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!