Sirius' blog Sirius' blog
首页
  • 学习笔记

    • 《C++》
    • 《MATLAB》
    • 《Python》
  • 学习笔记

    • 《Git》
    • 《CMake》
  • 技术文档
  • 博客搭建
  • 学习
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Sirius0v0

怕什么真理无穷,进一寸有一寸的欢喜
首页
  • 学习笔记

    • 《C++》
    • 《MATLAB》
    • 《Python》
  • 学习笔记

    • 《Git》
    • 《CMake》
  • 技术文档
  • 博客搭建
  • 学习
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 初识CMake:学习笔记与速查
  • CMake中常见的内置变量
  • 现代CMake命令行及CMakeLists.txt写法
  • CMake设置对象属性
  • CMake链接第三方库的方法
  • 使用CMake模块化项目
    • 推荐的目录组织结构
      • 各文件推荐写法
    • 一、划分子项目
    • 二、根目录的CMakeLists.txt配置
    • 三、子项目的CMakeLists.txt配置
      • GLOB与GLOB_RECRUSE的区别
    • 四、子项目的头文件
    • 五、子项目的源文件
    • 六、只有头文件,没有源文件的情况
    • 七、依赖其他模块但不解引用,则可以只前向声明不导入头文件
    • 八、依赖另一个子项目,则需要链接
    • 九、CMake中的include
  • 《CMake》学习笔记
Sirius0v0
2023-07-02
目录
推荐的目录组织结构
各文件推荐写法
一、划分子项目
二、根目录的CMakeLists.txt配置
三、子项目的CMakeLists.txt配置
GLOB与GLOB_RECRUSE的区别
四、子项目的头文件
五、子项目的源文件
六、只有头文件,没有源文件的情况
七、依赖其他模块但不解引用,则可以只前向声明不导入头文件
八、依赖另一个子项目,则需要链接
九、CMake中的include

使用CMake模块化项目

# 使用CMake模块化项目

该笔记参考BiliBili-【公开课】现代CMake模块化项目管理指南【C/C++】 (opens new window)

# 推荐的目录组织结构

  • 项目名/include/项目名/模块名.h
  • 项目名/src/模块名.cpp

如下是一个例子:

.
├── CMakeLists.txt
├── biology
│   ├── CMakeLists.txt
│   ├── include
│   │   └── biology
│   │       ├── Animal.h
│   │       └── Carer.h
│   └── src
│       ├── Animal.cpp
│       └── Carer.cpp
├── cmake
│   └── MyUsefulFuncs.cmake
└── pybmain
    ├── CMakeLists.txt
    ├── include
    │   └── pybmain
    │       └── myutils.h
    └── src
        └── main.cpp

头文件一般会在include目录里再嵌套一个项目名,目的是为了 避免头文件命名冲突,例如:

#include <pybmain/myutils.h>
#include <biology/myutils.h>
// 若没有项目名,就会产生冲突

# 各文件推荐写法

CMakeLists.txt:

CMakeLists.txt中推荐使用target_include_directories(项目名 PUBLIC include);

不要使用include_directories(include),这样会污染头文件空间

.c:

#include <项目名/模块名.h>
namespace 项目名 {
    void 函数名() { 函数实现 }
}

.h:

#pragma once
namespace 项目名 {
    void 函数名();
}

# 一、划分子项目

  • 即使只有一个子项目,也建议你创建一个子目录,方便以后追加新的子项目;

  • 子项目,例如biology 和 pybmain,他们分别在各自的目录下有自己的 CMakeLists.txt。

    • 一般一个项目是可执行文件(比如这里的pybmain),另一个是库文件(比如这里的biology)
    • 可执行文件是给用户使用的,一般只有交互的逻辑;而实际代码的实现逻辑一般都在库当中。

# 二、根目录的CMakeLists.txt配置

cmake_minimum_required(VERSION 3.18)

if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake;${CMAKE_MODULE_PATH}")

project(CppCMakeDemo LANGUAGES CXX)

include(MyUsefulFuncs)

add_subdirectory(pybmain)
add_subdirectory(biology)

根项目的 CMakeLists.txt 中,设置默认的构建模式,设置统一的 C++ 版本等各种选项。然后通过 project 命令初始化根项目。

随后通过 add_subdirectory 把两个子项目 pybmain 和 biology 添加进来,这会调用 pybmain/CMakeLists.txt 和 biology/CMakeLists.txt。

# 三、子项目的CMakeLists.txt配置

file(GLOB_RECURSE srcs CONFIGURE_DEPENDS src/*.cpp include/*.h)
add_library(biology STATIC ${srcs})
target_include_directories(biology PUBLIC include)

子项目中主要创建了静态库对象,通过GLOB_RECRUSE批量添加位于src和include下的源码与头文件。

  • 根项目的 CMakeLists.txt负责处理全局有效的设定。
  • 而子项目的 CMakeLists.txt 则仅考虑该子项目自身的设定,比如他的头文件目录,要链接的库等等。

# GLOB与GLOB_RECRUSE的区别

file (GLOB myvar CONFIGURE_DEPENDS src/*.cpp)
file (GLOB_RECURSE myvar CONFIGURE_DEPENDS src/*.cpp)
  • GLOB只能查找到往下一级的文件,而GLOB_RECURSE能查到嵌套的目录;
  • 添加CONFIGURE_DEPENDS选项会在你创建了新文件后,进行cmake --build时自动检测目录是否更新,并更新变量myvar的值,而无需手动重新运行cmake -B build

# 四、子项目的头文件

  • 这里需要给 biology库设置了头文件搜索路径include。
  • 由于子项目的 CMakeLists.txt 里指定的路径都是相对路径,所以这里指定的 include 实际上是:根/biology/include。

# 五、子项目的源文件

  • 这里利用file(...)给 biology 批量添加了 src/*.cpp 下的全部源码文件。

  • 因为子项目的 CMakeLists.txt 里指定的路径都是相对路径,所以这里指定 src 实际上是:根/biology/src。

# 六、只有头文件,没有源文件的情况

  • 有时我们会直接把实现直接写在头文件里,这时可以没有与之对应的源文件,只有一个头文件。

  • 注意:在头文件里直接实现函数时,要加 static 或 inline 关键字。(类与结构体可以不加)

    • 防止被重复定义

# 七、依赖其他模块但不解引用,则可以只前向声明不导入头文件

  • 假如模块 Carer 的头文件Carer.h 引用了其他模块中的 Animal 类,但是并没有解引用(如没有进行指针的解引用->,仅做变量声明使用) Animal,只有源文件Carer.cpp解引用了 Animal。

    • 那么这个头文件是不需要导入Animal.h 的,只需要一个前置声明 struct Animal,只有实际调用了 Animal 成员函数的源文件需要导入 Animal.h。
    • 好处:加快编译速度,防止循环引用。

# 八、依赖另一个子项目,则需要链接

  • 让 pybmain 链接上 biology:

    target_link_libraries(pybmain PUBLIC biology)
    
  • 由于 PUBLIC 属性具有传染性,根/biology/include 现在也加入 pybmain 的头文件搜索路径了,因此 pybmain 里可以 #include 到 biology 的头文件。

# 九、CMake中的include

  • 写 include(XXX),则他会在 CMAKE_MODULE_PATH 这个列表中的所有路径下查找 XXX.cmake 这个文件。

    • 因此在include前,首先需要把XXX.cmake文件的路径加到CMAKE_MODULE_PATH中;
    • CMAKE_MODULE_PATH列表的每个值用;分割。
  • 这样你可以在 XXX.cmake 里写一些你常用的函数,宏,变量等

示例MyUsefulFuncs.cmake内容如下:

macro (my_add_target name type)
    # 用法: my_add_target(pybmain EXECUTABLE)
    file(GLOB_RECURSE srcs CONFIGURE_DEPENDS src/*.cpp src/*.h)
    if ("${type}" MATCHES "EXECUTABLE")
        add_executable(${name} ${srcs})
    else()
        add_library(${name} ${type} ${srcs})
    endif()
    target_include_directories(${name} PUBLIC include)
endmacro()

set(SOME_USEFUL_GLOBAL_VAR    ON)
set(ANOTHER_USEFUL_GLOBAL_VAR OFF)
  • function-CMake (opens new window)和macro-CMake (opens new window)的区别可以详细查看文档
  • 简单来说:macro 相当于直接把代码粘贴过去,直接访问调用者的作用域。这里写的相对路径 include 和 src,是基于调用者所在路径。
  • function 则是会创建一个闭包,优先访问定义者的作用域。这里写的相对路径 include 和 src,则是基于定义者所在路径。
  • include和add_subdirectory同样,前者相当于直接粘贴,直接访问调用者的作用域,后者则优先访问定义者的作用域。
编辑 (opens new window)
#CMake
上次更新: 2023/08/14, 19:51:00
CMake链接第三方库的方法

← CMake链接第三方库的方法

最近更新
01
ipopt优化库配置及使用
07-21
02
ubuntu离线安装包的方法
07-21
03
其它控件的使用
03-05
更多文章>

Gitalking ...

Theme by Vdoing | Copyright © 2020-2025 Sirius0v0 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式