使用add_subdirectory vs find_package的软件包
我正在使用CMAKE的C ++代码库进行故障排除。代码库有几个流行的第三方软件包,它们直接嵌入到源树中。
对于一个软件包(例如foo),find_package
呼叫失败,因为它找不到fooconfig.cmake(或foo-config.cmake)。但是,使用add_subDirectory
,在其中一个cmakelists.txt中添加了foo项目。
- 如果我使用
add_subdirectory
,我是否需要使用find_package
? - 如果我使用
add_subDirectory
,确保Foo的所有依赖者都能找到Foo的标题和库的正确方法是什么?
I am troubleshooting a build on a C++ codebase which uses cmake. The codebase has several popular 3rd party packages which are embedded directly in the source tree.
For one package (say foo) a find_package
call fails as it could not find the FooConfig.cmake (or foo-config.cmake). However the foo project is already being added in one of the CMakeLists.txt using add_subdirectory
.
- Do I need to use
find_package
if I am usingadd_subdirectory
? - If I am using
add_subdirectory
, what is the correct way to ensure that all dependents of foo are able to locate the headers and libraries of foo?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
find_package
使用add_subDirectory
添加项目,将不会自动成功。如果您的第三方库之一依赖于
find_package
调用查找其依赖项,则您可能必须在周围的构建脚本中提供一些帮助,以确保其成功。以下是详细发生的事情:当通过
add_subDirectory
提出项目时,其目标将成为封闭项目的可见。也就是说,如果subprojecta
dieadd_library(a ...)
,则subprojectb
可以做targe> target_link_libraries(b public a)
使用库 a 作为其自己的可执行文件b
的依赖性。但是,如果两个库通常都不是作为封闭项目的一部分(对于第三方代码而言总是正确的),那么这可能不是
b
会做的。而是b
可能会做find_package(a)
调用,然后用作依赖性脚本提供的依赖关系。如果幸运的话,查找脚本将提供导入的目标,因此您会找到target_link_libraries(B Public A)
b
's <某处的呼叫target_link_libraries(b public a)
代码> cmakelists.txt 。如果它是一个较旧的查找脚本,则可以提供一堆变量,您会发现诸如target_link_libraries(b $ {a_libraries})之类的呼叫(以及其他包含目录和其他内容)。
因此,完成此工作实际上取决于
b
的构建脚本的样子。如果您没有自由修补构建脚本,那么您甚至可能会处于无法完成的情况。现在,对于
find_package
调用的特定问题:查找软件包用于查找已经构建的库。也就是说,在Cmake运行时,CMAKE CONFIG脚本或库二进制文件已经在磁盘上。在您的情况下,它们都没有生成,因为您仍在配置项目中,并且直到开始构建之前,这些文件才会生成。因此,find_package
几乎可以肯定会失败。您有两个选项如何潜在地解决此问题:
set
find_package
呼叫正在尝试填充的所有缓存变量。 >在B
的软件包中查找_**调用脚本将变成no-ops,只需重用您设置的值即可。b
'sfind_package
呼叫会发现一个而不是它将在独立构建中使用的一个。同样,这些细节是高度依赖的,这些细节都不保证很容易或完全工作。同样,确保以这种方式构建的构建在跨发电机之间保持便携性可能非常具有挑战性。
底线:除非您确实有强大的理由使用
add_subDirectory
,否则不要这样做。取而代之的是,让封闭项目使用一堆
execute_process
呼叫来构建每个依赖项 1 。这将始终工作,并且更容易正确设置。主要缺点是,所有依赖关系构建都发生在封闭项目的初始CMake配置运行期间,并且对依赖关系源的更改将不会被跟踪并触发自动重建。1 :也就是说:第一个调用 cmake and
cmake -build
(以及可能cmake -install
)项目a
;仅然后对项目b
做同样的事情。配置b
时,在配置命令行上提供查找a
所需的位置信息:cmake -da_root = ...
find_package
will not automatically succeed if the project was added usingadd_subdirectory
.If one of your third-party libraries relies on a
find_package
call to find its dependencies, you will likely have to provide some help in the surrounding build script to ensure it will succeed.Here is what happens in detail: When a project is pulled in via
add_subdirectory
, its targets will become visible to the enclosing projects. That is, if subprojectA
doesadd_library(a ...)
, then subprojectB
can dotarget_link_libraries(b PUBLIC a)
to make use of librarya
as a dependency for its own executableb
.However, if both libraries are usually don't being built together as part of an enclosing project (which is almost always true for third party code), this is probably not what
B
will be doing. InsteadB
will probably do afind_package(A)
call and then use as a dependency whatever that find script provides. If you're lucky, the find script will provide an imported target, so you will find a call of the formtarget_link_libraries(b PUBLIC a)
somewhere inB
'sCMakeLists.txt
. If it's an older find script, it may instead provide a bunch of variables and you will find calls liketarget_link_libraries(b ${A_LIBRARIES})
(and others for include directories and whatever else) instead.So making this work really all depends on how
B
's build scripts look like. If you're not at liberty to patch the build script, you might even end up in a situation where what you are asking for cannot be done.Now, for the particular problem of the
find_package
call: Find package is used to find a library that has already been built. That is, at the time that CMake runs, either a CMake config script or the library binaries are already on disk. In your scenario, neither of them have been generated, as you are still in the process of configuring the project, and those files won't get generated until you start building. So thefind_package
call will almost certainly fail out of the box.You have two options how to potentially resolve this:
set
all of the cache variables that thefind_package
call is trying to fill in. That way anyfind_*
calls insideB
's package find script will turn into no-ops, simply reusing the values that you set.B
'sfind_package
call will find that one instead of the one it would use in a standalone build.Again, the specifics are highly project dependent, neither of these is guaranteed to be easy or to work at all. Also, ensuring that a build structured in this way remains portable across generators can be very challenging.
Bottom line: Unless you really have strong reasons for using
add_subdirectory
, don't do this.Instead have the enclosing project use a bunch of
execute_process
calls to build each of the dependencies in isolation1. This will always work and is much easier to set up correctly. Main downside is that all the dependency building happens during the initial CMake configure run of the enclosing project and changes to the dependency's sources will not be tracked and trigger an automatic rebuild.1: That is: First call
cmake
andcmake --build
(and probablycmake --install
) on projectA
; only then do the same for projectB
. When configuringB
, provide the location information required for findingA
on the configure command line:cmake -DA_ROOT=...
这就是@comicsansms所说的方式。关于其他实用解决方案。如果您看Googletest在做什么,您会看到他们有两个不同的用法示例,一个用于
add_subdirectory
用法,另一个用于find_package
usage。然后,您然后使用内部目标(在其前面没有gtest ::
)。另一种是
您可以做的是添加图书馆别名统一此问题,就像此问题一样 (重复问题1 ,重复的问题2 )
,Googletest实际上在 cmake:添加命名空间别名库。虽然我不太依靠Googletest作者来获得CMAKE知识,但这实际上似乎是正确的做法!
It's just how @ComicSansMS said. Regarding additional practical solutions. If you look what GoogleTest is doing, you'd see they have two different usage examples, one for
add_subdirectory
usage, and another forfind_package
usage. In the former you then use the internal target (withoutGTest::
in front of it).and the alternative is
What you can do is to add an library alias to unify this, like this issue suggests (duplicate issue 1, duplicate issue 2)
And, GoogleTest actually implemented this suggestion in CMake: Add namespaced ALIAS library. While I would not much rely on GoogleTest authors for their CMake knowledge, this actually seems like the right thing to do!