跳至主要內容

CMake的使用

张威大约 9 分钟linux编译工具

CMake的使用

CMake介绍

CMake: 使用简单方便,可以跨平台,构建项目编译环境。比直接写Makefile简单(),可以通过简单的,一个命令便将我们项目想编译的可执行文件、静态库、动态库都编译出来了。

安装

Linux环境安装CMake

sudo apt install cmake
cmake -version #查看版本

或者 通过编译好的版本安装 Download CMakeopen in new window

  1. 下载cmake-3.29.2-linux-x86_64.tar.gzopen in new window

  2. 移动到Linux下并解压(jie'ya'h)

    tar -zxvf cmake-3.29.2-linux-x86_64.tar.gz
    cd cmake-3.29.2-linux-x86_64/bin
    
  3. 查看版本

    ./cmake -version
    
  4. 建立软链接

    sudo ln -s ~/app/cmake-3.29.2-linux-x86_64/bin/cmake /usr/bin/cmake
    cmake -version #再测试
    
    image-20240412142839918
    image-20240412142839918

vscode环境安装CMake

  1. vscode下载相应插件-CMakeCMake Tools

  2. shell终端查找cmake位置,前面我们建立的软链接,直接使用/usr/bin/cmake

    whereis cmake
    
  3. 点击CMake Tools的扩展设置

    image-20240412134918477
    image-20240412134918477
  4. 选择远程主机配置,找到Cmake:Build Environment,添加相应环境变量即可。

image-20240412143331454
image-20240412143331454

使用方法

Linux上CMake使用

先cmake在make

  • cd 到 build 目录下,执行cmake .. && make命令

vscode使用

  1. 需要在远程连接的工程根路径下建立一个主CMakeLists.txt。

  2. 主CMakeLists.txt添加相应配置,指定搜索的子目录为要参与构建的项目

  3. 工具启动时先处理主CMakeList.txt,再处理muduocode里的子CMakeList.txt,如图为子CMakeList.txt。

  4. 再直接点击vscode里cmake的按钮即可将生成的所有中间文件都放入一个build目录中,可执行文件放入bin目录中,无论是Linux上直接操作还是vscode进行配置,可以依据自己的需求进行改变。

CMake常用预定义变量

1、PROJECT_NAME:通过 project() 指定项目名称; 2、PROJECT_SOURCE_DIR:工程的根目录; 3、PROJECT_BINARY_DIR:执行 cmake 命令的目录; 4、CMAKE_CURRENT_SOURCE_DIR当前 CMakeList.txt 文件所在的目录; 5、CMAKE_CURRENT_BINARY_DIR:编译目录,可使用 add subdirectory 来修改; 6、EXECUTABLE_OUTPUT_PATH:二进制可执行文件输出位置; 7、LIBRARY_OUTPUT_PATH库文件输出位置; 8、BUILD_SHARED_LIBS:默认的库编译方式 ( shared 或 static ) ,默认为 static; 9、CMAKE_C_FLAGS:设置 C 编译选项; 10、CMAKE_CXX_FLAGS设置 C++ 编译选项; 11、CMAKE_CXX_FLAGS_DEBUG:设置编译类型 Debug 时的编译选项; 12、CMAKE_CXX_FLAGS_RELEASE:设置编译类型 Release 时的编译选项; 13、CMAKE_GENERATOR:编译器名称; 14、CMAKE_COMMAND:CMake 可执行文件本身的全路径; 15、CMAKE_BUILD_TYPE:工程编译生成的版本, Debug / Release;

添加可执行文件作为构建目标

同一目录,单个/多个源文件

  • 直接写

    cmake_minimum_required (VERSION 2.8) #指定运行此配置文件所需的 CMake 的最低版本
    project (demo1) #参数值是 demo1,该命令表示项目的名称是 demo1
    add_executable(main main.cpp) #将名为 main.cpp 的源文件编译成一个名称为 main 的可执行文件
    
  • 使用GLOB自动查找当前目录指定扩展名的文件,实现批量添加源文件。最好是启用CONFIGURE_DEPENDS选项,添加新文件时会自动更新变量

    cmake_minimum_required(VERSION 3.10)
    project(yx)
    file(GLOB sources CONFIGURE_DEPENDS *.cpp *.h)
    add_executable(main ${sources})
    
  • 使用GLOB_RECURSE可以包含所有子文件夹下的文件,为了避免将build目录里临时生成的cpp也加进来,建议把源码都放在src目录下

  • aux_source_directory自动会查找指定目录下的所有源文件,然后将结果存进指定变量名

    aux_source_directory(. sources) #就是把当前目录下需要的文件搜集到sources变量中
    
    cmake_minimum_required (VERSION 2.8)
    project (demo2)
    aux_source_directory (./ DIR_SRCS)
    add_executable (demo ${DIR_SRCS})
    
  • 使用set命令去新建变量来存放需要的源文件。因为aux_source_directory也存在弊端,它会把指定目录下的所有源文件都加进来,实际项目中可能有些是我们不需要的文件。

    cmake_minimum_required (VERSION 2.8)
    project (demo2)
    set ( DIR_SRCS
    	  ./main.cpp
    	  ./add.cpp )
    add_executable (demo ${DIR_SRCS})
    

多个目录,多个源文件

一般来说,当文件比较多时,我们会进行分类管理,根据功能把代码放在不同目录下,这样方便查找

03demo/ ├── add │ ├── add.cpp │ └── add.h ├── sub │ ├── sub.cpp │ └── sub.h └── main.cpp

方法一:include_directories指定头文件,aux_source_directory指定源文件

CMakeLists.txt 和 main.cpp 在同一目录下,内容修改成如下所示

cmake_minimum_required (VERSION 2.8)
project (demo3)
include_directories (./add ./sub)#向工程添加多个指定头文件的搜索路径,路径之间用空格分隔
aux_source_directory (./ DIR_SRCS)
aux_source_directory (./add DIR_SRCS1)
aux_source_directory (./sub DIR_SRCS2)
add_executable (demo ${DIR_SRCS} ${DIR_SRCS1} ${DIR_SRCS2})
方法二:(lib 库)分别在 add 和 sub 目录里各编写一个 CMakeLists.txt 文件

为了方便,我们可以先将 add 和 sub 目录里的文件**再由 main 函数调用根目录中的 CMakeLists.txt**

cmake_minimum_required (VERSION 2.8)
project (demo3)
include_directories (./add ./sub) #头文件
add_subdirectory (./add) #指明本项目包含子目录 add 和 sub,这样当执行 cmake 时,就会进入子目录去找 CMakeLists.txt 来生成 Makefile
add_subdirectory (./sub)
aux_source_directory (./ DIR_SRCS)
add_executable (demo ${DIR_SRCS})
target_link_libraries (demo myadd mysub) #指明可执行文件 demo 需要链接一个名为 myadd 和 mysub 的链接库

这种写法****

add 目录中的 CMakeLists.txt

aux_source_directory (./ DIR_LIB_SRCS)
add_library (myadd SHARED ${DIR_LIB_SRCS}) #将add目录的源文件编译成动态库

sub 目录中的 CMakeLists.txt

aux_source_directory (./ DIR_LIB_SRCS)
add_library (mysub STATIC ${DIR_LIB_SRCS})

add_library第 1 个参数指定库的名字;第 2 个参数决定是动态还是静态,不写****;第 3 个参数指定生成库的源文件。注意:SHARED 和 STATIC 是 cmake 的关键字,必须

常用的组织结构

把源文件放到 src 目录下 把头文件放到 include 目录下 把生成的库文件放到 lib 目录下 把生成的对象文件放到 build 目录下 把最终输出的 elf 文件放到 bin 目录

04demo/ ├── bin ├── build ├── include │ ├── add.h │ └── sub.h ├── lib └── src ├── lib_add │ └── add.cpp ├── sub │ └── sub.cpp └── main.cpp

  1. 最外层新建一个 CMakeLists.txt用于掌控全局,使用add_subdirectory来添加要生成 elf 文件的源码目录即可
cmake_minimum_required (VERSION 2.8)
project (demo4)
add_subdirectory (./src) #指定搜索的子目录
  1. src 目录下,新建一个 CMakeLists.txt
add_subdirectory (./lib_add) #指定搜索的子目录
aux_source_directory (./    DIR_SRCS1)
aux_source_directory (./sub DIR_SRCS2)
include_directories (../include)
link_directories (${PROJECT_SOURCE_DIR}/lib) #添加非标准库的搜索路径
add_executable (demo ${DIR_SRCS1} ${DIR_SRCS2})
target_link_libraries (demo myadd)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) #设置可执行文件的最终存储路径

EXECUTABLE_OUTPUT_PATHPROJECT_SOURCE_DIR是 cmake 自带的预定义变量

EXECUTABLE_OUTPUT_PATH:目标二进制可执行文件的存放位置 PROJECT_SOURCE_DIR:当前工程的根目录

  1. lib_add 目录下,也要新建一个 CMakeLists.txt
aux_source_directory (./ DIR_LIB_SRCS)
add_library (myadd_shared SHARED ${DIR_LIB_SRCS})
add_library (myadd_static STATIC ${DIR_LIB_SRCS})
set_target_properties (myadd_shared PROPERTIES OUTPUT_NAME "myadd")
set_target_properties (myadd_static PROPERTIES OUTPUT_NAME "myadd")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

添加编译选项

有时编译程序时想添加一些编译选项,如 -g、-Wall、-std=c++11 等,就可以使用add_compile_options来操作,也可以通过set命令修改CMAKE_CXX_FLAGSCMAKE_C_FLAGS,这两个是 cmake 自带的预定义变量,用于设置编译选项 这两种方式的效果是一样的,但请注意它们还是有区别的:

  • add_compile_options命令添加的编译选项是针对****
  • set命令设置CMAKE_C_FLAGSCMAKE_CXX_FLAGS变量则是分别只针对 c 和 c++ 编译器的
cmake_minimum_required (VERSION 2.8)
project (demo5)
#设置编译选项
#add_compile_options (-std=c++11 -Wall)
set (CMAKE_CXX_FLAGS "-std=c++11 -Wall ${CMAKE_CXX_FLAGS}")

aux_source_directory (./ DIR_SRCS)
add_executable (demo ${DIR_SRCS})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

cd 到 build 目录下,执行cmake .. && make命令,就可以在 bin 目录下得到 elf 文件

添加控制选项

希望在编译代码时****,这时可以使用 cmake 的option命令

假设我们现在的工程会生成 2 个 bin 文件,main1 和 main2,现在整体结构如下 06demo/ ├── bin ├── build ├── CMakeLists.txt └── src ├── CMakeLists.txt ├── └──

外层的 CMakeLists.txt 内容如下

cmake_minimum_required (VERSION 2.8)
project (demo6)
option (MYDEBUG "enable debug mode" OFF)
add_subdirectory (./src)

option命令

  • 其第一个参数是这个 option 的名字
  • 第二个参数是字符串,用来描述这个 option 是来干嘛的,
  • 第三个是 option 的值ONOFF,也可以不写,不写就是默认 OFF

src 目录下的 CMakeLists.txt,如下

add_executable (main1 main1.cpp)
if (MYDEBUG)
    add_executable (main2 main2.cpp)
else()
    message (STATUS "Currently is not in debug mode")
endif()
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
  • 这里使用了 if-else 根据 option 来决定是否编译 main2.cpp

message为用户打印显示一条消息,可以用下述可选的关键字指定消息类型

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

cd 到 build 目录下输入cmake .. && make就可以只编译出 main1,如果想编译出 main2

  1. 直接修改 CMakeLists.txt,把 OFF 改成 ON,这种方法有点麻烦
  2. cd 到 build 目录,然后输入**cmake .. -DMYDEBUG=ON && make**,这样就可以编译出 main1 和 main2

为什么要在 build 目录下运行 cmake?

如果不这样做,cmake 运行时就会跟文件,这样会对程序的目录结构造成污染,而在 build 目录下运行 cmake,生成的附带文件就只会待在 build 目录下,如果我们不想要这些文件就可以直接清空 build 目录,非常方便

CMAKE_BUILD_TYPE控制构建类型

默认空字符串,相当于Debug调试模式 其它模式:Release、MinSizeRel、RelWithDebInfo,这3种模式都定义了NDEBUG宏,会导致****

set(CMAKE_BUILD_TYPE Release)

大多数项目为了默认为Release模式,CMakeLists中会写如下三行

if (NOT CMAKE_BUILD_TYPE)
	set(CMAKE_BUILD_TYPE Release)
endif()

NDEBUG宏的使用

在CMakeLists中写上

add_definitions(-DNDEBUG)	#定义了NDEBUG宏

这样对于下面的test.cpp,assert不起作用,输出xx

#include <iostream>
#include <cassert>
int main()
{
    int m=3;
#ifdef NDEBUG
    std::cout<<"xx"<<std::endl;
#else
    std::cout<<"yy"<<std::endl;
#endif
    assert(m == 2);
    return 0;
}

几个目录

cmake_minimum_required(VERSION 3.29) 
project(LoggerCMake)
set(SRC_LIST Logger.cc test.cc)	#直接写源文件
add_executable(Logger ${SRC_LIST})
target_link_libraries(Logger log4cpp pthread)