使用add_subdirectory vs find_package的软件包

发布于 2025-02-12 00:00:30 字数 393 浏览 2 评论 0原文

我正在使用CMAKE的C ++代码库进行故障排除。代码库有几个流行的第三方软件包,它们直接嵌入到源树中。

对于一个软件包(例如foo),find_package呼叫失败,因为它找不到fooconfig.cmake(或foo-config.cmake)。但是,使用add_subDirectory,在其中一个cmakelists.txt中添加了foo项目。

  1. 如果我使用add_subdirectory,我是否需要使用find_package
  2. 如果我使用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.

  1. Do I need to use find_package if I am using add_subdirectory?
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你另情深 2025-02-19 00:00:30

find_package使用add_subDirectory添加项目,将不会自动成功。

如果您的第三方库之一依赖于find_package调用查找其依赖项,则您可能必须在周围的构建脚本中提供一些帮助,以确保其成功。

以下是详细发生的事情:当通过add_subDirectory提出项目时,其目标将成为封闭项目的可见。也就是说,如果subproject a die add_library(a ...),则subproject b可以做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's find_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 using add_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 subproject A does add_library(a ...), then subproject B can do target_link_libraries(b PUBLIC a) to make use of library a as a dependency for its own executable b.

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. Instead B will probably do a find_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 form target_link_libraries(b PUBLIC a) somewhere in B's CMakeLists.txt. If it's an older find script, it may instead provide a bunch of variables and you will find calls like target_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 the find_package call will almost certainly fail out of the box.

You have two options how to potentially resolve this:

  • Have the enclosing project manually set all of the cache variables that the find_package call is trying to fill in. That way any find_* calls inside B's package find script will turn into no-ops, simply reusing the values that you set.
  • Provide your own find script and ensure that B's find_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 and cmake --build (and probably cmake --install) on project A; only then do the same for project B. When configuring B, provide the location information required for finding A on the configure command line: cmake -DA_ROOT=...

嗼ふ静 2025-02-19 00:00:30

这就是@comicsansms所说的方式。关于其他实用解决方案。如果您看Googletest在做什么,您会看到他们有两个不同的用法示例,一个用于add_subdirectory用法,另一个用于find_package usage。然后,您然后使用内部目标(在其前面没有gtest ::)。

通过使用find_package(或pkg_check_modules)导入Googletest。例如,如果find_package(gtest config必需)成功,则可以将库用作gtest :: gtest :: gtestgtest :: gmock :: gmock。<。<。 /p>

另一种是

  include(提取)
fetchcontent_declare(
  googletest
  #指定您依赖的提交并定期更新。
  URL ...一些URL.TGZ在这里...
)

#...

fetchcontent_makeavailable(googletest)

#现在只需根据需要链接与gtest或gtest_main链接。例如
add_executable(示例示例.cpp)
target_link_libraries(示例gtest_main)
 

您可以做的是添加图书馆别名统一此问题,就像此问题一样重复问题1 重复的问题2

add_library(GTest::Main ALIAS gtest_main)

,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 for find_package usage. In the former you then use the internal target (without GTest:: in front of it).

Import GoogleTest by using find_package (or pkg_check_modules). For example, if find_package(GTest CONFIG REQUIRED) succeeds, you can use the libraries as GTest::gtest, GTest::gmock.

and the alternative is

include(FetchContent)
FetchContent_Declare(
  googletest
  # Specify the commit you depend on and update it regularly.
  URL ... some url.tgz here ...
)

# ...

FetchContent_MakeAvailable(googletest)

# Now simply link against gtest or gtest_main as needed. Eg
add_executable(example example.cpp)
target_link_libraries(example gtest_main)

What you can do is to add an library alias to unify this, like this issue suggests (duplicate issue 1, duplicate issue 2)

add_library(GTest::Main ALIAS gtest_main)

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文