一、Cmake 简介 cmake 是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。
二、常用命令 1、指定 cmake 的最小版本 cmake_minimum_required(VERSION 3.4.1)
这行命令是可选的,我们可以不写这句话,但在有些情况下,如果 CMakeLists.txt 文件中使用了一些高版本 cmake 特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行 cmake。
2、设置项目名称 这个命令不是强制性的,但最好都加上。它会引入两个变量 demo_BINARY_DIR 和 demo_SOURCE_DIR,同时,cmake 自动定义了两个等价的变量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
3、设置编译类型 add_executable(demo demo.cpp) # 生成可执行文件 add_library(common STATIC util.cpp) # 生成静态库 add_library(common SHARED util.cpp) # 生成动态库或共享库 add_library 默认生成是静态库,通过以上命令生成文件名字 在 Linux 下是: 复制 demo
libcommon.a
libcommon.so
在 Windows 下是: 复制 demo.exe
common.lib
common.dll
4、指定编译包含的源文件 (1)明确指定包含哪些源文件 复制 add_library (demo demo .cpp test .cpp util .cpp )
(2)搜索所有的 cpp 文件 复制 aux_source_directory (dir VAR ) 发现一个目录下所有的源代码文件并将列表存储在一个变量中;
aux_source_directory (. SRC_LIST ) # 搜索当前目录下的所有 .cpp文件;
add_library (demo $ {SRC_LIST });
QQG" id="hce1b53f-40QKvQQ G">(3)自定义搜索规则 复制 file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo ${SRC_LIST})
# 或者
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# 或者
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
5、查找指定的库文件 find_library(VAR name path)查找到指定的预编译库,并将它的路径存储在变量中;
默认的搜索路径为 cmake 包含的系统库,因此如果是 NDK 的公共库只需要指定库的 name 即可;
复制 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 )
类似的命令还有 find_file ()、find_path ()、find_program ()、find_package ()
6、设置包含的目录 复制 include_directories (
$ {CMAKE_CURRENT_SOURCE_DIR }
$ {CMAKE_CURRENT_BINARY_DIR }
$ {CMAKE_CURRENT_SOURCE_DIR }/ include
)
Linux 下还可以通过如下方式设置包含的目录:
复制 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}" )
7、设置链接库搜索目录 复制 link_directories (
$ {CMAKE_CURRENT_SOURCE_DIR }/ libs
)
Linux 下还可以通过如下方式设置包含的目录:
复制 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs" )
8、设置 target 需要链接的库 复制 target_link_libraries ( # 目标库
demo
# 目标库需要链接的库
# log - lib 是上面 find_library 指定的变量名
$ {log - lib } )
在 Windows 下,系统会根据链接库目录,搜索xxx.lib 文件,Linux 下会搜索 xxx.so 或者 xxx.a 文件,如果都存在会优先链接动态库(so 后缀)
(1)指定链接动态库或静态库 复制 target_link_libraries (demo libface .a ) # 链接libface .a
target_link_libraries (demo libface .so ) # 链接libface .so
(2)指定全路径 复制 target_link_libraries (demo $ {CMAKE_CURRENT_SOURCE_DIR }/ libs / libface .a )
target_link_libraries (demo $ {CMAKE_CURRENT_SOURCE_DIR }/ libs / libface .so )
(3)指定链接多个库 复制 target_link_libraries (demo
$ {CMAKE_CURRENT_SOURCE_DIR }/ libs / libface .a
boost_system .a
boost_thread
pthread )
9、设置变量 (1)set 直接设置变量的值 复制 set (SRC_LIST main .cpp test .cpp )
add_executable (demo $ {SRC_LIST })
(2)set 追加设置变量的值 复制 set (SRC_LIST main .cpp )
set (SRC_LIST $ {SRC_LIST } test .cpp )
add_executable (demo $ {SRC_LIST })
(3)list 追加或者删除变量的值 复制 set(SRC_LIST main.cpp)
list(APP END SRC_LIST test.cpp)
list(REMOVE_ITEM SRC_LIST main.cpp)
add_executable(demo ${SRC_LIST})
10、条件控制 (1) if…elseif…else…endif 逻辑判断和比较:
复制 if (expression ):expression 不为空(0 ,N ,NO ,OFF ,FALSE ,NOTFOUND)时为真
if (not exp ):与上面相反
if (var1 AND var2 )
if (var1 OR var2 )
if (COMMAND cmd ):如果 cmd 确实是命令并可调用为真
if (EXISTS dir ) if (EXISTS file ):如果目录或文件存在为真
if (file1 IS_NEWER_THAN file2 ):当 file1 比 file2 新,或 file1 / file2 中有一个不存在时为真,文件名需使用全路径
if (IS_DIRECTORY dir ):当 dir 是目录时为真
if (DEFINED var ):如果变量被定义为真
if (var MATCHES regex ):给定的变量或者字符串能够匹配正则表达式 regex 时为真,此处 var 可以用 var 名,也可以用 $ {var }
if (string MATCHES regex )
数字比较:
复制 if (variable LESS number ):LESS 小于
if (string LESS number )
if (variable GREATER number ):GREATER 大于
if (string GREATER number )
if (variable EQUAL number ):EQUAL 等于
if (string EQUAL number )
字母表顺序比较:
复制 if (variable STRLESS string )
if (string STRLESS string )
if (variable STRGREATER string )
if (string STRGREATER string )
if (variable STREQUAL string )
if (string STREQUAL string )
示例:
复制 if (MSVC )
set (LINK_LIBS common )
else ()
set (boost_thread boost_log .a boost_system .a )
endif ()
target_link_libraries (demo $ {LINK_LIBS })
# 或者
if (UNIX )
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -g" )
else ()
add_definitions (- D_SCL_SECURE_NO_WARNINGS
D_CRT_SECURE_NO_WARNINGS
- D_WIN32_WINNT = 0x601
- D_WINSOCK_DEPRECATED_NO_WARNINGS )
endif ()
if ($ {CMAKE_BUILD_TYPE } MATCHES "debug" )
...
else ()
...
endif ()
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. (2)while…endwhile 复制 while (condition )
...
endwhile ()
(3) foreach…endforeach 复制 foreach (loop_var RANGE start stop [step ])
...
endforeach (loop_var )
start 表示起始数,stop 表示终止数,step 表示步长,示例:
复制 foreach (i RANGE 1 9 2 )
message ($ {i })
endforeach (i )
# 输出:13579
11、打印信息 复制 message ($ {PROJECT_SOURCE_DIR })
message ("build with debug mode" )
message (WARNING "this is warnning message" )
message (FATAL_ERROR "this build has many error" ) # FATAL_ERROR 会导致编译失败
12、包含其它 cmake 文件 复制 include(./common.cmake) # 指定包含文件的全路径
include(def) # 在搜索路径中搜索def.cmake文件
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 设置include的搜索路径
三、常用变量 1、预定义变量 PROJECT_SOURCE_DIR:工程的根目录。 PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build。 PROJECT_NAME:返回通过 project 命令定义的项目名称。 CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径。 CMAKE_CURRENT_BINARY_DIR:target 编译目录。 CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径。 CMAKE_CURRENT_LIST_LINE:当前所在的行。 CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块。 EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置。 LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置。 2、环境变量 使用环境变量:
写入环境变量:
复制 set (ENV {Name } value ) # 这里没有“$”符号
3、系统信息 CMAKE_MAJOR_VERSION:cmake 主版本号,比如 3.4.1 中的 3。 CMAKE_MINOR_VERSION:cmake 次版本号,比如 3.4.1 中的 4。 CMAKE_PATCH_VERSION:cmake 补丁等级,比如 3.4.1 中的 1。 CMAKE_SYSTEM:系统名称,比如 Linux-2.6.22。 CMAKE_SYSTEM_NAME:不包含版本的系统名,比如 Linux。 CMAKE_SYSTEM_VERSION:系统版本,比如 2.6.22。 CMAKE_SYSTEM_PROCESSOR:处理器名称,比如 i686。 UNIX:在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin。 WIN32:在所有的 win32 平台下该值为 TRUE,包括 cygwin。 4、主要开关选项 BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库。
CMAKE_C_FLAGS:设置 C 编译选项,也可以通过指令 add_definitions() 添加。
CMAKE_CXX_FLAGS:设置 C++ 编译选项,也可以通过指令 add_definitions() 添加。
add_definitions(-DENABLE_DEBUG -DABC) # 参数之间用空格分隔。
四、项目示例 1、初试 cmake 的 helloworld 现在新建一个 hello.cpp 源码文件,代码如下:
复制 #include < stdio .h >
int main (int argc , char * argv []){
printf ("Hello CMake!\n" );
}
之前都是采用 gcc hello.cpp -o hello 命令来生成可执行文件,但现在我们用 CMake 这种方式来生成,新建一个 CMakeLists.txt 文件名大小写都按照这个来:
复制 # 指定工程名
PROJECT (HELLO )
# 现阶段,你只需要了解 SET 指令可以用来显式的定义变量即可
# 将 hello .cpp 赋值给 SRC_LIST 变量,也可以指定多个源文件,用空格隔开
# SET (SRC_LIST hello .cpp add .cpp sub .cpp )
SET (SRC_LIST hello .cpp )
# 输出打印构建目录
MESSAGE (STATUS "This is HELLO_BINARY_DIR " $ {HELLO_BINARY_DIR })
# 输出打印资源目录
MESSAGE (STATUS "This is HELLO_SOURCE_DIR " $ {HELLO_SOURCE_DIR })
# 输出打印资源目录,与HELLO_SOURCE_DIR 一样
MESSAGE (STATUS "This is PROJECT_SOURCE_DIR " $ {PROJECT_SOURCE_DIR })
# 输出打印 CMake 资源目录,与 PROJECT_SOURCE_DIR 一样
MESSAGE (STATUS "This is CMAKE_SOURCE_DIR " $ {CMAKE_SOURCE_DIR })
# 生成可执行文件 hello ,$ {SRC_LIST }是引用变量,也就是源文件 hello .cpp
ADD_EXECUTABLE (hello $ {SRC_LIST })
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 新建 build 目录,cd 到 build 目录下,敲 cmake .. 命令,ls 一下会发现 CMake 帮我们生成了 Makefile 等等一些文件。敲 make 命令生成 hello 可执行文件,ls 文件列表如下:
QQkfGT" data-card-editable="false" class="" data-syntax="java">
复制 ubuntu @VM - 0 - 9 - ubuntu :~ / NDK_Day88 / t1 / build$ ls
CMakeCache .txt CMakeFiles cmake_install .cmake hello Makefile
2、构建生成 .so 动态库 上面的例子看不出有啥优势,甚至说还不如用 gcc hello.cpp -o hello 来得快,但像 FFmpeg 、OpenCV 等等,类似这样复杂的项目,我们敲命令去构建项目是很麻烦的。下面我们来讲一个稍微复杂一点的例子:
mkdir 新建 3 个目录分别为 src、libs、include 。src 用来存放源文件 add.ccp、sub.cpp、div.cpp。include 用来存放头文件 add.h、div.h、sub.h 。源码如下:
复制 #include "add.h"
int add (int num1 , int num2 ){
return num1 + num2 ;
}
#include "sub.h"
int sub (int num1 , int num2 ){
return num1 - num2 ;
}
#include "div.h"
int div (int num1 , int num2 ){
return num1 / num2 ;
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 基于这些准备工作,我们想用 CMake 来构建一个 libmath.so 动态库,并且将其生成在 libs 目录文件夹下
复制 # 指定 cmake 最低编译版本
CMAKE_MINIMUM_REQUIRED (VERSION 3.14 )
PROJECT (MATH )
# 把当前工程目录下的 src 目录的下的所有 .cpp 和 .c 文件赋值给 SRC_LIST
# AUX_SOURCE_DIRECTORY ($ {PROJECT_SOURCE_DIR }/ src SRC_LIST )
FILE (GLOB SRC_LIST "${PROJECT_SOURCE_DIR}/src/*.cpp" )
# 打印 SRC_LIST 文件列表
# MESSAGE (STATUS $ {SRC_LIST })
# 指定头文件目录
INCLUDE_DIRECTORIES ($ {PROJECT_SOURCE_DIR }/ include )
# 指定输出 .so 动态库的目录位置
SET (LIBRARY_OUTPUT_PATH $ {PROJECT_SOURCE_DIR }/ lib )
# 指定生成动态库
ADD_LIBRARY (math SHARED $ {SRC_LIST })
# 指定生成版本号,VERSION指代动态库版本,SOVERSION指代API版本
# SET_TARGET_PROPERTIES (math PROPERTIES VERSION 1.2 SOVERSION 1 )
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 3、链接外部动态库和头文件 将 libs 目录和 include 目录 copy 到 hello.cpp 同级目录下,修改 hello.cpp 源码如下:
复制 #include < stdio .h >
#include "add.h"
#include "sub.h"
#include "div.h"
int main (int argc , char * argv []){
int a = 20 ;
int b = 10 ;
printf ("%d+%d=%d\n" ,a ,b ,add (a ,b ));
printf ("%d-%d=%d\n" ,a ,b ,sub (a ,b ));
printf ("%d/%d=%d\n" ,a ,b ,div (a ,b ));
return 0 ;
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 现在我引用了 include 目录下的头文件,同时需要链接 libs 目录下的 libmath.so ,我们再次创建一个 CMakeLists.txt 来生成可执行文件 hello。
复制 # 指定cmake最低编译版本
CMAKE_MINIMUM_REQUIRED (VERSION 3.14 )
# 指定工程的名称
PROJECT (HELLO )
#指定头文件目录位置
INCLUDE_DIRECTORIES ($ {PROJECT_SOURCE_DIR }/ include )
#添加共享库搜索路径
LINK_DIRECTORIES ($ {PROJECT_SOURCE_DIR }/ lib )
#生成可执行文件
ADD_EXECUTABLE (hello hello .cpp )
#为hello添加共享库链接
TARGET_LINK_LIBRARIES (hello math )
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 4、基于 FFmpeg 开发的 CMakeLists.txt 音视频 的播放,在线直播,音视频 通话开发,后面可能都得基于 FFmpeg 来写。那么首先我们需要编译 .so 动态库,然后把动态库和头文件 copy 到 AS 来开发,这里我已经编译好了一个 3.3.9 的版本,至于怎么写 shell 编译脚本,会在下篇文章中介绍。目前大伙先直接拿过来用就行了。我把编译好的 .so 动态库和 include 目录拷贝到 AS 工程的 jniLibs 目录下
复制 # 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 )
# 需要引入我们头文件 ,以这个配置的目录为基准
include_directories (src / main / jniLibs / include )
include_directories (src / main / jniLibs / other )
# 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 .
#添加共享库搜索路径
LINK_DIRECTORIES ($ {CMAKE_SOURCE_DIR }/ src / main / jniLibs / armeabi )
# 指定源文件目录
AUX_SOURCE_DIRECTORY ($ {CMAKE_SOURCE_DIR }/ src / main / cpp SRC_LIST )
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
$ {SRC_LIST }
)
# 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 .
# 链接额外的 ffmpeg 的编译
native - lib
# 编解码 (最重要的库 )
avcodec - 57
# 设备信息
avdevice - 57
# 滤镜特效处理库
avfilter - 6
# 封装格式处理库
avformat - 57
# 工具库 (大部分库都需要这个库的支持 )
avutil - 55
# 后期处理
postproc - 54
# 音频采样数据格式转换库
swresample - 2
# 视频 像素数据格式转换
swscale - 4
- landroid
# Links the target library to the log library
# included in the NDK .
$ {log - lib })
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.