教你用CMake构建各种常见的C++工程

我在学习过程中参阅了ttroy50的CMake-Examples教程,我觉得他的教程非常容易理解。在此,我表示对他们的感谢。

在本篇教程中,我只和大家分享一些常见情形的处理。

基本知识扫盲

  • CMake是一个跨平台的用于构建C/C++项目的工具,类似于Java项目项目中常用的Maven、Gradel
  • CMake需要和make、gcc/g++一起使用,它们在构建工程中的作用不同。
  • gcc/g++是linux下的一个编译器,用于将C/C++源码编译链接成可执行文件或者是库文件。(它的一个windows移植版本是mingw64,大家熟悉的C/C++ IDE 【CodeBlocks】【Dev-C++】中就内置了这个ming64)
  • gcc/g++可以用于直接编译单文件,然后在进行链接工作,最终形成可执行程序。但编译复杂工程时需要按照特定的顺序执行这些gcc/g++指令,人工敲指令不太方便,因此人们希望通过制定依赖规则等来自动化地编译链接,因此有了make这一工具
  • 使用make工具需要编写makefile文件来指定规则,make会在当前目录自动找到这个文件执行批处理构建工作;但对于复杂工程来说,依赖关系可能比较复杂,编写makefile也比较麻烦。这是,人们又设计了CMake用于生成makefile文件
  • 使用CMake工具需要编写CMakeLists.txt文件,用于在较高的抽象层次上指明构建任务,基本上类似于直接在IDE中设定表格选项。

构建:单文件工程

项目结构

PROJECT_A
├── CMakeLists.txt
└── main.cpp

CMakelists写法

# 用于指定需要的CMamke的最低版本
cmake_minimum_required(VERSION 3.5)

# 用于指定项目名称,名字可以和项目根目录名字不同
project (hello_cmake)

# 用于创建可执行文件,第一个参数是可执行文件名字,后面的参数是用于执行需要用到的源码文件
add_executable(hello_cmake main.cpp)

命令行

# 为了保持目录整洁,创建build文件夹用于存放各种构建生成的文件
mkdir build
cd build
# cmake的参数为项目CMakeList.txt所在目录,生成的文件会放在cmake执行的目录中
cmake ..
# 当前目录中已生成makefile,make可以自动发现它并执行
make
# 可执行文件已生成,直接运行即可
./hello_cmake

构建:多文件(含有头文件)的工程

项目结构

Project_B
├── CMakeLists.txt
├── include
│   └── Hello.h
└── src
    ├── Hello.cpp
    └── main.cpp

CMakelists写法

# 用于指定需要的CMamke的最低版本
cmake_minimum_required(VERSION 3.5)

# 用于指定项目名称,名字可以和项目根目录名字不同
# 相当于设置变量 ${PROJECT_NAME} = "hello_headers"
project (hello_headers)

# 设定一个变量方便后续使用,相当于 ${SOURCES} = "src/Hello.cpp src/main.cpp"
set(SOURCES
    src/Hello.cpp
    src/main.cpp
)

# 用于创建可执行文件,第一个参数是可执行文件名字,源码参数用变量代替
add_executable(hello_headers ${SOURCES})

# 用于指明构建目标的include目录,第一个参数为构建目标,第三个参数为include目录
target_include_directories(hello_headers PRIVATE ${PROJECT_SOURCE_DIR}/include)

说明

  • CMakeLists.txt中的语法规则和shell差不多,所有的变量、参数默认都是字符串类型,变量通过$进行引用
  • CMakeLists.txt中可以使用一些系统变量,如 ${PROJECT_SOURCE_DIR} 、${PROJECT_BINARY_DIR}等
    • ${PROJECT_SOURCE_DIR} 为源码所在目录,也就是cmake … 的指定的…,即./PROJECT_B
    • ${PROJECT_BINARY_DIR}为二进制文件所在目录,即cmake指令执行的目录./PROJECT_B/build

构建:静态/动态库的构建

项目结构

PROJECT_C
├── CMakeLists.txt
├── include
│   └── Hello.h
└── src
    ├── Hello.cpp
    └── main.cpp

CMakelists写法

cmake_minimum_required(VERSION 3.5)

project(hello_library)

############################################################
# 创建静态库
############################################################

# 用于创建库文件,类型为STATIC(静态库)/ SHARED(动态库),第三个参数指定所需源文件
# add_library(hello_library STATIC src/Hello.cpp)
add_library(hello_library SHARED src/Hello.cpp)

# 【可选】用于给库指定别名(ALIAS)
add_library(hello::library ALIAS hello_library)

# 用于指明构建目标的include目录,第一个参数为构建目标,第三个参数为include目录
target_include_directories(hello_library PUBLIC ${PROJECT_SOURCE_DIR}/include)

############################################################
# 创建可执行文件
############################################################

# 用于创建可执行文件,第一个参数是可执行文件名字,第二个参数为需要用到的源文件
add_executable(hello_binary src/main.cpp)

# 用于指明构建目标的依赖库,第一个参数为构建目标,第三个参数为依赖库
target_link_libraries( hello_binary PRIVATE hello_library)

说明

  • 静态、动态库的构建基本一致,只需要在add_library()中间STATIC替换为SHARED
  • 库的名字就是我们在add_library()中指定的这个,也可以给它指定别名;但生成的库文件后自动添加前缀lib和后缀.a(静态库)或.so(动态库),比如 libhello_library.a 和 libhello_library.so
  • 上面这种引用库的方式仅限在同一工程中使用,更通用的写法我会在后面给出
  • 可以看出,在一个CMakeLists.txt中是可以有多个构建任务的,可以未它们分别指明构建信息

构建:自定义静态/动态库的调用

项目结构

PROJECT_D
├── CMakeLists.txt
├── lib
|	└── libhello.so
├── include
│   └── Hello.h
└── src
    └── main.cpp

CMakelists写法

cmake_minimum_required(VERSION 3.5)

project(hello_link_library)

# 用于创建可执行文件,第一个参数是可执行文件名字,第二个参数为需要用到的源文件
add_executable(hello_binary src/main.cpp)

# 用于指明构建目标的依赖库,第一个参数为构建目标,第三个参数为依赖库
target_link_libraries(hello_binary PRIVATE ${PROJECT_SOURCE_DIR}/lib/libhello.so)

# 注意!还需要指明依赖库提供的include文件目录
target_include_directories(hello_binary PRIVATE ${PROJECT_SOURCE_DIR}/include)

说明

  • 在使用依赖库时,需要指定依赖库位置和依赖库提供的头文件
  • 为了方便,也可以通过设定变量的方式来避免直接在target_link_libraries() 中指明路径

构建:第三方依赖库的调用

项目结构

PROJECT_D
├── CMakeLists.txt
├── lib
|	└── libtorch
│   	├── bin
│   	├── build-hash
│   	├── build-version
│   	├── include
│   	├── lib
│   	└── share
├── include
│   └── Hello.h
└── src
    └── main.cpp

CMakelists写法

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(hello-thrid_part)

# 在系统PATH和用户指定的CMAKE_PREFIX_PATH中寻找指定库的信息
find_package(Torch REQUIRED)

# 用于创建可执行文件
add_executable(hello-binary src/main.cpp)
# 用于指明构建目标的依赖库
target_link_libraries(libtest PRIVATE ${TORCH_LIBRARIES})

说明

  • find_package()在系统PATH和用户指定的CMAKE_PREFIX_PATH中寻找指定库的信息

  • CMAKE_PREFIX_PATH 无法再CMakeLists.txt中设置,需要在执行cmake命令时设定

    cmake -DCMAKE_PREFIX_PATH=../lib/libtorch ..
    

    -D可以后和信息挨着,也可以有间隔,表示定义变量

总结

  • 设定变量

    set(<variable> <value...>
    
  • 创建输出实体(可执行文件/函数库)

    add_execuable(<name> <sources...>)
    add_library(<name> <[STATIC | SHARED]> <sources...>)
    
  • 指明依赖信息(包含信息、依赖库信息)

    target_include_directories(<target> <[PUBLIC PRIVATE INTERFACE]> <include_path...>)
    target_link_libraries(<target> <[PUBLIC PRIVATE INTERFACE]> <library...>)
    
  • 常用变量

    ${PROJECT_NAME}       #项目名称
    ${PROJECT_SOURCE_DIR} # 项目源码目录,在cmake命令中指明
    ${PROJECT_BINARY_DIR} # 项目二进制文件目录,cmake执行目录
    

参考资料

  1. cmake-examples, ttroy50, https://github.com/ttroy50/cmake-examples
  2. cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE, 大川搬砖, https://zhuanlan.zhihu.com/p/82244559
  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值