CMake:如何设置源,库和CMakeLists.txt依赖关系
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()
和CMake级的依赖关系add_subdirectory()
。
,你不需要知道支持库之间的依赖关系,并且-使用CMake级别的"include"-你只会得到你真正使用的目标。当然,你可以让所有的include目录和目标都是全局已知的,让编译器/链接器来整理剩下的。但这对我来说似乎是一种肿胀。
我也试图有一个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
s添加"include保护",如:
if (NOT TARGET LibA)
...
endif()
我一直在测试tamas提出的概念。Kenez和m.s.取得了一些有希望的成果。这些总结可以在我的以下回答中找到:
- 首选cmake项目结构 创建多个可执行文件的共享库
- 使cmake库自动被其他cmake包访问
多次添加相同的子目录是毫无疑问的,这不是CMake打算如何工作。有两种主要的替代方法可以以一种干净的方式完成:
-
在与应用程序相同的项目中构建库。对于你正在积极工作的库(当你在应用程序上工作时),更喜欢这个选项,这样它们可能会经常被编辑和重建。它们也会显示在同一个IDE项目中。
-
在外部项目中构建库(,我不是指ExternalProject)。对于那些只被你的应用程序使用,但你没有处理它们的库,你更喜欢这个选项。这是大多数第三方库的情况。
- 你的应用程序的
CMakeLists.txt
添加了库的子目录(和你的lib 'CMakeLists.txt
's不) - 你的应用的
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>)
因此库的包含目录的位置可以保留为库的内部事务。你只需要这样做;
target_link_libraries(LibC LibB)
则LibB
的include目录也会被添加到LibC
的编译中。如果LibB
没有被LibC
的公共头使用,则使用PRIVATE
修饰符:
target_link_libraries(LibC PRIVATE LibB)
<标题>方法# 2 h1> onfig-module,它描述了头文件和库文件的位置以及编译标志。您的应用程序的CMakeList.txt
假定库已经构建并安装,并且可以通过find_package
命令找到config-模块。这完全是另一个故事,所以我不会在这里详细说明。
注意事项:
- 你可以混合使用#1和#2,因为在大多数情况下,你将拥有不变的第三方库和正在开发的自己的库。
- #1和#2之间的折衷是使用ExternalProject模块,这是许多人的首选。这就像将库的外部项目(构建在它们自己的构建树中)包含到应用程序的项目中一样。在某种程度上,它结合了两种方法的缺点:你不能使用你的库作为目标(因为它们在不同的项目中),你不能调用
find_package
(因为当你的应用程序的CMakeLists
配置时,这些库还没有安装)。 - #2的一个变体是在外部项目中构建库,但不是安装工件,而是从它们的源/构建位置使用它们。要了解更多信息,请参阅
export()
命令。
- Windows 10-使用gtkmm-3.0库和g++[包括再现]的分段故障
- 如何将C++中的库和头与MinGW一起使用
- C++ — "算法"库和命名空间"std"
- 在 Julia 中使用 boost 库和 Windows 上的 Cxx.jl
- C++库和自注册类:客户端应用程序中的工厂映射为空
- 如果同时存在共享库和动态库,则链接器将首选哪个库?
- 如何在QApplication中应用QLibraryInfo中加载的库和插件配置
- 正确地编写一个类,并将pthread与vlc库和c++一起使用
- CMAKE:查找/添加Visual Studio或Windows SDK库和标头的正确方法?
- C ++:DECLSPEC,静态库和DLL
- C 汇编如何处理共享库和模板
- 如何使用C兼容库和API管理C 原理
- 如何使用libpq库和C 绑定无效的语句
- 使用提升库和 clion 链接时出错
- 如何编写 CMakeLists.txt 以使用 FreeImage 预编译库和头文件
- 动态加载库和运行时误解的显式链接
- Boost 库和 Visual Studio (C++) 出现问题
- 包含一批库和目录路径以"Include Additional Directories"视觉C++的任意方法
- 在 Linux 系统上使用 c++ 链接到共享库和静态库
- CMake:如何设置源,库和CMakeLists.txt依赖关系