自定义命令:清理旧的配方和包版本

注意

这主要是一个示例命令。内置的 conan remove *#!latest 语法,表示“除最新版本外的所有版本”,可能足以满足此用例,无需此自定义命令。

请先克隆源代码以重新创建此项目。您可以在 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
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
other/1.0
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]
hello/1.0
Removed package revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0:da39a3ee5e6b4b0d3255bfef95601890afd80709#cee90a74944125e7e9b4f74210bfec3f [Local cache]
Removed package revision: hello/1.0#9a77cdcff3a539b5b077dd811b2ae3b0:da39a3ee5e6b4b0d3255bfef95601890afd80709#7cddd50952de9935d6c3b5b676a34c48 [Local cache]
libcxx/0.1

如果您再次运行它,则不应该发生任何事情。

$ 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
other/1.0
hello/1.0
libcxx/0.1

代码导览

conan clean 命令具有以下代码

cmd_clean.py
from conan.api.conan_api import ConanAPI
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')
    args = parser.parse_args(*args)

    out = ConanOutput()
    remote = conan_api.remotes.get(args.remote) if args.remote else None
    output_remote = remote or "Local cache"

    # Getting all the recipes
    recipes = conan_api.search.recipes("*/*", remote=remote)
    for recipe in recipes:
        out.writeln(f"{str(recipe)}", fg=recipe_color)
        all_rrevs = conan_api.list.recipe_revisions(recipe, remote=remote)
        latest_rrev = all_rrevs[0] if all_rrevs else None
        for rrev in all_rrevs:
            if rrev != latest_rrev:
                conan_api.remove.recipe(rrev, remote=remote)
                out.writeln(f"Removed recipe revision: {rrev.repr_notime()} "
                            f"and all its package revisions [{output_remote}]", fg=removed_color)
            else:
                packages = conan_api.list.packages_configurations(rrev, remote=remote)
                for package_ref in packages:
                    all_prevs = conan_api.list.package_revisions(package_ref, remote=remote)
                    latest_prev = all_prevs[0] if all_prevs else None
                    for prev in all_prevs:
                    if prev != latest_prev:
                        conan_api.remove.package(prev, remote=remote)
                        out.writeln(f"Removed package revision: {prev.repr_notime()} [{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.search.recipes("*/*", remote=remote)
conan_api.list.recipe_revisions(recipe, remote=remote)
conan_api.remove.recipe(rrev, remote=remote)
conan_api.list.packages_configurations(rrev, remote=remote)
conan_api.list.package_revisions(package_ref, remote=remote)
conan_api.remove.package(prev, remote=remote)
  • conan_api.remotes.get(...)[RemotesAPI] 给定远程名称,返回一个 RemoteRegistry。

  • conan_api.search.recipes(...)[SearchAPI] 返回一个包含所有与给定模式匹配的配方的列表。

  • conan_api.list.recipe_revisions(...)[ListAPI] 返回一个包含给定配方引用所有配方版本的列表。

  • conan_api.list.packages_configurations(...)[ListAPI] 返回给定配方版本的不同配置(package_id)的列表。

  • conan_api.list.package_revisions(...)[ListAPI] 返回给定配方版本的所有包版本的列表。

  • conan_api.remove.recipe(...)[RemoveAPI] 删除给定的配方版本。

  • conan_api.remove.package(...)[RemoveAPI] 删除给定的包版本。

除此之外,以下几行代码值得特别注意

all_rrevs = conan_api.list.recipe_revisions(recipe, remote=remote)
latest_rrev = all_rrevs[0] if all_rrevs else None

...

packages = conan_api.list.packages_configurations(rrev, remote=remote)

...

all_prevs = conan_api.list.package_revisions(package_ref, remote=remote)
latest_prev = all_prevs[0] if all_prevs else None

基本上,这些 API 调用分别返回一个配方版本和包版本的列表,但我们保存第一个元素作为最新版本,因为这些调用始终获取一个有序列表。

如果您想了解更多关于 Conan API 的信息,请访问 ConanAPI 部分