Skip to content

Commit 348c639

Browse files
committed
Add tasks to generate dependencies blocks based on 'module-info.java'
1 parent 23280c8 commit 348c639

File tree

5 files changed

+372
-0
lines changed

5 files changed

+372
-0
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.gradlex.javamodule.dependencies.internal.dsl.AllDirectivesInternal;
3838
import org.gradlex.javamodule.dependencies.internal.dsl.GradleOnlyDirectivesInternal;
3939
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
40+
import org.gradlex.javamodule.dependencies.tasks.BuildFileDependenciesGenerate;
4041
import org.gradlex.javamodule.dependencies.tasks.ModuleDependencyReport;
4142
import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesOrderingCheck;
4243
import org.gradlex.javamodule.dependencies.tasks.ModuleInfoGenerate;
@@ -45,6 +46,8 @@
4546

4647
import java.io.File;
4748
import java.util.HashSet;
49+
import java.util.List;
50+
import java.util.stream.Collectors;
4851

4952
import static org.gradle.api.plugins.HelpTasksPlugin.HELP_GROUP;
5053
import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP;
@@ -206,6 +209,23 @@ private void setupMigrationTasks(Project project, JavaModuleDependenciesExtensio
206209

207210
generateAllModuleInfoFiles.configure(t -> t.dependsOn(generateModuleInfo));
208211
});
212+
213+
project.getTasks().register("generateBuildFileDependencies", BuildFileDependenciesGenerate.class, t -> {
214+
t.setGroup("java modules");
215+
t.setDescription("Generate 'dependencies' block in 'build.gradle.kts'");
216+
217+
t.getOwnProjectGroup().set(project.provider(() -> project.getGroup().toString()));
218+
t.getWithCatalog().set(true);
219+
sourceSets.all(sourceSet -> t.addDependencies(sourceSet.getName(),
220+
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_TRANSITIVE, sourceSet.getApiConfigurationName()),
221+
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES, sourceSet.getImplementationConfigurationName()),
222+
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC_TRANSITIVE, sourceSet.getCompileOnlyApiConfigurationName()),
223+
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC, sourceSet.getCompileOnlyConfigurationName()),
224+
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_RUNTIME, sourceSet.getRuntimeOnlyConfigurationName())
225+
));
226+
227+
t.getBuildFile().set(project.getLayout().getProjectDirectory().file("build.gradle.kts"));
228+
});
209229
}
210230

211231
private void setupOrderingCheckTasks(Project project, TaskProvider<Task> checkAllModuleInfo, JavaModuleDependenciesExtension javaModuleDependencies) {
@@ -262,4 +282,19 @@ private void declareDependency(String moduleName, File moduleInfoFile, Project p
262282
project.getDependencies().addProvider(configuration.getName(), javaModuleDependencies.create(moduleName, sourceSet));
263283
}
264284

285+
private List<BuildFileDependenciesGenerate.DependencyDeclaration> collectDependencies(Project project, JavaModuleDependenciesExtension javaModuleDependencies, SourceSet sourceSet, ModuleInfo.Directive directive, String scope) {
286+
ModuleInfo moduleInfo = javaModuleDependencies.getModuleInfoCache().get(sourceSet);
287+
if (moduleInfo == ModuleInfo.EMPTY) {
288+
// check if there is a whiltebox module-info we can use isntead
289+
File sourceSetDir = sourceSet.getJava().getSrcDirs().iterator().next().getParentFile();
290+
File whiteboxModuleInfoFile = new File(sourceSetDir, "java9/module-info.java");
291+
if (whiteboxModuleInfoFile.exists()) {
292+
moduleInfo = new ModuleInfo(project.getProviders().fileContents(project.getLayout().getProjectDirectory().file(whiteboxModuleInfoFile.getAbsolutePath())).getAsText().get(), whiteboxModuleInfoFile);
293+
}
294+
}
295+
return moduleInfo.get(directive).stream()
296+
.map(moduleName -> new BuildFileDependenciesGenerate.DependencyDeclaration(scope, moduleName, javaModuleDependencies.ga(moduleName).get()))
297+
.collect(Collectors.toList());
298+
}
299+
265300
}

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,22 @@
2727
import org.gradle.api.tasks.SourceSetContainer;
2828
import org.gradle.util.GradleVersion;
2929
import org.gradlex.javamodule.dependencies.dsl.ModuleVersions;
30+
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
31+
import org.gradlex.javamodule.dependencies.tasks.CatalogGenerate;
32+
33+
import java.io.File;
34+
import java.util.Arrays;
35+
import java.util.List;
36+
import java.util.stream.Collectors;
37+
import java.util.stream.Stream;
3038

3139
import static org.gradle.api.attributes.Usage.JAVA_RUNTIME;
3240
import static org.gradle.api.plugins.JavaPlatformPlugin.API_CONFIGURATION_NAME;
41+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES;
42+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME;
43+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC;
44+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE;
45+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE;
3346

3447
@SuppressWarnings("unused")
3548
@NonNullApi
@@ -43,6 +56,7 @@ public void apply(Project project) {
4356

4457
private void setupForJavaPlatformProject(Project project) {
4558
setupVersionsDSL(project, project.getConfigurations().getByName(API_CONFIGURATION_NAME));
59+
registerCatalogTask(project);
4660
}
4761

4862
private void setupForJavaProject(Project project) {
@@ -70,6 +84,7 @@ private void setupForJavaProject(Project project) {
7084
}
7185

7286
setupVersionsDSL(project, versions);
87+
registerCatalogTask(project);
7388
}
7489

7590
private void setupVersionsDSL(Project project, Configuration configuration) {
@@ -78,4 +93,46 @@ private void setupVersionsDSL(Project project, Configuration configuration) {
7893
project.getExtensions().create("moduleInfo", ModuleVersions.class, configuration, javaModuleDependencies);
7994
}
8095

96+
private void registerCatalogTask(Project project) {
97+
JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class);
98+
ModuleVersions moduleVersions = project.getExtensions().getByType(ModuleVersions.class);
99+
project.getTasks().register("generateCatalog", CatalogGenerate.class, t -> {
100+
t.setGroup("java modules");
101+
t.setDescription("Generate 'libs.versions.toml' file");
102+
103+
t.getOwnProjectGroup().set(project.provider(() -> project.getGroup().toString()));
104+
105+
t.getEntries().addAll(collectCatalogEntriesFromVersions(javaModuleDependencies, moduleVersions));
106+
project.getRootProject().getSubprojects().forEach(sub -> {
107+
File[] srcDirs = sub.getLayout().getProjectDirectory().dir("src").getAsFile().listFiles();
108+
(srcDirs == null ? Stream.<File>empty() : Arrays.stream(srcDirs)).forEach(srcDirSet -> {
109+
File moduleInfoFile = new File(srcDirSet, "java/module-info.java");
110+
if (!moduleInfoFile.exists()) {
111+
moduleInfoFile = new File(srcDirSet, "java9/module-info.java");
112+
}
113+
if (moduleInfoFile.exists()) {
114+
ModuleInfo moduleInfo = new ModuleInfo(project.getProviders().fileContents(project.getLayout().getProjectDirectory().file(moduleInfoFile.getAbsolutePath())).getAsText().get(), moduleInfoFile);
115+
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_TRANSITIVE)));
116+
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES)));
117+
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)));
118+
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC)));
119+
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_RUNTIME)));
120+
}
121+
});
122+
});
123+
124+
t.getEntries().addAll();
125+
126+
t.getCatalogFile().set(project.getRootProject().getLayout().getProjectDirectory().file("gradle/libs.versions.toml"));
127+
});
128+
}
129+
130+
private List<CatalogGenerate.CatalogEntry> collectCatalogEntriesFromVersions(JavaModuleDependenciesExtension javaModuleDependencies, ModuleVersions moduleVersions) {
131+
return moduleVersions.getDeclaredVersions().entrySet().stream().map(mv -> new CatalogGenerate.CatalogEntry(mv.getKey(), javaModuleDependencies.ga(mv.getKey()).get(), mv.getValue())).collect(Collectors.toList());
132+
}
133+
134+
private List<CatalogGenerate.CatalogEntry> collectCatalogEntriesFromModuleInfos(JavaModuleDependenciesExtension javaModuleDependencies, List<String> moduleNames) {
135+
return moduleNames.stream().map(moduleName -> new CatalogGenerate.CatalogEntry(moduleName, javaModuleDependencies.ga(moduleName).get(), null)).collect(Collectors.toList());
136+
}
137+
81138
}

src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,19 @@
2424
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
2525

2626
import javax.inject.Inject;
27+
import java.util.LinkedHashMap;
28+
import java.util.Map;
2729

2830
abstract public class ModuleVersions {
2931

32+
private final Map<String, String> declaredVersions = new LinkedHashMap<>();
3033
private final Configuration configuration;
3134
protected final JavaModuleDependenciesExtension javaModuleDependencies;
3235

36+
public Map<String, String> getDeclaredVersions() {
37+
return declaredVersions;
38+
}
39+
3340
@Inject
3441
protected abstract DependencyHandler getDependencies();
3542

@@ -58,5 +65,6 @@ public void version(String moduleName, String requiredVersion, Action<? super Mu
5865
dependencyConstraint.version(version);
5966
return dependencyConstraint;
6067
}));
68+
declaredVersions.put(moduleName, requiredVersion);
6169
}
6270
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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.tasks;
18+
19+
import org.gradle.api.DefaultTask;
20+
import org.gradle.api.file.RegularFileProperty;
21+
import org.gradle.api.model.ObjectFactory;
22+
import org.gradle.api.provider.ListProperty;
23+
import org.gradle.api.provider.Property;
24+
import org.gradle.api.tasks.Internal;
25+
import org.gradle.api.tasks.TaskAction;
26+
27+
import javax.inject.Inject;
28+
import java.io.File;
29+
import java.io.IOException;
30+
import java.nio.file.Files;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.Optional;
34+
import java.util.stream.Collectors;
35+
36+
public abstract class BuildFileDependenciesGenerate extends DefaultTask {
37+
public abstract static class SourceSetDependencies {
38+
39+
private final String name;
40+
41+
@Inject
42+
public SourceSetDependencies(String name) {
43+
this.name = name;
44+
}
45+
46+
public abstract ListProperty<DependencyDeclaration> getApiDependencies();
47+
48+
public abstract ListProperty<DependencyDeclaration> getImplementationDependencies();
49+
50+
public abstract ListProperty<DependencyDeclaration> getCompileOnlyApiDependencies();
51+
52+
public abstract ListProperty<DependencyDeclaration> getCompileOnlyDependencies();
53+
54+
public abstract ListProperty<DependencyDeclaration> getRuntimeOnlyDependencies();
55+
56+
private boolean isEmpty() {
57+
return getApiDependencies().get().isEmpty()
58+
&& getImplementationDependencies().get().isEmpty()
59+
&& getCompileOnlyApiDependencies().get().isEmpty()
60+
&& getCompileOnlyDependencies().get().isEmpty()
61+
&& getRuntimeOnlyDependencies().get().isEmpty();
62+
}
63+
}
64+
65+
public static class DependencyDeclaration {
66+
private final String scope;
67+
private final String moduleName;
68+
private final String fullId;
69+
70+
public DependencyDeclaration(String scope, String moduleName, String fullId) {
71+
this.scope = scope;
72+
this.moduleName = moduleName;
73+
this.fullId = fullId;
74+
}
75+
}
76+
77+
@Internal
78+
public abstract ListProperty<SourceSetDependencies> getDependencies();
79+
80+
@Internal
81+
public abstract Property<Boolean> getWithCatalog();
82+
83+
@Internal
84+
public abstract Property<String> getOwnProjectGroup();
85+
86+
@Internal
87+
public abstract RegularFileProperty getBuildFile();
88+
89+
@Inject
90+
public abstract ObjectFactory getObjects();
91+
92+
public void addDependencies(String name, List<DependencyDeclaration> api, List<DependencyDeclaration> implementation, List<DependencyDeclaration> compileOnlyApi, List<DependencyDeclaration> compileOnly, List<DependencyDeclaration> runtimeOnly) {
93+
SourceSetDependencies dependencies = getObjects().newInstance(SourceSetDependencies.class, name);
94+
dependencies.getApiDependencies().convention(api);
95+
dependencies.getImplementationDependencies().convention(implementation);
96+
dependencies.getCompileOnlyApiDependencies().convention(compileOnlyApi);
97+
dependencies.getCompileOnlyDependencies().convention(compileOnly);
98+
dependencies.getRuntimeOnlyDependencies().convention(runtimeOnly);
99+
getDependencies().add(dependencies);
100+
}
101+
102+
@TaskAction
103+
public void generate() throws IOException {
104+
File buildGradle = getBuildFile().get().getAsFile();
105+
List<String> fileContentToPreserve = Files.readAllLines(buildGradle.toPath());
106+
Optional<String> dependenciesBlock = fileContentToPreserve.stream().filter(line -> line.contains("dependencies")).findFirst();
107+
if (dependenciesBlock.isPresent()) {
108+
fileContentToPreserve = fileContentToPreserve.subList(0, fileContentToPreserve.indexOf(dependenciesBlock.get()));
109+
}
110+
111+
List<String> content = new ArrayList<>(fileContentToPreserve);
112+
if (!content.get(content.size() - 1).isEmpty()) {
113+
content.add("");
114+
}
115+
116+
if (!getDependencies().get().isEmpty()) {
117+
content.add("dependencies {");
118+
getDependencies().get().stream().sorted((a, b) -> ("main".equals(a.name)) ? -1 : a.name.compareTo(b.name)).forEach(sourceSetBlock -> {
119+
content.addAll(toDeclarationString(sourceSetBlock.getApiDependencies()));
120+
content.addAll(toDeclarationString(sourceSetBlock.getImplementationDependencies()));
121+
content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyApiDependencies()));
122+
content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyDependencies()));
123+
content.addAll(toDeclarationString(sourceSetBlock.getRuntimeOnlyDependencies()));
124+
if (!sourceSetBlock.isEmpty()) {
125+
content.add("");
126+
}
127+
});
128+
content.remove(content.size() - 1);
129+
content.add("}");
130+
}
131+
132+
Files.write(buildGradle.toPath(), content);
133+
}
134+
135+
private List<String> toDeclarationString(ListProperty<DependencyDeclaration> dependencies) {
136+
return dependencies.get().stream().map(d -> {
137+
String group = d.fullId.split(":")[0];
138+
String artifact = d.fullId.split(":")[1];
139+
String feature = null;
140+
if (artifact.contains("|")) {
141+
feature = artifact.split("\\|")[1];
142+
artifact = artifact.split("\\|")[0];
143+
}
144+
145+
String identifier;
146+
if (group.equals(getOwnProjectGroup().get())) {
147+
if (getWithCatalog().get()) {
148+
identifier = "projects." + artifact;
149+
} else {
150+
identifier = "project(\":" + artifact + "\")";
151+
}
152+
} else {
153+
if (getWithCatalog().get()) {
154+
identifier = "libs." + d.moduleName;
155+
} else {
156+
identifier = "\"" + group + ":" + artifact + "\"";
157+
}
158+
}
159+
160+
if (feature == null) {
161+
return " " + d.scope + "(" + identifier + ")";
162+
} else {
163+
return " " + d.scope + "(" + identifier + ") { capabilities { requireCapabilities(\"" + group + ":" + artifact + "-" + feature + "\") } }";
164+
}
165+
}).collect(Collectors.toList());
166+
}
167+
}

0 commit comments

Comments
 (0)