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.00
# Pantsbuild Pants Contrib Spindle
1
2
A 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.
3
4
## Package Information
5
6
- **Package Name**: pantsbuild.pants.contrib.spindle
7
- **Language**: Python
8
- **Installation**: `pip install pantsbuild.pants.contrib.spindle`
9
- **Plugin Type**: Pants build system contrib plugin
10
11
## Core Imports
12
13
```python
14
from pants.contrib.spindle.register import build_file_aliases, register_goals
15
from pants.contrib.spindle.targets.spindle_thrift_library import SpindleThriftLibrary
16
from pants.contrib.spindle.tasks.spindle_gen import SpindleGen
17
```
18
19
## Basic Usage
20
21
### Plugin Registration
22
23
This plugin is typically registered automatically when installed. In Pants configuration:
24
25
```python
26
# pants.ini or pants.toml
27
[DEFAULT]
28
# The plugin provides 'spindle_thrift_library' target type and 'spindle' task
29
```
30
31
### BUILD File Usage
32
33
```python
34
# In a BUILD file
35
spindle_thrift_library(
36
name='user-service-thrift',
37
sources=['user.thrift', 'profile.thrift'],
38
dependencies=[
39
'3rdparty:spindle-runtime',
40
],
41
)
42
```
43
44
### Command Line Usage
45
46
```bash
47
# Generate Scala code from Thrift files
48
pants gen.spindle src/main/thrift:user-service-thrift
49
50
# The generated Scala and Java files will be placed in the workspace
51
# under appropriate namespace directories
52
```
53
54
### Example Thrift File
55
56
```thrift
57
namespace java com.example.user
58
59
typedef string UserId
60
61
struct User {
62
1: required UserId id
63
2: required string name
64
3: optional string email
65
}
66
67
service UserService {
68
User getUser(1: UserId userId)
69
list<User> listUsers()
70
}
71
```
72
73
## Capabilities
74
75
### Plugin Registration
76
77
Registration functions that integrate the plugin with the Pants build system.
78
79
```python { .api }
80
def build_file_aliases():
81
"""
82
Returns build file aliases for the spindle plugin.
83
84
Returns:
85
BuildFileAliases: Aliases containing the 'spindle_thrift_library' target type
86
"""
87
88
def register_goals():
89
"""
90
Registers the 'spindle' task under the 'gen' goal.
91
92
Installs the SpindleGen task to be executed when 'gen.spindle' is invoked.
93
"""
94
```
95
96
### Target Types
97
98
Target type for defining Thrift libraries that will be compiled to Scala using Spindle.
99
100
```python { .api }
101
class SpindleThriftLibrary(ExportableJvmLibrary):
102
"""
103
A Scala library generated from Spindle IDL files.
104
105
This target type represents Thrift files that should be compiled to Scala
106
using the Spindle code generator. Inherits from ExportableJvmLibrary to
107
provide JVM library capabilities.
108
"""
109
110
def __init__(self, *args, **kwargs):
111
"""
112
Initialize a SpindleThriftLibrary target.
113
114
Automatically adds 'scala', 'codegen', and 'synthetic' labels to the target.
115
116
Args:
117
*args: Variable arguments passed to parent ExportableJvmLibrary
118
**kwargs: Keyword arguments passed to parent ExportableJvmLibrary
119
"""
120
```
121
122
### Code Generation Task
123
124
The main task that performs Thrift-to-Scala code generation using Spindle.
125
126
```python { .api }
127
class SpindleGen(NailgunTask):
128
"""
129
Task that generates Scala code from Thrift files using Spindle.
130
131
This task processes SpindleThriftLibrary targets, executes the Spindle
132
code generator, and creates synthetic Scala and Java library targets
133
with the generated code.
134
"""
135
136
@classmethod
137
def product_types(cls):
138
"""
139
Returns the product types produced by this task.
140
141
Returns:
142
list: ['scala'] - indicates this task produces Scala code
143
"""
144
145
@classmethod
146
def register_options(cls, register):
147
"""
148
Register command-line options for the SpindleGen task.
149
150
Args:
151
register: Option registration function
152
153
Registers:
154
--runtime-dependency: Runtime dependencies for generated code
155
--spindle-codegen tool: Spindle code generator tool configuration
156
"""
157
158
@property
159
def spindle_classpath(self):
160
"""
161
Get the classpath for the Spindle code generator tool.
162
163
Returns:
164
list: Classpath entries for spindle-codegen tool
165
"""
166
167
@property
168
def synthetic_target_extra_dependencies(self):
169
"""
170
Get additional dependencies for synthetic targets.
171
172
Returns:
173
set: Set of dependency targets from runtime-dependency option
174
"""
175
176
@property
177
def namespace_out(self):
178
"""
179
Get the output directory for generated code.
180
181
Returns:
182
str: Path to output directory (workdir/src/jvm)
183
"""
184
185
def codegen_targets(self):
186
"""
187
Get all targets that need code generation.
188
189
Returns:
190
list: List of SpindleThriftLibrary targets
191
"""
192
193
def sources_generated_by_target(self, target):
194
"""
195
Calculate the source files that will be generated for a target.
196
197
Args:
198
target: SpindleThriftLibrary target
199
200
Returns:
201
list: List of paths to generated source files
202
"""
203
204
def execute_codegen(self, targets):
205
"""
206
Execute the Spindle code generation process.
207
208
Args:
209
targets: List of SpindleThriftLibrary targets to process
210
211
Raises:
212
TaskError: If code generation fails
213
"""
214
215
def execute(self):
216
"""
217
Main execution method for the task.
218
219
Orchestrates the entire code generation process:
220
1. Identifies targets needing generation
221
2. Executes code generation for invalid targets
222
3. Creates synthetic Scala and Java library targets
223
4. Manages dependencies and build graph updates
224
5. Updates artifact cache if enabled
225
"""
226
```
227
228
### Utility Functions
229
230
Helper functions for calculating generated file paths and parsing Thrift files.
231
232
```python { .api }
233
def calculate_genfiles(source):
234
"""
235
Calculate which files will be generated from a Thrift source file.
236
237
Parses the Thrift file to extract namespace information and determines
238
the output file paths that Spindle will generate.
239
240
Args:
241
source (str): Path to Thrift source file relative to build root
242
243
Returns:
244
list: List of generated file basenames (without .scala/.java extensions)
245
246
Raises:
247
TaskError: If no Java namespace is found in the source file
248
"""
249
250
def calculate_scala_record_genfiles(namespace, source):
251
"""
252
Calculate generated file basenames for Scala records.
253
254
Args:
255
namespace (str): Java namespace from Thrift file
256
source (str): Path to source Thrift file
257
258
Returns:
259
list: List of generated file paths (basenames, add .scala/.java for full path)
260
"""
261
```
262
263
## Types
264
265
### Build System Integration Types
266
267
```python { .api }
268
BuildFileAliases:
269
"""
270
Pants build system type for registering target aliases.
271
Contains mappings from target names to target classes.
272
"""
273
274
TaskRegistrar:
275
"""
276
Pants build system type for registering tasks under goals.
277
Provides the task() method for goal registration.
278
"""
279
280
ExportableJvmLibrary:
281
"""
282
Base class for JVM library targets that can be exported.
283
Provides standard JVM library functionality and export capabilities.
284
"""
285
286
NailgunTask:
287
"""
288
Base class for tasks that execute JVM tools via Nailgun.
289
Provides JVM tool execution and classpath management.
290
"""
291
```
292
293
### Configuration Types
294
295
```python { .api }
296
JarDependency:
297
"""
298
Represents a JAR dependency with org, name, and revision.
299
Used to specify the Spindle codegen tool dependency.
300
"""
301
302
# Default tool configuration
303
SPINDLE_CODEGEN_DEFAULT = JarDependency(
304
org='com.foursquare',
305
name='spindle-codegen-binary_2.10',
306
rev='3.0.0-M7'
307
)
308
```
309
310
## Error Handling
311
312
The plugin raises `TaskError` exceptions in the following cases:
313
314
- **Missing namespace**: When a Thrift file lacks a required Java namespace declaration
315
- **Code generation failure**: When the Spindle code generator returns a non-zero exit code
316
- **File system errors**: When output directories cannot be created or accessed
317
318
## Plugin Configuration
319
320
### Default Settings
321
322
- **Runtime dependency**: `['3rdparty:spindle-runtime']`
323
- **Spindle version**: `3.0.0-M7` (com.foursquare:spindle-codegen-binary_2.10)
324
- **Templates**:
325
- Scala: `scala/record.ssp`
326
- Java: `javagen/record.ssp`
327
328
### Options
329
330
```bash
331
# Configure runtime dependencies
332
pants gen.spindle --runtime-dependency=path/to:runtime-dep
333
334
# Configure JVM options for code generation
335
pants gen.spindle --jvm-options="-Xmx2g -XX:MaxPermSize=256m"
336
```
337
338
## Generated Output
339
340
The plugin generates:
341
342
1. **Scala files**: `.scala` files containing Scala case classes and companion objects
343
2. **Java files**: `.java` files with Java representations for interoperability
344
3. **Synthetic targets**: Scala and Java library targets with proper dependency management
345
4. **Namespace structure**: Files organized according to Thrift namespace declarations
346
347
Generated files follow the pattern:
348
- Input: `com/example/user.thrift` with `namespace java com.example.user`
349
- Output: `src/jvm/com/example/user/user.scala` and `src/jvm/com/example/user/java_user.java`