为了账号安全,请及时绑定邮箱和手机立即绑定

CMake:如何设置源,库和CMakeLists.txt依赖项?

CMake:如何设置源,库和CMakeLists.txt依赖项?

C++
慕田峪9158850 2019-12-16 13:12:59
我有好几个项目(所有项目都是从同一个源树结构中使用CMake构建的)都使用了自己的数十种支持库。所以我想到了一个问题,如何在CMake中正确设置它。到目前为止,我仅发现CMake如何正确地在目标之间创建依赖关系,但是我仍然在设置具有全局依赖关系(项目级别确实了解全部)或具有本地依赖关系(每个子级别目标仅处理其目标)之间进行挣扎自己的依赖项)。这里是我的目录结构的简化例子,我现在想出了使用CMake和本地依赖性(例子中只有一个可执行的项目,App1但也有更多的实际,App2,App3等):Lib+-- LibA    +-- Inc        +-- a.h    +-- Src        +-- a.cc    +-- CMakeLists.txt+-- LibB    +-- Inc        +-- b.h    +-- Src        +-- b.cc    +-- CMakeLists.txt+-- LibC    +-- Inc        +-- c.h    +-- Src        +-- c.cc    +-- CMakeLists.txtApp1+-- Src    +-- main.cc+-- CMakeLists.txtLib / LibA / CMakeLists.txtinclude_directories(Inc ../LibC/Inc)add_subdirectory(../LibC LibC)add_library(LibA Src/a.cc Inc/a.h)target_link_libraries(LibA LibC)Lib / LibB / CMakeLists.txtinclude_directories(Inc)add_library(LibB Src/b.cc Inc/b.h)Lib / LibC / CMakeLists.txtinclude_directories(Inc ../LibB/Inc)add_subdirectory(../LibB LibB)add_library(LibC Src/c.cc Inc/c.h)target_link_libraries(LibC LibB)App1 / CMakeLists.txt(为便于复制,我在此处生成源/头文件)cmake_minimum_required(VERSION 2.8)project(App1 CXX)file(WRITE "Src/main.cc" "#include \"a.h\"\n#include \"b.h\"\nint main()\n{\na();\nb();\nreturn 0;\n}")file(WRITE "../Lib/LibA/Inc/a.h" "void a();")file(WRITE "../Lib/LibA/Src/a.cc" "#include \"c.h\"\nvoid a()\n{\nc();\n}")file(WRITE "../Lib/LibB/Inc/b.h" "void b();")file(WRITE "../Lib/LibB/Src/b.cc" "void b() {}")file(WRITE "../Lib/LibC/Inc/c.h" "void c();")file(WRITE "../Lib/LibC/Src/c.cc" "#include \"b.h\"\nvoid c()\n{\nb();\n}")include_directories(    ../Lib/LibA/Inc    ../Lib/LibB/Inc)目前,我更喜欢本地依赖项变体,因为它更易于使用。我只使用给出源级别的依赖关系,使用给出include_directories()链接级别的依赖关系,使用target_link_libraries()CMake级别给出依赖关系add_subdirectory()。这样,您就无需知道支持库之间的依赖关系,并且-有了CMake级别的“包含”,您最终只会使用真正使用的目标。可以肯定的是,您只需使所有包含目录和目标都为全局已知,然后让编译器/链接器将其余的分类即可。但这对我来说似乎是一种腹胀。我还尝试使用a Lib/CMakeLists.txt来处理Lib目录树中的所有依赖关系,但是最终我进行了很多if ("${PROJECT_NAME}" STREQUAL ...)检查,并遇到了一个问题,即如果不给出至少一个源文件,就无法创建将目标分组的中间库。
查看完整描述

3 回答

?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

多次添加同一子目录是毫无疑问的,这并不是CMake的工作方式。可以采用两种主要方法来进行清洁:


在与应用程序相同的项目中构建库。对于正在积极使用(在使用应用程序时)正在使用的库,请首选此选项,以便它们可能经常被编辑和重建。它们还将显示在同一IDE项目中。


在外部项目中构建您的库(我不是说ExternalProject)。对于仅由您的应用使用但未使用它们的库,请首选此选项。大多数第三方库就是这种情况。它们也不会使您的IDE工作区混乱。


方法1

您的应用CMakeLists.txt添加了库的子目录(而库的CMakeLists.txt不添加)

您的应用CMakeLists.txt负责添加所有直接和传递依赖项,并以正确的顺序添加它们

它假定添加的子目录libx将创建一些libx易于使用的目标(例如)target_link_libraries

附带说明:对于库来说,创建一个功能齐全的库目标是一个好习惯,即,其中包含使用该库所需的所有信息:


add_library(LibB Src/b.cc Inc/b.h)

target_include_directories(LibB PUBLIC

    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>)

因此,库的包含目录的位置可以保留为lib的内部事务。您只需要这样做;


target_link_libraries(LibC LibB)

然后LibB还将将的包含目录添加到的汇编中LibC。PRIVATE如果LibB的公共标头未使用,请使用修饰符LibC:


target_link_libraries(LibC PRIVATE LibB)

方法#2

在单独的CMake项目中构建和安装库。您的库将安装一个所谓的config-module,该模块描述头文件和库文件的位置并编译标志。您的应用程序CMakeList.txt假设已经建立并安装了库,并且可以通过find_package命令找到配置模块。这是另一个完整的故事,所以我在这里不再赘述。


一些注意事项:


您可以混合使用#1和#2,因为在大多数情况下,您将拥有不变的第三方库和正在开发的自己的库。

#1和#2之间的折衷方案是使用ExternalProject模块,这是许多人首选的模块。这就像将库的外部项目(在其自己的构建树中构建)包含到应用程序的项目中一样。在某种程度上,它结合了两种方法的缺点:您不能将库用作目标(因为它们位于不同的项目中),也不能调用find_package(因为在CMakeLists配置应用程序时未安装库)。

#2的一种变体是在外部项目中构建库,但不是安装工件,而是从源/构建位置使用它们。有关此的更多信息,请参见export()命令。



查看完整回答
反对 回复 2019-12-17
?
偶然的你

TA贡献1841条经验 获得超3个赞

方法1:我喜欢您对target_include_directories()命令的使用(CMake> = 2.8.12),这肯定使事情变得更容易。但是我正在寻找一种解决方案,其中库的用户不需要知道内部依赖项。方法2:我将研究使用二进制传递和find_package()命令。您能推荐任何有用的链接吗?

查看完整回答
反对 回复 2019-12-17
?
红糖糍粑

TA贡献1815条经验 获得超6个赞

关于ExternalProject模块:ExternalProject_Add()如果我们谈论更大的整体库,我认为该命令是可行的。但是对于我的50多个较小的库,我不认为这是一个选择,并且在某种程度上说,不打算以这种方式使用它的说法在这里也适用。开销是很大的:例如,我担心它-如果我使用该SOURCE_DIR选项-将启动每个库的编译器检测,并且我必须传递所有特定于构建环境的选项,包括。我的工具链文件

查看完整回答
反对 回复 2019-12-17
  • 3 回答
  • 0 关注
  • 437 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信