> 文档中心 > CMAKE的使用(底层依赖makefile)

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