【cmake】利用cmake的add_subdirectory管理内部和外部库

利用cmake的add_subdirectory管理内部和外部库

问题描述

内容参考自 cmake菜谱

请从 这里下载源码

假如你有一个稍微大一点的项目,那么就需要引用许多外部库。同时,你还有许多自己写的内部库。最后,你把他们都链接到自己的可执行文件上。

你的项目结构可能如下所示

  • 根目录
    • 自己的项目
      • 内部库A
      • 内部库B
    • 外部库

实践

项目结构

为了更好的说明,我们把所有的CMakeLists.txt文件都清理干净,然后从头开始写。

目录如下

.
├── external
│   ├── conversion.cpp
│   └── conversion.hpp
├── src
│   ├── evolution
│   │   ├── evolution.cpp
│   │   └── evolution.hpp
│   ├── initial
│   │   ├── initial.cpp
│   │   └── initial.hpp
│   ├── io
│   │   ├── io.cpp
│   │   └── io.hpp
│   ├── main.cpp
│   └── parser
│       ├── parser.cpp
│       └── parser.hpp
└── tests
    ├── catch.hpp
    └── test.cpp

解释:

  • external存放外部库
  • tests存放测试 使用catch2测试
  • src是本项目源码目录
    • evolution/initial/io/parser都是内部库
    • main 可执行目标

我们从上到下依次来建立CMakeLists.txt以管理项目

根目录 CMakeLists.txt

在根目录中建立 CMakeLists.txt

内容如下

# cmake最低版本
cmake_minimum_required(VERSION 3.20)
# 本项目名
project(organize-3rd-parties)

# 设置C++17标准,需要满足标准,不用C++拓展语法
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# 进入src文件夹下面的CMakeLists.txt
add_subdirectory(src)

# 进入external文件夹下面的CMakeLists.txt
add_subdirectory(external)

# # 开启测试
# enable_testing()
# # 进入 tests 文件夹下面的CMakeLists.txt
# add_subdirectory(tests)

我们这里先暂时把测试的部分注释掉。只关注管理内部和外部库。

src目录 CMakeLists.txt

在src中建立 CMakeLists.txt

内容如下

# 将main.cpp编译成可执行文件
add_executable(main main.cpp)

# 进入各个内部库子目录
add_subdirectory(evolution)
add_subdirectory(initial)
add_subdirectory(io)
add_subdirectory(parser)

# 链接外部库和内部库到可执行目标
target_link_libraries(main 
    PRIVATE         #不会传播
        evolution   #内部库
        initial     #内部库
        io          #内部库  
        parser      #内部库
        conversion  #外部库
)

内部和外部库目录的 CMakeLists.txt

以下几个库的管理方式都是一样的
src\evolution
src\initial
src\io
src\parser
external\CMakeLists.txt

我们举一个例子即可

src\evolution\CMakeLists.txt

# 编译静态库,暂不添加任何源码。
add_library(evolution "")

# 随后用target_sources添加源码。
# 这种方式更好,因为可以更细粒度地访问权限。
# 例如对cpp文件就是PRIVATE,而hpp则是PUBLIC
target_sources(evolution
PRIVATE
    # 注:${CMAKE_CURRENT_LIST_DIR}代表当前的CMakeLists.txt文件所在的绝对路径。
    ${CMAKE_CURRENT_LIST_DIR}/evolution.cpp
PUBLIC
    ${CMAKE_CURRENT_LIST_DIR}/evolution.hpp
)

# 增加头文件目录
target_include_directories(evolution
PUBLIC
    ${CMAKE_CURRENT_LIST_DIR}
)

对其他几个库同理,只要更改名字即可

注意:即使是外部库,也是同样的做法。对cmake来说,没有任何的区别。

构建运行结果(无测试)

cmake是一个元构建系统。就是说:它先生成对应系统下的原生构建工具(如VS的MSBuild 或者Linux的makefile)所需要的构建文件,然后再运行这些原生构建工具进行构建。

最基本的cmake使用分为两步

  1. configure(配置): 用于生成VS的sln文件(Windows),或者makefile文件(Linux/Mac)
  2. build(构建): 用于生成可执行文件和静态库,相当于VS点击build,或者makefile运行make命令

最后运行生成的可执行文件即可

我们这里采用的是Win10 + VS2022

总结起来就两步

cmake -B build
cmake --build build --config=Release

下面,我们详细观察输出结果

首先进行配置

PS E:\learn\cmake> cmake -B build
-- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.19043.
-- Configuring done
-- Generating done
-- Build files have been written to: E:/learn/cmake/build

然后构建

注意:假如你在Windows下使用VS,那么**–config=Release是必要的**。因为默认会进行Debug模式的构建。
而且实际上VS会把所有的四种变体都生成,因此你在配置步骤指定CMAKE_BUILD_TYPE是没有用的!

PS E:\learn\cmake> cmake --build build --config=Release
MSBuild version 17.3.1+2badb37d1 for .NET Framework
  Checking Build System
  Building Custom Rule E:/learn/cmake/external/CMakeLists.txt
  conversion.cpp
  conversion.vcxproj -> E:\learn\cmake\build\external\Release\conversion.lib
  Building Custom Rule E:/learn/cmake/src/evolution/CMakeLists.txt
  evolution.cpp
  evolution.vcxproj -> E:\learn\cmake\build\src\evolution\Release\evolution.lib
  Building Custom Rule E:/learn/cmake/src/initial/CMakeLists.txt
  initial.cpp
  initial.vcxproj -> E:\learn\cmake\build\src\initial\Release\initial.lib
  Building Custom Rule E:/learn/cmake/src/io/CMakeLists.txt
  io.cpp
  io.vcxproj -> E:\learn\cmake\build\src\io\Release\io.lib
  Building Custom Rule E:/learn/cmake/src/parser/CMakeLists.txt
  parser.cpp
  parser.vcxproj -> E:\learn\cmake\build\src\parser\Release\parser.lib
  Building Custom Rule E:/learn/cmake/src/CMakeLists.txt
  main.cpp
  main.vcxproj -> E:\learn\cmake\build\src\Release\main.exe
  Building Custom Rule E:/learn/cmake/CMakeLists.txt

可执行未见位于
build\src\Release\main.exe

运行

PS E:\learn\cmake\build\src\Release> .\main.exe 10 10 10
length: 10
number of steps: 10
rule: 10
     *
    *
   *
  *
 *
*
         *
        *
       *
      *
     *

至此,我们已经学会了如何使用add_subdirectory来管理内部和外部库。

下面是加餐。

补充1:测试

首先我们要开启测试:

在根目录CMakeLists.txt 把注释的部分解除注释

# 开启测试
enable_testing()
# 进入 tests 文件夹下面的CMakeLists.txt
add_subdirectory(tests)

我们采用catch2这个测试框架。你可以从这里找到catch2源码

由于catch2是个header-only的库(最新版已经不是了),所以你只需要把头文件所有源码全部复制下来,然后新建一个catch.hpp保存即可。保存的文件建议放在tests文件夹下,这样我们写的测试用例直接include catch.hpp即可使用。

新建 tests\CMakeLists.txt

# 编译测试用例为可执行文件
add_executable(test1
    test.cpp
)

# 把待测试的库连接到测试用例上
target_link_libraries(test1 evolution)

# 告知cmake增加了测试,名为my_test,执行test1这个可执行文件
add_test(
    NAME my_test
    COMMAND test1
)

构建和运行测试

cmake -B build
cmake --build build --config=Release
cd build
ctest -C Release

注意:

  1. ctest 是cmake运行测试的一个工具。这个工具会去寻找add_test来查看测试在哪。
  2. ctest 也需要编译之后才能用。它其实本质上和其他的目标没什么不同。
  3. 需要进入build目录才能使用ctest命令,否则会报错找不到测试用例。
  4. 在Windows下需要指定-C命令,同样是因为VS必须指定构建的变体才行。

输出结果

Test project E:/learn/cmake/build
    Start 1: my_test
1/1 Test #1: my_test ..........................   Passed    0.02 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   0.03 sec

补充2:把输出的exe放到根目录的bin目录下

默认情况下输出的exe在
build\src\Release\main.exe

这是在build目录下模拟了源文件目录的结构。

如果你希望把exe文件 或者 库文件直接输出到根目录的bin 或者 lib目录下,你可以把根目录下面的CMakeLists.txt增加如下3行:

# cmake最低版本
cmake_minimum_required(VERSION 3.20)
# 本项目名
project(organize-3rd-parties)

# 设置C++17标准,需要满足标准,不用C++拓展语法
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# 静态库输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/lib)
# 动态库输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/bin)
# 可执行文件输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/bin)


# 进入src文件夹下面的CMakeLists.txt
add_subdirectory(src)

# 进入external文件夹下面的CMakeLists.txt
add_subdirectory(external)

# 开启测试
enable_testing()
# 进入 tests 文件夹下面的CMakeLists.txt
add_subdirectory(tests)

实际上,就是利用了几个cmake的内置变量

变量名含义
CMAKE_ARCHIVE_OUTPUT_DIRECTORY静态库输出目录
CMAKE_LIBRARY_OUTPUT_DIRECTORY动态库输出目录
CMAKE_RUNTIME_OUTPUT_DIRECTORY可执行文件输出目录

更多内置变量含义请参考我的博文。我会持续更新。

输出结果:

在构建完成之后会出现bin和lib这两个目录
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值