带有子目录的 CMake

CMake with subdirectories

本文关键字:CMake 子目录      更新时间:2023-10-16

我正在尝试设置我的项目以使用 CMake 正确编译。

我的目录如下所示:

root
|- bin
|   |- // Where I want to build CMake from - using 'cmake ..'
|- build
|   |-
|- include
|   |- database
|   |    |- database.h
|- src
|- database
|    |- database.cpp
|- main
|- main.cpp

随着我的项目越来越大,我的子目录肯定会增长,并且认为 CMake 可能是一个学习的好主意。目前,我只能让 CMake 在我的 src/中没有子目录的情况下工作。但是,我确实希望这个项目会发展成许多子目录。

我是否需要在每个目录中包含.cpp文件的目录中.txt多个 CMakeList?谁能指出我正确的方向?

谢谢!

我将向您推荐本文,以深入讨论这两种主要方法,但是您可以通过几种方式构建这样的项目。

  • 一个CMakeLists.txt顶级文件,列出所有不同子目录中的所有源文件。通常,您只会在非常简单的项目中看到此内容。
  • 每个目录中有一个CMakeLists.txt文件,每个文件都由其父级使用add_subdirectory()引入。这种方法很常见,通常是推荐的方法。
  • 顶层有一个CMakeLists.txt文件,每个子目录都有自己的文件,其中列出了自己的源文件和目标。顶级文件CMakeLists.txt引入带有include()的子目录文件。这种方法不太常见,但在某些情况下可能比其他两种方法具有优势。不过,通常不是推荐的方法。

每个都有其优点和缺点。仅当文件和子目录很少时,才建议只有一个顶级CMakeLists.txt文件。一旦项目增长,将所有内容都保留在顶层可能会变得太多,并使生成的CMakeLists.txt文件更难遵循。它还具有缺点,即对文件添加或删除的更改不限于特定目录。这似乎没什么大不了的,但是如果有多个人在处理一个项目,并且您希望轻松查看其他人的更改会影响项目的哪个部分(例如在 git 历史记录中),那就更难了。如果同时添加/删除文件,从而更改文件的顶级CMakeLists.txt并且有可能发生冲突,则尤其如此。

一旦项目的大小变得不平凡,大多数人选择在每个子目录中添加一个CMakeLists.txt文件,并使用add_subdirectory()将它们组合在一起。 @TheQuantumPhysicist的回答给出了一个很好的例子,说明这如何有用,所以我不会在这里重复大部分细节。这种结构使您能够轻松打开/关闭构建树的整个部分,但更重要的是,它为每个子目录提供了自己的变量范围。如果要在源代码树的一部分中设置变量等,但在另一部分中看不到这些更改,这可能很重要。每个子目录还获取自己的目录属性范围。通过这些单独的作用域,可以更轻松地将编译器标志和其他临时值等内容应用于复杂目录结构的一个部分。

第三个选项是一个顶级CMakeLists.txt文件,每个子目录提供由include()引入的文件不太常见,但它与在每个子目录中使用一个CMakeLists.txt文件有相似之处。两者都将目录中文件的详细信息本地化为该目录中的CMakeLists.txt或其他类似名称的文件。因此,更改在版本控制历史记录等中变得更容易合并和理解。使用 CMake 3.12 或更早版本时,第三种方法允许而第二种方法不允许的一件事是,在使用target_sources()指定每个子目录中的源文件时,您可以更自由地使用target_link_libraries()。本答案顶部链接的文章详细介绍了为什么target_sources()可以有利。但是,如果您的最低 CMake 版本为 3.13 或更高版本,则第三种方法不如第二种方法,通常应避免使用。

最后,我建议你不要养成把构建树放在源代码树中的习惯。相反,将构建树创建为源树的同级。旨在保持源代码树不受构建的影响。所需要的只是让某人在源代码树中创建一个目录,其名称与您用于构建树的任何目录相同,以免出现问题(我已经不止一次看到过!您可能还希望为一个源代码树设置多个构建树,例如一个用于调试版本,另一个用于发布版本,因此将这些构建树放在源代码树之外也有助于保持源代码树不那么混乱。

您的项目似乎不需要多个CMakeLists.txt。多次拥有多个 CMakeList 的目的.txt是多方面的,但它似乎不适合您的项目。例如,一个好处是分层组织,这样您就可以分离要单独构建的不同单元,并最终链接。

如果您的根目录中有另一个名为tests的目录,请考虑这种情况。该目录包含单元测试,您希望开发人员可以选择是否编译它。在这种情况下,您将一个 CMakeLists.txt 放入tests中,并在主 CMakeList 中启用此功能.txt并使用以下add_subdirectory(tests)如下:

if(testing_enabled)
add_subdirectory(tests)
endif()

tests年,会有另一个CMakeLists.txt。这样,您就可以分离关注点。单元测试的开发与项目的开发无关,并且生成过程也是独立的。

另一个例子

如果您有两个用户可以选择的库,请考虑这种情况。在这种情况下,您的root有两个目录,lib1lib2.现在,您可以在主CMakeLists中执行以下操作.txt:

if(IWantLib1)
add_subdirectory(lib1)
else()
add_subdirectory(lib2)
endif()

lib1lib2目录都包含CMakeLists.txt。