generate()¶
此方法将在依赖关系图的计算和安装之后运行。这意味着它将在 conan install 命令之后运行,或者当在缓存中构建包时,它将在调用 build()
方法之前运行。
generate()
的目的是准备构建,生成必要的文件。这些文件通常是:
包含查找依赖项信息的文件,如
xxxx-config.cmake
CMake 配置脚本,或xxxx.props
Visual Studio 属性文件。环境激活脚本,如
conanbuild.bat
或conanbuild.sh
,它们定义构建所需的所有必要环境变量。工具链文件,如
conan_toolchain.cmake
,它包含当前 Conan 设置和选项与构建系统特定语法的映射。使用现代版本的 CMake 用户的CMakePresets.json
。通用构建信息,如
conanbuild.conf
文件,其中可能包含某些工具链(如 autotools)在build()
方法中使用的信息。特定的构建系统文件,如
conanvcvars.bat
,其中包含在使用 Microsoft 编译器编译时,某些构建系统(如 Ninja)所需的 Visual Studio vcvars.bat 调用。
其思想是 generate()
方法实现所有必要的逻辑,使 conan install 后的用户手动构建非常简单,并且使 build()
方法逻辑更简单。用户在其本地流程中产生的构建结果应该与使用 conan create
在缓存中完成的构建结果完全相同,无需额外的努力。
文件生成发生在由当前布局定义的 generators_folder
中。
在许多情况下,可能不需要 generate()
方法,声明 generators
属性可能就足够了
from conan import ConanFile
class Pkg(ConanFile):
generators = "CMakeDeps", "CMakeToolchain"
但是 generate()
方法可以显式实例化这些生成器,有条件地使用它们(如在 Windows 中使用一个构建系统,而在其他平台上使用另一个构建系统集成),自定义它们,或提供完全自定义的生成。
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain
class Pkg(ConanFile):
def generate(self):
tc = CMakeToolchain(self)
# customize toolchain "tc"
tc.generate()
# Or provide your own custom logic
generate()
方法的当前工作目录将是当前布局中定义的 self.generators_folder
。
对于自定义集成,将代码放入公共的 python_require
中是避免在多个配方中重复的好方法
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain
class Pkg(ConanFile):
python_requires = "mygenerator/1.0"
def generate(self):
mygen = self.python_requires["mygenerator"].module.MyGenerator(self)
# customize mygen behavior, like mygen.something= True
mygen.generate()
如果需要从依赖项收集或复制某些文件,也可以在 generate()
方法中进行,访问 self.dependencies
。列出来自依赖项“mydep”的不同包含目录和库目录可以像这样:
from conan import ConanFile
class Pkg(ConanFile):
def generate(self):
info = self.dependencies["mydep"].cpp_info
self.output.info("**includedirs:{}**".format(info.includedirs))
self.output.info("**libdirs:{}**".format(info.libdirs))
self.output.info("**libs:{}**".format(info.libs))
将 Windows 和 OSX 中的共享库复制到当前构建文件夹,可以像这样完成:
from conan import ConanFile
class Pkg(ConanFile):
def generate(self):
# NOTE: In most cases it is not necessary to copy the shared libraries
# of dependencies to use them. Conan environment generators that create
# environment scripts allow to use the shared dependencies without copying
# them to the current location
for dep in self.dependencies.values():
# This code assumes dependencies will only have 1 libdir/bindir, if for some
# reason they have more than one, it will fail. Use ``dep.cpp_info.libdirs``
# and ``dep.cpp_info.bindirs`` lists for those cases.
copy(self, "*.dylib", dep.cpp_info.libdir, self.build_folder)
# In Windows, dlls are in the "bindir", not "libdir"
copy(self, "*.dll", dep.cpp_info.bindir, self.build_folder)
注意
最佳实践
在大多数情况下,将共享库复制到
generate()
中的当前项目不是必要的,不应作为通用方法来执行。相反,默认启用的 Conan 环境生成器会自动生成环境脚本,如conanbuild.bat|.sh
或conanrun.bat|.sh
,其中包含必要的环境变量(PATH
、LD_LIBRARY_PATH
等),以便在运行时正确找到并使用依赖项的共享库。访问依赖项
self.dependencies["mydep"].package_folder
是可能的,但当依赖项“mydep”处于“可编辑”模式时,它将为None
。如果您计划使用可编辑包,请务必始终引用cpp_info.xxxdirs
。
另请参阅
请遵循 关于在配方中准备从源代码构建的教程。
self.dependencies¶
Conan 配方通过 self.dependencies
属性访问它们的依赖项。此属性通常被生成器(如 CMakeDeps
或 MSBuildDeps
)用来生成构建所需的必要文件。
本节介绍 self.dependencies
属性,因为它可能被用户直接在配方中使用,或者间接地用于创建自定义构建集成和生成器。
依赖项接口¶
可以使用以下语法访问当前配方的每个单独的依赖项
class Pkg(ConanFile):
requires = "openssl/0.1"
def generate(self):
openssl = self.dependencies["openssl"]
# access to members
openssl.ref.version
openssl.ref.revision # recipe revision
openssl.options
openssl.settings
if "zlib" in self.dependencies:
# do something
一些 重要 点
所有信息都是 只读 的。任何尝试修改依赖项信息的行为都是错误的,并且可能随时引发错误,即使它尚未引发错误。
也不能通过这种机制调用任何方法或尝试重用来自依赖项的代码。
此信息在某些配方方法中不存在,仅在评估完整依赖关系图后才存在。它不会存在于
configure()
、config_options
、export()
、export_source()
、set_name()
、set_version()
、requirements()
、build_requirements()
、system_requirements()
、source()
、init()
、layout()
中。任何尝试在这些方法中使用它都可能随时引发错误。目前,此信息只能在
generate()
和validate()
方法中使用。对于任何其他用途,请提交 Github 问题。
并非依赖项 conanfile 的所有字段都会公开,当前字段为:
package_folder:依赖项包二进制文件的文件夹位置
recipe_folder:包含依赖项的
conanfile.py
(和其他导出的文件)的文件夹recipe_metadata_folder:包含依赖项的可选配方元数据文件的文件夹
package_metadata_folder:包含依赖项的可选包元数据文件的文件夹
immutable_package_folder:当存在
finalize()
方法时,包含不可变工件的文件夹ref:一个包含
name
、version
、user
、channel
和revision
(配方修订版)的对象pref:一个包含
ref
、package_id
和revision
(包修订版)的对象buildenv_info:
Environment
对象,包含构建所需的必要环境信息。runenv_info:
Environment
对象,包含运行应用所需的必要环境信息。cpp_info: 依赖项的 include 目录、lib 目录等信息。
settings: 此依赖项的实际设置值。
settings_build: 此依赖项的实际构建设置值。
options: 此依赖项的实际选项值。
context: 此依赖项的上下文(构建、主机)。
conf_info: 此依赖项的配置信息,旨在应用于消费者。
dependencies: 此依赖项的传递依赖项。
is_build_context: 如果
context == "build"
,则返回True
。conan_data: 来自依赖项的
conandata.yml
文件的conan_data
属性。license: 依赖项的
license
属性。description: 依赖项的
description
属性。homepage: 依赖项的
homepage
属性。url: 依赖项的
url
属性。package_type: 依赖项的
package_type
。languages: 依赖项的
languages
。
迭代依赖项¶
可以以类似字典的方式迭代配方的所有依赖项。请注意,self.dependencies
包含所有当前的依赖项,包括直接依赖项和传递依赖项。当前配方的每个上游依赖项,只要对其有影响,都会在 self.dependencies
中有一个条目。
迭代依赖项可以这样做:
requires = "zlib/1.2.11", "poco/1.9.4"
def generate(self):
for require, dependency in self.dependencies.items():
self.output.info("Dependency is direct={}: {}".format(require.direct, dependency.ref))
将会输出:
conanfile.py (hello/0.1): Dependency is direct=True: zlib/1.2.11
conanfile.py (hello/0.1): Dependency is direct=True: poco/1.9.4
conanfile.py (hello/0.1): Dependency is direct=False: pcre/8.44
conanfile.py (hello/0.1): Dependency is direct=False: expat/2.4.1
conanfile.py (hello/0.1): Dependency is direct=False: sqlite3/3.35.5
conanfile.py (hello/0.1): Dependency is direct=False: openssl/1.1.1k
conanfile.py (hello/0.1): Dependency is direct=False: bzip2/1.0.8
其中 require
字典键是一个“需求”,可以包含当前配方和依赖项之间关系的说明符。目前它们可以是:
require.direct
:布尔值,如果它是直接依赖项则为True
,如果它是传递依赖项则为False
。require.build
:布尔值,如果它是构建上下文中的build_require
,例如cmake
,则为True
。require.test
:布尔值,如果它是主机上下文中的build_require
(使用self.test_requires()
定义),例如gtest
,则为True
。
dependency
字典值是上述描述的只读对象,用于访问依赖项的属性。
self.dependencies
包含一些辅助方法,用于根据某些条件进行过滤:
self.dependencies.host
:将过滤掉build=True
的需求,留下像zlib
或poco
这样的常规依赖项。self.dependencies.direct_host
:将过滤掉build=True
或direct=False
的需求。self.dependencies.build
:将过滤掉build=False
的需求,只留下构建上下文中的tool_requires
,例如cmake
。self.dependencies.direct_build
:将过滤掉build=False
或direct=False
的需求。self.dependencies.test
:将过滤掉build=True
或test=False
的需求,只留下主机上下文中的测试需求,例如gtest
。
它们可以以相同的方式使用:
requires = "zlib/1.2.11", "poco/1.9.4"
def generate(self):
cmake = self.dependencies.direct_build["cmake"]
for require, dependency in self.dependencies.build.items():
# do something, only build deps here
依赖项 cpp_info
接口¶
cpp_info
接口被构建系统大量使用以访问数据。此对象定义了全局属性和每个组件的属性,以访问诸如 include 文件夹之类的信息。
def generate(self):
cpp_info = self.dependencies["mydep"].cpp_info
cpp_info.includedirs
cpp_info.libdirs
cpp_info.components["mycomp"].includedirs
cpp_info.components["mycomp"].libdirs
在 cppinfo
对象中声明的所有路径(例如 cpp_info.includedirs
)都是绝对路径,无论依赖项是在缓存中还是 可编辑包中都有效。
另请参阅
CppInfo 模型。