第一章: 部署器基本介绍
在Conan中,部署器(Deployers)是一种机制,用于方便地将文件从一个文件夹(通常是Conan缓存)复制到用户文件夹。Conan提供了两个内置的部署器:full_deploy
和 direct_deploy
。此外,用户还可以通过 conan config install
命令管理自己的部署器。
部署器在生成器(Generators)之前运行,它们可以更改目标文件夹。例如,如果在 CMakeDeps
生成器之前运行 --deployer=full_deploy
部署器,则 CMakeDeps
生成的文件将指向 full_deploy
部署器在用户文件夹中完成的本地副本,而不是指向Conan缓存。可以通过提供多个 --deployer=
参数来指定多个部署器,它们将按照出现的顺序运行。
部署器可以是多配置的。对不同配置文件多次运行 conan install . --deployer=full_deploy
可以实现一个完全自包含的项目,包括所有的工件、二进制文件和构建文件。这个项目将完全独立于Conan,不再需要它来构建。可以使用 --deployer-folder
参数根据需要更改部署器的基础文件夹输出路径。
内置部署器有 full_deploy
和 direct_deploy
。full_deploy
部署每个依赖项的包文件夹到您的配方的 output_folder
中的一个子文件夹树中,基于构建上下文、依赖项名称和版本、构建类型和构建架构。direct_deploy
与 full_deploy
相同,但只处理您配方的直接依赖项。这些内置部署器目前处于预览阶段。
可以通过设置 conf tools.deployer:symlinks
为 False
来禁用部署器复制符号链接,这对于不支持符号链接的系统来说很方便。
自定义部署器可以通过 conan config install
管理。当寻找特定的部署器时,Conan将按以下顺序在这些位置查找部署器:
- 绝对路径
- 相对于当前工作目录(cwd)
- 在
[CONAN_HOME]/extensions/deployers
文件夹中 - 作为内置部署器
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 install
或 conan graph
命令时设置 tools.build:download_source=True
配置。
这个示例展示了如何创建一个基本的自定义部署器。根据您的具体需求,您可以调整这个函数来执行更复杂的部署逻辑,例如只复制特定的文件或文件夹,或者根据不同的构建配置创建不同的目标文件夹。
2.2 理解部署器参数
在自定义部署器的 deploy
函数中,有三个主要参数:graph
, output_folder
, 和 **kwargs
。下面是它们的详细介绍:
graph
:
- 类型:
ConanGraph
- 描述: 这个参数代表了整个依赖图,其中包含了所有的依赖项节点。每个节点代表了一个依赖项,包括它的信息和属性。
- 使用: 你可以遍历这个图来获取每个依赖项的信息。例如,
node.conanfile
可以访问节点的conanfile
对象,node.ref.name
和node.ref.version
分别可以获取依赖项的名称和版本。
output_folder
:
- 类型:
str
- 描述: 这个参数是一个字符串,代表了文件应该被部署到的目标文件夹的路径。
- 使用: 在部署文件时,你应该将文件复制到这个文件夹中。你可以根据需要创建子文件夹来组织不同的依赖项。
**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.name
和 node.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_path
和 file_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
文件夹。
第三章 小结
在本博客中,我们探讨了以下知识点:
- Conan Deployers: Conan的部署器(Deployers)是一种机制,用于将文件从一个文件夹(通常是Conan缓存)复制到用户文件夹。Conan提供了两个内置的部署器:
full_deploy
和direct_deploy
,同时支持自定义部署器【16†source】。 - 自定义部署器: 自定义部署器可以通过
conan config install
管理,并且需要实现一个deploy
函数,该函数接受依赖图 (graph
)、输出文件夹路径 (output_folder
) 和一个关键字参数字典 (**kwargs
) 作为参数【16†source】【17†source】【18†source】。 - Conan Graph: 依赖图 (
graph
) 包含了所有的依赖项节点,每个节点代表了一个依赖项,包括它的信息和属性。可以遍历这个图来获取每个依赖项的信息。 - 文件复制: 在自定义部署器中,通常需要将依赖项的文件从包文件夹复制到指定的输出文件夹。可以使用原生的
shutil
模块或 Conan 的conan.tools.files.copy
工具来实现文件复制。 - 优化和注释: 为了提高代码的可读性和可维护性,对自定义部署器函数进行优化和添加注释是很有帮助的。例如,使用 Conan 的
copy
工具替代原生的复制函数,并添加日志打印以跟踪部署过程中的依赖项。
通过这些知识点,我们了解了如何在 Conan 中使用和自定义部署器来管理依赖项的复制和部署过程。
结语
在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。
这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。
我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。