0
# Build System
1
2
The Quarkus build system is built around a producer-consumer model where build steps declare their inputs and outputs through build items. This enables dependency resolution, parallel execution, and extensibility for the deployment-time augmentation process.
3
4
## Core Imports
5
6
```java
7
// Build step annotations
8
import io.quarkus.deployment.annotations.BuildStep;
9
import io.quarkus.deployment.annotations.BuildProducer;
10
import io.quarkus.deployment.annotations.Record;
11
import io.quarkus.deployment.annotations.ExecutionTime;
12
import io.quarkus.deployment.annotations.Consume;
13
import io.quarkus.deployment.annotations.Produce;
14
import io.quarkus.deployment.annotations.BuildSteps;
15
import io.quarkus.deployment.annotations.Overridable;
16
import io.quarkus.deployment.annotations.ProduceWeak;
17
import io.quarkus.deployment.annotations.Weak;
18
19
// Build items base classes
20
import io.quarkus.deployment.builditem.BuildItem;
21
import io.quarkus.deployment.builditem.SimpleBuildItem;
22
import io.quarkus.deployment.builditem.MultiBuildItem;
23
24
// Bytecode recording
25
import io.quarkus.deployment.recording.RecorderContext;
26
import io.quarkus.deployment.recording.BytecodeRecorderImpl;
27
```
28
29
## Build Step Annotations
30
31
### @BuildStep
32
33
Marks a method as a build step that participates in the build chain.
34
35
```java { .api }
36
@Target(ElementType.METHOD)
37
@Retention(RetentionPolicy.RUNTIME)
38
@interface BuildStep {
39
/**
40
* Conditional execution suppliers - step runs only if ALL return true
41
*/
42
Class<? extends BooleanSupplier>[] onlyIf() default {};
43
44
/**
45
* Negative conditional execution suppliers - step runs only if ALL return false
46
*/
47
Class<? extends BooleanSupplier>[] onlyIfNot() default {};
48
}
49
```
50
51
**Usage Examples:**
52
53
```java
54
// Simple build step
55
@BuildStep
56
FeatureBuildItem registerFeature() {
57
return new FeatureBuildItem(Feature.REST);
58
}
59
60
// Conditional build step
61
@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
62
void processNativeImage(BuildProducer<NativeImageResourceBuildItem> resources) {
63
resources.produce(new NativeImageResourceBuildItem("META-INF/services/*"));
64
}
65
66
// Build step with multiple conditions
67
@BuildStep(
68
onlyIf = { DevelopmentMode.class, DevServicesEnabled.class },
69
onlyIfNot = { TestProfile.class }
70
)
71
void setupDevServices(BuildProducer<DevServicesBuildItem> devServices) {
72
// Development services setup
73
}
74
```
75
76
### @BuildProducer
77
78
Interface for producing build items during build step execution.
79
80
```java { .api }
81
interface BuildProducer<T extends BuildItem> {
82
/**
83
* Produces a single build item
84
*/
85
void produce(T item);
86
87
/**
88
* Produces multiple build items
89
*/
90
void produce(Collection<T> items);
91
}
92
```
93
94
**Usage Examples:**
95
96
```java
97
@BuildStep
98
void registerReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
99
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(MyClass.class)
100
.constructors()
101
.methods()
102
.fields()
103
.build());
104
}
105
106
@BuildStep
107
void registerMultipleCapabilities(BuildProducer<CapabilityBuildItem> capabilities) {
108
List<CapabilityBuildItem> items = Arrays.asList(
109
new CapabilityBuildItem(Capability.REST),
110
new CapabilityBuildItem(Capability.SECURITY)
111
);
112
capabilities.produce(items);
113
}
114
```
115
116
### @Record
117
118
Marks build steps that generate bytecode for runtime execution.
119
120
```java { .api }
121
@Target(ElementType.METHOD)
122
@Retention(RetentionPolicy.RUNTIME)
123
@interface Record {
124
/**
125
* When the recorded bytecode should execute
126
*/
127
ExecutionTime value() default ExecutionTime.RUNTIME_INIT;
128
129
/**
130
* Whether bytecode production is optional
131
*/
132
boolean optional() default false;
133
134
/**
135
* Use identity comparison for recorder parameters
136
*/
137
boolean useIdentityComparisonForParameters() default false;
138
}
139
140
enum ExecutionTime {
141
/**
142
* Execute from static initializer (build time)
143
*/
144
STATIC_INIT,
145
146
/**
147
* Execute from main method (runtime initialization)
148
*/
149
RUNTIME_INIT
150
}
151
```
152
153
**Usage Examples:**
154
155
```java
156
@BuildStep
157
@Record(ExecutionTime.STATIC_INIT)
158
void configureStaticInit(MyRecorder recorder,
159
ConfigurationBuildItem config) {
160
recorder.setupStaticConfiguration(config.getProperties());
161
}
162
163
@BuildStep
164
@Record(ExecutionTime.RUNTIME_INIT)
165
void configureRuntime(MyRecorder recorder,
166
LaunchModeBuildItem launchMode,
167
ShutdownContextBuildItem shutdownContext) {
168
recorder.initialize(launchMode.getLaunchMode(), shutdownContext);
169
}
170
171
// Optional recording - may not execute if conditions aren't met
172
@BuildStep
173
@Record(value = ExecutionTime.RUNTIME_INIT, optional = true)
174
void conditionalSetup(MyRecorder recorder,
175
Optional<DatabaseBuildItem> database) {
176
if (database.isPresent()) {
177
recorder.configureDatabase(database.get());
178
}
179
}
180
```
181
182
### Build Step Dependencies
183
184
Control execution order and dependencies between build steps.
185
186
```java { .api }
187
@Target(ElementType.METHOD)
188
@Retention(RetentionPolicy.RUNTIME)
189
@interface Consume {
190
/**
191
* Build item type that must be produced before this step runs
192
*/
193
Class<? extends BuildItem> value();
194
}
195
196
@Target(ElementType.METHOD)
197
@Retention(RetentionPolicy.RUNTIME)
198
@interface Produce {
199
/**
200
* Build item type this step produces
201
*/
202
Class<? extends BuildItem> value();
203
}
204
205
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})
206
@Retention(RetentionPolicy.RUNTIME)
207
@interface Weak {
208
// Marks weak dependencies that don't require execution order
209
}
210
211
@Target(ElementType.METHOD)
212
@Retention(RetentionPolicy.RUNTIME)
213
@interface ProduceWeak {
214
/**
215
* Weak build item type this step produces
216
*/
217
Class<? extends BuildItem> value();
218
}
219
```
220
221
**Usage Examples:**
222
223
```java
224
@BuildStep
225
@Consume(ApplicationIndexBuildItem.class)
226
void processAfterIndexing(ApplicationIndexBuildItem index,
227
BuildProducer<GeneratedClassBuildItem> generated) {
228
// This runs after application indexing is complete
229
}
230
231
@BuildStep
232
@Produce(CustomBuildItem.class)
233
CustomBuildItem createCustomItem() {
234
return new CustomBuildItem();
235
}
236
237
@BuildStep
238
@ProduceWeak(OptionalFeatureBuildItem.class)
239
OptionalFeatureBuildItem maybeProvideFeature() {
240
// Weak production - others can override this
241
return new OptionalFeatureBuildItem();
242
}
243
244
@BuildStep
245
void consumeWeakItem(@Weak OptionalFeatureBuildItem weak) {
246
// Weak consumption - step runs regardless of weak item presence
247
}
248
```
249
250
### Class-Level Annotations
251
252
```java { .api }
253
@Target(ElementType.TYPE)
254
@Retention(RetentionPolicy.RUNTIME)
255
@interface BuildSteps {
256
/**
257
* Class-wide conditional execution suppliers
258
*/
259
Class<? extends BooleanSupplier>[] onlyIf() default {};
260
261
/**
262
* Class-wide negative conditional execution suppliers
263
*/
264
Class<? extends BooleanSupplier>[] onlyIfNot() default {};
265
}
266
267
@Target(ElementType.METHOD)
268
@Retention(RetentionPolicy.RUNTIME)
269
@interface Overridable {
270
// Marks build items as overridable by other extensions
271
}
272
```
273
274
**Usage Examples:**
275
276
```java
277
@BuildSteps(onlyIf = DevelopmentMode.class)
278
public class DevModeProcessor {
279
280
@BuildStep
281
void setupHotReload() {
282
// Only runs in development mode
283
}
284
285
@BuildStep
286
void setupDevConsole() {
287
// Also only runs in development mode
288
}
289
}
290
291
@BuildStep
292
@Overridable
293
SecurityProviderBuildItem defaultSecurity() {
294
return new SecurityProviderBuildItem("default");
295
}
296
```
297
298
## Build Items
299
300
### Base Classes
301
302
```java { .api }
303
/**
304
* Base class for all build items
305
*/
306
abstract class BuildItem {}
307
308
/**
309
* Build item with single instance per build
310
*/
311
abstract class SimpleBuildItem extends BuildItem {}
312
313
/**
314
* Build item allowing multiple instances per build
315
*/
316
abstract class MultiBuildItem extends BuildItem {}
317
```
318
319
### Creating Custom Build Items
320
321
```java
322
// Simple build item (single instance)
323
public final class DatabaseConfigBuildItem extends SimpleBuildItem {
324
private final String url;
325
private final String driver;
326
327
public DatabaseConfigBuildItem(String url, String driver) {
328
this.url = url;
329
this.driver = driver;
330
}
331
332
public String getUrl() { return url; }
333
public String getDriver() { return driver; }
334
}
335
336
// Multi build item (multiple instances allowed)
337
public final class EntityBuildItem extends MultiBuildItem {
338
private final String className;
339
private final String tableName;
340
341
public EntityBuildItem(String className, String tableName) {
342
this.className = className;
343
this.tableName = tableName;
344
}
345
346
public String getClassName() { return className; }
347
public String getTableName() { return tableName; }
348
}
349
```
350
351
## Bytecode Recording
352
353
### RecorderContext
354
355
Provides context for bytecode recording operations.
356
357
```java { .api }
358
class RecorderContext {
359
/**
360
* Creates a new class writer for generating classes
361
*/
362
ClassCreator getClassCreator(String className);
363
364
/**
365
* Registers an object loader for custom object handling
366
*/
367
void registerObjectLoader(ObjectLoader loader);
368
369
/**
370
* Creates a recorder proxy for the given class
371
*/
372
<T> T getRecorderProxy(Class<T> recorderClass);
373
374
/**
375
* Converts a class to a runtime proxy
376
*/
377
RuntimeValue<Class<?>> classProxy(String className);
378
379
/**
380
* Creates a runtime value wrapper
381
*/
382
<T> RuntimeValue<T> newInstance(String className);
383
}
384
```
385
386
### ObjectLoader Interface
387
388
Custom object loading during bytecode recording.
389
390
```java { .api }
391
interface ObjectLoader {
392
/**
393
* Loads an object into bytecode
394
*/
395
ResultHandle load(BytecodeCreator body, Object obj, boolean staticInit);
396
397
/**
398
* Checks if this loader can handle the given object
399
*/
400
boolean canHandleObject(Object obj, boolean staticInit);
401
}
402
```
403
404
**Usage Examples:**
405
406
```java
407
public class CustomObjectLoader implements ObjectLoader {
408
409
@Override
410
public boolean canHandleObject(Object obj, boolean staticInit) {
411
return obj instanceof MyCustomType;
412
}
413
414
@Override
415
public ResultHandle load(BytecodeCreator body, Object obj, boolean staticInit) {
416
MyCustomType custom = (MyCustomType) obj;
417
418
return body.newInstance(
419
MethodDescriptor.ofConstructor(MyCustomType.class, String.class),
420
body.load(custom.getValue())
421
);
422
}
423
}
424
425
// Register in recorder context
426
@BuildStep
427
@Record(ExecutionTime.RUNTIME_INIT)
428
void setupRecording(RecorderContext context, MyRecorder recorder) {
429
context.registerObjectLoader(new CustomObjectLoader());
430
recorder.initialize();
431
}
432
```
433
434
## Extension Development Patterns
435
436
### Basic Extension Structure
437
438
```java
439
public class MyExtensionProcessor {
440
441
@BuildStep
442
FeatureBuildItem registerFeature() {
443
return new FeatureBuildItem("my-extension");
444
}
445
446
@BuildStep
447
CapabilityBuildItem registerCapability() {
448
return new CapabilityBuildItem("com.example.my-capability");
449
}
450
451
@BuildStep
452
void processConfiguration(MyExtensionConfig config,
453
BuildProducer<MyConfigBuildItem> producer) {
454
producer.produce(new MyConfigBuildItem(config));
455
}
456
457
@BuildStep
458
@Record(ExecutionTime.RUNTIME_INIT)
459
void setupRuntime(MyRecorder recorder,
460
MyConfigBuildItem config,
461
ShutdownContextBuildItem shutdown) {
462
recorder.initialize(config.getProperties(), shutdown);
463
}
464
}
465
```
466
467
### Conditional Extension Activation
468
469
```java
470
public class ConditionalProcessor {
471
472
@BuildStep(onlyIf = DatabasePresent.class)
473
void setupDatabase(BuildProducer<DatabaseBuildItem> database) {
474
// Only runs if database dependency is present
475
}
476
477
@BuildStep(onlyIfNot = ProductionMode.class)
478
void setupDevMode(BuildProducer<DevServicesBuildItem> devServices) {
479
// Only runs when not in production mode
480
}
481
}
482
483
public class DatabasePresent implements BooleanSupplier {
484
@Override
485
public boolean getAsBoolean() {
486
try {
487
Class.forName("javax.sql.DataSource");
488
return true;
489
} catch (ClassNotFoundException e) {
490
return false;
491
}
492
}
493
}
494
```
495
496
### Build Chain Customization
497
498
```java
499
public class BuildChainCustomizer {
500
501
public static Consumer<BuildChainBuilder> customize() {
502
return builder -> {
503
builder.addBuildStep(MyProcessor.class);
504
builder.addFinal(MyFinalProcessor.class);
505
builder.addInitial(MyInitialProcessor.class);
506
};
507
}
508
}
509
510
// Usage in augmentor
511
QuarkusAugmentor augmentor = QuarkusAugmentor.builder()
512
.setRoot(root)
513
.addBuildChainCustomizer(BuildChainCustomizer.customize())
514
.build();
515
```