【Conan 入门教程】从零开始编写第一个自定义部署器

简介: 【Conan 入门教程】从零开始编写第一个自定义部署器

第一章: 部署器基本介绍

在Conan中,部署器(Deployers)是一种机制,用于方便地将文件从一个文件夹(通常是Conan缓存)复制到用户文件夹。Conan提供了两个内置的部署器:full_deploydirect_deploy。此外,用户还可以通过 conan config install 命令管理自己的部署器。

部署器在生成器(Generators)之前运行,它们可以更改目标文件夹。例如,如果在 CMakeDeps 生成器之前运行 --deployer=full_deploy 部署器,则 CMakeDeps 生成的文件将指向 full_deploy 部署器在用户文件夹中完成的本地副本,而不是指向Conan缓存。可以通过提供多个 --deployer= 参数来指定多个部署器,它们将按照出现的顺序运行。

部署器可以是多配置的。对不同配置文件多次运行 conan install . --deployer=full_deploy 可以实现一个完全自包含的项目,包括所有的工件、二进制文件和构建文件。这个项目将完全独立于Conan,不再需要它来构建。可以使用 --deployer-folder 参数根据需要更改部署器的基础文件夹输出路径。

内置部署器有 full_deploydirect_deployfull_deploy 部署每个依赖项的包文件夹到您的配方的 output_folder 中的一个子文件夹树中,基于构建上下文、依赖项名称和版本、构建类型和构建架构。direct_deployfull_deploy 相同,但只处理您配方的直接依赖项。这些内置部署器目前处于预览阶段。

可以通过设置 conf tools.deployer:symlinksFalse 来禁用部署器复制符号链接,这对于不支持符号链接的系统来说很方便。

自定义部署器可以通过 conan config install 管理。当寻找特定的部署器时,Conan将按以下顺序在这些位置查找部署器:

  1. 绝对路径
  2. 相对于当前工作目录(cwd)
  3. [CONAN_HOME]/extensions/deployers 文件夹中
  4. 作为内置部署器

Conan将寻找一个 deploy() 方法来调用每个已安装文件。您的自定义部署器的函数签名应如下:

def deploy(graph, output_folder: str, **kwargs):
    # Your deployment logic here

其中 **kwargs 是必须的,即使未使用,因为未来的Conan版本中可能会添加新的参数,如果没有定义 **kwargs,这些参数将会导致破坏。

您可以通过 graph.root.conanfile 访问您的 conanfile 对象。有关如何迭代其依赖项的信息,请参阅 ConanFile.dependencies。现在,您的自定义部署器可以使用其所在文件的文件名来调用,就像它是一个内置部署器一样,例如 conan install . --deployer=my_custom_deployer。请注意,提供 .py 扩展名是可选的。

第二章 :开始自定义部署器

2.1 创建目录

如果在 ~/.conan2/extensions 目录下没有 deployers 文件夹,您可以手动创建这个文件夹。在 Linux 或 macOS 系统上,您可以使用以下命令:

mkdir -p ~/.conan2/extensions/deployers

这将创建 deployers 文件夹,如果 extensions 文件夹也不存在,也会一并创建。之后,您可以将自定义部署器的脚本放入这个 deployers 文件夹中。

请确保您的自定义部署器脚本具有正确的文件权限,以便 Conan 可以执行它们。通常,您应该给这些脚本文件设置可读和可执行权限。例如,如果您的自定义部署器脚本名为 my_custom_deployer.py,您可以使用以下命令设置权限:

chmod +rx ~/.conan2/extensions/deployers/my_custom_deployer.py

之后,您就可以在 Conan 命令中使用 --deployer=my_custom_deployer 来调用您的自定义部署器了。

2.2 编写部署器

2.2.1 基本的部署器示例

首先我们需要一个部署器示例,根据Conan 2.1的文档,自定义部署器通常应该包含一个 deploy 函数,该函数接受依赖图 (graph)、输出文件夹路径 (output_folder) 和一个关键字参数字典 (**kwargs) 作为参数【16†source】【17†source】【18†source】。这个 deploy 函数负责将文件从依赖项的源文件夹复制到指定的输出文件夹中。

以下是一个自定义部署器的示例,它将所有依赖项的源文件复制到一个特定的输出文件夹中:

from conan.tools.files import copy
import os
def deploy(graph, output_folder, **kwargs):
    # Note the kwargs argument is mandatory to be robust against future changes.
    for name, dep in graph.root.conanfile.dependencies.items():
        if dep.folders is None or dep.folders.source_folder is None:
            raise ConanException(f"Sources missing for {name} dependency.\n"
                                  "This deployer needs the sources of every dependency present to work, either building from source, "
                                  "or by using the 'tools.build:download_source' conf.")
        copy(graph.root.conanfile, "*", dep.folders.source_folder, os.path.join(output_folder, "dependency_sources", str(dep)))

在这个示例中,deploy 函数遍历了配方的所有依赖项,并将每个依赖项的源文件复制到 output_folder 下的 dependency_sources 文件夹中。这个示例使用了 conan.tools.files.copy 函数来执行复制操作。需要注意的是,为了确保 dep.folders.source_folder 被正确定义,必须在调用 conan installconan graph 命令时设置 tools.build:download_source=True 配置。

这个示例展示了如何创建一个基本的自定义部署器。根据您的具体需求,您可以调整这个函数来执行更复杂的部署逻辑,例如只复制特定的文件或文件夹,或者根据不同的构建配置创建不同的目标文件夹。

2.2 理解部署器参数

在自定义部署器的 deploy 函数中,有三个主要参数:graph, output_folder, 和 **kwargs。下面是它们的详细介绍:

  1. graph:
  • 类型: ConanGraph
  • 描述: 这个参数代表了整个依赖图,其中包含了所有的依赖项节点。每个节点代表了一个依赖项,包括它的信息和属性。
  • 使用: 你可以遍历这个图来获取每个依赖项的信息。例如,node.conanfile 可以访问节点的 conanfile 对象,node.ref.namenode.ref.version 分别可以获取依赖项的名称和版本。
  1. output_folder:
  • 类型: str
  • 描述: 这个参数是一个字符串,代表了文件应该被部署到的目标文件夹的路径。
  • 使用: 在部署文件时,你应该将文件复制到这个文件夹中。你可以根据需要创建子文件夹来组织不同的依赖项。
  1. **kwargs:
  • 类型: dict
  • 描述: 这是一个关键字参数字典,用于传递额外的参数。这个参数是可选的,但是建议包含它以便于将来的扩展性。
  • 使用: 在当前版本的Conan中,这个参数可能不会被直接使用,但是在将来的版本中,可能会添加新的参数。通过包含 **kwargs,你的部署器将能够兼容未来的参数变化,而不会因为缺少新参数而出现错误。

举个例子,如果你想在部署过程中打印每个依赖项的名称和版本,你可以这样做:

def deploy(graph, output_folder: str, **kwargs):
    for node in graph.nodes:
        print(f"Deploying {node.ref.name} version {node.ref.version}")
        # 其他部署逻辑...

这里,graph.nodes 遍历了所有的依赖项节点,然后你可以通过 node.ref.namenode.ref.version 来获取每个依赖项的名称和版本,并将它们打印出来。


如果你在 conan install 命令中不指定 --deploy-folder 参数来设置 output_folder,Conan 会使用默认的输出文件夹路径。默认情况下,这个路径是当前工作目录下的一个名为 deploy 的文件夹。也就是说,如果你的当前工作目录是 /home/user/project,那么默认的 output_folder 将是 /home/user/project/deploy

如果你直接拷贝文件到这个默认的 output_folder,那么所有的依赖项将被部署到 /home/user/project/deploy 文件夹下,按照你在自定义部署器中定义的结构组织。例如,如果你使用了前面提供的示例部署器,那么 expat/2.6.0 的文件将被拷贝到 /home/user/project/deploy/expat/2.6.0

如果你想将依赖项部署到不同的路径,你可以在 conan install 命令中使用 --deploy-folder 参数来指定一个自定义的输出文件夹路径。例如:

conan install . --deploy-folder=/path/to/custom/deploy/folder

这将设置 output_folder/path/to/custom/deploy/folder,并将所有依赖项部署到这个路径下。

2.3 写一个简单示例

把依赖项拷贝到指定目录

我们可以对自定义的 deploy 函数进行一些优化和注释添加,以便更好地理解和使用:

import os
import shutil
from conan.tools.files import copy
def deploy(graph, output_folder: str, **kwargs):
    # 遍历依赖图中的所有节点
    for node in graph.nodes:
        # 获取依赖项的名称和版本
        dep_name = node.ref.name
        #dep_version 不是字符串,而是 Version 对象。可以通过在构造 dest_folder 路径时将 dep_version 转换为字符串
        dep_version = str(node.ref.version)  # Convert Version object to string
        # 打印依赖项信息
        print(f"Deploying {dep_name} version {dep_version}")
        # 获取包的路径
        package_folder = node.conanfile.package_folder
        # 构建目标文件夹路径
        dest_folder = os.path.join(output_folder, dep_name, dep_version)
        # 如果目标文件夹不存在,则创建它
        if not os.path.exists(dest_folder):
            os.makedirs(dest_folder)
        # 复制文件夹内容
        if package_folder:
            # 使用Conan的copy工具来复制文件,以便更好地处理文件复制过程中的各种情况
            copy(node.conanfile, "*", package_folder, dest_folder)

os.path.join 是一个 Python 标准库函数,用于将多个路径组件连接成一个完整的路径。这个函数会根据你的操作系统自动处理路径分隔符,确保路径是正确的。

例如,假设你有一个文件夹的路径和一个文件名,你想要得到这个文件的完整路径:

folder_path = "/home/user/documents"
file_name = "report.txt"
full_path = os.path.join(folder_path, file_name)

在这个例子中,os.path.join 会将 folder_pathfile_name 连接起来,使用适当的路径分隔符(在 Unix 系统上是 /,在 Windows 系统上是 \)。所以 full_path 的值将是 /home/user/documents/report.txt

在部署器的上下文中,os.path.join 用于构建依赖项的目标文件夹路径。例如:

dest_folder = os.path.join(output_folder, dep_name, dep_version)

这里,output_folder 是部署的根目录,dep_name 是依赖项的名称,dep_version 是依赖项的版本。os.path.join 将它们连接起来,形成一个完整的路径,用于存储特定依赖项的文件。

2.4 执行安装

在Conan 2.1中,要在安装过程中执行自定义部署器并指定输出路径,您可以使用 --deployer 参数来指定部署器,以及 --deployer-folder 参数来设置部署器的输出文件夹路径。例如,如果您的自定义部署器名为 my_custom_deployer 并且您想将输出文件夹设置为 /path/to/output_folder,您可以使用以下命令:

conan install . --deployer=my_custom_deployer --deployer-folder=/path/to/output_folder
#conan install conanfile.py --build=missing -o *:shared=True --deployer=my_custom_deployer --deployer-folder=$(pwd)/third-party

这将使得自定义部署器将文件部署到指定的 /path/to/output_folder 路径下。如果不指定 --deployer-folder 参数,那么默认的输出路径将是当前工作目录下的 output_folder 文件夹。

第三章 小结

在本博客中,我们探讨了以下知识点:

  1. Conan Deployers: Conan的部署器(Deployers)是一种机制,用于将文件从一个文件夹(通常是Conan缓存)复制到用户文件夹。Conan提供了两个内置的部署器:full_deploydirect_deploy,同时支持自定义部署器【16†source】。
  2. 自定义部署器: 自定义部署器可以通过 conan config install 管理,并且需要实现一个 deploy 函数,该函数接受依赖图 (graph)、输出文件夹路径 (output_folder) 和一个关键字参数字典 (**kwargs) 作为参数【16†source】【17†source】【18†source】。
  3. Conan Graph: 依赖图 (graph) 包含了所有的依赖项节点,每个节点代表了一个依赖项,包括它的信息和属性。可以遍历这个图来获取每个依赖项的信息。
  4. 文件复制: 在自定义部署器中,通常需要将依赖项的文件从包文件夹复制到指定的输出文件夹。可以使用原生的 shutil 模块或 Conan 的 conan.tools.files.copy 工具来实现文件复制。
  5. 优化和注释: 为了提高代码的可读性和可维护性,对自定义部署器函数进行优化和添加注释是很有帮助的。例如,使用 Conan 的 copy 工具替代原生的复制函数,并添加日志打印以跟踪部署过程中的依赖项。

通过这些知识点,我们了解了如何在 Conan 中使用和自定义部署器来管理依赖项的复制和部署过程。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
编译器 C++ 开发者
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
918 1
|
5月前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
50609 72
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
9月前
|
人工智能 安全 Serverless
AgentRun Sandbox SDK 正式开源!集成 LangChain 等主流框架,一键开启智能体沙箱新体验
AgentRun Sandbox SDK 开源发布,基于Serverless架构提供安全、弹性的智能体运行环境。支持LangChain、AgentScope等主流框架,内置代码解释、浏览器操作等多模态沙箱能力,助力Agentic AI快速落地,让开发更轻盈,云端更安全。
|
缓存 算法 持续交付
【Conan 入门教程】总结 Conan 中的环境变量,设置主目录,配置文件等
【Conan 入门教程】总结 Conan 中的环境变量,设置主目录,配置文件等
1776 1
|
安全 网络安全 芯片
花无涯带你走进黑客世界8 改号软件
花无涯带你走进黑客世界 目前几章开始慢慢同步,没有看到的可以直接进进专栏看。 花无涯带你走进黑客世界 - 知乎专栏 这里把前几章也发出来,知乎的贴图比较麻烦,所以有案例的章节都发布在微博里头,
1014 0
花无涯带你走进黑客世界8 改号软件
|
人工智能 程序员 测试技术
亲测可用,IDEA 中使用满血版 DeepSeek R1!支持深度思考!免费!免配置!
之前介绍过在IDEA中使用DeepSeek的方案,但是很多人表示还是用的不够爽,比如用CodeChat的方案,只支持V3版本,不支持带推理的R1。想要配置R1的话有特别的麻烦。那么,今天,给大家带来一个亲测有效,可使用DeepSeek R1,同时支持问答+AI程序员,无需配置,快速使用的方案。那就是借助阿里的通义灵码,通义灵码最近刚刚做了重大更新,新上线了模型选择功能。在支持Qwen的模型之外,还同时支持了DeepSeek-V3 和 DeepSeek-R1 ,而且是满血版671B模型。
|
Docker 容器
docker可以通过镜像id导出镜像吗?
docker可以通过镜像id导出镜像吗?
499 0
|
Linux 数据安全/隐私保护 Android开发
10 个最佳 Linux 开源笔记软件
10 个最佳 Linux 开源笔记软件
1791 1
|
算法 图形学
【计算机图形学】实验一 DDA算法、Bresenham算法
【计算机图形学】实验一 DDA算法、Bresenham算法
1638 3
|
存储 Ubuntu 开发工具
在Ubuntu 16.04上安装Git的方法
在Ubuntu 16.04上安装Git的方法
622 0