Skip to content

Commit be2b2a2

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

File tree

4 files changed

+187
-66
lines changed

4 files changed

+187
-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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.javamodule.dependencies.dsl;
18+
19+
import org.gradle.api.tasks.SourceSet;
20+
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
21+
22+
abstract public class AllDirectives extends GradleOnlyDirectives {
23+
24+
public AllDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) {
25+
super(sourceSet, mainSourceSet, javaModuleDependencies);
26+
}
27+
28+
public void requires(String moduleName) {
29+
add(sourceSet.getImplementationConfigurationName(), moduleName);
30+
}
31+
32+
public void requiresTransitive(String moduleName) {
33+
add(sourceSet.getApiConfigurationName(), moduleName);
34+
}
35+
36+
public void requiresStatic(String moduleName) {
37+
add(sourceSet.getCompileOnlyConfigurationName(), moduleName);
38+
}
39+
40+
public void requiresStaticTransitive(String moduleName) {
41+
add(sourceSet.getCompileOnlyApiConfigurationName(), moduleName);
42+
}
43+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.javamodule.dependencies.dsl;
18+
19+
import org.gradle.api.artifacts.dsl.DependencyHandler;
20+
import org.gradle.api.tasks.SourceSet;
21+
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
22+
23+
import javax.inject.Inject;
24+
25+
public abstract class GradleOnlyDirectives {
26+
27+
@Inject
28+
protected abstract DependencyHandler getDependencies();
29+
30+
protected final SourceSet sourceSet;
31+
protected final SourceSet mainSourceSet;
32+
protected final JavaModuleDependenciesExtension javaModuleDependencies;
33+
34+
public GradleOnlyDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) {
35+
this.sourceSet = sourceSet;
36+
this.mainSourceSet = mainSourceSet;
37+
this.javaModuleDependencies = javaModuleDependencies;
38+
}
39+
40+
protected void add(String scope, String moduleName) {
41+
getDependencies().addProvider(scope, javaModuleDependencies.create(moduleName, mainSourceSet));
42+
}
43+
44+
public void runtimeOnly(String moduleName) {
45+
add(sourceSet.getRuntimeOnlyConfigurationName(), moduleName);
46+
}
47+
48+
public void annotationProcessor(String moduleName) {
49+
add(sourceSet.getAnnotationProcessorConfigurationName(), moduleName);
50+
}
51+
}

0 commit comments

Comments
 (0)