[英]CMake: How to set up source, library and CMakeLists.txt dependencies?
我有几个项目(所有建筑都使用来自相同源树结构的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 /力霸/的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
)
add_subdirectory(../Lib/LibA LibA)
add_subdirectory(../Lib/LibB LibB)
add_executable(App1 Src/main.cc)
target_link_libraries(App1 LibA LibB)
上例中的库依赖项看起来像这样:
App1 -> LibA -> LibC -> LibB
App1 -> LibB
目前我更喜欢本地依赖变体,因为它更容易使用。 我只是在源级别使用include_directories()
,在链接级别使用target_link_libraries()
和使用add_subdirectory()
在CMake级别提供依赖项。
有了这个,你不需要知道支持库之间的依赖关系 - 并且 - 使用CMake级别“包含” - 你只会最终得到你真正使用的目标。 当然,你可以让全局知道所有包含目录和目标,并让编译器/链接器整理其余部分。 但这对我来说似乎是一种臃肿。
我还尝试使用Lib/CMakeLists.txt
来处理Lib
目录树中的所有依赖项,但我最终得到了很多if ("${PROJECT_NAME}" STREQUAL ...)
检查以及我可以解决的问题创建中间库分组目标而不提供至少一个源文件。
所以上面的例子“到目前为止都很好”,但它会引发以下错误,因为你应该/不能两次添加一个CMakeLists.txt
:
CMake Error at Lib/LibB/CMakeLists.txt:2 (add_library):
add_library cannot create target "LibB" because another target with the
same name already exists. The existing target is a static library created
in source directory "Lib/LibB".
See documentation for policy CMP0002 for more details.
目前我看到了两个解决方案,但我认为这样做太复杂了。
1.覆盖add_subdirectory()
以防止重复
function(add_subdirectory _dir)
get_filename_component(_fullpath ${_dir} REALPATH)
if (EXISTS ${_fullpath} AND EXISTS ${_fullpath}/CMakeLists.txt)
get_property(_included_dirs GLOBAL PROPERTY GlobalAddSubdirectoryOnceIncluded)
list(FIND _included_dirs "${_fullpath}" _used_index)
if (${_used_index} EQUAL -1)
set_property(GLOBAL APPEND PROPERTY GlobalAddSubdirectoryOnceIncluded "${_fullpath}")
_add_subdirectory(${_dir} ${ARGN})
endif()
else()
message(WARNING "add_subdirectory: Can't find ${_fullpath}/CMakeLists.txt")
endif()
endfunction(add_subdirectory _dir)
2.在所有子级CMakeLists.txt
添加“include guard”,如:
if (NOT TARGET LibA)
...
endif()
我一直在测试tamas.kenez和ms建议的概念,并取得了一些有希望的结果。 摘要可以在以下答案中找到:
多次添加相同的子目录是不可能的,这不是CMake的工作方式。 有两种主要的替代方案可以干净利落地完成:
在与应用程序相同的项目中构建库。 对于您正在积极处理的图书馆(当您正在使用该应用程序时),请选择此选项,以便经常对其进行编辑和重建。 它们也将出现在同一个IDE项目中。
在外部项目中构建库( 我不是指ExternalProject )。 对于您的应用程序刚刚使用但尚未使用它们的库,请选择此选项。 大多数第三方库都是这种情况。 它们也不会使IDE工作区混乱。
CMakeLists.txt
添加了库的子目录(你的libs的CMakeLists.txt
没有) CMakeLists.txt
负责添加所有即时和传递依赖项并以正确的顺序添加它们 libx
添加子目录将创建一些可以与target_link_libraries
一起使用的目标(比如libx
) 作为旁注:对于库来说,创建一个功能齐全的库目标是一个很好的做法,即包含使用库所需的所有信息的库目标:
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
的汇编中。 如果LibC
的公共头文件未使用LibB
请使用PRIVATE
修饰符:
target_link_libraries(LibC PRIVATE LibB)
在单独的CMake项目中构建和安装库。 您的库将安装一个所谓的config-module ,它描述头文件和库文件的位置,并编译标志。 您的应用程序的CMakeList.txt
假定已经构建并安装了库,并且find_package
命令可以找到配置模块。 这是另一个故事,所以我不会在这里详述。
几点说明:
find_package
(因为在应用程序的CMakeLists
配置时未安装库)。 export()
命令 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.