Pants构建系统教程:创建自定义目标与构建目标
pants The Pants Build System 项目地址: https://gitcode.com/gh_mirrors/pa/pants
前言
Pants是一个现代化的构建系统,特别适合管理大型单体代码库(monorepo)。本教程将带领你了解如何在Pants中创建自定义构建目标(Goal)和自定义目标类型(Target),通过实现一个读取项目版本文件的功能来掌握Pants插件开发的基础知识。
准备工作
在开始之前,请确保:
- 已安装最新版本的Pants构建系统
- 项目中有一个包含版本号的
VERSION
文件(如1.2.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()
使用插件
- 在BUILD文件中定义版本文件目标:
version_file(
name="main-project-version",
source="VERSION",
)
- 运行命令查看版本:
pants project-version myapp
输出示例:
ProjectVersionFileView(path='myapp/VERSION', version='0.0.1')
进阶思考
虽然我们已经实现了一个基本功能,但还可以进一步改进:
- 验证版本号格式是否符合语义化版本规范
- 支持JSON格式输出
- 集成到
peek
目标中显示版本信息 - 处理多个版本文件目标的情况
这些改进将让你的插件更加健壮和实用。
总结
通过本教程,你学会了:
- 如何在Pants中创建自定义目标类型
- 如何定义新的构建目标
- Pants规则系统的基本工作原理
- 如何读取和显示文件内容
这些知识为你进一步开发更复杂的Pants插件打下了坚实基础。
pants The Pants Build System 项目地址: https://gitcode.com/gh_mirrors/pa/pants
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考