CMake快速入门

本文最后更新于:2024年10月22日 晚上

CMake快速入门

参考:

【软件构建: CMake 快速入门】 https://www.bilibili.com/video/BV1rR4y1E7n9/?share_source=copy_web&vd_source=cdc2983e744e85d26061233640e163f4

【cmake学习】set_target_properties 常见属性以及获取target 属性_set——target-CSDN博客

CMake,make,CMakeLists.txt,CMakeFiles之间的关系_.cmake .cmake.in cmakefile.txt 的关系-CSDN博客

【隐藏的细节:编译与链接】 https://www.bilibili.com/video/BV1TN4y1375q/?share_source=copy_web&vd_source=cdc2983e744e85d26061233640e163f4

CMake是一个构建(build)工具,完成代码编译链接打包过程,并且是跨平台的。

和CMake类似的有微软的MSBuild(在VS中使用)、安卓开发的Gradle(Android Studio)。

如上图所示,编译cpp时是以单个文件为单位,每个cpp文件会得到一个.o的文件,里面包含了机器可以执行的代码。有多个文件的时候,会用到其它头文件里定义的函数,而编译这个cpp时编译器是不知道要调用的函数地址是什么,所以会暂时设置为0,直到链接过程才会修正。

随着项目的复杂化,编译的.cpp和.h文件越来越多,这个编译和链接的过程也越来越复杂,于是make(Makefile)出现了(我的另一篇博客有所介绍:MakeFile入门笔记 - Kamino’s Blog),这个古老的技术可以帮助我们构建一颗依赖树,然后通过make命令来递归执行构建。

然而,随着项目变得更复杂,Makefile也变得繁杂了起来,而且它对于不同平台还不一样,于是cmake出现了,cmake可以生成makefile来给make用,只需要程序员把CMakeLists.txt写好就行。

快速例子

CMake依赖于CMakeLists.txt这个文本文件。

1
2
3
4
5
6
7
# 使用`#`来注释文件
# 这一行表示cmake的最低版本
cmake_minimum_required(VERSION 3.10)
# 项目名称,输出的文件就叫这个名字
project(Example)
# 项目Example需要构建可执行文件,由main.cpp编译得到
add_executable(Example main.cpp)

有了CMakeLists.txt之后,CMake需要根据编译平台进行配置(Configure),然后进行build。

build直接使用cmake build即可。

CMake参数和命令

  • add_executable

    就是为项目添加所有的源代码,可以无限加

    1
    add_executable(Example main.cpp my1.cpp my2.cpp my3.cpp)
  • aux_source_directory(dir var)

    glob操作,将dir下的所有源文件存到var变量中,后续通过${var}进行调用

    1
    2
    aux_source_directory(. SRC_LIST)
    add_executable(Example ${SRC_LIST})
  • include_directories(dir)

    之前是编译cpp文件,假如定义和实现分开,你有很多头文件,则可以让cmake自动去dir下寻找头文件(也可以多个目录)

    1
    include_directories(./include1 ./include1)
  • add_library(lib_name STATIC/SHARED src)

    生成静态库或者动态库,和add_executable的用法类似。

    1
    2
    3
    # 第一个参数是生成的库的名称 第二个是动态或者静态 后面是源文件
    add_library(func_shared SHARED ${SRC_LIST})
    add_library(func_shared STATIC ${SRC_LIST})
  • set_target_properties()

    设置目标的属性,一般可以用来设置版本号和输出名称

    1
    2
    3
    4
    5
    6
    7
    # 更改输出文件名
    # 将 hello_static 更名为 hello
    SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")

    # 设置版本号
    # 将hello的动态库版本设置为1.2 API版本设置为1
    SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
  • set

    用来定义或者修改变量的值,这些变量可以是普通变量、缓存变量或环境变量。

    1
    2
    3
    4
    # EXECUTABLE_OUTPUT_PATH是系统变量,这里设置可执行文件的输出目录到bin下
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin)
    # LIBRARY_OUTPUT_PATH是系统变量,这里设置链接库的输出目录到lib下
    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../lib)
  • find_library(var lib_name lib_path1 lib_path2)

    在lib_path路径下查找库(.so .a),并存到var变量

    1
    2
    # 将库myfunc的位置存到FUNC_LIB中
    find_library(FUNC_LIB myfunc ${PROJECT_BINARY_DIR}/../lib)
  • target_link_libraries(target lib_name)

    用来把lib连接到一个可执行文件中

    1
    2
    # 上一步找到的库连接到可执行文件
    target_link_libraries(hello ${FUNC_LIB})
  • find_package

    从计算机上寻找库,寻找到的库会保存XXX_FOUND/_DIR/_INCLUDE_DIR/_LIBS等变量。REQUIRED表示必须要找到package,否则报错。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 找OpenCV
    find_package(OpenCV REQUIRED)
    # 接下来可以使用这些路径了
    ${OpenCV_DIR}
    ${OpenCV_INCLUDE_DIRS}
    ${OpenCV_LIBS}
    # 也可以进行一些逻辑控制(if中直接用变量名)
    if(OpenCV_FOUND)
    target_link_libraries(...)
    else(OpenCV_FOUND)
    message(FATAL_ERROR ”GLOG library not found”)
    endif(OpenCV_FOUND)
  • 常用变量

    PROJECT_BINARY_DIR:cmake配置命令的目录,通常是build目录下。

    PROJECT_SOURCE_DIR:代码的路径

    通常假如源码路径是src,那么在src下创建一个build,再进入build,然后cmake ..,表示build的上一级是源码,但是编译结果保存在build下。

常用编译流程

1
2
3
4
5
6
7
rm -rf build  # 删除旧的build,假如没有旧的或者makefile没有变化,则不需要这一步
mkdir build
cd build
cmake .. # 重新生成makefile
make # 编译

make clean # 不一定需要,删除make之前生成的缓存