Write Your First Sourcegen (Java)
This page walks through the Java flow for adding a source generator
to your build — a small program that writes source files
compile then sees. For what a sourcegen script is, why bleep
makes it a first-class concept, and how it differs from a
bleep script, see
Source generation.
Prerequisites
- A working bleep project (see Your First Project)
- Java 17+
Step 1: The build
Add a scripts project to host the generator and point myapp at
it via sourcegen:. The scripts project depends on bleepscript,
the same dep you'd add for a regular bleep script:
$schema: https://raw.githubusercontent.com/oyvindberg/bleep/master/schema.json
$version: 1.0.0-M9
jvm:
name: graalvm-community:25.0.1
projects:
myapp:
java:
version: "25"
platform:
name: jvm
mainClass: com.example.Main
sourcegen:
project: scripts
main: scripts.GenConstants
scripts:
dependencies:
- build.bleep:bleepscript:${BLEEP_VERSION}
java:
version: "25"
platform:
name: jvm
Key points:
sourcegen:onmyappnames the project that holds the generator and the fully qualified main class. Bleep will run the generator beforemyappcompiles, and rerun it whenever the script project or its dependencies change.${BLEEP_VERSION}keeps the script'sbleepscriptdep in lockstep with whatever bleep is running you.- A project can list more than one sourcegen script — each gets its own isolated output directory.
Step 2: Write the generator
Extend bleepscript.BleepCodegenScript and override run. The
framework hands you a list of CodegenTargets — one per
project (or cross-id) that consumes this generator. Write source
files under target.sources():
package scripts;
import bleepscript.BleepCodegenScript;
import bleepscript.CodegenTarget;
import bleepscript.Commands;
import bleepscript.Started;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public final class GenConstants extends BleepCodegenScript {
public GenConstants() {
super("GenConstants");
}
@Override
public void run(Started started, Commands commands, List<CodegenTarget> targets, List<String> args) {
for (CodegenTarget target : targets) {
Path file = target.sources().resolve("generated/Constants.java");
try {
Files.createDirectories(file.getParent());
Files.writeString(file,
"package generated;\n" +
"\n" +
"public final class Constants {\n" +
" public static final int ANSWER = 42;\n" +
"}\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
BleepCodegenScript provides main for you. The output goes to a
temp directory the framework supplies; only on a successful return
does bleep sync the temp tree to the real generated-sources
directory. A failed run never leaves stale code behind.
Step 3: The consumer
myapp imports Constants like any other class — the
generated package is on the consumer's source path automatically:
package com.example;
import generated.Constants;
public final class Main {
public static void main(String[] args) {
System.out.println("answer: " + Constants.ANSWER);
}
}
Step 4: Run it
bleep run myapp
answer: 42
bleep compile triggers the generator, then compiles myapp against
the generated Constants class, then runs it. Subsequent invocations
skip the generator (its inputs haven't changed) until you edit the
script project.
Where to go from here
- Source generation (concept) —
the
BleepCodegenScriptAPI, cross-build targets, the temp-directory cleanup model, and how invalidation works. - Bleep scripts (concept) — the after-compile half: scripts that act on artifacts.
- Run sourcegen explicitly —
bleep sourcegen --watch myappre-runs generators when their inputs change.