CMakeDeps¶
CMakeDeps
生成器生成每个依赖项所需的文件,以便能够使用 cmake find_package()
函数来定位依赖项。它可以像这样使用
from conan import ConanFile
class App(ConanFile):
settings = "os", "arch", "compiler", "build_type"
requires = "hello/0.1"
generators = "CMakeDeps"
可以在 generate()
方法中进行完整的实例化,从而允许自定义配置。
from conan import ConanFile
from conan.tools.cmake import CMakeDeps
class App(ConanFile):
settings = "os", "arch", "compiler", "build_type"
requires = "hello/0.1"
def generate(self):
cmake = CMakeDeps(self)
cmake.generate()
cmake_minimum_required(VERSION 3.15)
project(compressor C)
find_package(hello REQUIRED)
add_executable(${PROJECT_NAME} src/main.c)
target_link_libraries(${PROJECT_NAME} hello::hello)
默认情况下,对于 hello
依赖项,您需要使用 find_package(hello)
并链接到目标 hello::hello
。请查看 影响 CMakeDeps 的属性,例如 cmake_target_name
,以自定义依赖项及其组件的 conanfile.py 中的文件和目标名称。
注意
CMakeDeps
旨在与 CMakeToolchain
生成器一起运行。它将把 CMAKE_PREFIX_PATH
和 CMAKE_MODULE_PATH
设置到正确的文件夹(conanfile.generators_folder
),以便 CMake 可以定位生成的 config/module 文件。
生成的文件¶
XXX-config.cmake:默认情况下,CMakeDeps 生成器将创建配置文件,声明依赖项及其组件(如果已声明)的目标。
FindXXX.cmake:仅当依赖项通过“module”或“both”设置了属性
cmake_find_mode
时。请参阅 影响 CMakeDeps 的属性 在依赖项中设置。其他必要的 *.cmake:文件,如版本、标志和目录数据或配置。
请注意,它还会生成一个 conandeps_legacy.cmake 文件。这是一个提供类似于 Conan 1 cmake
生成器行为的文件,允许使用 include(${CMAKE_BINARY_DIR}/generators/conandeps_legacy.cmake)
包含此文件,并提供一个单独的 CMake CONANDEPS_LEGACY
变量,允许链接所有直接和传递依赖项,而无需像这样显式枚举它们:target_link_libraries(app ${CONANDEPS_LEGACY})
。这为 Conan 1.X 用户提供了一个方便的功能,可以升级到 Conan 2 而无需更改其整体开发流程,但在其他情况下不建议这样做,并且使用 CMake 的规范流程显式使用 find_package()
和 target_link_libraries(... pkg1::pkg1 pkg2::pkg2)
与目标一起使用是正确的方法。
自定义¶
在创建的 CMakeDeps
对象中,您可以调整一些属性以更改默认行为。
配置¶
允许定义除了标准的 Release、Debug 等之外的自定义用户 CMake 配置。
def generate(self):
deps = CMakeDeps(self)
# By default, ``deps.configuration`` will be ``self.settings.build_type``
if self.options["hello"].shared:
# Assuming the current project ``CMakeLists.txt`` defines the ReleasedShared configuration.
deps.configuration = "ReleaseShared"
deps.generate()
CMakeDeps
是一个多配置生成器,它可以正确地为 Release/Debug 配置创建文件,以便同时被像 Visual Studio 这样的 IDE 使用。在单配置环境中,必须定义一个配置,该配置必须通过命令行中的 cmake ... -DCMAKE_BUILD_TYPE=<build-type>
参数提供(Conan 将在必要时自动执行此操作,在 CMake.configure()
帮助器中)。
build_context_activated¶
当您有 build-require 时,默认情况下不会生成配置文件(xxx-config.cmake)文件。但是,您可以使用 build_context_activated 属性激活它。
tool_requires = ["my_tool/0.0.1"]
def generate(self):
cmake = CMakeDeps(self)
# generate the config files for the tool require
cmake.build_context_activated = ["my_tool"]
cmake.generate()
build_context_suffix¶
当您将同一个包作为 build-require 和 regular require 时,它会导致生成器出现冲突,因为配置文件的文件名以及目标名称、变量名称等都会发生冲突。
例如,这在一些包含用于在构建时生成源代码的工具(因此它是 build_require)的依赖项(capnproto、protobuf…)中很常见,但同时也提供一个库来链接到最终的应用程序,因此您也有一个 regular require。解决此冲突在交叉构建时尤其重要,因为工具(将在构建机器上运行)属于与库不同的二进制包,该库将在主机机器上“运行”。
您可以使用 build_context_suffix 属性为依赖项指定一个后缀,以便构建上下文(工具依赖项)中依赖项的文件/目标/变量将被重命名。
tool_requires = ["my_tool/0.0.1"]
requires = ["my_tool/0.0.1"]
def generate(self):
cmake = CMakeDeps(self)
# generate the config files for the tool require
cmake.build_context_activated = ["my_tool"]
# disambiguate the files, targets, etc
cmake.build_context_suffix = {"my_tool": "_BUILD"}
cmake.generate()
build_context_build_modules¶
build_modules 还有一个问题。如您所知,依赖项的配方可以声明一个 cppinfo.build_modules 条目,其中包含一个或多个 .cmake 文件。当依赖项由 cmake find_package()
函数找到时,Conan 将自动包含这些文件。
默认情况下,Conan 仅包含来自 host
上下文(常规依赖项)的构建模块,以避免冲突,但您可以更改默认行为。
使用 build_context_build_modules 属性指定要包含来自 tool_requires 的 build_modules 的依赖项名称。
tool_requires = ["my_tool/0.0.1"]
def generate(self):
cmake = CMakeDeps(self)
# generate the config files for the tool require
cmake.build_context_activated = ["my_tool"]
# Choose the build modules from "build" context
cmake.build_context_build_modules = ["my_tool"]
cmake.generate()
check_components_exist¶
警告
check_components_exist
属性为实验性属性,可能会发生更改。
此属性默认为 False
。如果您希望在需要在使用者 find_package()
中指定组件时添加检查,请使用此属性。例如,如果我们正在使用声明多个组件的 Conan 包(如 Boost)。如果我们将属性设置为 True
,则使用者的 find_package()
调用将检查所需组件是否存在,并在不存在时引发错误。您可以在 generate()
方法中设置此属性。
requires = "boost/1.81.0"
...
def generate(self):
deps = CMakeDeps(self)
deps.check_components_exist = True
deps.generate()
然后,当使用 Boost 时,find_package()
将引发错误,因为 fakecomp 不存在。
cmake_minimum_required(VERSION 3.15)
...
find_package(Boost COMPONENTS random regex fakecomp REQUIRED)
...
参考¶
- class CMakeDeps(conanfile)¶
- generate()¶
此方法会将生成的文件保存到 conanfile.generators_folder。
- set_property(dep, prop, value, build_context=False)¶
使用此方法,您可以覆盖使用者从 Conan 配方中设置的 属性 值。这可以用于 cmake_file_name、cmake_target_name、cmake_find_mode、cmake_module_file_name、cmake_module_target_name、cmake_additional_variables_prefixes、cmake_config_version_compat、system_package_version、cmake_set_interface_link_directories、cmake_build_modules、nosoname 和 cmake_target_aliases。
- get_cmake_package_name(dep, module_mode=None)¶
获取 find_package(XXX) 的文件名。
- get_find_mode(dep)¶
- 参数:
dep – 依赖项
- 返回值:
“none” 或 “config” 或 “module” 或 “both” 或 “config”(未设置时)。
属性¶
以下属性会影响 CMakeDeps 生成器
cmake_file_name:为当前包生成配置文件将遵循
<VALUE>-config.cmake
模式,因此要查找包,您需要编写find_package(<VALUE>)
。cmake_target_name:要使用的目标的名称。
cmake_target_aliases:Conan 将为已存在目标创建的别名列表。
cmake_find_mode:默认为
config
。可能的值为config
:CMakeDeps 生成器将为依赖项创建配置文件。module
:将为依赖项创建模块配置文件(FindXXX.cmake)。both
:将生成配置文件和模块。none
:不会生成任何文件。例如,它可以用于创建一个系统包装器包,以便使用者在 CMake 安装配置文件路径中找到配置文件,而不是在 Conan 生成的路径中(因为已跳过)。
cmake_module_file_name:与cmake_file_name相同,但在使用
cmake_find_mode=module/both
生成模块时使用。如果未指定,则默认为cmake_file_name。cmake_module_target_name:与cmake_target_name相同,但在使用
cmake_find_mode=module/both
生成模块时使用。如果未指定,则默认为cmake_target_name。cmake_build_modules:
.cmake
文件的列表(相对于根包文件夹的路径),当使用者运行find_package()
时会自动包含这些文件。此属性不能在组件中设置,只能在根self.cpp_info
中设置。cmake_set_interface_link_directories:布尔值,应仅由未声明self.cpp_info.libs但具有
#pragma comment(lib, "foo")
(自动链接)声明的公共头文件的依赖项使用。这些依赖项应在其根cpp_info
级别的conanfile.py文件中添加此属性(目前不支持组件)。nosoname:布尔值,应仅由定义为
SHARED
且表示在没有soname
标志选项的情况下构建的库的依赖项使用。cmake_config_version_compat:默认值为
SameMajorVersion
,它可以取以下值:"AnyNewerVersion", "SameMajorVersion", "SameMinorVersion", "ExactVersion"
。它将在生成的<PackageName>ConfigVersion.cmake
文件中使用该策略。system_package_version:用于生成
<PackageName>ConfigVersion.cmake
文件的包版本。在创建系统包或其他包装器包时可能很有用,其中 Conan 包版本与最终引用的包版本不同,以保持与find_package(<PackageName> <Version>)
调用的兼容性。cmake_additional_variables_prefixes:在配置文件中创建 CMake 变量时要使用的前缀列表。默认情况下,这些变量使用
file_name
作为前缀创建,但设置此属性将创建具有指定前缀的附加变量以及默认的file_name
变量。
示例
def package_info(self):
...
# MyFileName-config.cmake
self.cpp_info.set_property("cmake_file_name", "MyFileName")
# Names for targets are absolute, Conan won't add any namespace to the target names automatically
self.cpp_info.set_property("cmake_target_name", "Foo::Foo")
# Automatically include the lib/mypkg.cmake file when calling find_package()
# This property cannot be set in a component.
self.cpp_info.set_property("cmake_build_modules", [os.path.join("lib", "mypkg.cmake")])
# Create a new target "MyFooAlias" that is an alias to the "Foo::Foo" target
self.cpp_info.set_property("cmake_target_aliases", ["MyFooAlias"])
self.cpp_info.components["mycomponent"].set_property("cmake_target_name", "Foo::Var")
# Create a new target "VarComponent" that is an alias to the "Foo::Var" component target
self.cpp_info.components["mycomponent"].set_property("cmake_target_aliases", ["VarComponent"])
# Skip this package when generating the files for the whole dependency tree in the consumer
# note: it will make useless the previous adjustements.
# self.cpp_info.set_property("cmake_find_mode", "none")
# Generate both MyFileNameConfig.cmake and FindMyFileName.cmake
self.cpp_info.set_property("cmake_find_mode", "both")
使用 CMakeDeps.set_property() 从使用者端覆盖属性¶
使用CMakeDeps.set_property()
方法,您可以从使用者端覆盖 Conan 配方设置的属性值。这可以对上面列出的每个属性进行操作。
假设我们有一个依赖于zlib/1.2.11的compressor/1.0包。zlib配方定义了一些属性
class ZlibConan(ConanFile):
name = "zlib"
...
def package_info(self):
self.cpp_info.set_property("cmake_find_mode", "both")
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
...
此配方定义了几个属性。例如,cmake_find_mode
属性设置为both
。这意味着为 Zlib 生成了模块和配置文件。也许我们需要更改此行为,只生成配置文件。您可以使用CMakeDeps.set_property()
方法在 compressor 配方中执行此操作
class Compressor(ConanFile):
name = "compressor"
requires = "zlib/1.2.11"
...
def generate(self):
deps = CMakeDeps(self)
deps.set_property("zlib", "cmake_find_mode", "config")
deps.generate()
...
您还可以使用set_property()
方法使上游配方设置的属性值无效,并使用 Conan 默认分配的值。为此,请将值None
设置为属性,如下所示
class Compressor(ConanFile):
name = "compressor"
requires = "zlib/1.2.11"
...
def generate(self):
deps = CMakeDeps(self)
deps.set_property("zlib", "cmake_target_name", None)
deps.generate()
...
执行此操作后,Zlib 库的生成目标名称将为zlib::zlib
,而不是ZLIB::ZLIB
此外,CMakeDeps.set_property()也可用于具有组件的包。在这种情况下,您需要提供包名称及其组件,并用双冒号(::)分隔。以下是一个示例
def generate(self):
deps = CMakeDeps(self)
deps.set_property("pkg::component", "cmake_target_name", <new_component_target_name>)
deps.generate()
...
禁用已安装 CMake 配置文件的 CMakeDeps¶
某些项目可能希望对下游使用者禁用CMakeDeps
生成器。这可以通过将cmake_find_mode
设置为none
来实现。如果项目想要提供自己的配置目标,则应将其附加到cpp_info
的buildirs
属性中。
此方法旨在与使用CMakeToolchain
生成器(将使用builddirs
属性填充)的下游使用者一起使用。
示例
def package(self):
...
cmake.install()
def package_info(self):
self.cpp_info.set_property("cmake_find_mode", "none") # Do NOT generate any files
self.cpp_info.builddirs.append(os.path.join("lib", "cmake", "foo"))
项目配置到导入目标配置的映射¶
如上所述,CMakeDeps
支持多个配置环境(调试、发布等)。这是通过根据安装依赖项时的build_type
设置在导入目标上填充属性来实现的。但是,当使用者项目使用单配置 CMake 生成器配置时,需要使用与已安装依赖项的build_type
设置匹配的值定义CMAKE_BUILD_TYPE
。
如果使用者 CMake 项目的配置构建类型与依赖项不同,则需要告诉 CMake 如何通过设置CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>
CMake 变量将当前项目的配置映射到导入的目标。
cd build-coverage/
conan install .. -s build_type=Debug
cmake .. -DCMAKE_BUILD_TYPE=Coverage -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_MAP_IMPORTED_CONFIG_COVERAGE=Debug