Pants build system plugin for generating Scala code from Thrift IDL files using Spindle
npx @tessl/cli install tessl/pypi-pantsbuild-pants-contrib-spindle@1.0.0A Pants build system plugin that enables automatic generation of Scala libraries from Thrift IDL files using Foursquare's Spindle code generator. This plugin integrates Spindle's thrift-to-Scala compilation into the Pants build pipeline, providing seamless dependency management and incremental compilation for large-scale projects.
pip install pantsbuild.pants.contrib.spindlefrom pants.contrib.spindle.register import build_file_aliases, register_goals
from pants.contrib.spindle.targets.spindle_thrift_library import SpindleThriftLibrary
from pants.contrib.spindle.tasks.spindle_gen import SpindleGenThis plugin is typically registered automatically when installed. In Pants configuration:
# pants.ini or pants.toml
[DEFAULT]
# The plugin provides 'spindle_thrift_library' target type and 'spindle' task# In a BUILD file
spindle_thrift_library(
name='user-service-thrift',
sources=['user.thrift', 'profile.thrift'],
dependencies=[
'3rdparty:spindle-runtime',
],
)# Generate Scala code from Thrift files
pants gen.spindle src/main/thrift:user-service-thrift
# The generated Scala and Java files will be placed in the workspace
# under appropriate namespace directoriesnamespace java com.example.user
typedef string UserId
struct User {
1: required UserId id
2: required string name
3: optional string email
}
service UserService {
User getUser(1: UserId userId)
list<User> listUsers()
}Registration functions that integrate the plugin with the Pants build system.
def build_file_aliases():
"""
Returns build file aliases for the spindle plugin.
Returns:
BuildFileAliases: Aliases containing the 'spindle_thrift_library' target type
"""
def register_goals():
"""
Registers the 'spindle' task under the 'gen' goal.
Installs the SpindleGen task to be executed when 'gen.spindle' is invoked.
"""Target type for defining Thrift libraries that will be compiled to Scala using Spindle.
class SpindleThriftLibrary(ExportableJvmLibrary):
"""
A Scala library generated from Spindle IDL files.
This target type represents Thrift files that should be compiled to Scala
using the Spindle code generator. Inherits from ExportableJvmLibrary to
provide JVM library capabilities.
"""
def __init__(self, *args, **kwargs):
"""
Initialize a SpindleThriftLibrary target.
Automatically adds 'scala', 'codegen', and 'synthetic' labels to the target.
Args:
*args: Variable arguments passed to parent ExportableJvmLibrary
**kwargs: Keyword arguments passed to parent ExportableJvmLibrary
"""The main task that performs Thrift-to-Scala code generation using Spindle.
class SpindleGen(NailgunTask):
"""
Task that generates Scala code from Thrift files using Spindle.
This task processes SpindleThriftLibrary targets, executes the Spindle
code generator, and creates synthetic Scala and Java library targets
with the generated code.
"""
@classmethod
def product_types(cls):
"""
Returns the product types produced by this task.
Returns:
list: ['scala'] - indicates this task produces Scala code
"""
@classmethod
def register_options(cls, register):
"""
Register command-line options for the SpindleGen task.
Args:
register: Option registration function
Registers:
--runtime-dependency: Runtime dependencies for generated code
--spindle-codegen tool: Spindle code generator tool configuration
"""
@property
def spindle_classpath(self):
"""
Get the classpath for the Spindle code generator tool.
Returns:
list: Classpath entries for spindle-codegen tool
"""
@property
def synthetic_target_extra_dependencies(self):
"""
Get additional dependencies for synthetic targets.
Returns:
set: Set of dependency targets from runtime-dependency option
"""
@property
def namespace_out(self):
"""
Get the output directory for generated code.
Returns:
str: Path to output directory (workdir/src/jvm)
"""
def codegen_targets(self):
"""
Get all targets that need code generation.
Returns:
list: List of SpindleThriftLibrary targets
"""
def sources_generated_by_target(self, target):
"""
Calculate the source files that will be generated for a target.
Args:
target: SpindleThriftLibrary target
Returns:
list: List of paths to generated source files
"""
def execute_codegen(self, targets):
"""
Execute the Spindle code generation process.
Args:
targets: List of SpindleThriftLibrary targets to process
Raises:
TaskError: If code generation fails
"""
def execute(self):
"""
Main execution method for the task.
Orchestrates the entire code generation process:
1. Identifies targets needing generation
2. Executes code generation for invalid targets
3. Creates synthetic Scala and Java library targets
4. Manages dependencies and build graph updates
5. Updates artifact cache if enabled
"""Helper functions for calculating generated file paths and parsing Thrift files.
def calculate_genfiles(source):
"""
Calculate which files will be generated from a Thrift source file.
Parses the Thrift file to extract namespace information and determines
the output file paths that Spindle will generate.
Args:
source (str): Path to Thrift source file relative to build root
Returns:
list: List of generated file basenames (without .scala/.java extensions)
Raises:
TaskError: If no Java namespace is found in the source file
"""
def calculate_scala_record_genfiles(namespace, source):
"""
Calculate generated file basenames for Scala records.
Args:
namespace (str): Java namespace from Thrift file
source (str): Path to source Thrift file
Returns:
list: List of generated file paths (basenames, add .scala/.java for full path)
"""BuildFileAliases:
"""
Pants build system type for registering target aliases.
Contains mappings from target names to target classes.
"""
TaskRegistrar:
"""
Pants build system type for registering tasks under goals.
Provides the task() method for goal registration.
"""
ExportableJvmLibrary:
"""
Base class for JVM library targets that can be exported.
Provides standard JVM library functionality and export capabilities.
"""
NailgunTask:
"""
Base class for tasks that execute JVM tools via Nailgun.
Provides JVM tool execution and classpath management.
"""JarDependency:
"""
Represents a JAR dependency with org, name, and revision.
Used to specify the Spindle codegen tool dependency.
"""
# Default tool configuration
SPINDLE_CODEGEN_DEFAULT = JarDependency(
org='com.foursquare',
name='spindle-codegen-binary_2.10',
rev='3.0.0-M7'
)The plugin raises TaskError exceptions in the following cases:
['3rdparty:spindle-runtime']3.0.0-M7 (com.foursquare:spindle-codegen-binary_2.10)scala/record.sspjavagen/record.ssp# Configure runtime dependencies
pants gen.spindle --runtime-dependency=path/to:runtime-dep
# Configure JVM options for code generation
pants gen.spindle --jvm-options="-Xmx2g -XX:MaxPermSize=256m"The plugin generates:
.scala files containing Scala case classes and companion objects.java files with Java representations for interoperabilityGenerated files follow the pattern:
com/example/user.thrift with namespace java com.example.usersrc/jvm/com/example/user/user.scala and src/jvm/com/example/user/java_user.java