CMake:如果你需要指定CMAKE_MODULE_PATH,find_package()的用途是什么?

时间:2013-12-23 15:58:00

标签: cmake

我正在尝试使用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()正常工作 如预期的那样。

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

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

4 个答案:

答案 0 :(得分:164)

命令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

请注意,导入的目标高度可配置。查看我的answer

<强>更新

答案 1 :(得分:1)

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

已经CMakeified的依赖项目的更正常的用例是使用CMake的external_project命令,然后包括子项目中的Use [Project] .cmake文件。如果您只需要Find [Project] .cmake脚本,请将其从子项目中复制到您自己项目的源代码中,然后您不需要扩充CMAKE_MODULE_PATH以便在系统级别找到子项目。 / p>

答案 2 :(得分:1)

  

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

如果你不相信CMake拥有该模块,那么 - 是的,那样做 - 排序:find_SomeLib.cmake及其依赖项复制到cmake/目录中。这就是我作为后备做的事情。但这是一个丑陋的解决方案。

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

答案 3 :(得分:1)

如果您正在运行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中为SomeLib创建一个${CMAKE_CURRENT_BUILD_DIR}文件,并通过向CMakeLists.txt添加两个对export()的调用,将该位置存储在User Package Registry中。与SomeLib关联:

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文件中发出您的SomeLib命令,而不会用CMAKE_MODULE_PATH来修饰“非跨平台硬编码路径”

何时可能是正确的方法

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

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

相关问题