tool_requires 中透明地使用 cmake 模块

当我们想重用另一个 Conan 包中的一些 .cmake 脚本时,有几种不同的可能情况,例如 .cmake 脚本是在常规的 requires 中还是在 tool_requires 中。

此外,可能需要 2 种不同的方法

  • 脚本的消费者可以在其 CMakeLists.txt 中显式地 include(MyScript)。这种方法非常明确且设置简单,只需在 recipe 中定义 self.cpp_info.builddirs,并且使用 CMakeToolchain 的消费者将能够自动执行 include() 并使用该功能。请参阅此处的示例

  • 当执行 find_package() 时,消费者希望自动加载依赖 cmake 模块。当前的示例实现了这种情况。

假设我们有一个包,旨在用作 tool_require,其 recipe 如下

myfunctions/conanfile.py
import os
from conan import ConanFile
from conan.tools.files import copy

class Conan(ConanFile):
    name = "myfunctions"
    version = "1.0"
    exports_sources = ["*.cmake"]

    def package(self):
        copy(self, "*.cmake", self.source_folder, self.package_folder)

    def package_info(self):
        self.cpp_info.set_property("cmake_build_modules", ["myfunction.cmake"])

并且 myfunction.cmake 文件位于

myfunctions/myfunction.cmake
function(myfunction)
    message("Hello myfunction!!!!")
endfunction()

我们可以执行 cd myfunctions && conan create .,这将创建包含 cmake 脚本的 myfunctions/1.0 包。

然后,一个消费包将如下所示

consumer/conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain

class Conan(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    tool_requires = "myfunctions/1.0"

    def generate(self):
        tc = CMakeToolchain(self)
        tc.generate()

        deps = CMakeDeps(self)
        # By default 'myfunctions-config.cmake' is not created for tool_requires
        # we need to explicitly activate it
        deps.build_context_activated = ["myfunctions"]
        # and we need to tell to automatically load 'myfunctions' modules
        deps.build_context_build_modules = ["myfunctions"]
        deps.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()

以及一个如下的 CMakeLists.txt

consumer/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(test)
find_package(myfunctions CONFIG REQUIRED)
myfunction()

然后,消费者将能够自动从依赖模块调用 myfunction()

$ conan build .
...
Hello myfunction!!!!

如果出于某种原因,消费者希望强制使用 tool_requires() 作为 CMake 模块,则消费者可以执行 deps.set_property("myfunctions", "cmake_find_mode", "module", build_context=True),然后 find_package(myfunctions MODULE REQUIRED) 将起作用。