// Copyright (C) 2024 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[part3_multi_project_builds]]
= Part 3: Multi-Project Builds

Learn the basics of structuring Gradle projects using subprojects and composite builds.

****
**In this section, you will:**

- Understand Multi-Project builds
- Understand Composite Builds
- Add a subproject to your Build
- Add a build to your Build
****

[[part3_begin]]
== Step 0. Before you Begin

1. You initialized your Java app in <<part1_gradle_init_project.adoc#part1_begin,part 1>>.
2. You understand the Gradle Build Lifecycle from <<part2_build_lifecycle.adoc#part2_begin,part 2>>.

[[ab_part3_step1]]
== Step 1. About Multi-Project Builds

Typically, builds contain multiple projects, such as shared libraries or separate applications that will be deployed in your ecosystem.

In Gradle, a multi-project build consists of:

- `settings.gradle(.kts)` file representing your Gradle build including required subprojects e.g. include("app", "model", "service")
- `build.gradle(.kts)` and source code for each subproject in corresponding subdirectories

Our build currently consists of a root project called `authoring-tutorial`, which has a single `app` subproject:

[.multi-language-sample]
=====
[source, kotlin]
----
.   // <1>
├── app // <2>
│   ... // <3>
│   └── build.gradle.kts // <4>
└── settings.gradle.kts  // <5>
----
<1> The `authoring-tutorial` root project
<2> The `app` subproject
<3> The `app` source code
<4> The `app` build script
<5> The optional settings file
=====
[.multi-language-sample]
=====
[source, groovy]
----
.   // <1>
├── app // <2>
│   ... // <3>
│   └── build.gradle // <4>
└── settings.gradle  // <5>
----
<1> The `authoring-tutorial` root project
<2> The `app` subproject
<3> The `app` source code
<4> The `app` build script
<5> The optional settings file
=====

[[ab_part3_step2]]
== Step 2. Add another Subproject to the Build

Imagine that our project is growing and requires a custom library to function.

Let's create this imaginary `lib`.
First, create a `lib` folder:

[source,bash]
----
$ mkdir lib
----

[source,bash]
----
$ cd lib
----

Create a file called `build.gradle(.kts)` and add the following lines to it:

====
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-libBuild/kotlin",files="build.gradle.kts[tags=lib-build]"]
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-libBuild/groovy",files="build.gradle[tags=lib-build]"]
====

Your project should look like this:

[.multi-language-sample]
=====
[source, kotlin]
----
.
├── app
│   ...
│   └── build.gradle.kts
├── lib
│   └── build.gradle.kts
└── settings.gradle.kts
----
=====
[.multi-language-sample]
=====
[source, groovy]
----
.
├── app
│   ...
│   └── build.gradle
├── lib
│   └── build.gradle
└── settings.gradle
----
=====

Let's add some code to the `lib` subproject.
Create a new directory:

[source,bash]
----
$ mkdir -p lib/src/main/java/com/gradle
----

Create a Java class called `CustomLib` in a file called `CustomLib.java` with the following source code:

.lib/src/main/java/com/gradle/CustomLib.java
[source,java]
----
package com.gradle;

public class CustomLib {
    public static String identifier = "I'm a String from a lib.";
}
----

The project should now have the following file and directory structure:

[.multi-language-sample]
=====
[source, kotlin]
----
.
├── app
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── java
│               └── authoring
│                   └── tutorial
│                       └── App.java
├── lib
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── gradle
│                       └── CustomLib.java
└── settings.gradle.kts
----
=====
[.multi-language-sample]
=====
[source, groovy]
----
.
├── app
│   ├── build.gradle
│   └── src
│       └── main
│           └── java
│               └── authoring
│                   └── tutorial
│                       └── App.java
├── lib
│   ├── build.gradle
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── gradle
│                       └── CustomLib.java
└── settings.gradle
----
=====

However, the `lib` subproject does not belong to the build, and you won't be able to execute `task3`, until it is added to the `settings.gradle(.kts)` file.

To add `lib` to the build, update the `settings.gradle(.kts)` file in the root accordingly:

====
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-addSubproject/kotlin",files="settings.gradle.kts[tags=add-subproject]"]
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-addSubproject/groovy",files="settings.gradle[tags=add-subproject]"]
====

Let's add the `lib` subproject as an `app` dependency in `app/build.gradle(.kts)`:

[.multi-language-sample]
=====
.app/build.gradle.kts
[source,kotlin]
----
dependencies {
    implementation(project(":lib")) // Add lib as an app dependency
}
----
=====
[.multi-language-sample]
=====
.app/build.gradle
[source, groovy]
----
dependencies {
    implementation(project(':lib')) // Add lib as an app dependency
}
----
=====

Update the `app` source code so that it imports the `lib`:

.app/src/main/java/authoring/tutorial/App.java
[source,java]
----
package authoring.tutorial;

import com.gradle.CustomLib;

public class App {
    public String getGreeting() {
        return "CustomLib identifier is: " + CustomLib.identifier;
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}
----

Finally, let's run the `app` with the command `./gradlew run`:

[source,bash]
----
$ ./gradlew run
----
[source,text]
----
> Configure project :app

> Task :app:processResources NO-SOURCE
> Task :lib:compileJava
> Task :lib:processResources NO-SOURCE
> Task :lib:classes
> Task :lib:jar
> Task :app:compileJava
> Task :app:classes

> Task :app:run
CustomLib identifier is: I'm a String from a lib.

BUILD SUCCESSFUL in 11s
8 actionable tasks: 6 executed, 2 up-to-date
----

Our build for the root project `authoring-tutorial` now includes two subprojects, `app` and `lib`.
`app` depends on `lib`.
You can build `lib` independent of `app`.
However, to build `app`, Gradle will also build `lib`.

[[step_3_understand_composite_builds]]
== Step 3. Understand Composite Builds

A composite build is simply a build that includes other builds.

Composite builds allow you to:

- Extract your build logic from your project build (and re-use it among subprojects)
- Combine builds that are usually developed independently (such as a plugin and an application)
- Decompose a large build into smaller, more isolated chunks

[[ab_part3_step4]]
== Step 4. Add build to the Build

Let's add a plugin to our build.
First, create a new directory called `license-plugin` in the `gradle` directory:

[source,bash]
----
$ cd gradle
----

[source,bash]
----
$ mkdir license-plugin
----

[source,bash]
----
$ cd license-plugin
----

Once in the `gradle/license-plugin` directory, run `gradle init`.
Make sure that you select the `Gradle plugin` project as well as the other options for the `init` task below:

[.multi-language-sample]
=====
[source, kotlin]
----
$ gradle init --dsl kotlin --type kotlin-gradle-plugin --project-name license
----
=====
[.multi-language-sample]
=====
[source, groovy]
----
$ gradle init --dsl groovy --type groovy-gradle-plugin --project-name license
----
=====

Select defaults for any additional prompts.

Your project should look like this:

[.multi-language-sample]
=====
[source, kotlin]
----
.
├── app
│   ...
│   └── build.gradle.kts
├── lib
│   ...
│   └── build.gradle.kts
├── gradle
│    ├── ...
│    └── license-plugin
│        ├── settings.gradle.kts
│        └── plugin
│            ├── gradle
│            │   └── ....
│            ├── src
│            │   ├── functionalTest
│            │   │   └── ....
│            │   ├── main
│            │   │   └── kotlin
│            │   │       └── license
│            │   │           └── LicensePlugin.kt
│            │   └── test
│            │       └── ...
│            └── build.gradle.kts
│
└── settings.gradle.kts
----
=====
[.multi-language-sample]
=====
[source, groovy]
----
.
├── app
│   ...
│   └── build.gradle
├── lib
│   ...
│   └── build.gradle
├── gradle
│    ├── ...
│    └── license-plugin
│        ├── settings.gradle
│        └── plugin
│            ├── gradle
│            │   └── ....
│            ├── src
│            │   ├── functionalTest
│            │   │   └── ....
│            │   ├── main
│            │   │   └── groovy
│            │   │       └── license
│            │   │           └── LicensePlugin.groovy
│            │   └── test
│            │       └── ...
│            └── build.gradle
│
└── settings.gradle
----
=====

Take the time to look at the `LicensePlugin.kt` or `LicensePlugin.groovy` code and the `gradle/license-plugin/settings.gradle(.kts)` file.
It's important to note that this is an entirely separate build with its own settings file and build script:

[.multi-language-sample]
=====
.gradle/license-plugin/settings.gradle.kts
[source,kotlin]
----
rootProject.name = "license"
include("plugin")
----
=====
[.multi-language-sample]
=====
.gradle/license-plugin/settings.gradle
[source, groovy]
----
rootProject.name = 'license'
include('plugin')
----
=====

To add our `license-plugin` build to the root project, update the root `settings.gradle(.kts)` file accordingly:

====
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-addBuild/kotlin",files="settings.gradle.kts[tags=add-build]"]
include::sample[dir="snippets/tutorials/authoring-builds/multiProjectBuilds-addBuild/groovy",files="settings.gradle[tags=add-build]"]
====

You can view the structure of the root project by running `./gradlew projects` in the root folder `authoring-tutorial`:

[source,bash]
----
$ ./gradlew projects
----
[source,text]
----
------------------------------------------------------------
Root project 'authoring-tutorial'
------------------------------------------------------------

Root project 'authoring-tutorial'
+--- Project ':app'
\--- Project ':lib'

Included builds
\--- Included build ':license-plugin'
----

Our build for the root project `authoring-tutorial` now includes two subprojects, `app` and `lib`, and another build, `license-plugin`.

When in the project root, running:

- `./gradlew build` - Builds `app` and `lib`
- `./gradlew :app:build` - Builds `app` and `lib`
- `./gradlew :lib:build` - Builds `lib` only
- `./gradlew :license-plugin:plugin:build` - Builds `license-plugin` only

There are many ways to design a project's architecture with Gradle.

Multi-project builds are great for organizing projects with many modules such as `mobile-app`, `web-app`, `api`, `lib`, and `documentation` that have dependencies between them.

Composite (include) builds are great for separating build logic (i.e., convention plugins) or testing systems (i.e., patching a library)

[.text-right]
**Next Step:** <<part4_settings_file#part4_settings_file,Settings File>> >>
