Skip to content

Commit

Permalink
Add tasks to generate dependencies blocks based on 'module-info.java'
Browse files Browse the repository at this point in the history
  • Loading branch information
jjohannes committed Jan 31, 2024
1 parent 23280c8 commit 348c639
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.gradlex.javamodule.dependencies.internal.dsl.AllDirectivesInternal;
import org.gradlex.javamodule.dependencies.internal.dsl.GradleOnlyDirectivesInternal;
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
import org.gradlex.javamodule.dependencies.tasks.BuildFileDependenciesGenerate;
import org.gradlex.javamodule.dependencies.tasks.ModuleDependencyReport;
import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesOrderingCheck;
import org.gradlex.javamodule.dependencies.tasks.ModuleInfoGenerate;
Expand All @@ -45,6 +46,8 @@

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

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

generateAllModuleInfoFiles.configure(t -> t.dependsOn(generateModuleInfo));
});

project.getTasks().register("generateBuildFileDependencies", BuildFileDependenciesGenerate.class, t -> {
t.setGroup("java modules");
t.setDescription("Generate 'dependencies' block in 'build.gradle.kts'");

t.getOwnProjectGroup().set(project.provider(() -> project.getGroup().toString()));
t.getWithCatalog().set(true);
sourceSets.all(sourceSet -> t.addDependencies(sourceSet.getName(),
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_TRANSITIVE, sourceSet.getApiConfigurationName()),
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES, sourceSet.getImplementationConfigurationName()),
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC_TRANSITIVE, sourceSet.getCompileOnlyApiConfigurationName()),
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC, sourceSet.getCompileOnlyConfigurationName()),
collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_RUNTIME, sourceSet.getRuntimeOnlyConfigurationName())
));

t.getBuildFile().set(project.getLayout().getProjectDirectory().file("build.gradle.kts"));
});
}

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

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,22 @@
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.util.GradleVersion;
import org.gradlex.javamodule.dependencies.dsl.ModuleVersions;
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
import org.gradlex.javamodule.dependencies.tasks.CatalogGenerate;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.gradle.api.attributes.Usage.JAVA_RUNTIME;
import static org.gradle.api.plugins.JavaPlatformPlugin.API_CONFIGURATION_NAME;
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES;
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME;
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC;
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE;
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE;

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

private void setupForJavaPlatformProject(Project project) {
setupVersionsDSL(project, project.getConfigurations().getByName(API_CONFIGURATION_NAME));
registerCatalogTask(project);
}

private void setupForJavaProject(Project project) {
Expand Down Expand Up @@ -70,6 +84,7 @@ private void setupForJavaProject(Project project) {
}

setupVersionsDSL(project, versions);
registerCatalogTask(project);
}

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

private void registerCatalogTask(Project project) {
JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class);
ModuleVersions moduleVersions = project.getExtensions().getByType(ModuleVersions.class);
project.getTasks().register("generateCatalog", CatalogGenerate.class, t -> {
t.setGroup("java modules");
t.setDescription("Generate 'libs.versions.toml' file");

t.getOwnProjectGroup().set(project.provider(() -> project.getGroup().toString()));

t.getEntries().addAll(collectCatalogEntriesFromVersions(javaModuleDependencies, moduleVersions));
project.getRootProject().getSubprojects().forEach(sub -> {
File[] srcDirs = sub.getLayout().getProjectDirectory().dir("src").getAsFile().listFiles();
(srcDirs == null ? Stream.<File>empty() : Arrays.stream(srcDirs)).forEach(srcDirSet -> {
File moduleInfoFile = new File(srcDirSet, "java/module-info.java");
if (!moduleInfoFile.exists()) {
moduleInfoFile = new File(srcDirSet, "java9/module-info.java");
}
if (moduleInfoFile.exists()) {
ModuleInfo moduleInfo = new ModuleInfo(project.getProviders().fileContents(project.getLayout().getProjectDirectory().file(moduleInfoFile.getAbsolutePath())).getAsText().get(), moduleInfoFile);
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_TRANSITIVE)));
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES)));
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)));
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC)));
t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_RUNTIME)));
}
});
});

t.getEntries().addAll();

t.getCatalogFile().set(project.getRootProject().getLayout().getProjectDirectory().file("gradle/libs.versions.toml"));
});
}

private List<CatalogGenerate.CatalogEntry> collectCatalogEntriesFromVersions(JavaModuleDependenciesExtension javaModuleDependencies, ModuleVersions moduleVersions) {
return moduleVersions.getDeclaredVersions().entrySet().stream().map(mv -> new CatalogGenerate.CatalogEntry(mv.getKey(), javaModuleDependencies.ga(mv.getKey()).get(), mv.getValue())).collect(Collectors.toList());
}

private List<CatalogGenerate.CatalogEntry> collectCatalogEntriesFromModuleInfos(JavaModuleDependenciesExtension javaModuleDependencies, List<String> moduleNames) {
return moduleNames.stream().map(moduleName -> new CatalogGenerate.CatalogEntry(moduleName, javaModuleDependencies.ga(moduleName).get(), null)).collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;

import javax.inject.Inject;
import java.util.LinkedHashMap;
import java.util.Map;

abstract public class ModuleVersions {

private final Map<String, String> declaredVersions = new LinkedHashMap<>();
private final Configuration configuration;
protected final JavaModuleDependenciesExtension javaModuleDependencies;

public Map<String, String> getDeclaredVersions() {
return declaredVersions;
}

@Inject
protected abstract DependencyHandler getDependencies();

Expand Down Expand Up @@ -58,5 +65,6 @@ public void version(String moduleName, String requiredVersion, Action<? super Mu
dependencyConstraint.version(version);
return dependencyConstraint;
}));
declaredVersions.put(moduleName, requiredVersion);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright the GradleX team.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.gradlex.javamodule.dependencies.tasks;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.TaskAction;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public abstract class BuildFileDependenciesGenerate extends DefaultTask {
public abstract static class SourceSetDependencies {

private final String name;

@Inject
public SourceSetDependencies(String name) {
this.name = name;
}

public abstract ListProperty<DependencyDeclaration> getApiDependencies();

public abstract ListProperty<DependencyDeclaration> getImplementationDependencies();

public abstract ListProperty<DependencyDeclaration> getCompileOnlyApiDependencies();

public abstract ListProperty<DependencyDeclaration> getCompileOnlyDependencies();

public abstract ListProperty<DependencyDeclaration> getRuntimeOnlyDependencies();

private boolean isEmpty() {
return getApiDependencies().get().isEmpty()
&& getImplementationDependencies().get().isEmpty()
&& getCompileOnlyApiDependencies().get().isEmpty()
&& getCompileOnlyDependencies().get().isEmpty()
&& getRuntimeOnlyDependencies().get().isEmpty();
}
}

public static class DependencyDeclaration {
private final String scope;
private final String moduleName;
private final String fullId;

public DependencyDeclaration(String scope, String moduleName, String fullId) {
this.scope = scope;
this.moduleName = moduleName;
this.fullId = fullId;
}
}

@Internal
public abstract ListProperty<SourceSetDependencies> getDependencies();

@Internal
public abstract Property<Boolean> getWithCatalog();

@Internal
public abstract Property<String> getOwnProjectGroup();

@Internal
public abstract RegularFileProperty getBuildFile();

@Inject
public abstract ObjectFactory getObjects();

public void addDependencies(String name, List<DependencyDeclaration> api, List<DependencyDeclaration> implementation, List<DependencyDeclaration> compileOnlyApi, List<DependencyDeclaration> compileOnly, List<DependencyDeclaration> runtimeOnly) {
SourceSetDependencies dependencies = getObjects().newInstance(SourceSetDependencies.class, name);
dependencies.getApiDependencies().convention(api);
dependencies.getImplementationDependencies().convention(implementation);
dependencies.getCompileOnlyApiDependencies().convention(compileOnlyApi);
dependencies.getCompileOnlyDependencies().convention(compileOnly);
dependencies.getRuntimeOnlyDependencies().convention(runtimeOnly);
getDependencies().add(dependencies);
}

@TaskAction
public void generate() throws IOException {
File buildGradle = getBuildFile().get().getAsFile();
List<String> fileContentToPreserve = Files.readAllLines(buildGradle.toPath());
Optional<String> dependenciesBlock = fileContentToPreserve.stream().filter(line -> line.contains("dependencies")).findFirst();
if (dependenciesBlock.isPresent()) {
fileContentToPreserve = fileContentToPreserve.subList(0, fileContentToPreserve.indexOf(dependenciesBlock.get()));
}

List<String> content = new ArrayList<>(fileContentToPreserve);
if (!content.get(content.size() - 1).isEmpty()) {
content.add("");
}

if (!getDependencies().get().isEmpty()) {
content.add("dependencies {");
getDependencies().get().stream().sorted((a, b) -> ("main".equals(a.name)) ? -1 : a.name.compareTo(b.name)).forEach(sourceSetBlock -> {
content.addAll(toDeclarationString(sourceSetBlock.getApiDependencies()));
content.addAll(toDeclarationString(sourceSetBlock.getImplementationDependencies()));
content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyApiDependencies()));
content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyDependencies()));
content.addAll(toDeclarationString(sourceSetBlock.getRuntimeOnlyDependencies()));
if (!sourceSetBlock.isEmpty()) {
content.add("");
}
});
content.remove(content.size() - 1);
content.add("}");
}

Files.write(buildGradle.toPath(), content);
}

private List<String> toDeclarationString(ListProperty<DependencyDeclaration> dependencies) {
return dependencies.get().stream().map(d -> {
String group = d.fullId.split(":")[0];
String artifact = d.fullId.split(":")[1];
String feature = null;
if (artifact.contains("|")) {
feature = artifact.split("\\|")[1];
artifact = artifact.split("\\|")[0];
}

String identifier;
if (group.equals(getOwnProjectGroup().get())) {
if (getWithCatalog().get()) {
identifier = "projects." + artifact;
} else {
identifier = "project(\":" + artifact + "\")";
}
} else {
if (getWithCatalog().get()) {
identifier = "libs." + d.moduleName;
} else {
identifier = "\"" + group + ":" + artifact + "\"";
}
}

if (feature == null) {
return " " + d.scope + "(" + identifier + ")";
} else {
return " " + d.scope + "(" + identifier + ") { capabilities { requireCapabilities(\"" + group + ":" + artifact + "-" + feature + "\") } }";
}
}).collect(Collectors.toList());
}
}
Loading

0 comments on commit 348c639

Please sign in to comment.