CMake:如何设置Source,Library和CMakeLists.txt依赖项? [英] CMake: How to setup Source, Library and CMakeLists.txt dependencies?

查看:5843
本文介绍了CMake:如何设置Source,Library和CMakeLists.txt依赖项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个项目(所有建立与CMake从相同的源代码树结构)都使用自己的混合出几十个支持库。



所以我想到了如何在CMake中正确设置这个问题。到目前为止,我只找到 CMake如何正确创建目标之间的依赖关系

这里是我的目录结构和我目前想出了使用CMake和本地依赖项(示例只显示一个可执行项目 App1 ,但是实际上有更多 App2 App3 ,...):

  Lib 
+ - LibA
+ - Inc
+ - ah
+ - Src
+ - a.cc
+ - CMakeLists.txt
+ - LibB
+ - Inc
+ - bh
+ - Src
+ - b.cc
+ - CMakeLists.txt
+ - LibC
+ - Inc
+ - ch
+ - 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 / ah)
target_link_libraries(LibA LibC)

/LibB/CMakeLists.txt

  include_directories(Inc)
add_library(LibB Src / b.cc Inc / bh)

Lib / LibC / CMakeLists.txt >

  include_directories(Inc ../LibB/Inc)
add_subdirectory(../ LibB LibB)
add_library Src / c.cc Inc / ch)
target_link_libraries(LibC LibB)

App1 / CMakeLists.txt (为了便于复制,我在这里生成源文件/头文件)

  cmake_minimum_required (VERSION 2.8)

项目(App1 CXX)

文件(WRITESrc / main.cc#include \ah\\\\
#include \bh \\ nint main()\\\
{\\\
a(); \\\
b(); \\\
return 0; \\\
})
文件/ lib / LibA / Inc / ahvoid a();)
文件(WRITE../Lib/LibA/Src/a.cc#include \ch \\ nvoid a()\\\
{\\\
c(); \\\
})
文件(WRITE../Lib/LibB/Inc/bhvoid b();)
文件(WRITE../Lib/LibB/Src/b.ccvoid b(){})
文件(WRITE../Lib/LibC/Inc/chvoid c );)
文件(WRITE../Lib/LibC/Src/c.cc#include \bh \\\\
void c()\\\
{\\\
b ; \\\
})

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

目前我更喜欢使用本地依赖项,因为它更容易使用。我只是在链接级别使用 target_link_libraries()和CMake级别在链接级别给出源级别的依赖性 include_directories() add_subdirectory()



有了这一点,你不需要知道支持库之间的依赖关系,并且 - 使用CMake级别包含,你只能结束目标真的用。果然,你可以让所有包括目录和目标已知全局,让编译器/链接器整理其余。



我也尝试过一个 Lib / CMakeLists.txt 以处理 Lib 目录树中的所有依赖项,但我最终有很多 if($ {PROJECT_NAME}STREQUAL ... )检查和我无法创建中间库分组目标而不给出至少一个源文件的问题。



所以上面的例子到目前为止这么好,但它引发以下错误,因为你应该/不能添加 CMakeLists.txt 两次:

 在Lib / LibB / CMakeLists.txt中的CMake错误:2(add_library):
add_library无法创建目标LibB,因为另一个目标与
相同名称已存在。现有目标是在源目录Lib / LibB中创建的
的静态库。
有关详细信息,请参阅policy CMP0002的文档。

现在我看到两个解决方案,但我想我有这样复杂。 / p>

1。覆盖 add_subdirectory()以防止重复

 函数(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警告add_subdirectory:找不到$ {_ fullpath} /CMakeLists.txt)
endif()
endfunction(add_subdirectory _dir)

2。在所有子级别 CMakeLists.txt 中添加包含警卫 :

  if(NOT TARGET LibA)
...
endif()

感谢您提供排序的帮助。



strong> EDIT:我一直在测试 tamas.kenez ms 有一些promissing的结果。总结可以在我的以下答案中找到:




解决方案

多次添加相同的子目录是没有问题的,这不是CMake如何工作。有两个主要的替代方法,以干净的方式:


  1. 在与您的应用程序相同的项目中构建库。对于您正在处理的库(当您在应用程序上工作时),优先选择此选项,因此它们可能会被频繁编辑和重建。


  2. 在外部项目中构建库(,我不是指ExternalProject >)。对于刚刚由您的应用程序使用的库,但不是对它们进行处理,优选此选项。这是大多数第三方库的情况。他们不会混淆您的IDE工作区。




方法#1




  • 您的应用程式的 CMakeLists.txt 新增了图书馆的子目录(以及您的库的 CMakeLists.txt 不会)

  • 您的应用程式的 CMakeLists.txt 负责将所有即时和并且以正确的顺序添加它们

  • 它假定为 libx 添加子目录将创建一些目标(例如 libx ),可以随时使用 target_link_libraries



作为旁注:对于库,创建一个全功能库目标,也就是包含使用库所需的所有信息是一个很好的做法:

  add_library(LibB Src / b.cc Inc / bh)
target_include_directories(LibB PUBLIC
$< BUILD_INTERFACE: $ {CMAKE_CURRENT_SOURCE_DIR} / Inc>)

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

  target_link_libraries(LibC LibB)
pre>

那么 LibB 的include dirs也将被添加到 LibC 。如果 LibC LibB ,请使用 PRIVATE c $ c>:

  target_link_libraries(LibC PRIVATE LibB)



方法2



在独立的CMake项目中构建并安装您的库。您的库将安装一个所谓的 config-module ,它描述了标题和库文件的位置以及编译标志。你的应用程序的 CMakeList.txt 假设库已经被构建和安装,config-modules可以通过 find_package 命令。



一些注意事项:




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

  • #1和#2正在使用许多人首选的 ExternalProject模块。这就像将你的库的外部项目(构建在自己的构建树中)包含到你的应用程序的项目中。在方法上,它结合了两种方法的缺点:你不能使用你的库作为目标(因为他们在一个不同的项目),你不能调用 find_package 因为libs没有安装你的应用程序的 CMakeLists 配置的时间。)

  • #2的一个变体是构建库外部项目,而不是安装工件从源代码/构建位置使用它们。有关详情,请参阅 export()命令


I have several projects (all building with CMake from the same source tree structure) all using their own mix out of dozens of supporting libraries.

So I came about the question how to setup this correctly in CMake. So far I have only found CMake how to correctly create dependencies between targets but I'm still struggling between setting up everything with global dependencies (the project level does know it all) or with local dependencies (each sub-level target only handles its own dependencies).

Here is reduced example of my directory structure and what I currently came up with using CMake and local dependencies (the example shows only one executable project App1, but there are actually more 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 (for the ease of reproducing it I generate the source/header files here)

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)

The library dependencies in the above example do look like this:

App1 -> LibA -> LibC -> LibB
App1 -> LibB

At the moment I prefer the local dependencies variant, because it's easier to use. I just give the dependencies at source level with include_directories(), at link level with target_link_libraries() and at CMake level with add_subdirectory().

With this you don't need to know the dependencies between the supporting libraries and - with the CMake level "includes" - you will only end-up with the targets you really use. Sure enough you could just make all include directories and targets known globally and let the compiler/linker sort out the rest. But this seems like a kind of bloating to me.

I also tried to have a Lib/CMakeLists.txt to handle all the dependencies in the Lib directory tree, but I ended up having a lot of if ("${PROJECT_NAME}" STREQUAL ...) checks and the problem that I can't create intermediate libraries grouping targets without giving at least one source file.

So the above example is "so far so good", but it throws the following error because you should/can not add a CMakeLists.txt twice:

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.

At the moment I see two solutions for this, but I think I got this way to complicated.

1. Overwriting add_subdirectory() to prevent duplicates

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. Adding an "include guard" to all sub-level CMakeLists.txt like:

if (NOT TARGET LibA)
    ...
endif()

Thanks in advance for your help sorting this out.

EDIT: I've been testing the concepts suggested by tamas.kenez and m.s. with some promissing results. The summaries can be found in my following answers:

解决方案

Adding the same subdirectory multiple times is out of question, it's not how CMake is intended to work. There are two main alternatives to do it in a clean way:

  1. Build your libraries in the same project as your app. Prefer this option for libraries you're actively working on (while you're working on the app) so they are likely to be frequently edited and rebuilt. They will also show up in the same IDE project.

  2. Build your libraries in an external project (and I don't mean ExternalProject). Prefer this option for libraries that are just used by your app but you're not working on them. This is the case for most third-party libraries. They will not clutter your IDE workspace, either.

Method #1

  • your app's CMakeLists.txt adds the subdirectories of the libraries (and your libs' CMakeLists.txt's don't)
  • your app's CMakeLists.txt is responsible to add all immediate and transitive dependencies and to add them in the proper order
  • it assumes that adding the subdirectory for libx will create some target (say libx) that can be readily used with target_link_libraries

As a sidenote: for the libraries it's a good practice to create a full-featured library target, that is, one that contains all the information needed to use the library:

add_library(LibB Src/b.cc Inc/b.h)
target_include_directories(LibB PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>)

So the location of include directories of the library can remain an internal affair of the lib. You will only have to do this;

target_link_libraries(LibC LibB)

then the include dirs of LibB will also be added to the compilation of LibC. Use the PRIVATE modifier if LibB is not used by the public headers of LibC:

target_link_libraries(LibC PRIVATE LibB)

Method #2

Build and install your libraries in seperate CMake projects. Your libraries will install a so-called config-module which describes the locations of the headers and library files and also compile flags. Your app's CMakeList.txt assumes the libraries has already been built and installed and the config-modules can be found by the find_package command. This is a whole another story so I won't go into details here.

A few notes:

  • You can mix #1 and #2 as in most cases you will have both unchanging, third-party libs and your own libraries under development.
  • A compromise between #1 and #2 is using the ExternalProject module, preferred by many. It's like including the external projects of your libraries (built in their own build tree) into your app's project. In way it combines the disadvantages of both approaches: you can't use your libraries as targets (because they're in a different project) and you can't call find_package (because the libs are not installed the time your app's CMakeLists is configuring).
  • A variant of #2 is to build the library in an external project but instead of installing the artifacts use them from their source/build locations. For more about this see the export() command.

这篇关于CMake:如何设置Source,Library和CMakeLists.txt依赖项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆