Need help with bach?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

178 Stars 39 Forks MIT License 4.8K Commits 19 Opened issues


🎼 Bach Builds (on(ly)) Modules

Services available


Need anything else?

Contributors list

Bach - Java Shell Builder - Builds (on(ly)) Modules

"The tools we use have a profound (and devious!) influence on our thinking habits, and, therefore, on our thinking abilities."

Edsger W. Dijkstra, 18 June 1975

Bach is a lightweight Java build tool that orchestrates JDK tools for building modular Java projects.

By default, it tries its best to call the right tool at the right time with the right arguments. Bach encourages developers to explore, learn, and master these foundation tools and their options in order to allow a good understanding of what is really going on when building a Java project. Pass those learnings and optimizations as fine-grained tweaks in a declarative manner back to Bach using pure Java syntax.

Fast-forward to sections: ♥ Motivation, ✔ Goals, and ❌ Non-Goals


  • Install JDK 16 or later.
  • Open a terminal and verify
    java --version
    jshell --version
    reports at least Java 16.
    > java --version                                          > jshell --version
    openjdk 16 2021-03-16                                     jshell 16
  • Create a new directory and change into it.
    > mkdir air && cd air
  • Initialize Bach using JShell's load file feature.
    air> jshell
    > The Bitly-shortened URL expands effectively to:
  • Boot Bach into a JShell session.
    air> .bach/bin/bach boot
  • Let's create a minimal modular Java project by writing a
    file in the current directory.
    module air {
    requires java.base;
    You may create the module declaration from within the running JShell via:
    jshell> Files.writeString(Path.of(""), """
     ...> module air {
     ...>   requires java.base;
     ...> }
     ...> """)
    $6 ==>
  • That's it! Now, let Bach build this minimal modular project by invoking the
    method on the instance return by
    jshell> bach().build()
    Build air 0
    Build 1 main module: air
    javac   --module air --module-version 0 --module-source-path air=. -encoding UTF-8 -d .bach\worksp...
    jar     --create --file .bach\workspace\modules\[email protected] -C .bach\workspace\classes-main\16\air .
    Check main modules
    jdeps   --check air --multi-release BASE --module-path .bach\workspace\modules;.bach\external-modules
    Generate API documentation
    javadoc --module air --module-source-path air=. -encoding UTF-8 -d .bach\workspace\documentation\api
    jar     --create --file .bach\workspace\documentation\ --no-manifest -C .bach\workspa...
    Assemble custom runtime image
    jlink   --add-modules air --module-path .bach\workspace\modules --compress 2 --no-header-files --n...
    Build took 2.822s
    Logbook written to file:///.../air/.bach/workspace/
  • Exit JShell and build the project directly on the command line prompt. ```text jshell> /exit | Goodbye

air> .bach/bin/bach build Build air 0 ... Build took ... Logbook written to ... ```

Inspect the
file stored in directory
after each run of Bach. The Logbook is a good source to learn which tool was called with which arguments. It also contains the output of each individual tool run together with additional information about the executing thread, the duration, and exit code. The Logbook also records all messages at debug level (same as passing
flag at the command line) and describes each generated module.


Here are some suggestions to try right after you "played" the Prelude.

  • Extend the

    environment variable with a
    element in order to save some key strokes.
    • PATH=%PATH%;.bach\bin
      on Windows
    • PATH=$PATH:.bach/bin
      on Linux and Macs.
  • Run

    bach --help
    to show Bach's usage help message including available options.
  • Try following options:

    • bach --skip-tools jdeps,javadoc,jlink build
    • bach --limit-tools javac,jar build
    • bach --project-version 1.2.3-ea+BAC8CAFE build
    • bach --project-targets-java 11 build
    • bach --project-targets-java 8 build
  • Create a
    file in subdirectory
    , run
    bach build
    from within the root directory again and launch the program via
    java --module-path .bach/workspace/modules --module air
    package air;                                      // air> tree
                                                    //  .
    class Main {                                      //  │
    public static void main(String... args) {       //  ├───.bach
      System.out.println("Aria");                   //  └───air
    }                                               //
    }                                                 //
  • Run

    bach boot
    to open a JShell session. Explore core Java features by writing plain Java code snippets; and list Bach's overlay API by calling
  • Browse other Projects Using Bach - you're welcome to add yours!

  • Discuss ideas and file issues at Bach's GitHub page.


The JDK contains a set of foundation tools but none of them guides developers from processing Java source files into shippable products: be it a reusable modular JAR file with its API documentation or an entire custom runtime image. There exists however an implicit workflow encoded in the available tools and their options. The (binary) output of one tool is the input of one or more other tools. With the introduction of modules in Java 9 some structural parts of that workflow got promoted into the language itself and resulted in explicit module-related tool options.

These structural information, encoded explicitly by developers in Java's module descriptors (
files), serves as basic building blocks for Bach's project model. Their location within the file tree, their module name, and their
directives are examples of such information. In addition, Bach defines an annotation named
in order to let developers define extra configuration information. Included are project-specific values such as a short name, a version that is applied to all modules of the project, a path matcher defining where to find modules, and a lot more. Most of these project-specific values have pre-defined default values; some of these values provide an auto-configuration feature.

Here's an excerpt of .bach/ module declaration (using Bach's

import com.github.sormuras.bach.ProjectInfo;
import com.github.sormuras.bach.ProjectInfo.*;

@ProjectInfo( name = "bach", // short name of the project, defaults to current working directory's name version = "17-ea", // is often overridden via CLI's --project-version 17-ea+1c4b8cc option

modules = "*/main/java", // a glob or regex pattern describing paths to files
compileModulesForJavaRelease = 16,    // support releases are 8..17 (consult `javac --help`)
includeSourceFilesIntoModules = true, // treat source folders as resource folders
tools = @Tools(skip = "jlink"),       // limit and filter executable tools by their names

tweaks = {         // a set of tool-specific tweaks amending the computed tool call arguments
    @Tweak(tool = "javac", option = "-encoding", value = "UTF-8"), // JEP 400 will kill this line
    @Tweak(tool = "javac", option = "-g"),

) module { requires com.github.sormuras.bach; }

Yes, Bach builds Bach...

Bach 17+BOOTSTRAP+2021-03-15T08:22:58.121801746Z (file:///home/runner/work/bach/bach/.bach/bin/)
Build bach 17-ea+1c4b8cc
Verify external modules located in file:///home/runner/work/bach/bach/.bach/external-modules/
Verified 11 external modules
Build 1 main module: com.github.sormuras.bach
  javac    --release 16 --module com.github.sormuras.bach --module-version 17-ea+1c4b8cc --module-source..
  jar      --create --file .bach/workspace/modules/[email protected] --main-class com.g..
Check main modules
  jdeps    --check com.github.sormuras.bach --multi-release BASE --module-path .bach/workspace/modules:...
Generate API documentation
  javadoc  --module com.github.sormuras.bach --module-source-path ./*/main/java --module-path .bach/exte..
  jar      --create --file .bach/workspace/documentation/ --no-manifest -C .ba..
Generate and check Maven consumer POM file
  pomchecker check-maven-central --file /home/runner/work/bach/bach/.bach/workspace/deploy/maven/com.git..
Build 4 test modules: com.github.sormuras.bach, test.base, test.integration, test.projects
  javac    --module com.github.sormuras.bach,test.base,test.integration,test.projects --module-source-pa..
  jar      --verbose --create --file .bach/workspace/modules-test/[email protected]+test.jar -C .bach/..
  jar      --verbose --create --file .bach/workspace/modules-test/[email protected]+test.jar -C .bach/work..
  jar      --verbose --create --file .bach/workspace/modules-test/[email protected]+test.jar -C .ba..
  jar      --verbose --create --file .bach/workspace/modules-test/[email protected]+test.ja..
Launch JUnit Platform for each module
  junit    --select-module com.github.sormuras.bach --fail-if-no-tests --reports-dir .bach/workspace/rep..
  junit    --select-module test.base --fail-if-no-tests --reports-dir .bach/workspace/reports/junit/test..
  junit    --select-module test.integration --fail-if-no-tests --reports-dir .bach/workspace/reports/jun..
  junit    --select-module test.projects --fail-if-no-tests --config junit.jupiter.execution.parallel.en..
Build took 18.788s
Logbook written to file:///home/runner/work/bach/bach/.bach/workspace/



  • builds Java projects.
  • builds modular Java projects.
  • is a lightweight wrapper for existing and future tools, mainly foundation tools provided by the JDK.
  • supports running modularized tools registered via the
  • supports running tools packaged in executable JAR files (via Java's
  • can be invoked directly from the command line, or programmatically via its modular API (in a JShell session).
  • infers basic project information from
  • uses standard Java syntax for extra configuration purposes (via
  • supports creation of multi-release JARs (via javac's and jar's
  • helps resolving missing external dependences by downloading required modules into a single project-local directory.
  • launches the JUnit Platform Console (if provided as
    tool by the project).


Bach will not support "all features known from other build tools".

If a feature F is not provided by an underlying tool, Bach will not support F.

Bach will not...

  • support non-Java projects.
  • support non-modular Java projects.
  • provide a GUI for the tool.
  • resolve conflicting external dependencies.
  • deploy modules to external hosting services.

Configuration And Customization

  • Declare
  • Use Bach's CLI arguments to configure a specific build run. Consult the message produced by
    bach --help
    for available flags and options.
  • Use
    on module
    (locacted at
    ) to declare static configuration.
  • Extend
    to augment and alter the default behaviour or even write your own build program.

be free - have fun

jdk16 experimental


We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.