generate()¶
此方法将在依赖图的计算和安装之后运行。这意味着它将在 conan install 命令之后运行,或者在缓存中构建包时,将在调用 build() 方法之前运行。
generate() 的目的是准备构建,生成必要的文件。这些文件通常会是
包含定位依赖项信息的 文件,例如
xxxx-config.cmakeCMake 配置脚本,或xxxx.propsVisual Studio 属性文件。环境激活脚本,例如
conanbuild.bat或conanbuild.sh,它们定义了构建所需的所有必要环境变量。工具链文件,例如
conan_toolchain.cmake,它包含当前 Conan 设置和选项与构建系统特定语法之间的映射。对于使用现代版本的 CMake 用户,还包括CMakePresets.json。通用构建信息,例如
conanbuild.conf文件,其中可能包含用于 autotools 等工具链的信息,以便在build()方法中使用。特定的构建系统文件,例如
conanvcvars.bat,其中包含某些构建系统(如 Ninja)在使用 Microsoft 编译器进行编译时所需的 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”处于“editable”模式时,它将是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 issue。
并非暴露了依赖项 conanfile 的所有字段,当前字段是:
package_folder:依赖项包二进制文件的文件夹位置
recipe_folder:包含依赖项的
conanfile.py(和其他导出的文件)的文件夹recipe_metadata_folder:包含依赖项可选配方元数据文件的文件夹
package_metadata_folder:包含依赖项可选包元数据文件的文件夹
immutable_package_folder:当存在
finalize()方法时,包含不可变工件的文件夹ref:一个 RecipeReference 对象,其中包含
name、version、user、channel和revision(配方修订版)pref:一个包含
ref、package_id和revision(包修订版)的对象buildenv_info:包含构建所需环境信息的
Environment对象runenv_info:包含运行应用程序所需环境信息的
Environment对象cpp_info:依赖项的 includedirs、libdirs 等。
settings:此依赖项的实际设置值
settings_build:此依赖项的实际构建设置值
options:此依赖项的实际选项值
context:此依赖项的上下文(build、host)
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_typelanguages:依赖项的
languages。extension_properties:依赖项的
extension_properties。应视为只读。recipe:依赖项的
recipe类型(例如,“Cache”)。这仅应用于 信息性 或报告目的。将其用于消费者端的任何条件逻辑都被视为不良做法且不受支持。
迭代依赖项¶
可以以类似字典的方式迭代配方的所有依赖项。请注意,self.dependencies 包含所有当前依赖项,包括直接和传递依赖项。当前依赖项的任何对当前依赖项有影响的上游依赖项,都将在 `self.dependencies` 中有一个条目。
可以这样迭代依赖项:
requires = "zlib/1.3.1", "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.3.1
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` 字典键是“requirement”,可以包含当前配方与依赖项之间关系的说明符。目前它们可以是:
require.direct:布尔值,如果它是直接依赖项,则为 `True`,否则为 `False`。require.build:布尔值,如果它是构建上下文中的build_require,则为 `True`,例如 `cmake`。require.test:布尔值,如果它是主机上下文中的build_require(使用self.test_requires()定义),则为 `True`,例如 `gtest`。
`dependency` 字典值是上面描述的只读对象,用于访问依赖项属性。
`self.dependencies` 包含一些用于根据某些条件进行过滤的助手:
self.dependencies.host:将过滤掉build=True的 requires,留下常规依赖项,如zlib或poco。self.dependencies.direct_host:将过滤掉build=True或direct=False的 requires。self.dependencies.build:将过滤掉build=False的 requires,仅留下构建上下文中的tool_requires,例如 `cmake`。self.dependencies.direct_build:将过滤掉build=False或direct=False的 requires。self.dependencies.test:将过滤掉build=True或test=False的 requires,仅留下主机上下文中的测试要求,例如 `gtest`。
它们可以以相同的方式使用:
requires = "zlib/1.3.1", "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` 接口被构建系统大量用于访问数据。此对象定义全局和每个组件的属性,用于访问包含文件夹等信息:
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`)都是绝对路径,无论依赖项是在缓存中还是可编辑包(editable package)中,都可以正常工作。
另请参阅
CppInfo 模型。