【问题标题】:How can I build a C++ project with multiple interdependent subdirectories?如何构建具有多个相互依赖的子目录的 C++ 项目?
【发布时间】:2011-10-18 19:10:52
【问题描述】:

我有一个 C++ 项目,在该项目中,我更多地将目录用作一种组织元素——就像在 Java 中使用包或在 PHP 中使用目录的方式一样。目录并非旨在成为自给自足的元素,而只是一种组织整个项目并防止我被来源淹没的方式。如何构建我的 CMakeLists.txt 文件来处理这个问题?制作目录库似乎不适合这里,因为它们都是相互依赖的,不打算以这种方式使用。

作为一个相关问题,我在 CMake 中看到的大多数关于多个子目录的示例(这些示例并不多)都忽略或掩盖了设置 include_directories 的问题,这是我的问题一直有麻烦。没有梳理我的源文件以确定哪个文件取决于哪个目录和哪个目录,是否有只是将/src/ 下的所有目录设置为潜在的包含目录并让 CMake 确定哪些是实际依赖的?

这是一个示例结构:

--src
  --top1
    --mid1
      --bot1
        --src1.cpp
        --hdr1.h
      --bot2
        --src2.cpp
        --hdr2.h
    --mid2
      --bot3
        --src3.cpp
        --src4.cpp
        --hdr3.h
  --top2
    --mid3
      --src5.cpp
      --hdr4.h

如此等等。如何构建我的 CMakeLists.txt 文件来处理这种结构?

【问题讨论】:

  • 你一定要使用 CMake 吗?
  • 1.) 您是否使用首选的生成器? 2.) 你真的认为 CMake 应该弄清楚什么依赖什么?我认为应该可以定义相互独立的模块(例如 mid1、mid2、top2)。 3.) 你是否包括#include "hdr2.h" 或类似 的东西?
  • @benoit 我想不是,但这就是我目前正在使用的。我宁愿不使用 Automake,当我的构建没有那么复杂时,似乎需要付出很多努力来学习构建系统。
  • @Philipp 1) 那不是 Cmake 吗? 2)我可以将目录定义为相互独立的模块。但这将超越使用组织的意义,而不是模块化的目录结构。在我的项目中,几乎所有东西都是相互依赖的,模块会比我想要的大得多。 3)我包括“hdr2.h”
  • 如果您的文件夹不构成相对隔离的库,那么您的组织方法只是一种错觉 - 复杂的依赖关系可能仍会蔓延。我建议将模板和非模板代码分开。如果可能,请尝试在 .cpp 上实现模板并实例化您需要的模板,仅此而已

标签: c++ cmake


【解决方案1】:

由于您项目中的目录结构只是为了让您的文件井井有条,因此一种方法是使用CMakeLists.txt 自动查找src 目录中的所有源文件,并将所有目录添加为包含目录其中一个头文件。以下 CMake 文件可以作为起点:

cmake_minimum_required(VERSION 3.12)

project (Foo)

file (GLOB_RECURSE Foo_SOURCES CONFIGURE_DEPENDS "src/*.cpp")
file (GLOB_RECURSE Foo_HEADERS CONFIGURE_DEPENDS "src/*.h")

set (Foo_INCLUDE_DIRS "")
foreach (_headerFile ${Foo_HEADERS})
    get_filename_component(_dir ${_headerFile} PATH)
    list (APPEND Foo_INCLUDE_DIRS ${_dir})
endforeach()
list (REMOVE_DUPLICATES Foo_INCLUDE_DIRS)

add_executable(FooExe ${Foo_SOURCES})
target_include_directories(FooExe PRIVATE ${Foo_INCLUDE_DIRS})

两个file(GLOB_RECURSE ... 命令确定源文件和头文件的集合。 foreach 循环从所有头文件的列表中计算包含目录的集合。 CONFIGURE_DEPENDS 标志告诉 CMake 在构建时重新运行 glob 命令。

计算源文件集的一个缺点是 CMake 不会自动检测何时将新文件添加到源代码树中。然后您必须手动重新创建构建文件。

【讨论】:

  • 我已经尝试过类似的东西,但使用不同的 include_directories 逻辑(失败得很惨)。我会试试看的。
  • 谢谢你!我无法理解这很难找到——没有人在子目录中组织他们的源文件吗?
  • @Cygon - 显然不是。 :-)
  • 这个答案非常简洁。但是,现在正确的解决方案是使用 target_include_directories 等。
  • target_include_directories 在我认为的 cmake 版本 2.8.11+ 上可用。
【解决方案2】:

虽然@sakra 对这个问题给出了很好的答案,但我认为更深入地探讨它更合适。

出于多种原因,我们希望将代码分成模块和库。比如代码封装、复用性、更容易调试等。这个想法也会在编译过程中传播。

换句话说,我们要将编译过程分成几个小的编译步骤,每个步骤属于一个模块。所以每个模块都必须有自己的编译过程。这就是我们为每个目录使用一个CMakeLists.txt 文件的原因。因此,每个目录都会有自己的编译命令,并且在项目的根目录中会有一个主 CMakeLists.txt 文件。

这是一个例子。考虑以下项目结构:

src/
|
- main.cpp
|
_sum/
    |
    - sum.h
    |
    - sum.cpp

我们将有一个CmakeLists.txt 每个目录。第一个目录是项目的根目录,src/ 文件夹在其中。这是该文件的内容:

cmake_minimum_required(VERSION 3.4)
project(multi_file)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-Wall")

add_subdirectory(src) 

下一个CMakeLists.txt 将位于src/ 目录中:

add_subdirectory("sum")

add_executable(out main.cpp)
target_link_libraries(out sum)

最后一个将在sum/目录中:

add_library(sum SHARED sum.cpp)

我希望这会有所帮助。我创建了一个github repository,以防您觉得需要查看代码或需要进一步解释。

【讨论】:

    【解决方案3】:

    我不是 CMake 方面的专家,但由于没有其他答案,我将查看文档并试一试。在不同的目录中组织源文件和包含文件几乎是常态。

    看起来 CMake 允许您提供包含目录的列表: http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:include_directories

    比如:

    include_directories("src/top1/mid1/bot1" "src/top1/mid1/bot2/" ... )
    

    这些被传递给编译器,以便它可以找到头文件并将传递给每个源文件。因此,您的任何源文件都应该能够包含任何头文件(我认为这是您所要求的)。

    与此类似,您应该能够在add_executable 命令中列出所有源文件:

    add_executable(name "src/top1/mid1/bot1/src1.cpp" "src/top1/id1/bot2/src2.cpp" ...)
    

    所以这将是构建所有东西的一种天真的方式。每个源文件都将被编译并在所有这些目录中查找头文件,然后目标文件将链接在一起。考虑是否有任何方法可以简化这一点,这样您就不需要这么多的包含文件夹,也许只有几个常见的头文件需要被所有源文件引用。如果事情变得更复杂,您可以将子层次结构构建到库等中。还可以考虑分离源文件和头文件(例如在 src 和包含中)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-02
      • 2013-05-11
      • 2011-05-30
      • 1970-01-01
      • 1970-01-01
      • 2020-03-13
      • 2018-01-09
      • 1970-01-01
      相关资源
      最近更新 更多