本文档翻译自:https://docs.conan.io/en/latest/reference/conanfile/attributes.html
name
这是一个字符串,至少2个字符,最多50个字符(尽管建议使用较短的名称),用于定义程序包名称。 这将是 <PkgName>/version@user/channel
。 它应与以下正则表达式 ^[a-zA-Z0-9_][a-zA-Z0-9_\+\.-]{1,50}$
匹配,因此以字母数字或下划线开头,然后是字母数字,下划线,+,。,- 字符。
如果未在命令行中定义配方,则仅将配方export
到本地缓存(export
和create
命令)时才需要该名称。 它可能从环境变量甚至是定义它的任何python代码中获取其值(例如,从磁盘读取环境变量的函数或文件)。 但是,最常见和建议的方法是以纯文本形式将其定义为常量,或将其提供为命令行参数。
version
版本属性将定义包引用的版本部分:PkgName/<version>@user/channel
这是一个字符串,可以采用任何值,与name
属性匹配相同的约束。如果版本遵循 X.Y.Z-pre1+build2
形式的语义版本控制,则该值可用于通过版本范围而不是确切版本来要求此软件包。
如果未在命令行中定义配方,则仅对于将配方export
到本地缓存(export
和create
命令)严格来说是必需的。它可能从环境变量甚至是定义它的任何python代码中获取其值(例如,从磁盘读取环境变量的函数或文件)。请注意,此值可能在其他地方的配方中使用(如在source()
方法中从其他位置检索代码的方法),使该值不恒定意味着它可能在不同的上下文中(例如,在不同的机器上或针对不同的用户)导致不可重复或不可预测的结果。最常见和建议的方法是以纯文本形式将其定义为常量,或将其提供为命令行参数。
description
这是一个可选的但强烈建议使用的文本字段,其中包含包装的说明以及可能对消费者有用的任何信息。 第一行可能用作包装的简短说明。
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
description = """This is a Hello World library.
A fully featured, portable, C++ library to say Hello World in the stdout,
with incredible iostreams performance"""
homepage
使用此属性指示要打包的库的主页。 这对于将配方链接到库本身的进一步说明(例如其功能概述,文档,常见问题以及其他相关信息)很有用。
class EigenConan(ConanFile):
name = "eigen"
version = "3.3.4"
homepage = "http://eigen.tuxfamily.org"
url
如果要打包第三方库,则即使是典型的情况,也可能只是开发打包代码。 此类代码通常也会通过协作进行更改,因此应将其存储在git之类的VCS中,并可能放在GitHub或类似服务上。 如果确实要维护这样的存储库,请在url
属性中指出它,以便可以轻松找到它。
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
url = "https://github.com/conan-io/hello.git"
网址是软件包存储库的网址,即不一定是原始源代码。 它是可选的,但强烈建议它指向GitHub,Bitbucket或您首选的代码协作平台。 当然,如果库源代码中包含conanfile,则可以指向它,然后在source()
方法中使用url
。
这是推荐属性,但不是必需属性。
license
此字段用于目标源代码和二进制文件(即正在打包的代码)的许可证,而不是conanfile.py本身的许可证。 此信息通常由conan info命令以及其他搜索和报告工具显示。
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
license = "MIT"
此属性可以包含多个逗号分隔的许可证。 它是一个文本字符串,因此可以包含任何文本,包括指向其他地方的许可证文件的超链接。
但是,我们强烈建议开源项目的打包人员使用SPDX许可证列表)中的SPDX)标识符,而不要使用 free-formed 的文本。 这将帮助希望自动进行许可证兼容性检查的人员,例如您的软件包的使用者,或者您的软件包具有开放源代码依赖项。
这是推荐属性,但不是必需属性。
author
旨在添加有关作者的信息,以防与conan用户不同。 conan用户可能是组织,项目,公司或组的名称,并且许多用户对该帐户具有权限。 在这种情况下,作者信息可以明确定义谁是包的创建者/维护者。
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
author = "John J. Smith (john.smith@company.com)"
topics
主题提供了一种有用的方式来将相关标签分组在一起,并快速告诉开发人员软件包的含义。 主题还使客户更容易找到您的配方。 按主题过滤软件包或在Bintray软件包页面中重用它们可能很有用。topic
属性应该是一个包含所需主题的元组。
class ProtocInstallerConan(ConanFile):
name = "protoc_installer"
version = "0.1"
topics = ("protocol-buffers", "protocol-compiler", "serialization", "rpc")
user, channel
这些字段在conan中是可选的,它们对于从社区中识别出针对您公司的更改的分支配方很有用。 使用这些字段,您可以保持相同的名称和版本,并使用用户/渠道来消除配方的歧义。
这些字段的值可以在conanfile.py中访问:
from conans import ConanFile
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
def requirements(self):
self.requires("common-lib/version")
if self.user and self.channel:
# If the recipe is using them, I want to consume my fork.
self.requires("say/0.1@%s/%s" % (self.user, self.channel))
else:
# otherwise, I'll consume the community one
self.requires("say/0.1")
只有已经导出的软件包(本地缓存或远程服务器中的软件包)才能分配 user/channel 。 对于在用户空间中工作的软件包配方,默认情况下没有当前user/channel ,尽管可以在**conan install**
时使用以下命令定义它们:
$ conan install <path to conanfile.py> user/channel
:::success
Warning
现在已弃用了用于为这些字段分配值的环境变量CONAN_USERNAME和CONAN_CHANNEL,并将在Conan 2.0中将其删除。 不要使用它们来填充self.user和self.channel的值。
:::
default_user, default_channel
对于在用户空间中工作的软件包配方,请使用本地方法如 conan install .
和conan build .
,没有当前的user/channel。如果要在配方中访问self.user
或self.channel
,则需要声明环境变量CONAN_USERNAME
和CONAN_CHANNEL
,或者可以设置属性default_user
和default_channel
。 您也可以使用python @property
:
from conans import ConanFile
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
default_user = "myuser"
@property
def default_channel(self):
return "mydefaultchannel"
def requirements(self):
self.requires("pkg/0.1@%s/%s" % (self.user, self.channel))
settings
有几件事可能会影响正在创建的程序包,即如果某些输入不同,则最终程序包也会有所不同(例如,不同的二进制文件)。
开发项目范围的变量,例如编译器,其版本或OS本身。 必须定义这些变量,并且它们不能在conanfile中列出默认值,因为这没有意义。
很明显,在大多数情况下,更改操作系统会生成不同的二进制文件。 更改编译器或编译器版本也会更改二进制文件,二进制文件可能具有兼容的ABI,但在任何情况下软件包都将有所不同。
由于这些原因,conan配方中最常见的约定是通过以下四个设置来区分二进制文件,这反映在柯南新命令中使用的conanfile.py模板中:
settings = "os", "compiler", "build_type", "arch"
当conan为具有上述设置的给定组合的软件包生成已编译的二进制文件时,它会通过散列这些设置的当前值来为该二进制文件生成唯一的ID。
但是,例如仅标头库会发生什么呢? 这种库的最终软件包不是二进制的,并且在大多数情况下,它们是相同的,除非它是自动生成代码的。 我们可以在conanfile中指出:
from conans import ConanFile
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
# We can just omit the settings attribute too
settings = None
def build(self):
#empty too, nothing to build in header only
您还可以通过重新声明settings属性来限制现有设置和可接受的值:
class HelloConan(ConanFile):
settings = {"os": ["Windows"],
"compiler": {"Visual Studio": {"version": [11, 12]}},
"arch": None}
在此示例中,我们刚刚定义了该程序包仅在Windows 10,VS 10和11下运行。尝试在具有其他设置的其他平台上构建该程序包将引发错误。 我们还定义了运行时(VS的MD和MT标志)与我们无关(也许我们使用的是通用的?)。 使用无作为值表示保持原始值,以避免重新键入它们。 然后,” arch”:None完全等同于” arch”:[“ x86”,” x86_64”,” arm”]检查引用或您的〜/ .conan / settings.yml文件。
由于重新定义整个设置属性可能很乏味,因此有时在configure()
方法中删除或调整特定字段要简单得多。 例如,如果我们的程序包在VS中与运行时无关,则可以删除该设置字段:
settings = "os", "compiler", "build_type", "arch"
def configure(self):
self.settings.compiler["Visual Studio"].remove("runtime")
可以使用属性语法检查设置以实现条件逻辑:
def build(self):
if self.settings.os == "Windows" and self.settings.compiler.version == "15":
# do some special build commands
elif self.settings.arch == "x86_64":
# Other different commands
这些比较会进行内容检查,例如,如果您输入self.settings.os =="Windos"
这样的错字,Conan将会失败并告诉您这不是有效的settings.os
值,以及可能的值范围。
同样,如果您尝试访问不存在的某些设置(例如Visual Studio设置的self.settings.compiler.libcxx
),则conan 将无法告知该编译器不存在libcxx
。
如果要对设置值进行安全检查,可以使用get_safe()
方法:
def build(self):
# Will be None if doesn't exist
arch = self.settings.get_safe("arch")
# Will be None if doesn't exist
compiler_version = self.settings.get_safe("compiler.version")
# Will be the default version if the return is None
build_type = self.settings.get_safe("build_type", default="Release")
如果该设置或子设置不存在并且未分配默认值,则get_safe()
方法将返回None
。
options
当使用不同的设置时,conan软件包的配方可以生成不同的二进制软件包,但也可以自定义每个软件包的配置,从而生成不同的二进制文件。
一个特定的库通常是共享的或静态的。 请注意,这是可选的,不同的软件包可以具有此选项,也可以不具有(例如,仅标头的软件包),并且不同的软件包可以对此选项具有不同的值,而设置则通常与所有安装的软件包具有相同的值 (尽管也可以控制,为特定的程序包定义不同的设置)
选项在软件包配方中定义为名称和允许值的字典:
class MyPkg(ConanFile):
...
options = {"shared": [True, False]}
选项被定义为ConanFile内的python字典,其中每个键必须是带有选项标识符的字符串,并且值是包含所有可能选项值的列表:
class MyPkg(ConanFile):
...
options = {"shared": [True, False],
"option1": ["value1", "value2"],}
可以输入每个选项的值或纯字符串,对于可以采用任何值的选项,都有一个特殊值ANY。
如果使用者(通过命令行使用配方,项目,配置文件或用户)未定义选项,则属性default_options的目的是定义选项的默认值。 值得注意的是,未初始化的选项将获得值None,并且如果其包含在有效值列表中,则它将是有效值。 该属性也应定义为python字典,尽管由于遗留原因其他定义也可能有效。
class MyPkg(ConanFile):
...
options = {"shared": [True, False],
"option1": ["value1", "value2"],
"option2": "ANY"}
default_options = {"shared": True,
"option1": "value1",
"option2": 42}
def build(self):
shared = "-DBUILD_SHARED_LIBS=ON" if self.options.shared else ""
cmake = CMake(self)
self.run("cmake . %s %s" % (cmake.command_line, shared))
...
如前所述,可以使用不同的方式来定义配方中选项的值,让我们遍历上面定义的示例配方mypkg的所有选项:
- 在配方本身中使用属性default_options。
- 在需要使用此配方的配方的default_options中:此处定义的值将覆盖配方中的默认值。
当然,这将与使用conanfile.txt的方式相同: ```python [requires] mypkg/0.1@user/channelclass OtherPkg(ConanFile):
requires = "mypkg/0.1@user/channel"
default_options = {"mypkg:shared": False}
[options] mypkg:shared=False
- 也可以使用配置文件为配方的选项定义默认值。 只要使用该配方,它们就会适用:
```python
# file "myprofile"
# use it as $ conan install -pr=myprofile
[settings]
setting=value
[options]
MyPkg:shared=False
定义选项值(所有选项的优先级最高)的最后一种方法是在命令行中使用命令参数-o
传递这些值:
$ conan install . -o MyPkg:shared=True -o OtherPkg:option=value
options的值也可以在configure()
和config_options()
方法中有条件地赋值(甚至删除),相应的部分有记录这些用例的示例。 但是,有条件地将值分配给选项可能会有其缺点,如母版部分中所述。
一个重要的注意事项是如何评估这些选项的值以及我们可以在Python中实现的不同条件的行为。 如前所述,可以在Python代码中(将字典分配给default_options)或通过字符串(使用conanfile.txt,配置文件或通过命令行)来定义选项的值。 为了提供一致的实施,请考虑以下注意事项:
类型值和字符串的求值相同,因此所有这些输入的行为都相同:
default_options = {"shared": True, "option": None}
default_options = {"shared": "True", "option": "None"}
mypkg:shared=True
,mypkg:option = None
在配置文件,命令行或conanfile.txt上均不存在
隐式转换为boolean不区分大小写,因此表达式
bool(self.options.option)
:- 对于值True,” True”和” true”以及在Python代码中将以相同方式求值的任何其他值,等于True。
- 对于值False,” False”和” false”,空字符串以及预期的0和” 0”,等于False。
使用is进行比较始终等于False,因为选项类型封装在Conan类中时,类型会有所不同。
- 与==符号的显式比较区分大小写,因此:
self.options.option = "False"
满足assert self.options.option == False
,assert self.options.option == "False"
,但assert self.options.option != "false"
。
- 不同的行为具有
self.options.option = None
,因为assert self.options.option!= None
。
如果要对选项值进行安全检查,则可以使用get_safe()
方法:
def build(self):
# Will be None if doesn't exist
fpic = self.options.get_safe("fPIC")
# Will be the default version if the return is None
shared = self.options.get_safe("shared", default=False)
如果该选项不存在并且未分配默认值,则get_safe()
方法将返回None
。