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

Mar*_*ant 141 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
Run Code Online (Sandbox Code Playgroud)

现在,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)
Run Code Online (Sandbox Code Playgroud)

第一个命令定义了CMake在搜索之后搜索的位置,Find*.cmake并且我添加SomeLibFindSomeLib.cmake可以找到的目录,因此find_package()按预期工作.

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

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

小智 180

命令find_package有两种模式:Module模式和Config模式.您Module实际需要Config模式时尝试使用模式.

模块模式

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

CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake
Run Code Online (Sandbox Code Playgroud)

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})
Run Code Online (Sandbox Code Playgroud)

请注意,它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)
Run Code Online (Sandbox Code Playgroud)

配置文件的简化版本:

> 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}")
Run Code Online (Sandbox Code Playgroud)

默认项目安装在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
Run Code Online (Sandbox Code Playgroud)

配置模式(使用)

使用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
Run Code Online (Sandbox Code Playgroud)

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

更新

  • @Dimitris是的,它可以简化一点点.我已经更新了github示例,所以现在它不使用`configure_package_config_file`.顺便说一句,如果你有任何其他建议,你可以发送拉请求. (2认同)

Rya*_*ley 6

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

简要操作方法

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

add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)
Run Code Online (Sandbox Code Playgroud)

在其中创建一个XXXConfig.cmake文件,并通过添加两个对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
Run Code Online (Sandbox Code Playgroud)

在依赖的项目文件find_package(SomeLib REQUIRED)中发出命令,而不用“非跨平台硬编码路径”修改.CMakeLists.txtSomeLibCMAKE_MODULE_PATH

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

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

但是,如果您从未SomeLib在工作流程中实际安装,则调用EXPORT(PACKAGE <name>)可以让您避免硬编码路径。当然,如果您正在安装SomeLib,您可能知道您的平台CMAKE_MODULE_PATH等,因此@user2288008 的出色答案将让您满意。