How to do multi-project in Gradle.
Before multi-project, first try creating and running a jar file in a single project.
gradle initialization
$ gradle init
Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1
Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1
Project name (default: JavaMultiProject): 
> Task :init
Get more help with your project: https://guides.gradle.org/creating-new-gradle-builds
BUILD SUCCESSFUL in 6s
2 actionable tasks: 2 executed
Modify the automatically created build.gradle file a bit. The important thing is to create the jar task.
build.gradle
apply plugin: 'java'
apply plugin: 'application'
sourceCompatibility = 11
targetCompatibility = 11
repositories {
    jcenter()
}
dependencies {
}
mainClassName = 'App'
jar {
    manifest {
        attributes 'Main-Class': "App"
    }
}
Create a source to run.
src/main/java/App.java
public class App {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}
build and run
$ gradle build
BUILD SUCCESSFUL in 2s
5 actionable tasks: 1 executed, 4 up-to-date
$ java -jar build/libs/JavaMultiProject.jar 
Hello
You have successfully created and run the jar file in a single project.
Now let's create a multi-project.
This is the directory structure.
Constitution
.(JavaMultiProject)
├── bar  (Application project)
│   ├── src
│   ├── build.gradle
├── foo  (Application project)
│   ├── src
│   ├── build.gradle
├── share  (Project to be used as a common library)
│   ├── src
│   ├── build.gradle
├── build.gradle
├── setting.gradle
Create a directory.
python
$ mkdir -p bar/src/main/java
$ mkdir -p foo/src/main/java
$ mkdir -p share/src/main/java
If you want to make a multi-project, set it in setting.gradle.
Specify the subproject in include.
setting.gradle
rootProject.name = 'JavaMultiProject'
include 'foo', 'bar', 'share'
This makes each for`` bar share directory recognized as a subproject.
IntelliJ IDEA also recognizes the project in this way.
![]()  | 
|---|
In build.gradle that exists in the project root, specify the settings that apply to all subprojects by specifying subprojects.
build.gradle(root)
allprojects {
}
subprojects {
    apply plugin: 'java'
    sourceCompatibility = 11
    targetCompatibility = 11
    repositories {
        jcenter()
    }
}
Next is the subproject build.gradle.
Let's start with the application project bar.
bar/build.gradle
apply plugin: 'application'
dependencies {
    implementation project(":share")
}
mainClassName = 'com.example.Bar'
jar {
    manifest {
        attributes 'Main-Class': "com.example.Bar"
    }
    from configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
To explain the important part, The ʻimplementation project (": share") `in dependencies defines that it depends on another subproject, share.
I also specified from ~ in the jar task, which means that the dependent libraries are included in the jar of the Bar project.
By the way, if the from method does not exist, the NoClassDefFoundError error will be output because the dependent library is not included in the jar. Just in case I got stuck with this.
python
$ java -jar bar/build/libs/bar.jar 
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/Hello
	at com.example.Bar.main(Bar.java:5)
Caused by: java.lang.ClassNotFoundException: com.example.Hello
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
	... 1 more
The foo project is almost the same as the bar project, but I'll mention it just in case. Only mainClassName is changed to Foo.
foo/build.gradle
apply plugin: 'application'
dependencies {
    implementation project(":share")
}
mainClassName = 'com.example.Foo'
jar {
    manifest {
        attributes 'Main-Class': "com.example.Foo"
    }
    from configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
share's build.gradle doesn't mention anything this time.
share/build.gradle
Finally, I will write the code.
bar/src/main/java/com/exmaple/Bar.java
package com.example;
public class Bar {
    public static void main(String[] args) {
        var hello = new Hello("Bar");
        System.out.println(hello.say());
    }
}
foo/src/main/java/com/exmaple/Foo.java
package com.example;
public class Foo {
    public static void main(String[] args) {
        var hello = new Hello("Foo");
        System.out.println(hello.say());
    }
}
share/src/main/java/com/exmaple/Hello.java
package com.example;
public class Hello {
    private final String name;
    public Hello(String name) {
        this.name = name;
    }
    public String say() {
        return name + " Hello!";
    }
}
I will try to build.
Subproject tasks are executed with : (project name): (task name).
Also, the build of the share project on which bar depends will be executed at the same time.
python
$ gradle :bar:build
BUILD SUCCESSFUL in 804ms
7 actionable tasks: 7 up-to-date
$ ls -l bar/build/libs/
total 8
-rw-r--r--  1 ysaito  staff  1591  4 29 21:15 bar.jar
If you unzip the created jar for reference, you can see that Hello.class inside the share is included in the created bar.jar.
Unzip the jar and check
$ jar xvf bar.jar 
  META-INF/Was created
 META-INF/MANIFEST.MF was deployed
  com/Was created
  com/example/Was created
 com/example/Bar.class has been expanded
 com/example/Hello.class has been expanded
Now that the jar is created, let's run it.
python
$ java -jar bar/build/libs/bar.jar 
Bar Hello!
$ java -jar foo/build/libs/foo.jar 
Foo Hello!
Gradle User Guide-> Build Multi-Project
Recommended Posts