Hooks

Conan Hooks 是一项旨在扩展 Conan 功能的特性,用于在包创建过程的不同阶段(如构建前和构建后)执行某些正交操作,例如质量检查。

Hook 结构

Hook 是一个 Python 函数,将在 Conan 工作流的特定点执行,以在不修改客户端源或 recipe 源的情况下自定义客户端行为。

这是一个简单 hook 的示例

hook_example.py
 from conan.tools.files import load

 def pre_export(conanfile):
     for field in ["url", "license", "description"]:
         field_value = getattr(conanfile, field, None)
         if not field_value:
             conanfile.output.error(f"[REQUIRED ATTRIBUTES] Conanfile doesn't have '{field}'.
                                       It is recommended to add it as attribute.")

这个 hook 在 recipe 被导出之前检查其内容。基本上,pre_export() 函数检查 conanfile 对象的属性,以查看是否存在 URL、许可证和描述,如果缺失,则通过 conanfile.output 向用户发出警告消息。这在 recipe 导出到本地缓存之前完成。

任何类型的 Python 脚本都可以执行。您可以创建全局函数并从不同的 hook 函数中调用它们,从相对模块导入,并发出警告、错误,甚至引发异常以中止 Conan 客户端的执行。

从模块导入

hook 接口应始终放在一个以 hook_ 开头且以 .py 扩展名结尾的 Python 文件中。它还应该存储在 <conan_home>/extensions/hooks 文件夹中。但是,如果您的系统中安装了导入的模块,或者它们是随 Conan 一起安装的,则可以使用其功能。

hook_example.py
 import requests
 from conan.tools.files import replace_in_file

 def post_package(conanfile):
     if not os.path.isdir(os.path.join(conanfile.package_folder, "licenses")):
         response = requests.get('https://api.github.com/repos/company/repository/contents/LICENSE')

您还可以从相对模块导入功能

hooks
├── custom_module
│   ├── custom.py
│   └── __init__.py
└── hook_printer.py

在我的 custom_module 中的 custom.py 内部有

custom.py
 def my_printer(conanfile):
     conanfile.output.info("my_printer(): CUSTOM MODULE")

它可以在 hook 中通过导入模块使用,就像普通的 Python 一样

hook_printer.py
 from custom_module.custom import my_printer

 def pre_export(conanfile):
     my_printer(conanfile)

Hook 接口

这里您可以看到所有可用 hook 函数的完整示例

hook_full.py
 def pre_export(conanfile):
     conanfile.output.info("Running before to execute export() method.")

 def post_export(conanfile):
     conanfile.output.info("Running after of executing export() method.")

 def pre_validate(conanfile):
     conanfile.output.info("Running before executing the validate() method.")

 def post_validate(conanfile):
     conanfile.output.info("Running after executing the validate() method.")

 def pre_source(conanfile):
     conanfile.output.info("Running before to execute source() method.")

 def post_source(conanfile):
     conanfile.output.info("Running after of executing source() method.")

 def pre_generate(conanfile):
     conanfile.output.info("Running before to execute generate() method.")

 def post_generate(conanfile):
     conanfile.output.info("Running after of executing generate() method.")

 def pre_build(conanfile):
     conanfile.output.info("Running before to execute build() method.")

 def post_build(conanfile):
     conanfile.output.info("Running after of executing build() method.")

 def post_build_fail(conanfile):
     conanfile.output.info("Running after failed execution of build() method.")

 def pre_package(conanfile):
     conanfile.output.info("Running before to execute package() method.")

 def post_package(conanfile):
     conanfile.output.info("Running after of executing package() method.")

 def pre_package_info(conanfile):
     conanfile.output.info("Running before to execute package_info() method.")

 def post_package_info(conanfile):
     conanfile.output.info("Running after of executing package_info() method.")

hook 的函数旨在对其执行进行自我描述。例如,pre_package() 函数在 recipe 的 package() 方法执行之前调用。

所有 hook 方法都只填充相同的单个对象

  • conanfile: 它是一个常规的 ConanFile 对象,从接收 Conan 命令的 recipe 中加载。它具有其正常的属性和动态对象,如 build_folderpackage_folderoutputdependenciesoptions 等。

存储、激活和共享

Hooks 是存储在 <conan_home>/extensions/hooks 文件夹下的 Python 文件,其文件名应以 hook_ 开头并以 .py 扩展名结尾

一旦 hook 文件存储在 hook 文件夹中,hook 的激活就会自动完成。如果存储在子文件夹中,它也会自动工作。

要停用一个 hook,应将其文件从 hook 文件夹中删除。没有可以停用但保留文件存储在 hook 文件夹中的配置。

官方 Hooks

Conan hooks GitHub 中有其自己的仓库中维护的一些官方 hooks,但大多数只与 Conan 1.x 兼容,所以请先查阅 README 以获取哪些 hooks 与 Conan v2 兼容的信息。