管理软件包元数据文件¶
警告
此功能为实验性功能,可能会有破坏性更改。更多信息请参阅Conan 稳定性部分。
Conan软件包通常由几个C和C++构件、头文件、已编译的库和可执行文件组成。但还有一些文件对于正常使用此类软件包可能不是必需的,但出于合规、技术或业务原因可能非常重要,例如:
完整的构建日志
测试可执行文件
运行测试套件产生的测试结果
调试构件,如大的.pdb文件
覆盖率、 sanitizers 或其他源代码或二进制分析工具的结果
关于构建、确切机器、环境、作者、CI数据的上下文和元数据
其他合规和安全相关文件
存储和跟踪这些文件有几个重要原因,如法规、合规、安全、可复现性和可追溯性。这些文件的难点在于它们可能很大/很重,如果我们把它们存储在包内(只是在 package()
方法中复制构件),这将使包的体积大大增加,并且会影响下载、解压和使用包的速度。这种情况经常发生,无论是在开发者的机器上还是在 CI 中,都会影响开发者体验和基础设施成本。此外,软件包是不可变的,也就是说,一旦创建了软件包,就不应该修改它。如果我们想在软件包创建后,甚至在上传后添加额外的元数据文件,这可能会成为一个问题。
元数据文件 功能允许以集成和统一的方式创建、上传、追加和存储与软件包关联的元数据,同时避免对开发者和 CI 的速度和成本产生影响,因为默认情况下,在软件包使用时不会下载和解压元数据文件。
重要的是要强调有两种元数据:
配方元数据,与
conanfile.py
配方相关联,元数据应与从该配方创建的所有二进制文件(包名、版本和配方修订版)通用。这种元数据可能不那么常见,但例如对源代码进行某些扫描的结果,对于所有配置和构建都是通用的,可以是配方元数据。软件包二进制元数据,与给定特定配置的软件包二进制文件相关联,并由
package_id
表示。构建日志、测试报告等,特定于二进制配置的将是软件包元数据。
在配方中创建元数据¶
配方可以在其方法中直接定义元数据。一个常见的用例是存储日志。使用 self.recipe_metadata_folder
和 self.package_metadata_folder
,配方可以将文件存储在这些位置。
import os
from conan import ConanFile
from conan.tools.files import save, copy
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
def layout(self):
# Or something else, like the "cmake_layout(self)" built-in layout
self.folders.build = "mybuild"
self.folders.generators = "mybuild/generators"
def export(self):
# logs that might be generated in the recipe folder at "export" time.
# these would be associated with the recipe repo and original source of the recipe repo
copy(self, "*.log", src=self.recipe_folder,
dst=os.path.join(self.recipe_metadata_folder, "logs"))
def source(self):
# logs originated in the source() step, for example downloading files, patches or other stuff
save(self, os.path.join(self.recipe_metadata_folder, "logs", "src.log"), "srclog!!")
def build(self):
# logs originated at build() step, the most common ones
save(self, "mylogs.txt", "some logs!!!")
copy(self, "mylogs.txt", src=self.build_folder,
dst=os.path.join(self.package_metadata_folder, "logs"))
请注意,“配方”方法(那些对所有二进制文件通用的方法,如 export()
和 source()
)应该使用 self.recipe_metadata_folder
,而“包”特定的方法(build()
、package()
)应该使用 self.package_metadata_folder
。
对该配方执行 conan create
,将在 Conan 缓存中创建“metadata”文件夹。我们可以通过以下方式查看这些文件夹:
$ conan create .
$ conan cache path pkg/0.1 --folder=metadata
# folder containing the recipe metadata
$ conan cache path pkg/0.1:package_id --folder=metadata
# folder containing the specific "package_id" binary metadata
也可以使用“本地流程”命令并获取本地“metadata”文件夹。如果我们想这样做,强烈建议使用 layout()
方法,如上所示,以避免弄乱当前文件夹。然后,本地命令将允许测试和调试该功能。
$ conan source .
# check local metadata/logs/src.log file
$ conan build .
# check local mybuild/metadata/logs/mylogs.txt file
注意:请注意,本地创建的元数据不会在 conan export-pkg
命令期间导出到 Conan 缓存。一些元数据,如在 export()
方法中生成的元数据,可以在缓存中生成,因为 conan export-pkg
命令会调用该方法,但是“build”文件夹内的元数据不会被导出。如果你想将该元数据添加到导出的包中,你可以在 conan export-pkg
之后,使用 conan cache path
报告的路径来复制它,如下面“使用命令添加元数据”部分所述。
使用钩子创建元数据¶
如果跨配方存在一些通用元数据,则可以在不修改配方的情况下使用钩子捕获它。假设我们有一个更简单的配方:
import os
from conan import ConanFile
from conan.tools.files import save, copy
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
no_copy_source = True
def layout(self):
self.folders.build = "mybuild"
self.folders.generators = "mybuild/generators"
def source(self):
save(self, "logs/src.log", "srclog!!")
def build(self):
save(self, "logs/mylogs.txt", "some logs!!!")
如我们所见,这根本没有使用元数据文件夹。现在定义以下钩子:
import os
from conan.tools.files import copy
def post_export(conanfile):
conanfile.output.info("post_export")
copy(conanfile, "*.log", src=conanfile.recipe_folder,
dst=os.path.join(conanfile.recipe_metadata_folder, "logs"))
def post_source(conanfile):
conanfile.output.info("post_source")
copy(conanfile, "*", src=os.path.join(conanfile.source_folder, "logs"),
dst=os.path.join(conanfile.recipe_metadata_folder, "logs"))
def post_build(conanfile):
conanfile.output.info("post_build")
copy(conanfile, "*", src=os.path.join(conanfile.build_folder, "logs"),
dst=os.path.join(conanfile.package_metadata_folder, "logs"))
这些钩子的使用效果与配方内方法非常相似:在 conan create
执行时,元数据文件将在缓存中创建,并且对于 conan source
和 conan build
本地流程,也会在本地创建。
使用命令添加元数据¶
元数据文件可以在软件包创建后添加或修改。要实现这一点,使用 conan cache path
命令将返回用于执行该操作的文件夹,因此复制、创建或修改该位置的文件将实现此目的。
$ conan create . --name=pkg --version=0.1
$ conan cache path pkg/0.1 --folder=metadata
# folder to put the metadata, initially empty if we didn't use hooks
# and the recipe didn't store any metadata. We can copy and put files
# in the folder
$ conan cache path pkg/0.1:package_id --folder=metadata
# same as above, for the package metadata, we can copy and put files in
# the returned folder
此元数据在 Conan 缓存中本地添加。如果你想更新服务器元数据,需要从缓存中上传。
上传元数据¶
到目前为止,元数据已在本地创建,并存储在 Conan 缓存中。将元数据上传到服务器与现有的 conan upload
命令集成在一起。
$ conan upload "*" -c -r=default
# Uploads recipes, packages and metadata to the "default" remote
...
pkg/0.1: Recipe metadata: 1 files
pkg/0.1:da39a3ee5e6b4b0d3255bfef95601890afd80709: Package metadata: 1 files
默认情况下,当配方或软件包上传到服务器时,conan upload
将上传配方和软件包元数据。但有些情况下 Conan 会完全避免上传,如果它检测到修订版已存在于服务器上,则不会上传配方或软件包。如果元数据已在本地修改或添加了新文件,我们可以使用以下命令显式强制上传:
# We added some metadata to the packages in the cache
# But those packages already exist in the server
$ conan upload "*" -c -r=default --metadata="*"
...
pkg/0.1: Recipe metadata: 1 files
pkg/0.1:da39a3ee5e6b4b0d3255bfef95601890afd80709: Package metadata: 1 files
--metadata
参数允许指定我们正在上传的元数据文件。如果我们将它们组织在文件夹中,我们可以指定 --metadata="logs*"
来仅上传日志元数据,而不上传其他可能的元数据,如 test
元数据。
# Upload only the logs metadata of the zlib/1.2.13 binaries
# This will upload the logs even if zlib/1.2.13 is already in the server
$ conan upload "zlib/1.2.13:*" -r=remote -c --metadata="logs/*"
# Multiple patterns are allowed:
$ conan upload "*" -r=remote -c --metadata="logs/*" --metadata="tests/*"
有时,即使元数据缓存文件夹包含文件,也可能希望在不上传元数据的情况下上传软件包。要忽略上传元数据,请使用空的元数据模式参数:
# Upload only the packages, not the metadata
$ conan upload "*" -r=remote -c --metadata=""
--metadata=""
与 --metadata="*"
混合使用是不允许的,并且会引发错误。
# Invalid command, it will raise an error
$ conan upload "*" -r=remote -c --metadata="" --metadata="logs/*"
ERROR: Empty string and patterns can not be mixed for metadata.
下载元数据¶
如上所述,元数据默认不会被下载。当使用 conan install
或 conan create
从服务器获取依赖项时,不会下载这些服务器的元数据。
从服务器恢复元数据的方法是使用 conan download
命令显式指定:
# Get the metadata of the "pkg/0.1" package
$ conan download pkg/0.1 -r=default --metadata="*"
...
$ conan cache path pkg/0.1 --folder=metadata
# Inspect the recipe metadata in the returned folder
$ conan cache path pkg/0.1:package_id --folder=metadata
# Inspect the package metadata for binary "package_id"
元数据的检索是通过 download
按包进行的。如果我们想下载整个依赖图的元数据,则需要使用“package-lists”:
$ conan install . --format=json -r=remote > graph.json
$ conan list --graph=graph.json --format=json > pkglist.json
# the list will contain the "remote" origin of downloaded packages
$ conan download --list=pkglist.json --metadata="*" -r=remote
请注意,“package-list”仅包含与“remote”来源下载的包相关的元数据。如果它们之前已在缓存中,那么它们将不会列在“remote”来源下,元数据也不会被下载。如果你想收集依赖项元数据,请记住在从服务器安装包时下载它。还有其他可能性,例如一个自定义命令,可以自动收集并从服务器下载依赖项元数据。
删除元数据¶
目前无法从服务器端使用 Conan 删除元数据,因为元数据是“累加的”,可以添加新数据,但不能删除它(否则,在不先下载所有先前元数据的情况下添加新元数据是不可能的,这可能非常低效且容易出错,特别是对可能的竞争条件敏感)。
从服务器端删除元数据的建议是使用服务器可能提供的工具、Web界面或API。
注意
最佳实践
元数据不应该对于使用包是必需的。应该可以在不下载其元数据的情况下使用配方和包。如果元数据对使用包是必需的,那么它就不是元数据,应该打包成头文件和二进制文件。
元数据读取访问不应该是频繁的操作,也不是开发者必须做的事情。元数据读取的目的是在特殊情况下,例如出于合规性原因需要恢复某些构建日志,或者可能需要一些测试可执行文件来进行调试或重新检查崩溃。
Conan 不会对元数据文件进行任何压缩或解压缩。如果有很多元数据文件,请考虑自己进行 zip 压缩,否则上传这么多文件可能会花费很长时间。如果你需要处理不同类型的元数据(日志、测试、报告),按类别 zip 压缩文件可能更好,以便能够使用
--metadata=xxx
参数进行过滤。
test_package 作为元数据¶
这是一个使用元数据的说明性示例,将完整的 test_package
文件夹作为元数据存储,以便以后恢复和执行它。请注意,这不一定适用于生产环境。
让我们从一个钩子开始,该钩子自动将 test_package
文件夹存储为配方元数据:
import os
from conan.tools.files import copy
def post_export(conanfile):
conanfile.output.info("Storing test_package")
folder = os.path.join(conanfile.recipe_folder, "test_package")
copy(conanfile, "*", src=folder,
dst=os.path.join(conanfile.recipe_metadata_folder, "test_package"))
请注意,此钩子没有考虑到 test_package
可能包含大量临时构建对象而变得混乱(应该在添加到元数据之前清理它),并且它没有检查 test_package
可能根本不存在而导致崩溃。
当创建并上传包时,它会将包含 test_package
的配方元数据上传到服务器。
$ conan create ...
$ conan upload "*" -c -r=default # uploads metadata
...
pkg/0.1: Recipe metadata: 1 files
让我们删除本地副本,并假设包已安装,但元数据未安装:
$ conan remove "*" -c # lets remove the local packages
$ conan install --requires=pkg/0.1 -r=default # this will not download metadata
在此阶段,如果已安装的包在我们的应用程序中出现故障,我们可以恢复 test_package
,下载它,并将其复制到当前文件夹:
$ conan download pkg/0.1 -r=default --metadata="test_package*"
$ conan cache path pkg/0.1 --folder=metadata
# copy the test_package folder from the cache, to the current folder
# like `cp -R ...`
# Execute the test_package
$ conan test metadata/test_package pkg/0.1
pkg/0.1 (test package): Running test()
另请参阅
待办:提供示例说明如何使用自定义部署器或命令收集完整依赖图的元数据。
这是一项实验性功能。我们期待听到您的反馈、用例和需求,以不断改进此功能。请在 Github issues 上报告。