Pants构建系统教程:创建自定义目标与构建目标

Pants构建系统教程:创建自定义目标与构建目标

pants The Pants Build System pants 项目地址: https://gitcode.com/gh_mirrors/pa/pants

前言

Pants是一个现代化的构建系统,特别适合管理大型单体代码库(monorepo)。本教程将带领你了解如何在Pants中创建自定义构建目标(Goal)和自定义目标类型(Target),通过实现一个读取项目版本文件的功能来掌握Pants插件开发的基础知识。

准备工作

在开始之前,请确保:

  1. 已安装最新版本的Pants构建系统
  2. 项目中有一个包含版本号的VERSION文件(如1.2.3
  3. 了解Python基础语法

创建插件基础结构

首先,我们需要创建一个in-repo插件。在项目根目录下创建以下结构:

pants-plugins/
└── project_version/
    ├── __init__.py
    ├── target_types.py
    ├── rules.py
    └── register.py

pants.toml中配置插件路径:

pythonpath = ["%(buildroot)s/pants-plugins"]
backend_packages = [
  "pants.backend.python",
  "project_version",
]

定义自定义目标类型

在Pants中,目标(Target)是构建系统的基本单元。我们将创建一个表示版本文件的目标类型:

# target_types.py
from pants.engine.target import COMMON_TARGET_FIELDS, SingleSourceField, Target

class ProjectVersionTarget(Target):
    alias = "version_file"
    core_fields = (*COMMON_TARGET_FIELDS, SingleSourceField)
    help = "表示项目VERSION文件的目标类型"

这个目标类型包含:

  • alias: 目标在BUILD文件中的名称
  • core_fields: 包含标准字段和source字段
  • help: 帮助文档

注册目标类型

register.py中注册我们的目标类型:

from typing import Iterable
from pants.engine.target import Target
from project_version.target_types import ProjectVersionTarget

def target_types() -> Iterable[type[Target]]:
    return [ProjectVersionTarget]

现在可以运行pants help version_file查看目标帮助信息。

创建构建目标(Goal)

构建目标是Pants命令行接口的核心。我们将创建一个project-version目标来显示项目版本:

# rules.py
from pants.engine.goal import Goal, GoalSubsystem
from pants.engine.rules import collect_rules, goal_rule

class ProjectVersionSubsystem(GoalSubsystem):
    name = "project-version"
    help = "显示项目VERSION文件中的版本号"

class ProjectVersionGoal(Goal):
    subsystem_cls = ProjectVersionSubsystem
    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY

@goal_rule
async def goal_show_project_version() -> ProjectVersionGoal:
    return ProjectVersionGoal(exit_code=0)

def rules():
    return collect_rules()

更新register.py以包含规则:

from typing import Iterable
import project_version.rules as project_version_rules
from pants.engine.target import Target
from project_version.target_types import ProjectVersionTarget

def target_types() -> Iterable[type[Target]]:
    return [ProjectVersionTarget]

def rules():
    return [*project_version_rules.rules()]

现在可以运行pants project-version测试目标是否正常工作。

实现版本文件读取功能

我们将创建一个数据类来表示版本文件信息:

@dataclass(frozen=True)
class ProjectVersionFileView:
    path: str
    version: str

然后实现读取版本文件的规则:

@rule
async def get_project_version_file_view(
    target: ProjectVersionTarget,
) -> ProjectVersionFileView:
    sources = await Get(HydratedSources, HydrateSourcesRequest(target[SourcesField]))
    digest_contents = await Get(DigestContents, Digest, sources.snapshot.digest)
    file_content = digest_contents[0]
    return ProjectVersionFileView(
        path=file_content.path, 
        version=file_content.content.decode("utf-8").strip()
    )

更新目标规则以使用这个功能:

@goal_rule
async def goal_show_project_version(
    console: Console, targets: Targets
) -> ProjectVersionGoal:
    targets = [tgt for tgt in targets if tgt.alias == ProjectVersionTarget.alias]
    results = await MultiGet(
        Get(ProjectVersionFileView, ProjectVersionTarget, target) for target in targets
    )
    for result in results:
        console.print_stdout(str(result))
    return ProjectVersionGoal(exit_code=0)

完整实现

将所有部分组合起来,完整的插件实现如下:

# rules.py
from dataclasses import dataclass
from pants.engine.console import Console
from pants.engine.fs import DigestContents
from pants.engine.goal import Goal, GoalSubsystem
from pants.engine.internals.native_engine import Digest
from pants.engine.internals.selectors import Get, MultiGet
from pants.engine.rules import collect_rules, goal_rule, rule
from pants.engine.target import (HydratedSources, HydrateSourcesRequest,
                               SourcesField, Targets)
from project_version.target_types import ProjectVersionTarget

@dataclass(frozen=True)
class ProjectVersionFileView:
    path: str
    version: str

@rule
async def get_project_version_file_view(
    target: ProjectVersionTarget,
) -> ProjectVersionFileView:
    sources = await Get(HydratedSources, HydrateSourcesRequest(target[SourcesField]))
    digest_contents = await Get(DigestContents, Digest, sources.snapshot.digest)
    file_content = digest_contents[0]
    return ProjectVersionFileView(
        path=file_content.path, version=file_content.content.decode("utf-8").strip()
    )

class ProjectVersionSubsystem(GoalSubsystem):
    name = "project-version"
    help = "显示项目VERSION文件中的版本号"

class ProjectVersionGoal(Goal):
    subsystem_cls = ProjectVersionSubsystem
    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY

@goal_rule
async def goal_show_project_version(
    console: Console, targets: Targets
) -> ProjectVersionGoal:
    targets = [tgt for tgt in targets if tgt.alias == ProjectVersionTarget.alias]
    results = await MultiGet(
        Get(ProjectVersionFileView, ProjectVersionTarget, target) for target in targets
    )
    for result in results:
        console.print_stdout(str(result))
    return ProjectVersionGoal(exit_code=0)

def rules():
    return collect_rules()

使用插件

  1. 在BUILD文件中定义版本文件目标:
version_file(
    name="main-project-version",
    source="VERSION",
)
  1. 运行命令查看版本:
pants project-version myapp

输出示例:

ProjectVersionFileView(path='myapp/VERSION', version='0.0.1')

进阶思考

虽然我们已经实现了一个基本功能,但还可以进一步改进:

  1. 验证版本号格式是否符合语义化版本规范
  2. 支持JSON格式输出
  3. 集成到peek目标中显示版本信息
  4. 处理多个版本文件目标的情况

这些改进将让你的插件更加健壮和实用。

总结

通过本教程,你学会了:

  1. 如何在Pants中创建自定义目标类型
  2. 如何定义新的构建目标
  3. Pants规则系统的基本工作原理
  4. 如何读取和显示文件内容

这些知识为你进一步开发更复杂的Pants插件打下了坚实基础。

pants The Pants Build System pants 项目地址: https://gitcode.com/gh_mirrors/pa/pants

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦凡湛Sheila

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值