Skip to content

Building from Source

Petrus Pradella edited this page Aug 13, 2026 · 14 revisions

Building from Source

What this page covers: how to compile and locally publish EveryDatabase — the single-JDK build (one JDK 25 toolchain), the Jabel dual-target that turns modern Java source into Java 8 bytecode, the gradlew tasks you'll actually use, and publishToMavenLocal.


The 60-second version

git clone https://github.com/EverNife/EveryDatabase.git
cd EveryDatabase

# One JDK for the whole build — JDK 25.
export JAVA_HOME=/path/to/jdk-25            # PowerShell: $env:JAVA_HOME = "C:\path\to\jdk-25"

./gradlew :core:build                       # compile + run every test in :core

That's it: a single JDK 25 builds, tests, and runs everything. The published jars still target Java 8 — the build arranges that for you (see Jabel below). No second JDK to configure by hand.

📌 Note — the wrapper pins Gradle 9.5.1, which runs on a JDK 25 launcher directly. Always invoke through ./gradlew (PowerShell: .\gradlew) so the wrapped version is used.


Prerequisites

Need Why
JDK 25 The only JDK you need. It launches Gradle, compiles & runs all code — production and tests — through one toolchain.
Docker (optional) Only for the SQL/Mongo integration suites against real servers. Without it, build with -PnoDocker. See Running the Tests.

💡 Tip — point JAVA_HOME at JDK 25 and you're done. There is no second JDK to install: the FinalCraft Jabel fork rides JDK 25's javac, so production and test code both compile on the single toolchain.


How modern Java syntax becomes Java 8 bytecode

EveryDatabase is authored in modern Java but ships Java 8 bytecode. The arrangement lives centrally in the root build.gradle and applies to :core, :libby, :manager, and :manager-jedis:

  • The project-wide toolchain is Java 25 (languageVersion = JavaLanguageVersion.of(25)) — it compiles and runs everything, production and tests, with the full modern API available to tests.
  • For those four modules, compileJava is overridden: sourceCompatibility = 25 keeps modern syntax available while options.release = 8 holds the Java 8 bytecode/API floor, and the Jabel annotation processor lifts javac's source-level check so modern syntax (switch expressions, var, text blocks, instanceof patterns, records-as-syntax, …) emits Java 8 bytecode.
// root build.gradle — the production-compile override (abridged)
configure([project(':core'), project(':libby'), project(':manager'), project(':manager-jedis')]) {
    dependencies {
        annotationProcessor libs.jabel
        compileOnly         libs.jabel
    }
    compileJava {
        sourceCompatibility = 25        // modern *syntax* (IDE hint; Jabel lifts the check)
        options.release = 8             // Java 8 *bytecode* and *API* floor
    }
}

Two consequences to internalize before touching production code:

⚠️ Gotcha — production code may use modern syntax but must stay Java-8-runtime-safe: no Java 9+ library APIs. options.release = 8 enforces this at compile time. (Hence StorageExecutors reflects newVirtualThreadPerTaskExecutor instead of calling it directly — the API doesn't exist on a Java 8 floor.) Test code is unrestricted up to Java 25.

⚠️ Gotcha — there is no second JDK. The FinalCraft Jabel fork rides JDK 25's javac (unlike upstream Jabel, which only worked up to JDK 17), so production and tests compile on the single Java 25 toolchain — Gradle does not auto-detect a JDK 17. Don't "simplify" the build by deleting the compileJava override — it's what keeps the Java 8 target.


The tasks you'll actually run

The Gradle module paths are :core, :libby, :manager, :manager-jedis.

./gradlew :core:build                  # compile + run all :core tests
./gradlew :core:test                   # run all :core tests
./gradlew :core:test -PskipStress      # skip the @Tag("stress") 10k-record suites (much faster)
./gradlew :core:test -PnoDocker        # no Docker — SQL/Mongo suites self-skip
./gradlew :manager:test                # the refs/caching add-on (embedded backends; multi-backend self-skips w/o Docker)
./gradlew build                        # build every module

Run one class or one method:

./gradlew :core:test --tests "*MariaDbStorageTest"
./gradlew :core:test --tests "*MariaDbStorageTest.inTransaction_commit_savesAreVisible"

Full test mechanics — Docker compose, throwaway databases, self-skip behavior — are on Running the Tests.

📌 Note-parameters is set on both compileJava and compileTestJava, so parameter names survive into the compiled jars (handy for reflection-based mapping). Don't strip it.


Publishing locally

To consume your local build from another project on the same machine, publish to the Maven cache (~/.m2/repository):

./gradlew :core:publishToMavenLocal        # jar + sources + pom for everydatabase-core
./gradlew publishToMavenLocal              # all modules at once

Each module sets its own everydatabase-* artifactId under group br.com.finalcraft.everydatabase, version 1.1.0. Then point the consumer at mavenLocal():

repositories { mavenLocal() }
dependencies {
    implementation 'br.com.finalcraft.everydatabase:everydatabase-core:1.3.0'
}

💡 Tip — publishing to the remote Maven repo (maven.petrus.dev/public) is credential-gated (PETRUSMAVEN_ACTOR / PETRUSMAVEN_TOKEN env vars) and is a maintainer action. For local development always use publishToMavenLocal. Artifact coordinates and the install snippets live on Installation.


Build invariants not to regress

A Gradle-9 quirk is already handled in the build. If you edit the build scripts, keep it:

  • Every test JVM needs testRuntimeOnly libs.junit.platform.launcher — Gradle 9 stopped providing the JUnit Platform launcher from its own distribution. It's declared once for all subprojects.

See also

Clone this wiki locally