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

我有好几个项目(所有项目都是从同一个源树结构中使用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.txt

App1

+-- Src

    +-- main.cc

+-- CMakeLists.txt

Lib / LibA / CMakeLists.txt


include_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.txt


include_directories(Inc)

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

Lib / LibC / CMakeLists.txt


include_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 ...)检查,并遇到了一个问题,即如果不给出至少一个源文件,就无法创建将目标分组的中间库。



慕田峪9158850
浏览 438回答 3
3回答

慕盖茨4494581

多次添加同一子目录是毫无疑问的,这并不是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&nbsp; &nbsp; $<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()命令。

偶然的你

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

红糖糍粑

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