如何构建 CMake 项目
Posted
技术标签:
【中文标题】如何构建 CMake 项目【英文标题】:How to structure CMake projects 【发布时间】:2020-06-10 11:09:45 【问题描述】:我开始使用 CMake,但不确定如何在以下设置中使用它。
我有一些 library-cmake-projects(源代码),它们可能相互依赖。
Libs
-A
-B
-C
(假设A
取决于C
和B
取决于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 增长了几年时,我开始了一个使用 libA
的新项目,我现在必须遵循 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 项目的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 QtCreator 为 Android/iOS 构建基于 CMake 的项目