# Building Java Libraries Sample

version 9.1.0-20250829161021+0000

- Groovy DSL

- Kotlin DSL

| You can open this sample in an IDE that supports Gradle.

This guide demonstrates how to create a Java library with Gradle using `gradle init`. You can follow the guide step-by-step to create a new project from scratch or download the complete sample project using the links above.

## What you’ll build

You’ll generate a Java library that follows Gradle’s conventions.

## What you’ll need

- A text editor or IDE - for example IntelliJ IDEA

- A Java Development Kit (JDK), version 8 or higher - for example AdoptOpenJDK

- The latest Gradle distribution

## Create a project folder

Gradle comes with a built-in task, called `init`, that initializes a new Gradle project in an empty folder. The `init` task uses the (also built-in) `wrapper` task to create a Gradle wrapper script, `gradlew`.

The first step is to create a folder for the new project and change directory into it.

    $ mkdir demo
    $ cd demo

## Run the init task

From inside the new project directory, run the `init` task using the following command in a terminal: `gradle init`. When prompted, select the `2: library` project type and `1: Java` as the implementation language. Next you can choose the DSL for writing buildscripts - `1 : Kotlin` or `2: Groovy`. For the other questions, press enter to use the default values.

The output will look like this:

    $ gradle init

    Select type of build to generate:
      1: Application
      2: Library
      3: Gradle plugin
      4: Basic (build structure only)
    Enter selection (default: Application) [1..4] 2

    Select implementation language:
      1: Java
      2: Kotlin
      3: Groovy
      4: Scala
      5: C++
      6: Swift
    Enter selection (default: Java) [1..6] 1

    Enter target Java version (min: 7, default: 21):

    Project name (default: demo):

    Select application structure:
      1: Single application project
      2: Application and library project
    Enter selection (default: Single application project) [1..2] 1

    Select build script DSL:
      1: Kotlin
      2: Groovy
    Enter selection (default: Kotlin) [1..2]

    Select test framework:
      1: JUnit 4
      2: TestNG
      3: Spock
      4: JUnit Jupiter
    Enter selection (default: JUnit Jupiter) [1..4]

    Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

    BUILD SUCCESSFUL
    1 actionable task: 1 executed

The `init` task generates the new project with the following structure:

    ├── gradle (1)
    │   ├── libs.versions.toml (2)
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew (3)
    ├── gradlew.bat (3)
    ├── settings.gradle.kts (4)
    └── lib
        ├── build.gradle.kts (5)
        └── src
            ├── main
            │   └── java (6)
            │       └── demo
            │           └── Library.java
            └── test
                └── java (7)
                    └── demo
                        └── LibraryTest.java

    ├── gradle (1)
    │   ├── libs.versions.toml (2)
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew (3)
    ├── gradlew.bat (3)
    ├── settings.gradle (4)
    └── lib
        ├── build.gradle (5)
        └── src
            ├── main
            │   └── java (6)
            │       └── demo
            │           └── Library.java
            └── test
                └── java (7)
                    └── demo
                        └── LibraryTest.java

1 | Generated folder for wrapper files
2 | Generated version catalog
3 | Gradle wrapper start scripts
4 | Settings file to define build name and subprojects
5 | Build script of `lib` project
6 | Default Java source folder
7 | Default Java test source folder

You now have the project setup to build a Java library.

## Review the project files

The `settings.gradle(.kts)` file has two interesting lines:

settings.gradle.kts

    rootProject.name = "demo"
    include("lib")

settings.gradle

    rootProject.name = 'demo'
    include('lib')

- `rootProject.name` assigns a name to the build, which overrides the default behavior of naming the build after the directory it’s in. It’s recommended to set a fixed name as the folder might change if the project is shared - e.g. as root of a Git repository.

- `include("lib")` defines that the build consists of one subproject called `lib` that contains the actual code and build logic. More subprojects can be added by additional `include(…​)` statements.

Our build contains one subproject called `lib` that represents the Java library we are building. It is configured in the `lib/build.gradle(.kts)` file:

lib/build.gradle.kts

    plugins {
        `java-library` (1)
    }

    repositories {
        mavenCentral() (2)
    }

    dependencies {
        testImplementation(libs.junit.jupiter) (3)

        testRuntimeOnly("org.junit.platform:junit-platform-launcher")

        api(libs.commons.math3) (4)

        implementation(libs.guava) (5)
    }

    tasks.named<Test>("test") {
        useJUnitPlatform() (6)
    }

lib/build.gradle

    plugins {
        id 'java-library' (1)
    }

    repositories {
        mavenCentral() (2)
    }

    dependencies {
        testImplementation libs.junit.jupiter (3)

        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

        api libs.commons.math3 (4)

        implementation libs.guava (5)
    }

    tasks.named('test') {
        useJUnitPlatform() (6)
    }

1 | Apply the java-library plugin for API and implementation separation.
2 | Use Maven Central for resolving dependencies.
3 | Use JUnit Jupiter for testing.
4 | This dependency is exported to consumers, that is to say found on their compile classpath.
5 | This dependency is used internally, and not exposed to consumers on their own compile classpath.
6 | Use JUnit Platform for unit tests.

The file `src/main/java/demo/Library.java` is shown here:

Generated src/main/java/demo/Library.java

    /*
     * This source file was generated by the Gradle 'init' task
     */
    package demo;

    public class Library {
        public boolean someLibraryMethod() {
            return true;
        }
    }

The generated test, `src/test/java/demo/Library.java` is shown next:

Generated src/test/java/demo/LibraryTest.java

    /*
     * This source file was generated by the Gradle 'init' task
     */
    package demo;

    import org.junit.jupiter.api.Test;
    import static org.junit.jupiter.api.Assertions.*;

    class LibraryTest {
        @Test void someLibraryMethodReturnsTrue() {
            Library classUnderTest = new Library();
            assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
        }
    }

The generated test class has a single JUnit Jupiter test. The test instantiates the `Library` class, invokes a method on it, and checks that it returns the expected value.

More information about the features the `java-library` plugin adds to any JVM library project, such as API and implementation separation, can be found in the Java Library Plugin documentation.

## Assemble the library JAR

To build the project, run the `build` task. You can use the regular `gradle` command, but when a project includes a wrapper script, it is considered good form to use it instead.

    $ ./gradlew build

    BUILD SUCCESSFUL in 0s
    4 actionable tasks: 4 executed

| The first time you run the wrapper script, `gradlew`, there may be a delay while that version of `gradle` is downloaded and stored locally in your `~/.gradle/wrapper/dists` folder.

The first time you run the build, Gradle will check whether or not you already have the required dependencies in your cache under your `~/.gradle` directory. If not, the libraries will be downloaded and stored there. The next time you run the build, the cached versions will be used. The `build` task compiles the classes, runs the tests, and generates a test report.

You can view the test report by opening the HTML output file, located at `lib/build/reports/tests/test/index.html`.

You can find your newly packaged JAR file in the `lib/build/libs` directory with the name `lib.jar`. Verify that the archive is valid by running the following command:

    $ jar tf lib/build/libs/lib.jar
    META-INF/
    META-INF/MANIFEST.MF
    lib/
    lib/Library.class

You should see the required manifest file —`MANIFEST.MF`— and the compiled `Library` class.

| All of this happens without any additional configuration in the build script because Gradle’s `java-library` plugin assumes your project sources are arranged in a conventional project layout. You can customize the project layout if you wish as described in the user manual.

Congratulations, you have just completed the first step of creating a Java library! You can now customize this to your own project needs.

## Customize the library JAR

You will often want the name of the JAR file to include the library version. This is achieved by setting a top-level `version` property in the build script:

build.gradle.kts

    version = "0.1.0"

build.gradle

    version = '0.1.0'

| Next to the version, other important identity properties of a library are it’s name and group. The name is directly derived from the subproject name that represents the library. It’s `lib` in the example so you probably want to adjust it by changing the name of the `lib` folder and the corresponding `include(…​)` statement in the `settings.gradle(.kts)` file. The group is used to give your library full coordinates when published. You can define it directly in the build script by setting the `group` property similar to how you set the version (shown above).

Now run the `jar` task:

    $ ./gradlew jar

    BUILD SUCCESSFUL
    2 actionable tasks: 1 executed, 1 up-to-date

You’ll notice that the resulting JAR file at `lib/build/libs/lib-0.1.0.jar` contains the version as expected.

Another common requirement is customizing the manifest file, typically by adding one or more attributes. Let’s include the library name and version in the manifest file by configuring the `jar` task. Add the following to the end of your build script:

build.gradle.kts

    tasks.jar {
        manifest {
            attributes(mapOf("Implementation-Title" to project.name,
                             "Implementation-Version" to project.version))
        }
    }

build.gradle

    tasks.named('jar') {
        manifest {
            attributes('Implementation-Title': project.name,
                       'Implementation-Version': project.version)
        }
    }

To confirm that these changes work as expected, run the `jar` task again, and this time also unpack the manifest file from the JAR:

    $ ./gradlew jar
    $ jar xf lib/build/libs/lib-0.1.0.jar META-INF/MANIFEST.MF

Now view the contents of the `META-INF/MANIFEST.MF` file and you should see the following:

META-INF/MANIFEST.MF

    Manifest-Version: 1.0
    Implementation-Title: lib
    Implementation-Version: 0.1.0

## Generating Sources JAR

You can easily generate a sources JAR for your library:

build.gradle.kts

    java {
        withSourcesJar()
    }

build.gradle

    java {
        withSourcesJar()
    }

The additional JAR will be produced as part of the `assemble` or `build` lifecycle tasks and will be part of the publication. The resulting file is found in `lib/build/libs`, with a name using the conventional classifier `-sources`.

## Adding API documentation

The `java-library` plugin has built-in support for Java’s API documentation tool via the `javadoc` task.

The code generated by the Build Init plugin already placed a comment on the `demo/Library.java` file. Replace `/` in the comment by `/*` so that it becomes `javadoc` markup:

src/main/java/demo/Library.java

    /**
     * This java source file was generated by the Gradle 'init' task.
     */
     ...

Run the `javadoc` task.

    $ ./gradlew javadoc

    BUILD SUCCESSFUL
    2 actionable tasks: 1 executed, 1 up-to-date

You can view the generated `javadoc` files by opening the HTML file located at `lib/build/docs/javadoc/index.html`.

You can also generate a Javadoc JAR for your library:

build.gradle.kts

    java {
        withJavadocJar()
    }

build.gradle

    java {
        withJavadocJar()
    }

The additional JAR will be produced as part of the `assemble` or `build` lifecycle tasks and will be part of the publication. The resulting file is found in `lib/build/libs`, with a name using the conventional classifier `-javadoc`.

## Publish a Build Scan

The best way to learn more about what your build is doing behind the scenes, is to publish a Build Scan. To do so, just run Gradle with the `--scan` flag.

    $ ./gradlew build --scan

    BUILD SUCCESSFUL in 0s
    4 actionable tasks: 4 executed

    Publishing a Build Scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service.
    Do you accept these terms? [yes, no] yes

    Gradle Terms of Service accepted.

    Publishing Build Scan...
    https://gradle.com/s/5u4w3gxeurtd2

Click the link and explore which tasks where executed, which dependencies where downloaded and many more details!

## Summary

That’s it! You’ve now successfully configured and built a Java library project with Gradle. You’ve learned how to:

- Initialize a project that produces a Java library

- Run the build and view the test report

- Customize the Jar files the build produces

Now you could complete this exercise by trying to compile some Java code that uses the library you just built.

## Next steps

Building a library is just one aspect of reusing code across project boundaries. From here, you may be interested in:

- Building Java & JVM projects

- Java Library Plugin documentation

- Consuming JVM libraries using dependency management

- Publishing JVM libraries

- Working with multi-project builds