Skip to content

Commit bee7f3d

Browse files
committed
Support using Gradle's 'include' in settings plugin
This allows to configure project paths individually via the native include("...") statement while still using other functionality provided by the settings plugin.
1 parent a870abc commit bee7f3d

File tree

7 files changed

+178
-34
lines changed

7 files changed

+178
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Java Module Dependencies Gradle Plugin - Changelog
22

33
## Version 1.8
4+
* [#136](https://github.com/gradlex-org/java-module-dependencies/pull/136) Support hierarchical project paths in Settings DSL
45
* [#141](https://github.com/gradlex-org/java-module-dependencies/pull/141) Introduce `org.gradlex.java-module-dependencies.register-help-tasks` property
56
* [#127](https://github.com/gradlex-org/java-module-dependencies/issues/127) Less configuration cache misses when modifying `module-info.java` (Thanks [TheGoesen](https://github.com/TheGoesen))
67
* [#128](https://github.com/gradlex-org/java-module-dependencies/issues/128) Less configuration cache misses when using Settings plugin (Thanks [TheGoesen](https://github.com/TheGoesen))

README.MD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,19 @@ javaModules { // use instead of 'include(...)'
129129
}
130130
```
131131
132+
If you need more control over the properties of a Gradle subproject, in particular to define a nested project path,
133+
you can still use Gradle's `include(...)` and then register the subproject with this plugin.
134+
135+
```
136+
include(":project:with:custom:path")
137+
javaModules {
138+
module(project(":project:with:custom:path")) {
139+
group = "org.example" // define group early so that all subprojects know all groups
140+
plugin("java-library") // apply plugin to the Module's subproject to omit 'build.gradle'
141+
}
142+
}
143+
```
144+
132145
## Project structure definition when using this plugin as Project Plugin
133146
134147
In this setup, subprojects with Java Modules are configured as in any traditional Gradle build: by using the

src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.gradle.api.model.ObjectFactory;
2121
import org.gradle.api.provider.ListProperty;
2222
import org.gradle.api.provider.Property;
23-
import org.gradle.api.provider.Provider;
2423

2524
import javax.inject.Inject;
2625
import java.io.File;
@@ -80,8 +79,7 @@ public void module(String subDirectory, Action<Module> action) {
8079
}
8180

8281
Module addModule(String subDirectory) {
83-
Module module = getObjects().newInstance(Module.class, root);
84-
module.getDirectory().convention(subDirectory);
82+
Module module = getObjects().newInstance(Module.class, new File(root, subDirectory));
8583
module.getGroup().convention(getGroup());
8684
module.getPlugins().addAll(getPlugins());
8785
return module;

src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,27 @@ public void module(String directory) {
7272
* Register and configure Module located in the given folder, relative to the build root directory.
7373
*/
7474
public void module(String directory, Action<Module> action) {
75-
Module module = getObjects().newInstance(Module.class, settings.getRootDir());
76-
module.getDirectory().set(directory);
75+
Module module = getObjects().newInstance(Module.class, new File(settings.getRootDir(), directory));
7776
action.execute(module);
78-
includeModule(module, new File(settings.getRootDir(), module.getDirectory().get()));
77+
includeModule(module, new File(settings.getRootDir(), directory));
78+
}
79+
80+
/**
81+
* {@link JavaModulesExtension#module(ProjectDescriptor, Action)}
82+
*/
83+
public void module(ProjectDescriptor project) {
84+
module(project, m -> {});
85+
}
86+
87+
/**
88+
* Register and configure Module already registered as project by an 'include' statement.
89+
*/
90+
public void module(ProjectDescriptor project, Action<Module> action) {
91+
Module module = getObjects().newInstance(Module.class, project.getProjectDir());
92+
module.getArtifact().set(project.getName());
93+
module.getArtifact().finalizeValue(); // finalize, as the project name can no longer be changed
94+
action.execute(module);
95+
configureModule(module, project);
7996
}
8097

8198
/**
@@ -94,7 +111,7 @@ public void directory(String directory, Action<Directory> action) {
94111
action.execute(moduleDirectory);
95112

96113
for (Module module : moduleDirectory.customizedModules.values()) {
97-
includeModule(module, new File(modulesDirectory, module.getDirectory().get()));
114+
includeModule(module, module.directory);
98115
}
99116
Provider<List<String>> listProvider = getProviders().of(ValueModuleDirectoryListing.class, spec -> {
100117
spec.getParameters().getExclusions().set(moduleDirectory.getExclusions());
@@ -119,7 +136,7 @@ public void versions(String directory) {
119136
String projectName = Paths.get(directory).getFileName().toString();
120137
settings.include(projectName);
121138
settings.project(":" + projectName).setProjectDir(new File(settings.getRootDir(), directory));
122-
settings.getGradle().getLifecycle().beforeProject(new ApplyJavaModuleVersionsPluginAction(projectName));
139+
settings.getGradle().getLifecycle().beforeProject(new ApplyJavaModuleVersionsPluginAction(":" + projectName));
123140
}
124141

125142
private void includeModule(Module module, File projectDir) {
@@ -128,28 +145,32 @@ private void includeModule(Module module, File projectDir) {
128145
ProjectDescriptor project = settings.project(":" + artifact);
129146
project.setProjectDir(projectDir);
130147

148+
configureModule(module, project);
149+
}
150+
151+
private void configureModule(Module module, ProjectDescriptor project) {
131152
String mainModuleName = null;
132-
for (String path : module.getModuleInfoPaths().get()) {
133-
ModuleInfo moduleInfo = moduleInfoCache.put(projectDir, path,
134-
module.getArtifact().get(), module.getGroup(), settings.getProviders());
135-
if (path.contains("/main/")) {
153+
for (String moduleInfoPath : module.getModuleInfoPaths().get()) {
154+
ModuleInfo moduleInfo = moduleInfoCache.put(project.getProjectDir(), moduleInfoPath,
155+
project.getPath(), module.getArtifact().get(), module.getGroup(), settings.getProviders());
156+
if (moduleInfoPath.contains("/main/")) {
136157
mainModuleName = moduleInfo.getModuleName();
137158
}
138159
}
139160

140161
String group = module.getGroup().getOrNull();
141162
List<String> plugins = module.getPlugins().get();
142-
moduleProjects.add(new ModuleProject(artifact, group, plugins, mainModuleName));
163+
moduleProjects.add(new ModuleProject(project.getPath(), group, plugins, mainModuleName));
143164
}
144165

145166
private static class ModuleProject {
146-
private final String artifact;
167+
private final String path;
147168
private final String group;
148169
private final List<String> plugins;
149170
private final String mainModuleName;
150171

151-
public ModuleProject(String artifact, String group, List<String> plugins, String mainModuleName) {
152-
this.artifact = artifact;
172+
public ModuleProject(String path, String group, List<String> plugins, String mainModuleName) {
173+
this.path = path;
153174
this.group = group;
154175
this.plugins = plugins;
155176
this.mainModuleName = mainModuleName;
@@ -170,7 +191,7 @@ public ApplyPluginsAction(List<ModuleProject> moduleProjects, ModuleInfoCache mo
170191
@Override
171192
public void execute(Project project) {
172193
for (ModuleProject m : moduleProjects) {
173-
if (project.getName().equals(m.artifact)) {
194+
if (project.getPath().equals(m.path)) {
174195
if (m.group != null) project.setGroup(m.group);
175196
project.getPlugins().apply(JavaModuleDependenciesPlugin.class);
176197
project.getExtensions().getByType(JavaModuleDependenciesExtension.class).getModuleInfoCache().set(moduleInfoCache);
@@ -187,15 +208,15 @@ public void execute(Project project) {
187208
@NonNullApi
188209
private static class ApplyJavaModuleVersionsPluginAction implements IsolatedAction<Project> {
189210

190-
private final String projectName;
211+
private final String projectPath;
191212

192-
public ApplyJavaModuleVersionsPluginAction(String projectName) {
193-
this.projectName = projectName;
213+
public ApplyJavaModuleVersionsPluginAction(String projectPath) {
214+
this.projectPath = projectPath;
194215
}
195216

196217
@Override
197218
public void execute(Project project) {
198-
if (projectName.equals(project.getName())) {
219+
if (projectPath.equals(project.getPath())) {
199220
project.getPlugins().apply(JavaPlatformPlugin.class);
200221
project.getPlugins().apply(JavaModuleVersionsPlugin.class);
201222
project.getExtensions().getByType(JavaPlatformExtension.class).allowDependencies();

src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@
2828

2929
public abstract class Module {
3030

31-
/**
32-
* The directory, relative to the build root directory, in which the Module is located.
33-
*/
34-
public abstract Property<String> getDirectory();
35-
3631
/**
3732
* The 'artifact' name of the Module. This corresponds to the Gradle subproject name. If the Module is published
3833
* to a Maven repository, this is the 'artifact' in the 'group:artifact' identifier to address the published Jar.
@@ -58,14 +53,17 @@ public abstract class Module {
5853
*/
5954
public abstract ListProperty<String> getPlugins();
6055

56+
File directory;
57+
6158
@Inject
62-
public Module(File root) {
63-
getArtifact().convention(getDirectory().map(f -> Paths.get(f).getFileName().toString()));
64-
getModuleInfoPaths().convention(getDirectory().map(projectDir -> listChildren(root, projectDir + "/src")
59+
public Module(File directory) {
60+
this.directory = directory;
61+
getArtifact().convention(directory.getName());
62+
getModuleInfoPaths().convention(listSrcChildren()
6563
.map(srcDir -> new File(srcDir, "java/module-info.java"))
6664
.filter(File::exists)
6765
.map(moduleInfo -> "src/" + moduleInfo.getParentFile().getParentFile().getName() + "/java")
68-
.collect(Collectors.toList())));
66+
.collect(Collectors.toList()));
6967
}
7068

7169
/**
@@ -76,8 +74,8 @@ public void plugin(String id) {
7674
getPlugins().add(id);
7775
}
7876

79-
private Stream<File> listChildren(File root, String projectDir) {
80-
File[] children = new File(root, projectDir).listFiles();
77+
private Stream<File> listSrcChildren() {
78+
File[] children = new File(directory, "src").listFiles();
8179
return children == null ? Stream.empty() : Arrays.stream(children);
8280
}
8381
}

src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ public File getFolder(SourceSet sourceSet, ProviderFactory providers) {
8181
* @param projectRoot the project that should hold a Java module
8282
* @return parsed module-info.java for the given project assuming a standard Java project layout
8383
*/
84-
public ModuleInfo put(File projectRoot, String moduleInfoPath, String artifact, Provider<String> group, ProviderFactory providers) {
84+
public ModuleInfo put(File projectRoot, String moduleInfoPath, String projectPath, String artifact, Provider<String> group, ProviderFactory providers) {
8585
File folder = new File(projectRoot, moduleInfoPath);
8686
if (maybePutModuleInfo(folder, providers)) {
8787
ModuleInfo thisModuleInfo = moduleInfo.get(folder);
88-
moduleNameToProjectPath.put(thisModuleInfo.getModuleName(), ":" + artifact);
88+
moduleNameToProjectPath.put(thisModuleInfo.getModuleName(), projectPath);
8989
Path parentDirectory = Paths.get(moduleInfoPath).getParent();
9090
String capabilitySuffix = parentDirectory == null ? null : sourceSetToCapabilitySuffix(parentDirectory.getFileName().toString());
9191
if (capabilitySuffix != null) {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package org.gradlex.javamodule.dependencies.test.initialization
2+
3+
import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild
4+
import spock.lang.Specification
5+
6+
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
7+
8+
class SettingsPluginIncludeTest extends Specification {
9+
10+
@Delegate
11+
GradleBuild build = new GradleBuild()
12+
13+
def setup() {
14+
settingsFile.text = '''
15+
plugins { id("org.gradlex.java-module-dependencies") }
16+
'''
17+
appBuildFile.delete()
18+
libBuildFile.delete()
19+
}
20+
21+
def "can define included subprojects as modules"() {
22+
given:
23+
settingsFile << '''
24+
include(":project:with:custom:path")
25+
javaModules {
26+
module(project(":project:with:custom:path")) {
27+
group = "org.example"
28+
plugin("java-library")
29+
}
30+
module(project(":project:with:custom")) {
31+
group = "org.example"
32+
plugin("java-library")
33+
}
34+
}
35+
'''
36+
37+
file('project/with/custom/path/src/main/java/module-info.java') << 'module abc.liba { }'
38+
file('project/with/custom/src/main/java/module-info.java') << '''module abc.libb {
39+
requires abc.liba;
40+
}'''
41+
42+
when:
43+
def result = runner(':project:with:custom:compileJava').build()
44+
45+
then:
46+
result.task(":project:with:custom:path:compileJava").outcome == SUCCESS
47+
result.task(":project:with:custom:compileJava").outcome == SUCCESS
48+
}
49+
50+
def "can define included subprojects with custom project directory as modules"() {
51+
given:
52+
settingsFile << '''
53+
include(":project:with:custom:path")
54+
project(":project:with:custom:path").projectDir = file("lib")
55+
project(":project:with:custom").projectDir = file("app")
56+
javaModules {
57+
module(project(":project:with:custom:path")) {
58+
group = "org.example"
59+
plugin("java-library")
60+
}
61+
module(project(":project:with:custom")) {
62+
group = "org.example"
63+
plugin("java-library")
64+
}
65+
}
66+
'''
67+
68+
file("project/with").mkdirs()
69+
libModuleInfoFile << 'module abc.lib { }'
70+
appModuleInfoFile << '''module abc.app {
71+
requires abc.lib;
72+
}'''
73+
74+
when:
75+
def result = runner(':project:with:custom:jar').build()
76+
77+
then:
78+
result.task(":project:with:custom:path:compileJava").outcome == SUCCESS
79+
result.task(":project:with:custom:compileJava").outcome == SUCCESS
80+
file("lib/build/libs/path.jar").exists()
81+
file("app/build/libs/custom.jar").exists()
82+
}
83+
84+
def "projects with same name but different paths are supported"() {
85+
given:
86+
settingsFile << '''
87+
include(":app1:feature1:data")
88+
include(":app1:feature2:data")
89+
90+
rootProject.children.forEach { appContainer ->
91+
appContainer.children.forEach { featureContainer ->
92+
featureContainer.children.forEach { module ->
93+
javaModules.module(module) { plugin("java-library") }
94+
}
95+
}
96+
}
97+
'''
98+
99+
file('app1/feature1/data/src/main/java/module-info.java') << 'module f1x.data { }'
100+
file('app1/feature2/data/src/main/java/module-info.java') << '''module f2x.data {
101+
requires f1x.data;
102+
}'''
103+
104+
when:
105+
def result = runner(':app1:feature2:data:jar').build()
106+
107+
then:
108+
result.task(":app1:feature1:data:jar").outcome == SUCCESS
109+
result.task(":app1:feature2:data:jar").outcome == SUCCESS
110+
file("app1/feature1/data/build/libs/data.jar").exists()
111+
file("app1/feature2/data/build/libs/data.jar").exists()
112+
}
113+
}

0 commit comments

Comments
 (0)