Creating new targets
How to add a custom target type.
When to create a new target type?
Adding new target types is most helpful when you are adding support for a new language.
If you instead want to reduce boilerplate in BUILD files, such as changing default values, use macros .
If you are already using a target type, but need to store additional metadata for your plugin, add a new field to the target type.
Step 1: Define the target type
To define a new target:
- Subclass
pants.engine.target.Target
. - Define the class property
alias
. This is the symbol that people use in BUILD files. - Define the class property
core_fields
.
For core_fields
, we recommend including COMMON_TARGET_FIELDS
to add the useful tags
and description
fields. You also likely want to include Dependencies
and Sources
.
from pants.engine.target import (
COMMON_TARGET_FIELDS,
Dependencies,
Sources,
StringField,
Target,
)
class CustomField(StringField):
alias = "custom_field"
class CustomTarget(Target):
"""A custom target to demo the Target API.
This docstring will be used in the output of `<<pantscmd>> target-types`.
"""
alias = "custom_target"
core_fields = (*COMMON_TARGET_FIELDS, Dependencies, Sources, CustomField)
Tip: subclass the
Sources
fieldYou will usually want to subclass the
Sources
field to give custom functionality, such as setting a default value.You can also set the class property
expected_file_extensions
to enforce that only certain files are used, such asexpected_file_extensions = (".json", ".txt")
.
Using the fields of a pre-existing target type
Sometimes, you may want to create a new target type that behaves similarly to one that already exists, except for some small changes.
For example, you might like how
python_binary()
behaves in general, but you have a Django application and keep writingsources=["manage.py"]
. Normally, you should write a macro to set this default value; but, here, you also want to add new Django-specific fields, so you decide to create a new target type.Rather than subclassing the original target type, use this pattern:
from pants.backend.python.target_types import PythonBinary, PythonBinarySources from pants.engine.target import Target from pants.util.ordered_set import FrozenOrderedSet class DjangoManagePySources(PythonBinarySources): default = ("manage.py",) class DjangoManagePy(Target): alias = "django_manage_py" core_fields = ( *(FrozenOrderedSet(PythonBinary.core_fields) - {PythonBinarySources}), DjangoManagePySources, )
In this example, we register all of the fields of
PythonBinary
, except for the fieldPythonBinarySources
. We instead register our custom fieldDjangoManagePySources
.
Step 2: Register the target type in register.py
register.py
Now, in your register.py
, add the target type to the def target_types()
entry point.
from plugins.target_types import CustomTarget
def target_types():
return [CustomTarget]
You can confirm this works by running <<pantscmd>> target-types --details=custom_target
.
Have
--v1
activated? Write a V1 binding.If you still use V1, you must write a V1 binding for your new target type so that V1 code can parse your target.
Precisely, you must define a class that inherits
pants.build_graph.target.Target
, set its constructor to take all of that target's field as parameters, then register the target withdef build_file_aliases()
.For example, in your plugin's
register.py
file:from pants.build_graph.build_file_aliases import BuildFileAliases from pants.build_graph.target import Target as TargetV1 class LegacyCustomTarget(TargetV1): def __init__( self, sources=(), dependencies=(), custom_field=None, custom_field2=None, **kwargs, ): super().__init__(**kwargs) def build_file_aliases(): return BuildFileAliases(targets={"custom_target": LegacyCustomTarget})
Updated over 3 years ago