CMAKE的使用(底层依赖makefile)
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习](C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂)
安装cmake3.9
1.1卸载已经安装的旧版本的CMake(非必须的)
apt-get autoremove cmake
1.2 文件下载:
wget https://cmake.org/files/v3.9/cmake-3.9.1-Linux-x86_64.tar.gz
解压:
tar zxvf cmake-3.9.1-Linux-x86_64.tar.gz
查看解压后的目录:
tree -L 2 cmake-3.9.1-Linux-x86_64
1.3 创建软连接
mv cmake-3.9.1-Linux-x86_64 /opt/cmake-3.9.1ln -sf /opt/cmake-3.9.1/bin/* /usr/bin/
CMake的语法1:(CMakeLists.txt 是固定名称,不能改变)
指令 | PROJECT |
语法 | PROJECT(projectname [CXX] [C] [Java]) |
说明 |
指定工程名称,并可指定工程支持的语言(可忽略)。 有两个隐式的定义变量:_BINARY_DIR 和 _SOURCE_DIR。cmake已预订了PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR.建议使用这两个变量。 即使修改了⼯程名称,也不会影响这两个变量。如果使⽤了_SOURCE_DIR,修改⼯程名称后,需要同时修改这些 变量。 |
指令 | SET |
语法 | SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) |
说明 | SET 指令可以⽤来显式的定义变量,⽐如SET(SRC_LIST main.c)。如果有多个源⽂件,也可 以定义成SET(SRC_LIST main.c t1.c t2.c)。 |
指令 | MESSAGE |
语法 | MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...) |
说明 | 这个指令⽤于向终端输出⽤户定义的信息,它包含了三种类型: SEND_ERROR:产⽣错误,⽣成过程被跳过 STATUS:输出前缀为-的信息。 FATAL_ERROR:⽴即终⽌所有cmake过程。 |
指令 | ADD_EXECUTABLE |
语法 | ADD_EXECUTABLE([BINARY] [SOURCE_LIST]) |
说明 | 定义了这个⼯程会⽣成⼀个⽂件名为[BINARY]可执⾏⽂件,相关的源⽂件是 SOURCE_LIST 中定义的源⽂件列表 |
CMake的使用1
1.1单个目录实现
#CMake 最低版本要求 cmake --version 查看版本cmake_minimum_required (VERSION 2.8)#工程名和二进制(目标名可以不一样的)PROJECT (0COICE)#手动加入文件SET(SRC_LIST main.c)#打印路径MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})#最终的目标名字 SRC_LIST源文件的目录ADD_EXECUTABLE(main ${SRC_LIST})
// main.c#include int main(int argc, char *argv[]){ printf("main CMake!!!\n"); return 0;}
2.1 目录分层
DCMAKE_INSTALL_PREFIX 语法:
语法 DCMAKE_INSTALL_PREFIX 操作 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr 说明 使用make install 把可执行文件安装到指定的目录下 ADD_SUBDIRECTORY 语法:
语法 ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) 操作 ADD_SUBDIRECTORY(src)#以空格隔开添加多个目录 说明 此指令⽤于向当前⼯程添加存放源⽂件的⼦⽬录 INSTALL语法:
语法 INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION ] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT ] [OPTIONAL] ] [...] ) 说明 指定安装路径
#2-2.1/CMakeLists.txt# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)PROJECT(0VOICE)# 添加子目录ADD_SUBDIRECTORY(src)#INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/0voice)# 默认/usr/local/#指定自定义目录,比如 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..#将doc目录复制一份到执行文件的上级目录和bin同级的 ./share/doc/cmake/0voiceINSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice)#2-2.1/src/CMakeLists.txt# 单个目录实现# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 工程# PROJECT(0VOICE)# 手动加入文件SET(SRC_LIST main.c)MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})ADD_EXECUTABLE(main ${SRC_LIST})# 将执行文件安装到bin目录# 默认/usr/local/#指定自定义目录,比如 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..INSTALL(TARGETS main RUNTIME DESTINATION bin)#2-2.1/src/main.c#include int main(int argc, char *argv[]){ printf("main CMake!!!\n"); return 0;}
3 多个目录实现有两种
3.1子目录编译成库文件
INCLUDE_DIRECTORIES语法: 用来找头文件 : I NCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir1") ADD_SUBDIRECTORY 语法: 添加子目录: ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir1")ADD_LIBRARY 语法: ADD_LIBRARY ( hello_shared SHARED libHelloSLAM.cpp ) # ⽣成动态库 ADD_LIBRARY ( hello_shared STATIC libHelloSLAM.cpp ) ⽣成静态库 TARGET_LINK_LIBRARIES 语法: 用来连接库到执行文件上:T ARGET_LINK_LIBRARIES(darren dir1 dir2) # 加载当前所有的源码,和makefile wildcard类似
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# ./CMakeLists.txt# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)PROJECT(0VOICE)ADD_SUBDIRECTORY(src bin)#INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/0voice)INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice)
#./src/CMakeLists.txt# 单个目录实现# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 工程PROJECT(0VOICE)# 手动加入文件SET(SRC_LIST main.c)MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})# 添加头文件路径#INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir1")#或者INCLUDE_DIRECTORIES(dir1)MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR -> " ${CMAKE_CURRENT_SOURCE_DIR})# 添加 dir1 子目录ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir1")# 添加头文件路径INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir2")# 添加 dir2 子目录ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir2")ADD_EXECUTABLE(darren ${SRC_LIST} )TARGET_LINK_LIBRARIES(darren dir1 dir2)# 将执行文件安装到bin目录INSTALL(TARGETS darren RUNTIME DESTINATION bin)
// ./src/main.c#include #include "dir1.h"#include "dir2.h"int main(int argc, char *argv[]){ printf("0voice CMake!!!\n"); printDir1(); printDir12(); printDir2(); return 0;}
#src/dir1/CMakeLists.txt dir2中的一样# 加载所有的源码,和makefile wildcard类似AUX_SOURCE_DIRECTORY(. DIR_SRCS)# SET(DIR_SRCS dir1.c dir12.c)# 默认是静态库 SHARED动态库 生成的名字为 lib + dir1 = libdir1.soADD_LIBRARY (dir1 SHARED ${DIR_SRCS})
// src/dir1/dir1.c dir2中的差不多#include "dir1.h"void printDir1(){ printf("I am dir1\n");}// src/dir1/dir2.c#include "dir1.h"void printDir2(){ printf("I am dir2\n");}// src/dir1/dir1.h#include void printDir1();void printDir12();
3.2 子目录使用源码编译,对应的子目录并没有CMakeLists.txt
和3.1不同的只是src下的CMakeLists.txt
# 单个目录实现# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 工程# PROJECT(0VOICE)# 手动加入文件SET(SRC_LIST main.c)MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})#设置子目录 先设置子目录set(SUB_DIR_LIST "${CMAKE_CURRENT_SOURCE_DIR}/dir1" "${CMAKE_CURRENT_SOURCE_DIR}/dir2")foreach(SUB_DIR ${SUB_DIR_LIST}) #遍历源文件 aux_source_directory(${SUB_DIR} SRC_LIST) MESSAGE(STATUS "SUB_DIR-> " ${SUB_DIR}) MESSAGE(STATUS "SRC_LIST-> " ${SRC_LIST})endforeach()# 添加头文件路径INCLUDE_DIRECTORIES("dir1")INCLUDE_DIRECTORIES("dir2")ADD_EXECUTABLE(darren ${SRC_LIST} )# 将执行文件安装到bin目录INSTALL(TARGETS darren RUNTIME DESTINATION bin)
遍历目录下的所有源文件
总结:两种方式的不同点在于:方式一是把两个子目录下的源文件编译成动态库,和静态库,然后进行链接的。方式二是在main.c级目录上直接读取文件过来编译。如果不是每个文件都是需要编译成库文件的话,推荐使用第二种比较方便,如果都是编译成库文件的话,就使用第一种。
4.生成库
4.1生成动态库
# 设置release版本还是debug版本if(${CMAKE_BUILD_TYPE} MATCHES "Release") MESSAGE(STATUS "Release版本") SET(BuildType "Release")else() SET(BuildType "Debug") MESSAGE(STATUS "Debug版本")endif()#设置lib库目录 PROJECT_SOURCE_DIR当前工程目录SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)# debug和release版本目录不一样#设置生成的so动态库最后输出的路径SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})# -fPIC 动态库必须的选项ADD_COMPILE_OPTIONS(-fPIC)# 查找当前目录下的所有源文件# 并将名称保存到 DIR_LIB_SRCS 变量AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)# 生成静态库链接库Dir1#ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})# 生成动态库ADD_LIBRARY (Dir1 SHARED ${DIR_LIB_SRCS})
#include "dir1.h"void printDir1(){ printf("I am dir1\n");}
#include void printDir1();
4.2生成静态库 + 安装到指定的目录(只有CMakeLists.txt不一样)
# 设置release版本还是debug版本if(${CMAKE_BUILD_TYPE} MATCHES "Release") MESSAGE(STATUS "Release版本") SET(BuildType "Release")else() SET(BuildType "Debug") MESSAGE(STATUS "Debug版本")endif()#设置lib库目录SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)# debug和release版本目录不一样#设置生成的so动态库最后输出的路径SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})ADD_COMPILE_OPTIONS(-fPIC)# 查找当前目录下的所有源文件# 并将名称保存到 DIR_LIB_SRCS 变量AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)# 生成静态库链接库Dir1ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})# 将库文件安装到lib目录INSTALL(TARGETS Dir1 DESTINATION lib)# 将头文件includeINSTALL(FILES dir1.h DESTINATION include) #INSTALL(FILES *.h DESTINATION include)
5.1调用静态库
# 单个目录实现# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 工程PROJECT(0VOICE)# 手动加入文件SET(SRC_LIST main.c)MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")#库的路径,静态库在哪,就指定到哪个路径LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")ADD_EXECUTABLE(darren ${SRC_LIST})# 链接动态库TARGET_LINK_LIBRARIES(darren Dir1)
#include #include "dir1.h"int main(int argc, char *argv[]){ printf("0voice CMake!!!\n"); printDir1(); return 0;}
5.2 调用动态库
# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 工程PROJECT(0VOICE)# 手动加入文件SET(SRC_LIST main.c)MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")# 引用动态库ADD_EXECUTABLE(darren ${SRC_LIST})# 优先连接动态库#TARGET_LINK_LIBRARIES(darren Dir1)# 强制使用静态库 完整的库文件名libDir1.aTARGET_LINK_LIBRARIES(darren libDir1.a)
#include #include "dir1.h"int main(int argc, char *argv[]){ printf("0voice CMake!!!\n"); printDir1(); return 0;}
#编译步骤(1)创建build目录mkdir build(2)进入build目录cd build(3)编译debug版本cmake -DCMAKE_BUILD_TYPE=Debug ..make生成的执行文件和库在 /release/linux/Debug(3)编译release版本cmake -DCMAKE_BUILD_TYPE=Release ..make生成的执行文件和库在 /release/linux/Release# 说明Debug版会使用参数-g;Release版使用-O3 –DNDEBUG