NDK开发之CMake

CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。 能够输出各种各样的makefile或者project文件。CMake并不直接构建出最终的软件, 而是产生其他工具的脚本(如makefile),然后再依据这个工具的构建方式使用。

CMake是一个比make更高级的编译配置工具,它可以根据不同的平台、不同的编译器, 生成相应的makefile或vcproj项目,从而达到跨平台的目的。

Android Studio利用CMake生成的是ninja。ninja是一个小型的关注速度的构建系统。 我们不需要关心ninja的脚本,知道怎么配置CMake就可以了。

CMake其实是一个跨平台的支持产出各种不同的构建脚本的一个工具。

在Android Studio 2.2及以上,构建原生库的默认工具是CMake. 在项目中会存在一个CMakeLists.txt文件。

配置项释义

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 最低支持的版本,注意:这里并不能代表最终的版本,最终版本在app.build.gradle中设置的
cmake_minimum_required(VERSION 3.x.x)

# 当前工程名,以前的旧版本,是没有设置的,这个可以设置,也可以不设置
project("ndk_cmake")

# 批量导入 cpp c源文件
# file 可以定义一个变量 SOURCE, GLOB(使用GLOB从源树中收集源文件列表,就可以开心的 *.cpp *.c *.h)
file(GLOB SOURCE *.cpp *.c)

# 添加一个库(动态库SHARED,静态库STATIC)
add_library(native-lib # 库的名字 ---> libnative-lib.so
SHARED # 动态库

# cpp的源文件:把cpp源文件编译成 libnative-lib.so 库
${SOURCE}
)

# 查找一个 NDK工具中的 动态库(liblog.so)

# 路径:D:\Android\Sdk\ndk\21.0.6113669\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\lib\arm-linux-androideabi\16\liblog.so
# -1(因为local.properties知道了NDK版本,或者是你当前的NDK版本)
# -2(因为我的手机是arm32的 所以 == arm-linux-androideabi 而且还我运行过)
# -3(因为 minSdkVersion 16)
find_library(log-lib
log )

# native-lib是我们的总库,也就是我们在 apk/lib/libnative-lib.so
# 然后 把log库链接到 总库中去,总库的cpp代码就可以使用 android/log.h的库实现代码了
target_link_libraries(native-lib # 被链接的总库
${log-lib} # 链接的具体库

# getndk
)

# log 信息输出的查看
# 以前的Cmake版本都是在output.txt, 现在最新版本Cmake在metadata_generation_stderr.txt或cmake_server_log
# 想及时更新你的日志,请安装一次即可 or Linked_C++_Projects
# 在Build也可以查看,注意:是点击Sync Now 才会看到

#
# (无) = 重要消息;
# STATUS = 非重要消息;
# WARNING = CMake 警告, 会继续执行;
# AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
# SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤;
# FATAL_ERROR = CMake 错误, 终止所有处理过程;
#

message(STATUS "Message1>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
message(STATUS "Message2>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
message(STATUS "Message3>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
message("10 OldCmakeVersion:output.txt, NewCmakeVersion:cmake_server_log.txt")

# TODO ---------------------------------------------------------------------------

# TODO CMake变量
# 声明变量:set(变量名 变量值)
set(var 666)
# 引用变量:message 命令用来打印
message("var = ${var}")
# CMake中所有变量都是string类型。可以使用set()和unset()命令来声明或移除一个变量
# 移除变量
unset(var)
message("my_var = ${var}") # 会取不到值,因为被移除了

# TODO CMake列表(lists)
# 声明列表:set(列表名 值1 值2 ... 值N) 或 set(列表名 "值1;值2;...;值N")
set(list_var 1 2 3 4 5) # 字符串列表呢? CMake中所有变量都是string类型
# 或者
set(list_var2 "1;2;3;4;5") # 字符串列表呢? CMake中所有变量都是string类型

message("list_var = ${list_var}")
message("list_var2 = ${list_var2}")

# TODO CMake流程控制-条件命令
# true(1,ON,YES,TRUE,Y,非0的值)
# false(0,OFF,NO,FALSE,N,IGNORE,NOTFOUND)
set(if_tap OFF) # 定义一个变量if_tap,值为false
set(elseif_tap ON) # 定义一个变量elseif_tap,值为ture

if(${if_tap})
message("if")
elseif(${elseif_tap})
message("elseif")
else(${if_tap}) # 可以不加入 ${if_tap}
message("else")
# endif(${if_tap}) # 结束if
endif() # 结束if 可以不加
# 注意:elseif和else部分是可选的,也可以有多个elseif部分,缩进和空格对语句解析没有影响

# TODO CMake流程控制-循环命令
set(a "")
# a STREQUAL "xxx"(a等不等xxx,不等于)
# NOT == !
while(NOT a STREQUAL "xxx")
set(a "${a}x")
message(">>>>>>a = ${a}")
endwhile()
#[[ 注意:
break()命令可以跳出整个循环
continue()可以继续当前循环
]]

foreach(item 1 2 3)
message("1item = ${item}")
endforeach(item) # 结束for

foreach(item RANGE 2) # RANGE 默认从0开始, 所以是:0 1 2
message("2item = ${item}")
endforeach(item)

foreach(item RANGE 1 6 2) # 1 3 5 每次跳级2
message("3item = ${item}")
endforeach(item)

set(list_va3 1 2 3) # 列表
# foreach(item IN LISTS ${list_va3}) 没有报错,没有循环
foreach(item IN LISTS list_va3)
message("4item = ${item}")
endforeach(item)

# TODO CMake自定义函数 Shell的函数很类似
#
# ARGC:表示传入参数的个数
# ARGV0:表示第一个参数,ARGV1、ARGV2以此类推即可
# ARGV:表示所有参数
#
function(num_method n1 n2 n3)
message("call num_method method")
message("n1 = ${n1}")
message("n2 = ${n2}")
message("n3 = ${n3}")
message("ARGC = ${ARGC}")
message("arg1 = ${ARGV0} arg2 = ${ARGV1} arg3 = ${ARGV2}")
message("all args = ${ARGV}")
endfunction(num_method)
num_method(1 2 3) # 调用num_method函数

# 静态库和动态库本质