This guide explains how to replace a module dependency with a local fork of the module’s sources, assuming the module itself is built with Gradle.

Using a local fork allows you to:

  • Apply and test custom patches to a dependency.

  • Work with an unreleased version of a library.

  • Avoid relying on the published binary version.

Prerequisites

  • The module must be built with Gradle.

  • You must have a local copy of the module’s source code.

  • The local module should be set up as a Gradle build.

Gradle composite builds automatically substitute external dependencies with local forks.

Step 1: Create a Composite Build

Assume your initial project structure looks like this:

project
├── settings.gradle(.kts)               (1)
└── my-app
    ├── build.gradle(.kts)              (2)
    └── src                             (3)
1 Existing settings file
2 Existing build file
3 Existing app project that uses the external dependency

In this example, our app depends on com.squareup.okhttp:okhttp:2.7.5, and we want to use a local fork of it:

my-app/build.gradle.kts
dependencies {
    implementation("com.squareup.okhttp:okhttp:2.7.5")
}
my-app/build.gradle
dependencies {
    implementation("com.squareup.okhttp:okhttp:2.7.5")
}

First, create a new folder for your local fork of the dependency:

project
├── settings.gradle(.kts)
├── my-app
│   ├── build.gradle(.kts)
│   └── src
└── my-fork     (1)
1 New directory for the composite build containing the local fork

In your root project’s settings.gradle(.kts) file, add an includeBuild statement pointing to my-fork:

settings.gradle.kts
rootProject.name = "how_to_use_a_local_fork"

include("my-app")
includeBuild("my-fork")  // Path to your local fork
settings.gradle
rootProject.name = "how_to_use_a_local_fork"

include("my-app")
includeBuild("my-fork")  // Path to your local fork

This instructs Gradle to automatically substitute the external dependency coordinates with your local build.

Step 2: Include The Local Fork

Composite builds allow us to use a local version of okhttp with minimal configuration changes.

Assuming the local fork is already a Gradle build, copy or move it into the my-fork directory. The forked module will have its own settings and build files.

The updated project structure will look like this:

project
├── settings.gradle(.kts)
├── my-app
│   ├── build.gradle(.kts)
│   └── src
└── my-fork                             (1)
    ├── settings.gradle(.kts)           (2)
    └── okhttp
        ├── build.gradle(.kts)          (3)
        └── src                         (4)
1 New included build for fork of okhttp
2 Settings file for the forked module
3 Build file for the forked module
4 Source code of the forked module

The source code of com.squareup.okhttp:okhttp:2.7.5 is in the okhttp/src folder.

The settings file should look like this:

my-fork/settings.gradle.kts
rootProject.name = "my-fork"

include("okhttp") // The forked module as a subproject
my-fork/settings.gradle
rootProject.name = "my-fork"

include("okhttp") // The forked module as a subproject

The build file must have the same GAV coordinates as required by my-app:

my-fork/okhttp/build.gradle.kts
plugins {
    id("java-library")
}

// Matches implementation("com.squareup.okhttp:okhttp:2.7.5") in my-app build file
group = "com.squareup.okhttp" // Matches original dependency
version = "2.7.5" // Matches original dependency version = "2.7.5"
my-fork/okhttp/build.gradle
plugins {
    id("java-library")
}

// Matches implementation("com.squareup.okhttp:okhttp:2.7.5") in my-app build file
group = "com.squareup.okhttp" // Matches original dependency
version = "2.7.5" // Matches original dependency version = "2.7.5"

Step 3: Declare the Dependency Normally

In your project’s build.gradle(.kts), continue declaring the dependency using the same external coordinates as originally:

my-app/build.gradle.kts
repositories {
    mavenCentral() // Don't remove this!
}

dependencies {
    implementation("com.squareup.okhttp:okhttp:2.7.5") // This doesn't need to change!
}
my-app/build.gradle
repositories {
    mavenCentral() // Don't remove this!
}

dependencies {
    implementation("com.squareup.okhttp:okhttp:2.7.5") // This doesn't need to change!
}

You don’t need explicit substitution statements in the build.gradle(.kts) file; Gradle handles substitution automatically.

With this setup, Gradle will automatically use your local fork instead of downloading the dependency from remote repositories.

Step 4: Troubleshooting

If Gradle is still attempting to resolve your dependency externally, verify that:

  • The local fork (build.gradle.kts) specifies exactly the same coordinates (group, name, and version) as your external dependency.

  • Your settings.gradle.kts correctly references your local fork with includeBuild.

Summary

Gradle composite builds provide a straightforward and efficient method to substitute external module dependencies with local forks.