如果仍然需要指定CMAKE_MODULE_PATH,find_package()有什么用?


167

我正在尝试使用CMake来构建跨平台的构建系统。现在,该软件具有一些依赖性。我自己编译了它们并将它们安装在我的系统上。

已安装的一些示例文件:

-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake

现在,CMake有了一个find_package()可以打开Find*.cmake文件并在系统上的库之后进行搜索并定义一些变量(如SomeLib_FOUNDetc)的功能。

我的CMakeLists.txt包含以下内容:

set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)

第一个命令定义了CMake在哪里搜索,Find*.cmake然后我添加SomeLibFindSomeLib.cmake可以找到的目录,因此find_package()可以正常工作。

但这find_package()有点奇怪,因为存在的原因之一就是要摆脱非跨平台的硬编码路径。

通常如何做?我应该将cmake/目录复制SomeLib到我的项目中并设置CMAKE_MODULE_PATH相对目录吗?


这种模式对我来说似乎很奇怪。使用CMake的库不应以这种方式公开其“查找”模块。您是如何找到这样的方式来找到“ SomeLib”的?那是哪一个库?
SirDarius

2
cmake.org/Wiki/…中也做了类似的操作。这是OGRE。
MarcDefiant 2013年

2
您链接到的部分提到了这一点:“由于CMake(当前)未发布,因此您必须将其发布到项目中。” 这是我在flvmeta中所做的,以查找LibYAML(请参阅github.com/noirotm/flvmeta/tree/master/cmake/modules)。模块路径指向我的项目内的该目录。
SirDarius

3
我通常将FindXXX模块复制到我的项目中并设置CMAKE_MODULE_PATH(如果这些模块当然不在CMake中),我也在其他项目中也多次看到这种模式
szx 2013年

Answers:


213

命令find_package有两种模式:Module模式和Config模式。Module当您实际需要Config模式时,您正在尝试使用模式。

模块模式

Find<package>.cmake文件位于的项目。像这样:

CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake

CMakeLists.txt 内容:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES

include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})

请注意,它CMAKE_MODULE_PATH具有较高的优先级,在您需要重写标准Find<package>.cmake文件时可能很有用。

配置模式(安装)

<package>Config.cmake文件位于外部,并由install 其他项目的命令生成(Foo例如)。

foo 图书馆:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Foo)

add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)

配置文件的简化版本:

> cat FooConfig.cmake 
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")

默认情况下,项目安装在CMAKE_INSTALL_PREFIX目录中:

> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake

配置模式(使用)

使用find_package(... CONFIG)包括FooConfig.cmake进口的目标foo

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Boo)

# import library target `foo`
find_package(Foo CONFIG REQUIRED)

add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a

请注意,导入的目标是高度可配置的。看我的回答

更新资料


1
您的回答很好。但是,github上的示例更为复杂,因为它可以是IMO。在子目录(模块)导出单个工件的常见情况下(比如说lib和标题),您不需要生成自定义* Config.cmake。结果,可以显着减少配置。我想我会自己做一个类似的例子。
Dimitris

2
@Dimitris是的,可以简化一点。我已经更新了github示例,所以现在不使用了configure_package_config_file。顺便说一下,如果您有任何其他建议,可以向我发送拉取请求。

1
@rusio这是我的例子。它支持整体构建(根文件夹中的所有模块)或自主构建(每个模块分别需要安装)。
Dimitris

1
@Dimitris好的,现在我明白了。通常,您“优化掉”的文件用于加载诸如find_dependency之类的额外内容。我认为这是一个很好的开始模板,因此即使实际上没有使用它,我也会保留它。其余的代码看起来更加简单,因为您缺少一些功能,例如版本,dll导出,布局bin/lib(尝试安装可执行文件并在Windows上运行)。而且名称空间看起来非常漂亮,所以我也将它们保留下来:)另外,我还添加了monolithicbuild。

1
您的每个例子对我都很有帮助。谢谢你们俩!
zmb

2

如果您cmake要生成SomeLib自己的文件(例如作为超级构建的一部分),请考虑使用User Package Registry。这不需要硬编码的路径,并且是跨平台的。在Windows(包括mingw64)上,它可以通过注册表运行。如果检查一下find_packages()命令的CONFIG模式是如何构造安装前缀列表的,则您会看到用户软件包注册表是其中之一。

简要说明

通过将目标SomeLib添加到CMakeLists.txt创建它们的文件中的导出集中,从而将您需要的目标关联到该外部项目之外:

add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)

通过为其创建一个XXXConfig.cmake文件并将此位置存储在User Package Registry中,方法是将两个对export()的调用添加到与以下内容相关联的:SomeLib${CMAKE_CURRENT_BUILD_DIR}CMakeLists.txtSomeLib

export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib)                                                    # Store location of SomeLibConfig.cmake

在不依赖“非跨平台硬编码路径”的情况下,在依赖于项目find_package(SomeLib REQUIRED)CMakeLists.txt文件中发出命令。SomeLibCMAKE_MODULE_PATH

什么时候可能是正确的方法

这种方法可能最适合您永远不会在build目录下游使用软件的情况(例如,您正在交叉编译并且永远不会在计算机上安装任何东西,或者您只是为了在其中运行测试而构建软件)构建目录),因为它在您的“构建”输出中创建了指向.cmake文件的链接,该链接可能是临时的。

但是,如果您从未真正安装SomeLib在工作流中,则EXPORT(PACKAGE <name>)可以通过调用避免使用硬编码的路径。而且,当然,如果您正在安装SomeLib,您可能知道您的平台CMAKE_MODULE_PATH等,因此@ user2288008的出色答案将为您解决。


1

不需要自己指定模块路径。CMake附带了自己的一组内置find_package脚本,它们的位置位于默认的CMAKE_MODULE_PATH中。

对于已经被CMake化的依赖项目,更常规的用例是使用CMake的external_project命令,然后包含子项目中的Use [Project] .cmake文件。如果只需要Find [Project] .cmake脚本,请将其复制到子项目中并复制到您自己项目的源代码中,然后就无需在系统级别上查找该子项目时就必须扩展CMAKE_MODULE_PATH。


12
their location is in the default CMAKE_MODULE_PATH默认情况下CMAKE_MODULE_PATH为空

可以在2018年确认@ user2288008的评论。CMAKE_MODULE_PATH在Windows上为空。
Jeroen '18年

它是项目特定的变量,用于项目随附的模块。“默认情况下为空,它打算由项目设置。” cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway,

1

通常如何做?我应该将cmake/SomeLib 的目录复制到我的项目中,并相对设置CMAKE_MODULE_PATH吗?

如果您不信任CMake拥有该模块,那么- 是的,请执行以下操作-将:find_SomeLib.cmake及其依赖项复制到您的cmake/目录中。这就是我的后备工作。虽然这是一个丑陋的解决方案。

请注意,FindFoo.cmake每个模块都是平台依赖和平台独立之间的桥梁-它们在各种平台特定的位置中查找以获取名称与平台无关的变量中的路径。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.