Skip to content

Compiling

Fabr compiles a target’s Java sources with javac from a JDK it downloads, for the Java release you choose, against the jars its dependencies resolve to. This page covers what replaces maven-compiler-plugin’s and Gradle’s java { } configuration.

With Maven or Gradle In fabr
maven.compiler.release / options.release JVM_TARGET, or a java<release> flag on the target.
A Maven toolchain / Gradle toolchain { languageVersion } JDK.
<annotationProcessorPaths> / annotationProcessor processors.
module-info.java with the module path Recognised, as Gradle does it; see Modules.
project.build.sourceEncoding Always UTF-8.
-parameters in <compilerArgs> / options.compilerArgs On by default; see Parameter names.
Other <compilerArgs> / options.compilerArgs See Not supported.

For every compile fabr runs:

Terminal window
javac --release <N> -encoding UTF-8 -proc:none -implicit:none -g -parameters \
-d classes -s generated -classpath <the jars> <the sources>

with -processorpath <the processors> in place of -proc:none where the target has annotation processors, and --module-path where it is a module. The sources the processors generate (-s) are kept beside the classes, and are part of the target’s javadoc and sources. -parameters is left out with the java/no_parameters flag.

-g records all of javac’s debug information, local variable names included, as Maven and Gradle record it. A release build (BUILD_TYPE=release) is given -g:source,lines instead: file names and line numbers, for stack traces, without the local variable names a debugger shows. Only .java files may be among the sources; anything else is the error java_compile compiles .java sources only, and was given …. Put other files in resources.

The classpath is the jars of the target’s dependencies and everything they require, nearest first, in the order they are declared.

JVM_TARGET is the Java release the compiled classes run on, written jvm<release>, and is given to javac as --release. It is jvm21 unless you set it:

JVM_TARGET = jvm17;

--release both sets the class-file version and compiles against that release’s API, so code built for jvm17 can’t call a method Java 21 added.

When a target’s sources are written for an earlier release, say so with a flag in its deps: java8, java11, java17, java21 or java25. The target is then compiled for that release, whatever JVM_TARGET says, so a library can keep supporting Java 11 while the rest of the project moves on:

jar_package compat {
srcs = src/main/java:**/*.java;
deps = java11 @mvn:org.slf4j:slf4j-api:1.7.36;
}

A flag later than JVM_TARGET is an error, since the classes couldn’t run: the sources are written for Java 21, which a JVM_TARGET of jvm17 cannot run.

The compiled classes record their methods’ parameter names, as javac -parameters makes them, for code that reads them by reflection, such as Spring binding a request parameter to a method parameter of the same name. Maven and Gradle leave this off unless a project, or Spring Boot’s plugin, turns it on. A target that has the java/no_parameters flag in its deps leaves the names out:

jar_package app {
srcs = src/main/java:**/*.java;
deps = java/no_parameters;
}

JDK names the Eclipse Temurin release the Java tools come from, which fabr downloads from the Adoptium API for your platform and keeps in its cache. It is @jdk:21.0.12.1+1 unless you set it:

JDK = @jdk:17.0.13+11;

A release is named as Adoptium names it, without its jdk- prefix: @jdk:17.0.13+11, or @jdk:8u302-b08 for Java 8. fabr ls @jdk lists the releases published for your platform, newest first. Fabr fetches the build for macOS, Linux (glibc or musl) or Windows, on x64 or arm64, where Adoptium publishes one, and checks the archive’s SHA-256.

Each tool is a setting of its own, taken from JDK by default: JAVAC is the compiler, JAVA the runtime everything is run on (fabr run, tests, and Java tools in a build), and JAVADOC the javadoc generator. Set one to take it from another release, as in JAVA = @jdk:17.0.13+11:java; to run on Java 17 what a Java 21 compiler built.

The JDK on your machine is never used, so JAVA_HOME makes no difference. To use a mirror of the Adoptium API:

jdk_distribution @jdk { url = "https://adoptium.example.com"; }

processors names the annotation processors javac runs. They are resolved apart from the sources’ own dependencies, so a processor’s own dependencies never reach your classpath. A processor whose annotations your code uses, such as Lombok, also goes in compile_deps, which puts it on the compile classpath without passing it on:

jar_package app {
srcs = src/main/java:**/*.java;
compile_deps = @mvn:org.projectlombok:lombok:1.18.34;
processors = @mvn:org.projectlombok:lombok:1.18.34;
}

A target with no processors is compiled with -proc:none, so a processor that happens to be on the classpath never runs. That matches what javac does by default from JDK 23, and differs from earlier JDKs, which ran any processor they found.

Sources that include a module-info.java are a named module. Their dependencies are placed as Gradle places them: a jar that is a module, because it holds a module-info.class or its manifest names an Automatic-Module-Name, goes on the module path, and any other jar on the classpath. A target without a module-info.java puts every jar on the classpath.

fabr run runs a modular program on the module path too; see Running programs.

  • There is no way to pass further options to javac, such as -Xlint or -Werror.
  • The source encoding is always UTF-8.