cmake中多级CMakeLists.txt调用

2023-05-16

文章目录

  • 一.工程目录结构
  • 二.工程源代码
    • 2.1 上层目录
      • 2.1.1 cmaketest/CMakeLists.txt
      • 2.1.2 cmaketest/main.cpp
      • 2.1.3 cmaketest/inc/func1.hpp
      • 2.1.4 cmaketest/inc/func2.hpp
      • 2.1.5 cmaketest/src/func1.cpp
      • 2.1.6 cmaketest/src/func2.cpp
    • 2.2 subfunc及subsubfunc子目录
      • 2.2.1 cmaketest/subfunc/CMakeLists.txt
      • 2.2.2 cmaketest/subfunc/subfunc.hpp
      • 2.2.3 cmaketest/subfunc/subfunc.cpp
      • 2.2.4 cmaketest/subfunc/subsubfunc/CMakeLists.txt
      • 2.2.5 cmaketest/subfunc/subsubfunc/subsubfunc.hpp
      • 2.2.6 cmaketest/subfunc/subsubfunc/subsubfunc.cpp
    • 2.3 unit-test子目录
      • 2.3.1 cmaketest/unit-test/CMakeLists.txt
      • 2.3.2 cmaketest/unit-test/unit-test.cpp
    • 2.4 源代码git路径
  • 三.编译及出现的问题
  • 四.build目录分析
  • 五.程序执行结果
  • 六.总结

  在c/c++工程开发中,往往会涉及多级CMakeLists.txt的调用,并且调用方式错综复杂,主要有以下两种方式:

  1. 子目录中的CMakeLists.txt独立生成目标,不作为主目标生成过程主的依赖存在,与主目标并无任何关系。
  2. 子目录中的CMakeLists.txt作为主目标的依赖源文件,不单独生成目标,作为主目标生成过程主的部分源文件,通常以生成.a静态库的方式提供给主CMakeLists.txt使用。

一.工程目录结构

  下面给出了测试工程目录,进行了两项测试:一是unit-test目录作为独立生成目标,其CMakeLists.txt在主CMakeLists.txt主被调用;二是subfunc和subsubfunc作为主CMakeLists.txt向下两级的依赖,为主CMakeLists.txt提供源文件支持,其CMakeLists.txt为逐级调用的方式:CMakeLists.txt----->/subfunc/CMakeLists.txt------>/subfunc/subsubfunc/CMakeLists.txt
  具体目录结构如下:

cmaketest
├── build                                   // 编译目录,生成的目标执行文件文件、静态库、中间缓存文件均存在此处
├── inc                                     // 主头文件目录
│   └── func1.hpp
│   └── func2.hpp
│
└── src                                     // 主源文件目录
│    └── func1.cpp
│    └── func2.cpp
│
└── subfunc                                 // 依赖的一级子目录
│    └── subsubfunc                         // 依赖的二级子目录
│    │        └── subsubfunc.cpp
│    │        └── subsubfunc.hpp
│    │        └──CMakeLists.txt             // 被cmaketest/subfunc/CMakeLists.txt调用
│    └── subfunc.cpp
│    └── subfunc.hpp
│    └── CMakeLists.txt                     // 被cmaketest/CMakeLists.txt调用
│
└── unit-test                               // 单元测试目录,独立生成目标文件
│    └──unit-test.cpp
│    └── CMakeLists.txt                    // 被cmaketest/CMakeLists.txt调用
│
├── main.cpp
├── CMakeLists.txt                        // 最上层、主CMakeLists.txt 

二.工程源代码

2.1 上层目录

2.1.1 cmaketest/CMakeLists.txt

cmake_minimum_required(VERSION 3.8)     # 1.cmake版本
PROJECT(cmaketest)                      # 2.工程名

# set the project name
set(PROJECT_NAME cmaketest)             # 3.设置工程名

# specify the C++ standard 
set(CMAKE_CXX_STANDARD 17)              # 4.设置c++标准为c++17
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 5.设置本地头文件路径,注意:子目录中的头文件通过target_include_directories添加到${PROJECT_NAME}INCLUDE_DIRECTORIES(
    inc                 # 上层头文件路径
    ${SUB_INCLUDE_DIR}  # 下级头文件目录
)

# 6.将源文件路径添加到变量SRC_LIST中
AUX_SOURCE_DIRECTORY(.          SRC_LIST)
AUX_SOURCE_DIRECTORY(src        SRC_LIST)

# 7.生成目标(可执行文件):cmaketest
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_LIST})

# 8.设置编译时依赖的subfunc静态库
target_link_libraries(${PROJECT_NAME}    #目标:tcu
    subfunc        # sub子目录下的静态库文件
    subsubfunc     # subsub子目录下的静态库文件
)

# 9.添加子目录,这样子目录中的CMakeLists.txt才会被调用
add_subdirectory(subfunc)    # 调用subfunc子目录中的CMakeLists.txt,生成静态库而不生成新目标,目标与主CMakeLists.txt中设定的一致

add_subdirectory(unit-test)   # 调用unit-test子目录中的CMakeLists.txt,生成新目标,目标与主CMakeLists.txt中设定的无关,仅仅是调用

注意:INCLUDE_DIRECTORIES包含的是的头文件的路径可以被各级子目录中的目标所使用,而target_include_directories包含的头文件路径只能被特定目标所使用;另外,注意这里采用的是变量传递( ${SUB_INCLUDE_DIR}引入子路径 )的方式引入子目录中的头文件路径。

2.1.2 cmaketest/main.cpp

#include <iostream>
#include <string>
#include "func1.hpp"   //应用层头文件1
#include "func2.hpp"   //应用层头文件2

int main(int argc, char *argv[])
{
    func1();  //调用上层func1
    func2();  //调用上层func2
    return 0;
}

2.1.3 cmaketest/inc/func1.hpp

#ifndef __FUNC1_HPP__
#define __FUNC1_HPP__

int func1(void);

#endif

2.1.4 cmaketest/inc/func2.hpp

#ifndef __FUNC2_HPP__
#define __FUNC2_HPP__

int func2(void);

#endif

2.1.5 cmaketest/src/func1.cpp

#include "subfunc.hpp"   //subfunc头文件
#include "func1.hpp"     //应用层头文件1
#include <iostream>
#include <string>

int func1(void)
{
    std::cout<<"------------func1函数调用开始----------"<<std::endl;
    subfunc1();
    std::cout<<"------------func1函数调用结束----------"<<std::endl<<std::endl;
    return 0;
}

2.1.6 cmaketest/src/func2.cpp

#include "subfunc.hpp"   //subfunc头文件
#include "func2.hpp"     //应用层头文件1
#include <iostream>
#include <string>

int func2(void)
{
    std::cout<<"------------func2函数调用开始----------"<<std::endl;
    subfunc2();
    std::cout<<"------------func2函数调用结束----------"<<std::endl;
    return 0;
}

2.2 subfunc及subsubfunc子目录

2.2.1 cmaketest/subfunc/CMakeLists.txt

# 1.将本目录下的所有.c 文件添加到SUB_DIR_LIB_SRCS变量
AUX_SOURCE_DIRECTORY(. SUB_DIR_SRC_LIST)

# 2.设置当前的头文件路径
set(SUB_INCLUDE_DIR 
    ${CMAKE_CURRENT_SOURCE_DIR}          # 当前源文件路径
    ${SUB_SUB_INCLUDE_DIR}               # 由下层subsubfunc目录传递的头文件路径
    CACHE INTERNAL "subfunc include dir" # 这个字符串相当于对变量SUB_INCLUDE_DIR的描述说明,不能省略,但可以自己随便定义,只有添加了这个描述SUB_INCLUDE_DIR变量才能被上层CMakeLists.txt调用!!!
)

MESSAGE(STATUS "subfunc层头文件路径 :${SUB_INCLUDE_DIR}")

# 3.生成静态库
add_library(subfunc ${SUB_DIR_SRC_LIST})

# 4.添加subsubfunc子目录,这样子目录中的CMakeLists.txt才会被调用
add_subdirectory(subsubfunc)

2.2.2 cmaketest/subfunc/subfunc.hpp

#ifndef __SUB_FUNC_HPP__
#define __SUB_FUNC_HPP__

int subfunc1(void);
int subfunc2(void);

#endif

2.2.3 cmaketest/subfunc/subfunc.cpp

#include "subfunc.hpp"
#include "subsubfunc.hpp"
#include <iostream>
#include <string>

int subfunc1(void)
{
    std::cout<<"------subfunc1函数调用开始------"<<std::endl;
    /* 中间调用subsubfunc1函数 */
subsubfunc1();
    std::cout<<"------subfunc1函数调用结束------"<<std::endl;
    return 0;
}
int subfunc2(void)
{
    std::cout<<"------subfunc2函数调用开始------"<<std::endl;
subsubfunc2();

    /* 中间调用subsubfunc2函数 */

    std::cout<<"------subfunc2函数调用结束------"<<std::endl;
    return 0;
}

2.2.4 cmaketest/subfunc/subsubfunc/CMakeLists.txt

# 1.将本目录下的所有.c 文件添加到SUB_DIR_LIB_SRCS变量
AUX_SOURCE_DIRECTORY(. SUB_SUB_DIR_SRC_LIST)

# 2.设置当前的头文件路径
set(SUB_SUB_INCLUDE_DIR 
    ${CMAKE_CURRENT_SOURCE_DIR}              # 当前源文件路径
    CACHE INTERNAL "subsubfunc include dir"  # 这个字符串相当于对变量SUB_SUB_INCLUDE_DIR的描述说明,不能省略,但可以自己随便定义,只有添加了这个描述SUB_SUB_INCLUDE_DIR变量才能被上层CMakeLists.txt调用!!!
)

MESSAGE(STATUS "subsubfunc层头文件路径 :${SUB_SUB_INCLUDE_DIR}")

# 3.生成静态库
add_library(subsubfunc ${SUB_SUB_DIR_SRC_LIST})

2.2.5 cmaketest/subfunc/subsubfunc/subsubfunc.hpp

#ifndef __SUB_SUB_FUNC_HPP__
#define __SUB_SUB_FUNC_HPP__

int subsubfunc1(void);
int subsubfunc2(void);

#endif

2.2.6 cmaketest/subfunc/subsubfunc/subsubfunc.cpp

#include "subsubfunc.hpp"

#include <iostream>
#include <string>

int subsubfunc1(void)
{
    std::cout<<"-subsubfunc1函数调用开始-"<<std::endl;
    
    std::cout<<"-subsubfunc1函数调用结束-"<<std::endl;
    return 0;
}
int subsubfunc2(void)
{
    std::cout<<"-subsubfunc2函数调用开始-"<<std::endl;

    std::cout<<"-subsubfunc2函数调用结束-"<<std::endl;
    return 0;
}

分析:注意头文件目录变量的逐级向上传递,通过设置SUB_SUB_INCLUDE_DIR变量(必须添加CACHE INTERNAL "subsubfunc include dir"描述),将subsubfunc文件下的头文件路径传递给了上级sunfunc文件下的CMakeLists.txt;通过设置SUB_INCLUDE_DIR变量(必须添加CACHE INTERNAL "subfunc include dir"描述),将subfunc文件下的头文件路径(包含之前获得的${SUB_SUB_INCLUDE_DIR})传递给了最上层文件下的CMakeLists.txt。

2.3 unit-test子目录

2.3.1 cmaketest/unit-test/CMakeLists.txt

add_executable(unit-test unit-test.cpp)
target_link_libraries(unit-test boost_system pthread)

2.3.2 cmaketest/unit-test/unit-test.cpp

#include <boost/asio.hpp> 
#include <iostream>

int main(int argc,char* argv[])
{
    std::cout<<"unit-test代码调用!!!"<<std::endl;
    return 0;
}

2.4 源代码git路径

https://github.com/hututu578/cmaketest

三.编译及出现的问题

  在项目路径下创建/build文件夹,用于存放cmake编译过程中生成的各种中间文件、库、最终目标文件等:

cd build
cmake . .
make

  在cmake编译的过程中发现了一个问题,就是一开始执行cmake . .时候,并没有完成头文件变量的传递,导致make编译出错。cmake . .执行与make执行结果如下:
cmake . .
在这里插入图片描述
make
在这里插入图片描述
  可以看到,由于SUB_SUB_INCLUDE_DIR变量并没有传递到subfunc层的CMakeLists.txt,从而导致了在subfunc层并没有包含全部发头文件路径,导致编译的时候找不到subsubfunc.hpp。
解决方案:
  经过反复测试发现,多执行几次cmake . .(三次以上)就可以解决这个问题,猜测是因为多次执行cmake . .的过程完成了SUB_SUB_INCLUDE_DIR和SUB_INCLUDE_DIR变量向上层的传递,多执行几次cmake . .的执行结果如下:
在这里插入图片描述

四.build目录分析

  下面是执行编译完成后的build文件目录结构:
在这里插入图片描述
可以看出,unit-test作为独立的子目录,生成了独立的目标文件unit-test。而在subfunc下生成了libsubfunc.a的静态库文件,在subsubfunc下生成了libsubsubfunc.a的静态库文件,这两个库文件供最上层CMakeLists.txt调用,并最终生成一个目标文件cmaketest。

五.程序执行结果

在这里插入图片描述

六.总结

  上述测试了两种类型的多级CMakeLists.txt调用,一种是独立的unit-test生成独立的目标文件,与主CMakeLists.txt仅有一个调用与被调用的关系,并不存在任何的编译依关系;
  而另一种多级CMakeLists.txt调用之间存在上下级的依赖关系,下层的源代码给上层的调用提供支持(以生成静态库的方式),这里进行了分层设计,下层的头文件路径仅传递给调用的上一层而不会传递给最上层,这种变量传递的方式提高了代码的分层性,这里需要注意变量的设置(CACHE INTERNAL属性的必须添加)以完成下层到上层的变量传递,上述的两种分层CMakeLists.txt调用方式可覆盖基本的全部开发场景。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cmake中多级CMakeLists.txt调用 的相关文章

随机推荐

  • 第一个自己写的程序

    22年8月份花了三周时间快速过了一遍某站一位大佬的视频 xff0c 前几天刷朋友圈时偶然看见一位道友写了个矩阵乘积的计算器 xff0c 瞬间给了我灵感 xff0c 直接开始操作 理想很丰满 xff0c 现实很扯淡 xff0c 刚开始写了个4
  • CEF学习质料

    目录 一 编译CEF3里的lib xff1a 1 下载CEF3 http opensource spotify com cefbuilds index html 2 下载CMake xff0c 运行CMake GUI exe 3 CMake
  • 【无标题】

    5 1 内存模型基础 这里从两方面来讲内存模型 xff1a 一方面是基本结构 xff0c 这与事务在内存中是怎样布局的有关 xff1b 另一方面就是并发 对于并发基本结构很重要 xff0c 特别是在低层原子操作 所以我将会从基本结构讲起 C
  • C++`中的原子操作和原子类型

    5 2 C 43 43 中的原子操作和原子类型 原子操作 是个不可分割的操作 在系统的所有线程中 xff0c 你是不可能观察到原子操作完成了一半这种情况的 xff1b 它要么就是做了 xff0c 要么就是没做 xff0c 只有这两种可能 如
  • 编写一个使用锁的线程安全查询表

    6 3 基于锁设计更加复杂的数据结构 栈和队列都很简单 xff1a 接口相对固定 xff0c 并且它们应用于比较特殊的情况 并不是所有数据结构都像它们一样简单 xff1b 大多数数据结构支持更加多样化的操作 原则上 xff0c 这将增大并行
  • __declspec(dllexport)和__declspec(dllimport)以及QT中public: static struct QMetaObject const xxx:staticMe

    假设你的头文件如下 xff1a span class token macro property span class token directive hash span span class token directive keyword
  • /MD 与 /MT、/MTD与/MDD的区别

    VS在 属性页的 C C 43 43 gt Code Generation gt Runtime Library 一项中总共有四个选项 MD 与 MT MTD与 MDD xff0c 它们分别有什么区别 xff1f 1 MD 与 MT 用于R
  • C++多线程案列

    C 43 43 多线程案列 话不多说 xff0c 直接上代码 xff1a span class token comment CMakeList txt ThreadDemo1 的 CMake 项目 xff0c 在此处包括源代码并定义 spa
  • 右值引用、移动语义、完美转发

    右值引用 移动语义 完美转发 左值 右值 xff1a 在c 43 43 中 xff0c 所有的值不是左值 xff0c 就是右值 有名字的对象都是左值 xff0c 右值没有名字 还有一个可以区分左值和右值的方法 xff1a 看能不能对表达式取
  • Deep Meta Learning for Real-Time Target-Aware Visual Tracking 论文阅读

    这篇文章是韩国的一个组做的 一直没中 直到19年中了ICCV xff0c 据说是第一篇将元学习引入目标跟踪的文章 xff0c 用的架构是siamese网络的架构 xff0c 但是在模型在线更新的时候使用了meta learning的思想 M
  • 单链表倒序

    单链表倒序 题目来源 牛客网 题目描述 输入一个链表 xff0c 按链表从尾到头的顺序返回一个ArrayList span class token keyword public span span class token keyword c
  • VS2019中编写C语言

    建立C 43 43 控制台程序 xff0c 将main函数所在的文件后缀名从cpp改成c xff0c 然后复制下列模板即可 xff1b 模板如下 xff1a dsa zju cpp 此文件包含 34 main 34 函数 程序执行将在此处开
  • CMake编译工程/第一个CMakeLists.txt(最详细案例演示)

    目录 在 linux 平台下使用 CMake 构建C C 43 43 工程的流程 当前项目目录结构 最小CMake工程 进入文件夹5 3 1 xff0c VScode打开项目文件5 3 1 在项目5 3 1顶层目录中 xff0c New F
  • 数据分析岗-机器学习相关知识

    1 解释共线性 我们进行回归分析需要了解每个自变量对因变量的单纯效应 xff0c 多重共线性就是说自变量间存在某种函数关系 xff0c 如果你的两个自变量间 xff08 X1和X2 xff09 存在函数关系 xff0c 那么X1改变一个单位
  • make,Makefile简易教程

    一 概述 make是一个类UNIX系统下的编译命令 xff0c 也可以理解为一个项目管理工具 xff0c 通过make可以按照自己指定的编译命令编译整个项目 xff0c 相当于将在命令行的编译命令按序执行 xff0c 省去了反复键入编译命令
  • @Transactional注解事务失效的七种原因分析

    64 Transactional是一种基于注解管理事务的方式 xff0c spring通过动态代理的方式为目标方法实现事务管理的增强 64 Transactional使用起来方便 xff0c 但也需要注意引起 64 Transactiona
  • C头文件相互包含

    今天遇见一个很头疼的事 xff0c 就是1 h头文件包含2 h xff0c 但是1 h里面却找不到2 h定义的一个结构体变量 最后排查发现是2 h里面又包含了1 h导致的 C语言中头文件包含的处理原则 之前一直以为 xff0c 一个 c文件
  • spring-kafka通过@KafkaListener实现消费者监听流程分析

    文章目录 主流程处理EnableKafka注解实现BeanPostProcessor接口postProcessAfterInitialization扫描 64 KafkaListenerregisterListenerContainer注册
  • c语言strrchr函数,strrchr_字符串 | Strings_C_参考手册_非常教程

    strrchr 在头文件中定义 char strrchr const char str xff0c int ch 通过 str 指向的以空字符结尾的字节字符串 每个字符解释为无符号字符 查找 ch 的最后一次出现 在转换为 char 之后
  • cmake中多级CMakeLists.txt调用

    文章目录 一 工程目录结构二 工程源代码2 1 上层目录2 1 1 cmaketest CMakeLists txt2 1 2 cmaketest main cpp2 1 3 cmaketest inc func1 hpp2 1 4 cma