// 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>>.

== 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
=====

== 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,text]
----
mkdir lib
----

[source,text]
----
cd lib
----

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

[.multi-language-sample]
=====
.lib/build.gradle.kts
[source,kotlin]
----
plugins {
    id("java")
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.3")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation("com.google.guava:guava:32.1.1-jre")
}

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

tasks.register("task3"){
    println("REGISTER TASK3: This is executed during the configuration phase")
}

tasks.named("task3"){
    println("NAMED TASK3: This is executed during the configuration phase")
    doFirst {
        println("NAMED TASK3 - doFirst: This is executed during the execution phase")
    }
    doLast {
        println("NAMED TASK3 - doLast: This is executed during the execution phase")
    }
}
----
=====
[.multi-language-sample]
=====
.lib/build.gradle
[source, groovy]
----
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation 'com.google.guava:guava:32.1.1-jre'
}

test {
    useJUnitPlatform()
}

tasks.register('task3') {
    println('REGISTER TASK3: This is executed during the configuration phase')
}

tasks.named('task3') {
    println('NAMED TASK3: This is executed during the configuration phase')
    doFirst {
        println('NAMED TASK3 - doFirst: This is executed during the execution phase')
    }
    doLast {
        println('NAMED TASK3 - doLast: This is executed during the execution phase')
    }
}
----
=====

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,text]
----
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:

[.multi-language-sample]
=====
.settings.gradle.kts
[source,kotlin]
----
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}

rootProject.name = "authoring-tutorial"

include("app")
include("lib") // Add lib to the build
----
=====
[.multi-language-sample]
=====
.settings.gradle
[source, groovy]
----
plugins {
    id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}

rootProject.name = 'authoring-tutorial'

include('app')
include('lib') // Add lib to the build
----
=====

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]
----
$ ./gradlew run

> 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

== 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]
----
cd gradle
----

[source]
----
mkdir license-plugin
----

[source]
----
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:

[.multi-language-sample]
=====
.settings.gradle.kts
[source,kotlin]
----
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}

rootProject.name = "authoring-tutorial"

include("app")
include("subproject")

includeBuild("gradle/license-plugin") // Add the new build
----
=====
[.multi-language-sample]
=====
.settings.gradle
[source, groovy]
----
plugins {
    id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}

rootProject.name = 'running-tutorial-groovy'

include('app')
include('lib')

includeBuild('gradle/license-plugin')
----
=====

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

[source]
----
$ ./gradlew projects

------------------------------------------------------------
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>> >>
