当需要指定CMAKE_MODULE_PATH时,find_package()有什么用?

我试图得到一个跨平台的构建系统工作使用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_FOUND等。

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

set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)
第一个命令定义了CMake在Find*.cmake之后搜索的位置,我添加了SomeLib的目录,在那里可以找到FindSomeLib.cmake,所以find_package()可以工作 如预期。< / p >

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

这通常是如何做到的?我是否应该将SomeLibcmake/目录复制到我的项目中,并相对地设置CMAKE_MODULE_PATH ?

258411 次浏览

你不需要需要来指定模块路径本身。CMake自带一组内置的find_package脚本,它们的位置在默认的CMAKE_MODULE_PATH中。

对于已被CMakeified的依赖项目,更正常的用例是使用CMake的external_project命令,然后包括use [Project]。来自子项目的Cmake文件。如果你只需要查找[项目]。cmake脚本,将其从子项目复制到您自己的项目源代码中,然后您就不需要增加CMAKE_MODULE_PATH就可以在系统级上找到子项目。

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

模块模式

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)在导入的目标foo中包含FooConfig.cmake:

> 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

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

更新

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

如果你不相信CMake有这个模块,那么——是的,这样做——有点像:find_SomeLib.cmake及其依赖项复制到你的cmake/目录。那是我做的备用方案。但这是一个丑陋的解决方案。

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

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

简短的指南

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

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

SomeLib${CMAKE_CURRENT_BUILD_DIR}中为SomeLib创建一个XXXConfig.cmake文件,并通过向与SomeLib相关的CMakeLists.txt中添加两次对export ()的调用来将该位置存储在用户包注册表中:

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

在依赖于SomeLib的项目的CMakeLists.txt文件中发出你的find_package(SomeLib REQUIRED)命令,而不需要对CMAKE_MODULE_PATH进行“非跨平台硬编码路径”修补。

当这可能是正确的方法时

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

但是如果你从来没有在你的工作流中安装SomeLib,调用EXPORT(PACKAGE <name>)可以让你避免硬编码的路径。当然,如果你正在安装SomeLib,你可能知道你的平台,CMAKE_MODULE_PATH,等等,所以@user2288008的精彩答案会覆盖你。