Skip to content

Commit ffe75b9

Browse files
committed
Add DSL for module dependencies that cannot be defined in module-info
1 parent 6411b61 commit ffe75b9

File tree

4 files changed

+156
-66
lines changed

4 files changed

+156
-66
lines changed

src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesExtension.java

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package org.gradlex.javamodule.dependencies;
1818

19+
import org.gradle.api.Project;
1920
import org.gradle.api.Task;
2021
import org.gradle.api.artifacts.Configuration;
2122
import org.gradle.api.artifacts.ConfigurationContainer;
23+
import org.gradle.api.artifacts.Dependency;
24+
import org.gradle.api.artifacts.ProjectDependency;
2225
import org.gradle.api.artifacts.VersionCatalog;
2326
import org.gradle.api.artifacts.VersionCatalogsExtension;
2427
import org.gradle.api.artifacts.VersionConstraint;
@@ -41,11 +44,14 @@
4144
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfoCache;
4245

4346
import javax.inject.Inject;
47+
import java.io.File;
4448
import java.util.Collections;
49+
import java.util.Comparator;
4550
import java.util.HashMap;
4651
import java.util.List;
4752
import java.util.Map;
4853
import java.util.Optional;
54+
import java.util.stream.Collectors;
4955

5056
import static java.util.Optional.empty;
5157

@@ -142,6 +148,53 @@ private Provider<String> mapByPrefix(Provider<String> moduleName) {
142148
);
143149
}
144150

151+
public Provider<Dependency> create(String moduleName, SourceSet sourceSetWithModuleInfo) {
152+
return getProviders().provider(() -> {
153+
Map<String, String> allProjectNamesAndGroups = getProject().getRootProject().getSubprojects().stream().collect(
154+
Collectors.toMap(Project::getName, p -> (String) p.getGroup()));
155+
156+
Provider<Map<String, Object>> gav = getModuleNameToGA().getting(moduleName).orElse(mapByPrefix(getProviders().provider(() -> moduleName))).map(ga -> findGav(ga, moduleName));
157+
158+
ModuleInfo moduleInfo = getModuleInfoCache().get(sourceSetWithModuleInfo);
159+
String ownModuleNamesPrefix = moduleInfo.moduleNamePrefix(getProject().getName(), sourceSetWithModuleInfo.getName());
160+
161+
String moduleNameSuffix = ownModuleNamesPrefix == null ? null :
162+
moduleName.startsWith(ownModuleNamesPrefix + ".") ? moduleName.substring(ownModuleNamesPrefix.length() + 1) :
163+
ownModuleNamesPrefix.isEmpty() ? moduleName : null;
164+
165+
String parentPath = getProject().getParent() == null ? "" : getProject().getParent().getPath();
166+
Optional<String> perfectMatch = allProjectNamesAndGroups.keySet().stream().filter(p -> p.replace("-", ".").equals(moduleNameSuffix)).findFirst();
167+
Optional<String> existingProjectName = allProjectNamesAndGroups.keySet().stream().filter(p -> moduleNameSuffix != null && moduleNameSuffix.startsWith(p.replace("-", ".") + "."))
168+
.max(Comparator.comparingInt(String::length));
169+
170+
if (perfectMatch.isPresent()) {
171+
Dependency projectDependency = getDependencies().create(getProject().project(parentPath + ":" + perfectMatch.get()));
172+
projectDependency.because(moduleName);
173+
return projectDependency;
174+
} else if (existingProjectName.isPresent()) {
175+
// no exact match -> add capability to point at Module in other source set
176+
String projectName = existingProjectName.get();
177+
ProjectDependency projectDependency = (ProjectDependency) getDependencies().create(getProject().project(parentPath + ":" + projectName));
178+
String capabilityName = projectName + moduleNameSuffix.substring(projectName.length()).replace(".", "-");
179+
projectDependency.capabilities(c -> c.requireCapabilities(
180+
allProjectNamesAndGroups.get(projectName) + ":" + capabilityName));
181+
projectDependency.because(moduleName);
182+
return projectDependency;
183+
} else if (gav.isPresent()) {
184+
Dependency dependency = getDependencies().create(gav.get());
185+
dependency.because(moduleName);
186+
if (!gav.get().containsKey(GAV.VERSION)) {
187+
warnVersionMissing(moduleName, gav.get(), moduleInfo.getFilePath());
188+
}
189+
return dependency;
190+
} else {
191+
getProject().getLogger().lifecycle(
192+
"[WARN] [Java Module Dependencies] javaModuleDependencies.moduleNameToGA.put(\"" + moduleName + "\", \"group:artifact\") mapping is missing.");
193+
return null;
194+
}
195+
});
196+
}
197+
145198
/**
146199
* Converts 'Module Name' and 'Version' to GAV coordinates that can be used in
147200
* dependency declarations as String: "group:name:version"
@@ -179,10 +232,6 @@ public Provider<Map<String, Object>> gav(String moduleName) {
179232
return ga(moduleName).map(ga -> findGav(ga, moduleName));
180233
}
181234

182-
Provider<Map<String, Object>> gavNoError(String moduleName) {
183-
return getModuleNameToGA().getting(moduleName).orElse(mapByPrefix(getProviders().provider(() -> moduleName))).map(ga -> findGav(ga, moduleName));
184-
}
185-
186235
/**
187236
* If a Version Catalog is used:
188237
* Converts 'Module Name' and the matching 'Version' from the Version Catalog to
@@ -308,6 +357,23 @@ private <T> Provider<T> errorIfNotFound(Provider<String> moduleName) {
308357
});
309358
}
310359

360+
private void warnVersionMissing(String moduleName, Map<String, Object> ga, File moduleInfoFile) {
361+
if (getWarnForMissingVersions().get()) {
362+
getProject().getLogger().warn("[WARN] [Java Module Dependencies] No version defined in catalog - " + ga.get(GAV.GROUP) + ":" + ga.get(GAV.ARTIFACT) + " - "
363+
+ moduleDebugInfo(moduleName.replace('.', '_'), moduleInfoFile, getProject().getRootDir()));
364+
}
365+
}
366+
367+
private String moduleDebugInfo(String moduleName, File moduleInfoFile, File rootDir) {
368+
return moduleName
369+
+ " (required in "
370+
+ moduleInfoFile.getAbsolutePath().substring(rootDir.getAbsolutePath().length() + 1)
371+
+ ")";
372+
}
373+
374+
@Inject
375+
protected abstract Project getProject();
376+
311377
@Inject
312378
protected abstract ObjectFactory getObjects();
313379

src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java

Lines changed: 23 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323
import org.gradle.api.Task;
2424
import org.gradle.api.artifacts.Configuration;
2525
import org.gradle.api.artifacts.ConfigurationContainer;
26-
import org.gradle.api.artifacts.Dependency;
27-
import org.gradle.api.artifacts.ProjectDependency;
2826
import org.gradle.api.artifacts.VersionCatalogsExtension;
27+
import org.gradle.api.plugins.ExtensionContainer;
2928
import org.gradle.api.plugins.JavaPlugin;
30-
import org.gradle.api.provider.Provider;
3129
import org.gradle.api.tasks.SourceSet;
3230
import org.gradle.api.tasks.SourceSetContainer;
3331
import org.gradle.api.tasks.TaskProvider;
3432
import org.gradle.util.GradleVersion;
33+
import org.gradlex.javamodule.dependencies.dsl.AllDirectives;
34+
import org.gradlex.javamodule.dependencies.dsl.GradleOnlyDirectives;
3535
import org.gradlex.javamodule.dependencies.internal.bridges.DependencyAnalysisBridge;
3636
import org.gradlex.javamodule.dependencies.internal.bridges.ExtraJavaModuleInfoBridge;
3737
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
@@ -41,13 +41,8 @@
4141
import org.gradlex.javamodule.dependencies.tasks.ModulePathAnalysis;
4242
import org.gradlex.javamodule.dependencies.tasks.ModuleVersionRecommendation;
4343

44-
import javax.annotation.Nullable;
4544
import java.io.File;
46-
import java.util.Comparator;
4745
import java.util.HashSet;
48-
import java.util.Map;
49-
import java.util.Optional;
50-
import java.util.stream.Collectors;
5146

5247
import static org.gradle.api.plugins.HelpTasksPlugin.HELP_GROUP;
5348
import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP;
@@ -99,6 +94,8 @@ private void setupForJavaProject(Project project, JavaModuleDependenciesExtensio
9994
t.setDescription("Check scope and order of directives in 'module-info.java' files");
10095
});
10196

97+
setupDirectivesDSL(project, javaModuleDependencies);
98+
10299
setupOrderingCheckTasks(project, checkAllModuleInfo, javaModuleDependencies);
103100
setupModuleDependenciesTask(project);
104101
setupReportTasks(project, javaModuleDependencies);
@@ -114,6 +111,21 @@ private void setupExtraJavaModulePluginBridge(Project project, JavaModuleDepende
114111
e -> ExtraJavaModuleInfoBridge.autoRegisterPatchedModuleMappings(project, javaModuleDependencies));
115112
}
116113

114+
private void setupDirectivesDSL(Project project, JavaModuleDependenciesExtension javaModuleDependencies) {
115+
ExtensionContainer extensions = project.getExtensions();
116+
SourceSetContainer sourceSets = extensions.getByType(SourceSetContainer.class);
117+
SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
118+
sourceSets.all(sourceSet -> {
119+
File moduleInfoFile = new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java");
120+
String extensionName = sourceSet.getName() + "ModuleInfo";
121+
if (moduleInfoFile.exists()) {
122+
extensions.create(extensionName, GradleOnlyDirectives.class, sourceSet, mainSourceSet, javaModuleDependencies);
123+
} else {
124+
extensions.create(extensionName, AllDirectives.class, sourceSet, mainSourceSet, javaModuleDependencies);
125+
}
126+
});
127+
}
128+
117129
private void setupModuleDependenciesTask(Project project) {
118130
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
119131
TaskProvider<ModuleDependencyReport> moduleDependencies = project.getTasks().register("moduleDependencies", ModuleDependencyReport.class, t -> {
@@ -235,69 +247,18 @@ private void readModuleInfo(ModuleInfo.Directive moduleDirective, SourceSet sour
235247
return;
236248
}
237249
ModuleInfo moduleInfo = javaModuleDependenciesExtension.getModuleInfoCache().get(sourceSet);
238-
String ownModuleNamesPrefix = moduleInfo.moduleNamePrefix(project.getName(), sourceSet.getName());
239250
for (String moduleName : moduleInfo.get(moduleDirective)) {
240-
declareDependency(moduleName, ownModuleNamesPrefix, moduleInfo.getFilePath(), project, configuration, javaModuleDependenciesExtension);
251+
declareDependency(moduleName, moduleInfo.getFilePath(), project, sourceSet, configuration, javaModuleDependenciesExtension);
241252
}
242253
}
243254

244-
private void declareDependency(String moduleName, @Nullable String ownModuleNamesPrefix, File moduleInfoFile, Project project, Configuration configuration, JavaModuleDependenciesExtension javaModuleDependencies) {
255+
private void declareDependency(String moduleName, File moduleInfoFile, Project project, SourceSet sourceSet, Configuration configuration, JavaModuleDependenciesExtension javaModuleDependencies) {
245256
if (JDKInfo.MODULES.contains(moduleName)) {
246257
// The module is part of the JDK, no dependency required
247258
return;
248259
}
249260

250-
Map<String, String> allProjectNamesAndGroups = project.getRootProject().getSubprojects().stream().collect(
251-
Collectors.toMap(Project::getName, p -> (String) p.getGroup()));
252-
253-
Provider<Map<String, Object>> gav = javaModuleDependencies.gavNoError(moduleName);
254-
String moduleNameSuffix = ownModuleNamesPrefix == null ? null :
255-
moduleName.startsWith(ownModuleNamesPrefix + ".") ? moduleName.substring(ownModuleNamesPrefix.length() + 1) :
256-
ownModuleNamesPrefix.isEmpty() ? moduleName : null;
257-
258-
String parentPath = project.getParent() == null ? "" : project.getParent().getPath();
259-
Optional<String> perfectMatch = allProjectNamesAndGroups.keySet().stream().filter(p -> p.replace("-", ".").equals(moduleNameSuffix)).findFirst();
260-
Optional<String> existingProjectName = allProjectNamesAndGroups.keySet().stream().filter(p -> moduleNameSuffix != null && moduleNameSuffix.startsWith(p.replace("-", ".") + "."))
261-
.max(Comparator.comparingInt(String::length));
262-
263-
if (perfectMatch.isPresent()) {
264-
Dependency projectDependency = project.getDependencies().add(
265-
configuration.getName(), project.project(parentPath + ":" + perfectMatch.get()));
266-
assert projectDependency != null;
267-
projectDependency.because(moduleName);
268-
} else if (existingProjectName.isPresent()) {
269-
// no exact match -> add capability to point at Module in other source set
270-
String projectName = existingProjectName.get();
271-
ProjectDependency projectDependency = (ProjectDependency) project.getDependencies().add(
272-
configuration.getName(), project.project(parentPath + ":" + projectName));
273-
assert projectDependency != null;
274-
String capabilityName = projectName + moduleNameSuffix.substring(projectName.length()).replace(".", "-");
275-
projectDependency.capabilities(c -> c.requireCapabilities(
276-
allProjectNamesAndGroups.get(projectName) + ":" + capabilityName));
277-
projectDependency.because(moduleName);
278-
} else if (gav.isPresent()) {
279-
project.getDependencies().addProvider(configuration.getName(), gav, d -> d.because(moduleName));
280-
if (!gav.get().containsKey(GAV.VERSION)) {
281-
warnVersionMissing(moduleName, gav.get(), moduleInfoFile, project, javaModuleDependencies);
282-
}
283-
} else {
284-
project.getLogger().lifecycle(
285-
"[WARN] [Java Module Dependencies] javaModuleDependencies.moduleNameToGA.put(\"" + moduleName + "\", \"group:artifact\") mapping is missing.");
286-
}
287-
}
288-
289-
private void warnVersionMissing(String moduleName, Map<String, Object> ga, File moduleInfoFile, Project project, JavaModuleDependenciesExtension javaModuleDependencies) {
290-
if (javaModuleDependencies.getWarnForMissingVersions().get()) {
291-
project.getLogger().warn("[WARN] [Java Module Dependencies] No version defined in catalog - " + ga.get(GAV.GROUP) + ":" + ga.get(GAV.ARTIFACT) + " - "
292-
+ moduleDebugInfo(moduleName.replace('.', '_'), moduleInfoFile, project.getRootDir()));
293-
}
294-
}
295-
296-
private String moduleDebugInfo(String moduleName, File moduleInfoFile, File rootDir) {
297-
return moduleName
298-
+ " (required in "
299-
+ moduleInfoFile.getAbsolutePath().substring(rootDir.getAbsolutePath().length() + 1)
300-
+ ")";
261+
project.getDependencies().addProvider(configuration.getName(), javaModuleDependencies.create(moduleName, sourceSet));
301262
}
302263

303264
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.gradlex.javamodule.dependencies.dsl;
2+
3+
import org.gradle.api.tasks.SourceSet;
4+
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
5+
6+
abstract public class AllDirectives extends GradleOnlyDirectives {
7+
8+
public AllDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) {
9+
super(sourceSet, mainSourceSet, javaModuleDependencies);
10+
}
11+
12+
public void requires(String moduleName) {
13+
add(sourceSet.getImplementationConfigurationName(), moduleName);
14+
}
15+
16+
public void requiresTransitive(String moduleName) {
17+
add(sourceSet.getApiConfigurationName(), moduleName);
18+
}
19+
20+
public void requiresStatic(String moduleName) {
21+
add(sourceSet.getCompileOnlyConfigurationName(), moduleName);
22+
}
23+
24+
public void requiresStaticTransitive(String moduleName) {
25+
add(sourceSet.getCompileOnlyApiConfigurationName(), moduleName);
26+
}
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.gradlex.javamodule.dependencies.dsl;
2+
3+
import org.gradle.api.artifacts.Dependency;
4+
import org.gradle.api.artifacts.dsl.DependencyHandler;
5+
import org.gradle.api.tasks.SourceSet;
6+
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
7+
8+
import javax.inject.Inject;
9+
10+
public abstract class GradleOnlyDirectives {
11+
12+
@Inject
13+
protected abstract DependencyHandler getDependencies();
14+
15+
protected final SourceSet sourceSet;
16+
protected final SourceSet mainSourceSet;
17+
protected final JavaModuleDependenciesExtension javaModuleDependencies;
18+
19+
public GradleOnlyDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) {
20+
this.sourceSet = sourceSet;
21+
this.mainSourceSet = mainSourceSet;
22+
this.javaModuleDependencies = javaModuleDependencies;
23+
}
24+
25+
protected void add(String scope, String moduleName) {
26+
getDependencies().addProvider(scope, javaModuleDependencies.create(moduleName, mainSourceSet));
27+
}
28+
29+
public void runtimeOnly(String moduleName) {
30+
add(sourceSet.getRuntimeOnlyConfigurationName(), moduleName);
31+
}
32+
33+
public void annotationProcessor(String moduleName) {
34+
add(sourceSet.getAnnotationProcessorConfigurationName(), moduleName);
35+
}
36+
}

0 commit comments

Comments
 (0)