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
文件,其中可以包含用于build()
方法的 autotools 等工具链的信息。特定构建系统文件,例如
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”的不同 include 目录、lib 目录可以通过以下方式实现:
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
属性,因为用户可能会直接在配方中使用它,或者间接地用于创建自定义构建集成和生成器。
Dependencies interface¶
可以使用以下语法访问当前配方的每个单独依赖项:
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:此依赖项的上下文(构建、主机)
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
。extension_properties:依赖项的
extension_properties
。应视为只读。
Iterating dependencies¶
可以以类似字典的方式迭代配方的所有依赖项。请注意,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
,例如cmake
,则为True
。require.test
:布尔值,如果它是在主机上下文中build_require
(使用self.test_requires()
定义),例如gtest
,则为True
。
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,仅留下主机上下文中的测试 requirement,例如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
Dependencies cpp_info
interface¶
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 模型。