如何构建 CMake 项目

Posted

技术标签:

【中文标题】如何构建 CMake 项目【英文标题】:How to structure CMake projects 【发布时间】:2020-06-10 11:09:45 【问题描述】:

我开始使用 CMake,但不确定如何在以下设置中使用它。

我有一些 library-cmake-projects(源代码),它们可能相互依赖。

Libs
-A
-B
-C

(假设A 取决于CB 取决于C

如何让 cmake 知道其他目录中需要的模块?

【问题讨论】:

这是“经典”项目之一......并且没有通用方法。您提出了几个问题,例如“这种方法是否正确?”,但是尝试这些方法怎么样?简而言之:您注意到的所有方法都是可行的。试着跟随他们。你会遇到一个具体的问题吗,你可能会问这个问题(但不要忘记在问之前搜索)。在目前的形式中,问题过于宽泛 "如何让 cmake 知道其他目录中需要的模块?" - 如果没有代码,这个问题仍然是模糊。再一次:尝试编写代码。如您所知,add_subdirectory 可以用于包含其他项目,然后尝试使用它。在编写代码时,您可能会遇到真正的问题。在尝试解决问题时,您可能会找到几种解决方案。尝试应用它们。由于某些原因,您可能会发现这些方法是不可接受的。是时候提出具体问题具体要求的问题了。 【参考方案1】:

关于 add_subdirectory,您应该像您的源代码一样对其进行结构化。

所以,假设您的文件夹结构如下所示:

/
-- App
-- libs
  -- A
  -- B
  -- C

然后你会像这样放置 CMakeLists.txt:

/
-- CMakeLists.txt
-- App
  -- CMakeLists.txt
-- libs
  -- CMakeLists.txt
  -- A
    -- CMakeLsts.txt
  -- B
    -- CMakeLists.txt
  -- C
    -- CMakeLists.txt

/CMakeLists.txt

add_subdirectory(App)
add_subdirectory(libs)

/App/CMakeLists.txt

add_executable(App ...)
target_link_libraries(App A B)

/libs/CMakeLists.txt

add_subdirectory(A)
add_subdirectory(B)
add_subdirectory(C)

/libs/A/CMakeListst.txt

add_library(A ...)
target_link_libraries(A C)

/libs/B/CMakeListst.txt

add_library(B ...)
target_link_libraries(B C)

/libs/C/CMakeListst.txt

add_library(C ...)

认为 add_subdirectory 与依赖管理有任何关系是不好的,它与源文件和构建文件的组织方式有关。它也有作用域,但大多数时候你不需要担心它。如果您直接将所有三个 add_subdirectory 调用放在*** CMakeLists.txt 中,则不一定需要 /libs/CMakelists.txt,但它可以帮助组织。

例如,我会避免使用 add_subdirectory 来包含相邻文件夹。假设目标在那里,并从更高的地方包含进来。

请注意,这是我通常会推荐的,因为它似乎是设计师的想法,但如果您出于某种原因需要其他东西,或者对什么是正确的设计有不同的看法,那么请随意构建不一样。

【讨论】:

好的,这是“将它包含在顶层”的方法。你有什么建议吗,当 libs 增长了几年时,我开始了一个使用 lib A 的新项目,我现在必须遵循 A 的依赖树将所有需要的子目录添加到我的顶部等级? 您说不要使用add_subdirectory 来包含相邻文件夹。但是 cmake 中是否有另一个工具可以包含导致编译的 neigbouring 文件夹? @generic_opto_guy 哦,您完全可以使用 add_subdirectory 来包含相邻文件夹。您可以提供任何相对或绝对路径。我有一个项目,我正是这样做的,而且它是正确的。话虽如此,如果您有 A 的子目录,则不可以在 A 的 CMakeLists.txt 中使用 add_subdirectory。只需遵循文件夹结构,您就不会重复包含任何内容。【参考方案2】:

add_subdirectory 是正确的做法吗?

不,add_subdirectory() 用于遍历 CMake 项目中的其他 CMakeLists.txt 文件。它没有指定依赖项。在现代 CMake 中,您实际上只需要 target_link_libraries 来指定依赖项,正如 this answer 中所建议的那样。

如何将C 添加到A 以使此构建成为可能?

我不想将C 直接添加到App

您可以通过将先前定义的目标C 链接到A 来使A 依赖于C

add_library(C SHARED ... )

...

add_library(A SHARED ... )

target_link_libraries(A PUBLIC C)

注意,这里使用PUBLIC 确保C 被添加到A 的链接接口,因此它将被传播 到消费目标。现在,当您将A 链接到App 时,您将获得C,并且无需显式指定C

# Link A (and C) to App.
target_link_libraries(App PRIVATE A)

App也依赖B的情况如何处理?使用add_subdirectory 并且不小心,这将尝试定义C 两次。包含警卫是正确的解决方案吗?

根据我的经验,这不是问题,链接器能够找出重复链接的 C 库。如果它确实成为一个问题,您可以简单地使B 依赖关系PRIVATE 的范围。这样,C 就不会通过B 传播到App

target_link_libraries(B PRIVATE C)

...

target_link_libraries(App PRIVATE B A)

我鼓励您通读链接的 target_link_libraries() 文档,其中有许多关于这些类型依赖项的推荐 CMake 用法示例。

【讨论】:

这个答案让我意识到,我真正的困惑是,如何以最少的努力让 cmake 知道模块?

以上是关于如何构建 CMake 项目的主要内容,如果未能解决你的问题,请参考以下文章

如何构建 CMake 项目

如何构建具有 cmake 文件的 c++/c 项目?

CMake:如何构建外部项目并包含其目标

如何使用 QtCreator 为 Android/iOS 构建基于 CMake 的项目

如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?

使用 CMake 构建后,如何在 Visual Studio 中创建项目以使用 OpenMesh?