如何构建具有多个相互依赖的子目录的C ++项目?

时间:2011-08-03 04:37:42

标签: c++ cmake

我有一个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文件来处理这种结构?

3 个答案:

答案 0 :(得分:57)

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

cmake_minimum_required(VERSION 3.0)

project (Foo)

file(GLOB_RECURSE Foo_SOURCES "src/*.cpp")
file(GLOB_RECURSE Foo_HEADERS "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循环从所有头文件列表中计算包含目录集。

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

答案 1 :(得分:3)

虽然@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,以防您觉得需要查看代码或需要进一步说明。

答案 2 :(得分:2)

我不是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和include中)。