自定义命令:清理旧的配方和包修订版本¶
注意
这主要是一个示例命令。内置的 conan remove *#!latest
语法,表示“除最新版本外的所有版本”,可能已经足够满足此用例,而无需此自定义命令。
警告
使用此命令需要 Conan 2.21.0 或更高版本。
请首先克隆源代码以重新创建此项目。您可以在 GitHub 的 examples2 仓库中找到它们。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/examples/extensions/commands/clean
在此示例中,我们将了解如何创建/使用自定义命令:conan clean。它会从本地缓存或远程仓库中删除每个配方及其包修订版本,但保留最新的配方版本中的最新包修订版本。
注意
为了更好地理解此示例,强烈建议您先阅读 自定义命令参考。
定位命令¶
将命令文件 cmd_clean.py
复制到您的 [YOUR_CONAN_HOME]/extensions/commands/
文件夹(如果不存在则创建)。如果您不知道 [YOUR_CONAN_HOME]
的位置,可以运行 conan config home 进行查看。
运行它¶
现在,您应该能在命令提示符中看到新命令。
$ conan -h
...
Custom commands
clean Deletes (from local cache or remotes) all recipe and package revisions but the
latest package revision from the latest recipe revision.
$ conan clean -h
usage: conan clean [-h] [-r REMOTE] [--force]
Deletes (from local cache or remotes) all recipe and package revisions but
the latest package revision from the latest recipe revision.
optional arguments:
-h, --help show this help message and exit
-r REMOTE, --remote REMOTE
Will remove from the specified remote
--force Remove without requesting a confirmation
最后,如果您执行 conan clean
$ conan clean
Found 4 pkg/version recipes matching */* in local cache
Do you want to remove all the recipes revisions and their packages ones, except the latest package revision from the latest recipe one? (yes/no): yes
Keeping recipe revision: other/1.0#31da245c3399e4124e39bd4f77b5261f and its latest package revisions [Local cache]
Removed package revision: other/1.0#31da245c3399e4124e39bd4f77b5261f:da39a3ee5e6b4b0d3255bfef95601890afd80709#a16985deb2e1aa73a8480faad22b722c [Local cache]
Removed recipe revision: other/1.0#721995a35b1a8d840ce634ea1ac71161 and all its package revisions [Local cache]
Keeping recipe revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0 and its latest package revisions [Local cache]
Removed package revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0:da39a3ee5e6b4b0d3255bfef95601890afd80709#cee90a74944125e7e9b4f74210bfec3f [Local cache]
Removed package revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0:da39a3ee5e6b4b0d3255bfef95601890afd80709#7cddd50952de9935d6c3b5b676a34c48 [Local cache]
Keeping recipe revision: libcxx/0.1#abcdef1234567890abcdef1234567890 and its latest package revisions [Local cache]
如果再次运行,应该不会发生任何事情。
$ conan clean
Do you want to remove all the recipes revisions and their packages ones, except the latest package revision from the latest recipe one? (yes/no): yes
Keeping recipe revision: other/1.0#31da245c3399e4124e39bd4f77b5261f and its latest package revisions [Local cache]
Keeping recipe revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0 and its latest package revisions [Local cache]
Keeping recipe revision: libcxx/0.1#abcdef1234567890abcdef1234567890 and its latest package revisions [Local cache]
代码解析¶
命令 conan clean
具有以下代码:
from conan.api.conan_api import ConanAPI
from conan.api.model import PackagesList, ListPattern
from conan.api.input import UserInput
from conan.api.output import ConanOutput, Color
from conan.cli.command import OnceArgument, conan_command
recipe_color = Color.BRIGHT_BLUE
removed_color = Color.BRIGHT_YELLOW
@conan_command(group="Custom commands")
def clean(conan_api: ConanAPI, parser, *args):
"""
Deletes (from local cache or remotes) all recipe and package revisions but
the latest package revision from the latest recipe revision.
"""
parser.add_argument('-r', '--remote', action=OnceArgument,
help='Will remove from the specified remote')
parser.add_argument('--force', default=False, action='store_true',
help='Remove without requesting a confirmation')
args = parser.parse_args(*args)
def confirmation(message):
return args.force or ui.request_boolean(message)
ui = UserInput(non_interactive=False)
out = ConanOutput()
remote = conan_api.remotes.get(args.remote) if args.remote else None
output_remote = remote or "Local cache"
# List all recipes revisions and all their packages revisions as well
pkg_list = conan_api.list.select(ListPattern("*/*#*:*#*", rrev=None, prev=None), remote=remote)
if pkg_list and not confirmation("Do you want to remove all the recipes revisions and their packages ones, "
"except the latest package revision from the latest recipe one?"):
out.writeln("Aborted")
return
# Split the package list into based on their recipe reference
for sub_pkg_list in pkg_list.split():
latest = max(sub_pkg_list.items(), key=lambda item: item[0])[0]
out.writeln(f"Keeping recipe revision: {latest.repr_notime()} "
f"and its latest package revisions [{output_remote}]", fg=recipe_color)
for rref, packages in sub_pkg_list.items():
# For the latest recipe revision, keep the latest package revision only
if latest == rref:
# Get the latest package timestamp for each package_id
latest_pref_list = [max([p for p in packages if p.package_id == pkg_id], key=lambda p: p.timestamp)
for pkg_id in {p.package_id for p in packages}]
for pref in packages:
if pref not in latest_pref_list:
conan_api.remove.package(pref, remote=remote)
out.writeln(f"Removed package revision: {pref.repr_notime()} [{output_remote}]", fg=removed_color)
else:
# Otherwise, remove all outdated recipe revisions and their packages
conan_api.remove.recipe(rref, remote=remote)
out.writeln(f"Removed recipe revision: {rref.repr_notime()} "
f"and all its package revisions [{output_remote}]", fg=removed_color)
让我们分析最重要的部分。
解析器¶
参数 parser
是 Python 命令行解析库 argparse.ArgumentParser
的一个实例,如果您想了解更多关于其 API 的信息,请访问 其官方网站。
用户输出¶
ConanOutput()
:用于管理用户输出的类。在此示例中,我们仅使用 out.writeln(message, fg=None, bg=None)
,其中 fg
是字体前景色,bg
是字体背景色。此外,您还拥有一些预定义的方法,如 out.info()
、out.success()
、out.error()
等。
Conan 公共 API¶
警告
此功能是实验性的,可能会发生破坏性更改。有关更多信息,请参阅 Conan 稳定性 部分。
此示例中最重要的一部分是通过 conan_api
参数使用 Conan API。以下是一些在此自定义命令中使用的示例:
conan_api.remotes.get(args.remote)
conan_api.list.select(ListPattern("*/*#*:*#*", rrev=None, prev=None), remote=remote)
conan_api.remove.recipe(rrev, remote=remote)
conan_api.remove.package(prev, remote=remote)
conan_api.remotes.get(...)
:[RemotesAPI]
根据远程名称返回一个 RemoteRegistry。conan_api.list.select(...)
:[ListAPI]
返回一个包含所有与给定模式匹配的配方的列表。conan_api.remove.recipe(...)
:[RemoveAPI]
删除给定的配方修订版本及其所有包修订版本。conan_api.remove.package(...)
:[RemoveAPI]
删除给定的包修订版本。
此外,以下几行值得特别关注:
for sub_pkg_list in pkg_list.split():
latest = max(sub_pkg_list.items(), key=lambda item: item[0])[0]
...
latest_pref_list = [max([p for p in packages if p.package_id == pkg_id], key=lambda p: p.timestamp)
for pkg_id in {p.package_id for p in packages}]
基本上,pkg_list.split()
为相同的配方引用返回一个列表。然后,sub_pkg_list.items()
返回一个元组列表 (Recipe Reference, Packages References)
,因此最后,使用 max(..., key=...)
根据时间戳获取最新的配方引用。之后,创建 latest_pref_list
以仅保留每个包 ID 的最新包修订版本。它会遍历包 ID 集合以根据时间戳获取最新的包修订版本。
如果您想了解更多关于 Conan API 的信息,请访问 ConanAPI 部分。