CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io--quarkus--quarkus-core-deployment

Core deployment-time APIs and infrastructure for building Quarkus applications with native image support and development-time features

Pending
Overview
Eval results
Files

build-system.mddocs/

Build System

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.

Core Imports

// Build step annotations
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.annotations.Overridable;
import io.quarkus.deployment.annotations.ProduceWeak;
import io.quarkus.deployment.annotations.Weak;

// Build items base classes
import io.quarkus.deployment.builditem.BuildItem;
import io.quarkus.deployment.builditem.SimpleBuildItem;
import io.quarkus.deployment.builditem.MultiBuildItem;

// Bytecode recording
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.deployment.recording.BytecodeRecorderImpl;

Build Step Annotations

@BuildStep

Marks a method as a build step that participates in the build chain.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface BuildStep {
    /**
     * Conditional execution suppliers - step runs only if ALL return true
     */
    Class<? extends BooleanSupplier>[] onlyIf() default {};
    
    /**
     * Negative conditional execution suppliers - step runs only if ALL return false
     */
    Class<? extends BooleanSupplier>[] onlyIfNot() default {};
}

Usage Examples:

// Simple build step
@BuildStep
FeatureBuildItem registerFeature() {
    return new FeatureBuildItem(Feature.REST);
}

// Conditional build step
@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
void processNativeImage(BuildProducer<NativeImageResourceBuildItem> resources) {
    resources.produce(new NativeImageResourceBuildItem("META-INF/services/*"));
}

// Build step with multiple conditions
@BuildStep(
    onlyIf = { DevelopmentMode.class, DevServicesEnabled.class },
    onlyIfNot = { TestProfile.class }
)
void setupDevServices(BuildProducer<DevServicesBuildItem> devServices) {
    // Development services setup
}

@BuildProducer

Interface for producing build items during build step execution.

interface BuildProducer<T extends BuildItem> {
    /**
     * Produces a single build item
     */
    void produce(T item);
    
    /**
     * Produces multiple build items
     */
    void produce(Collection<T> items);
}

Usage Examples:

@BuildStep
void registerReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
    reflectiveClasses.produce(ReflectiveClassBuildItem.builder(MyClass.class)
        .constructors()
        .methods()
        .fields()
        .build());
}

@BuildStep  
void registerMultipleCapabilities(BuildProducer<CapabilityBuildItem> capabilities) {
    List<CapabilityBuildItem> items = Arrays.asList(
        new CapabilityBuildItem(Capability.REST),
        new CapabilityBuildItem(Capability.SECURITY)
    );
    capabilities.produce(items);
}

@Record

Marks build steps that generate bytecode for runtime execution.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Record {
    /**
     * When the recorded bytecode should execute
     */
    ExecutionTime value() default ExecutionTime.RUNTIME_INIT;
    
    /**
     * Whether bytecode production is optional
     */
    boolean optional() default false;
    
    /**
     * Use identity comparison for recorder parameters
     */
    boolean useIdentityComparisonForParameters() default false;
}

enum ExecutionTime {
    /**
     * Execute from static initializer (build time)
     */
    STATIC_INIT,
    
    /**
     * Execute from main method (runtime initialization)  
     */
    RUNTIME_INIT
}

Usage Examples:

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void configureStaticInit(MyRecorder recorder, 
                        ConfigurationBuildItem config) {
    recorder.setupStaticConfiguration(config.getProperties());
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void configureRuntime(MyRecorder recorder,
                     LaunchModeBuildItem launchMode,
                     ShutdownContextBuildItem shutdownContext) {
    recorder.initialize(launchMode.getLaunchMode(), shutdownContext);
}

// Optional recording - may not execute if conditions aren't met
@BuildStep
@Record(value = ExecutionTime.RUNTIME_INIT, optional = true)
void conditionalSetup(MyRecorder recorder,
                     Optional<DatabaseBuildItem> database) {
    if (database.isPresent()) {
        recorder.configureDatabase(database.get());
    }
}

Build Step Dependencies

Control execution order and dependencies between build steps.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Consume {
    /**
     * Build item type that must be produced before this step runs
     */
    Class<? extends BuildItem> value();
}

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME)
@interface Produce {
    /**
     * Build item type this step produces
     */
    Class<? extends BuildItem> value();
}

@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface Weak {
    // Marks weak dependencies that don't require execution order
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ProduceWeak {
    /**
     * Weak build item type this step produces
     */
    Class<? extends BuildItem> value();
}

Usage Examples:

@BuildStep
@Consume(ApplicationIndexBuildItem.class)
void processAfterIndexing(ApplicationIndexBuildItem index,
                         BuildProducer<GeneratedClassBuildItem> generated) {
    // This runs after application indexing is complete
}

@BuildStep
@Produce(CustomBuildItem.class)
CustomBuildItem createCustomItem() {
    return new CustomBuildItem();
}

@BuildStep
@ProduceWeak(OptionalFeatureBuildItem.class)
OptionalFeatureBuildItem maybeProvideFeature() {
    // Weak production - others can override this
    return new OptionalFeatureBuildItem();
}

@BuildStep
void consumeWeakItem(@Weak OptionalFeatureBuildItem weak) {
    // Weak consumption - step runs regardless of weak item presence
}

Class-Level Annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface BuildSteps {
    /**
     * Class-wide conditional execution suppliers
     */
    Class<? extends BooleanSupplier>[] onlyIf() default {};
    
    /**
     * Class-wide negative conditional execution suppliers
     */
    Class<? extends BooleanSupplier>[] onlyIfNot() default {};
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)  
@interface Overridable {
    // Marks build items as overridable by other extensions
}

Usage Examples:

@BuildSteps(onlyIf = DevelopmentMode.class)
public class DevModeProcessor {
    
    @BuildStep
    void setupHotReload() {
        // Only runs in development mode
    }
    
    @BuildStep
    void setupDevConsole() {
        // Also only runs in development mode
    }
}

@BuildStep
@Overridable
SecurityProviderBuildItem defaultSecurity() {
    return new SecurityProviderBuildItem("default");
}

Build Items

Base Classes

/**
 * Base class for all build items
 */
abstract class BuildItem {}

/**
 * Build item with single instance per build
 */
abstract class SimpleBuildItem extends BuildItem {}

/**
 * Build item allowing multiple instances per build
 */
abstract class MultiBuildItem extends BuildItem {}

Creating Custom Build Items

// Simple build item (single instance)
public final class DatabaseConfigBuildItem extends SimpleBuildItem {
    private final String url;
    private final String driver;
    
    public DatabaseConfigBuildItem(String url, String driver) {
        this.url = url;
        this.driver = driver;
    }
    
    public String getUrl() { return url; }
    public String getDriver() { return driver; }
}

// Multi build item (multiple instances allowed)  
public final class EntityBuildItem extends MultiBuildItem {
    private final String className;
    private final String tableName;
    
    public EntityBuildItem(String className, String tableName) {
        this.className = className;
        this.tableName = tableName;
    }
    
    public String getClassName() { return className; }
    public String getTableName() { return tableName; }
}

Bytecode Recording

RecorderContext

Provides context for bytecode recording operations.

class RecorderContext {
    /**
     * Creates a new class writer for generating classes
     */
    ClassCreator getClassCreator(String className);
    
    /**
     * Registers an object loader for custom object handling
     */
    void registerObjectLoader(ObjectLoader loader);
    
    /**
     * Creates a recorder proxy for the given class
     */
    <T> T getRecorderProxy(Class<T> recorderClass);
    
    /**
     * Converts a class to a runtime proxy
     */
    RuntimeValue<Class<?>> classProxy(String className);
    
    /**
     * Creates a runtime value wrapper
     */
    <T> RuntimeValue<T> newInstance(String className);
}

ObjectLoader Interface

Custom object loading during bytecode recording.

interface ObjectLoader {
    /**
     * Loads an object into bytecode
     */
    ResultHandle load(BytecodeCreator body, Object obj, boolean staticInit);
    
    /**
     * Checks if this loader can handle the given object
     */
    boolean canHandleObject(Object obj, boolean staticInit);
}

Usage Examples:

public class CustomObjectLoader implements ObjectLoader {
    
    @Override
    public boolean canHandleObject(Object obj, boolean staticInit) {
        return obj instanceof MyCustomType;
    }
    
    @Override
    public ResultHandle load(BytecodeCreator body, Object obj, boolean staticInit) {
        MyCustomType custom = (MyCustomType) obj;
        
        return body.newInstance(
            MethodDescriptor.ofConstructor(MyCustomType.class, String.class),
            body.load(custom.getValue())
        );
    }
}

// Register in recorder context
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void setupRecording(RecorderContext context, MyRecorder recorder) {
    context.registerObjectLoader(new CustomObjectLoader());
    recorder.initialize();
}

Extension Development Patterns

Basic Extension Structure

public class MyExtensionProcessor {
    
    @BuildStep
    FeatureBuildItem registerFeature() {
        return new FeatureBuildItem("my-extension");
    }
    
    @BuildStep
    CapabilityBuildItem registerCapability() {
        return new CapabilityBuildItem("com.example.my-capability");
    }
    
    @BuildStep
    void processConfiguration(MyExtensionConfig config,
                             BuildProducer<MyConfigBuildItem> producer) {
        producer.produce(new MyConfigBuildItem(config));
    }
    
    @BuildStep
    @Record(ExecutionTime.RUNTIME_INIT)
    void setupRuntime(MyRecorder recorder,
                     MyConfigBuildItem config,
                     ShutdownContextBuildItem shutdown) {
        recorder.initialize(config.getProperties(), shutdown);
    }
}

Conditional Extension Activation

public class ConditionalProcessor {
    
    @BuildStep(onlyIf = DatabasePresent.class)
    void setupDatabase(BuildProducer<DatabaseBuildItem> database) {
        // Only runs if database dependency is present
    }
    
    @BuildStep(onlyIfNot = ProductionMode.class)
    void setupDevMode(BuildProducer<DevServicesBuildItem> devServices) {
        // Only runs when not in production mode
    }
}

public class DatabasePresent implements BooleanSupplier {
    @Override
    public boolean getAsBoolean() {
        try {
            Class.forName("javax.sql.DataSource");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

Build Chain Customization

public class BuildChainCustomizer {
    
    public static Consumer<BuildChainBuilder> customize() {
        return builder -> {
            builder.addBuildStep(MyProcessor.class);
            builder.addFinal(MyFinalProcessor.class);
            builder.addInitial(MyInitialProcessor.class);
        };
    }
}

// Usage in augmentor
QuarkusAugmentor augmentor = QuarkusAugmentor.builder()
    .setRoot(root)
    .addBuildChainCustomizer(BuildChainCustomizer.customize())
    .build();

Install with Tessl CLI

npx tessl i tessl/maven-io--quarkus--quarkus-core-deployment

docs

build-system.md

configuration.md

dev-mode.md

index.md

packaging.md

utilities.md

tile.json