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 requires,您需要使用 find_package(hello) 并链接到目标 hello::hello。请查看 影响 CMakeDeps 的属性,例如 cmake_target_name,以自定义依赖项及其组件中的文件和目标名称。
注意
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 对象中的一些属性,以更改默认行为
配置¶
允许定义自定义用户 CMake 配置,除了标准的 Release、Debug 等配置。
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** 和 **常规 require** 时,它会导致生成器发生冲突,因为配置文件名称以及目标名称、变量名称等将发生冲突。
例如,对于一些包含用于在构建时生成源代码的工具的 requirements(capnproto、protobuf…),这是一种典型情况(因此是 **build_require**),但同时也提供一个库来链接到最终应用程序,因此您也有一个 **常规 require**。解决此冲突对于交叉构建尤其重要,因为工具(将在构建机器上运行)属于与将在主机机器上“运行”的库不同的二进制包。
您可以使用 **build_context_suffix** 属性为 requirement 指定后缀,以便构建上下文(工具 require)中的文件/目标/变量将被重命名
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** 还有另一个问题。如您所知,requirements 的 recipes 可以声明一个 cppinfo.build_modules 条目,其中包含一个或多个 **.cmake** 文件。当 cmake find_package() 函数找到 requirement 时,Conan 会自动包含这些文件。
默认情况下,Conan 仅包含来自 host 上下文(常规 requires)的构建模块,以避免冲突,但您可以更改默认行为。
使用 **build_context_build_modules** 属性指定包含来自 **tool_requires** 的 **build_modules** 的 requirement 名称
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。如果您希望在要求指定组件时添加检查,请使用此属性。例如,如果我们正在使用一个声明了多个组件的 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 recipes 设置的 属性 值。可以对
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_build_modules、nosoname、cmake_target_aliases和cmake_extra_variables进行操作。
- get_cmake_package_name(dep, module_mode=None)¶
获取
find_package(XXX)调用文件的名称
- get_find_mode(dep)¶
- 参数:
dep – requirement
- 返回:
其中之一
"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 生成器将为依赖项创建 config 脚本。module:将创建模块 config(FindXXX.cmake)脚本,用于依赖项。both:将生成 config 和模块。none:不会生成任何文件。例如,它可以用于创建系统包装器包,以便消费者在 CMake 安装 config 路径中找到 config 文件,而不是在 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:(在 2.14 中 **已弃用**)。它用于
#pragma comment(lib, "foo")很久以前,但不再需要 CMakeDeps 生成器。在新的CMakeConfigDeps生成器中,需要正确地在cpp_info中指定库详细信息,与 CPS 实践保持一致。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: 在 config 文件中创建 CMake 变量时要使用的前缀列表。这些变量默认以
file_name作为前缀创建,但设置此属性将在默认的file_name之外创建具有指定前缀的额外变量。cmake_extra_variables: 要添加到生成的 config 文件的额外变量的字典。字典的键是变量名,值是变量值,可以是纯字符串、数字或类似字典的 python 对象,该对象必须指定
value(字符串/数字)、cache(布尔值)、type(CMake 缓存类型)以及可选地,docstring(字符串:默认为变量名)和force(布尔值)键。请注意,这比tools.cmake.cmaketoolchain:extra_variablesconf 中定义的那些值优先级较低。
示例
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 adjustments.
# 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")
# Add extra variables to the generated config file
self.cpp_info.set_property("cmake_extra_variables", {
"FOO": 42,
"CMAKE_GENERATOR_INSTANCE": "${GENERATOR_INSTANCE}/buildTools/",
"CACHE_VAR_DEFAULT_DOC": {"value": "hello world",
"cache": True, "type": "STRING"}
})
使用 CMakeDeps.set_property() 覆盖消费者侧的属性¶
使用 CMakeDeps.set_property() 方法,您可以覆盖来自 Conan 配方设置的属性值,从消费者端进行覆盖。可以对上述所有属性执行此操作。
假设我们有一个 compressor/1.0 包,它依赖于 zlib/1.3.1。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 将生成模块和 config 文件。也许我们需要更改此行为,只生成 config 文件。您可以使用 CMakeDeps.set_property() 方法在 compressor 配方中执行此操作
class Compressor(ConanFile):
name = "compressor"
requires = "zlib/1.3.1"
...
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.3.1"
...
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 支持多种配置环境(Debug、Release 等)。这是通过根据安装依赖项时的 build_type 设置填充导入目标上的属性来实现的。但是,当消费者项目使用单配置 CMake 生成器配置时,需要使用与已安装依赖项匹配的值定义 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