Skip to content

Commit

Permalink
Add configuration option for 'versionsProvidingConfiguration'
Browse files Browse the repository at this point in the history
This way, a build setup can be created with a Configuration available
in ALL projects that resolves to the same version of every module
used. Then the metadata input to the transform, to process
'requireAllDefinedDependencies', is the same for a certain Jar
independent of the classpath it is used on.
  • Loading branch information
jjohannes committed Sep 16, 2024
1 parent 4c83a98 commit 7c8412a
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE;
import static org.gradle.api.attributes.Category.LIBRARY;
Expand Down Expand Up @@ -195,16 +194,9 @@ private void registerTransform(String fileExtension, Project project, ExtraJavaM
p.getMergeJarIds().set(artifacts.map(new IdExtractor()));
p.getMergeJars().set(artifacts.map(new FileExtractor(project.getLayout())));

p.getRequiresFromMetadata().set(project.provider(() -> sourceSets.stream().flatMap(s -> Stream.of(
s.getRuntimeClasspathConfigurationName(),
s.getCompileClasspathConfigurationName(),
s.getAnnotationProcessorConfigurationName()
))
.flatMap(resolvable -> existingComponentsOfInterest(configurations.getByName(resolvable), extension))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (k1, k2) -> k1)).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, c -> new PublishedMetadata(c.getKey(), c.getValue(), project)))
));

Provider<Set<String>> componentsOfInterest = componentsOfInterest(extension);
p.getRequiresFromMetadata().set(componentsOfInterest.map(gaSet -> gaSet.stream()
.collect(Collectors.toMap(ga -> ga, ga -> new PublishedMetadata(ga, project, extension)))));
p.getAdditionalKnownModules().set(extractFromModuleDependenciesPlugin(project));
});
t.getFrom().attribute(artifactType, fileExtension).attribute(javaModule, false);
Expand Down Expand Up @@ -238,22 +230,11 @@ private Provider<Map<String, String>> extractFromModuleDependenciesPlugin(Projec
});
}

private Stream<Map.Entry<String, Configuration>> existingComponentsOfInterest(Configuration resolvable, ExtraJavaModuleInfoPluginExtension extension) {
Set<String> componentsOfInterest = componentsOfInterest(extension);
if (componentsOfInterest.isEmpty()) {
return Stream.empty();
}

return resolvable.getIncoming().getResolutionResult().getAllComponents().stream()
.filter(c -> componentsOfInterest.contains(ga(c.getId())))
.collect(Collectors.toMap(c -> c.getId().toString(), c -> resolvable)).entrySet().stream();
}

private static Set<String> componentsOfInterest(ExtraJavaModuleInfoPluginExtension extension) {
return extension.getModuleSpecs().get().values().stream()
private static Provider<Set<String>> componentsOfInterest(ExtraJavaModuleInfoPluginExtension extension) {
return extension.getModuleSpecs().map(specs -> specs.values().stream()
.filter(ExtraJavaModuleInfoPlugin::needsDependencies)
.map(ModuleSpec::getIdentifier)
.collect(Collectors.toSet());
.collect(Collectors.toSet()));
}

private static boolean needsDependencies(ModuleSpec moduleSpec) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public abstract class ExtraJavaModuleInfoPluginExtension {
public abstract Property<Boolean> getFailOnMissingModuleInfo();
public abstract Property<Boolean> getFailOnAutomaticModules();
public abstract Property<Boolean> getDeriveAutomaticModuleNamesFromFileNames();
public abstract Property<String> getVersionsProvidingConfiguration();

/**
* Add full module information for a given Jar file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,14 +367,19 @@ private byte[] addModuleInfo(ModuleInfo moduleInfo, Map<String, List<String>> pr
moduleVisitor.visitRequire("java.base", 0, null);

if (moduleInfo.requireAllDefinedDependencies) {
String fullIdentifier = moduleInfo.getIdentifier() + ":" + version;
PublishedMetadata requires = getParameters().getRequiresFromMetadata().get().get(fullIdentifier);
String identifier = moduleInfo.getIdentifier();
PublishedMetadata requires = getParameters().getRequiresFromMetadata().get().get(identifier);

if (requires == null) {
throw new RuntimeException("[requires directives from metadata] " +
"Cannot find dependencies for '" + moduleInfo.getModuleName() + "'. " +
"Are '" + moduleInfo.getIdentifier() + "' the correct component coordinates?");
}
if (requires.getErrorMessage() != null) {
throw new RuntimeException("[requires directives from metadata] " +
"Cannot read metadata for '" + moduleInfo.getModuleName() + "': " +
requires.getErrorMessage());
}

for (String ga : requires.getRequires()) {
String depModuleName = gaToModuleName(ga);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,49 @@

import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.result.DependencyResult;
import org.gradle.api.artifacts.result.ResolvedComponentResult;
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.Bundling;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.Usage;
import org.gradle.api.attributes.java.TargetJvmEnvironment;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.util.GradleVersion;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE;
import static org.gradle.api.attributes.Category.LIBRARY;
import static org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE;

public class PublishedMetadata implements Serializable {
private static final Attribute<String> CATEGORY_ATTRIBUTE_UNTYPED = Attribute.of(CATEGORY_ATTRIBUTE.getName(), String.class);
private static final String DEFAULT_VERSION_SOURCE_CONFIGURATION = "definedDependenciesVersions";

private final String gav;
private final List<String> requires = new ArrayList<>();
private final List<String> requiresTransitive = new ArrayList<>();
private final List<String> requiresStaticTransitive = new ArrayList<>();
private String errorMessage = null;

PublishedMetadata(String gav, Configuration origin, Project project) {
PublishedMetadata(String gav, Project project, ExtraJavaModuleInfoPluginExtension extension) {
this.gav = gav;
List<String> compileDependencies = componentVariant(origin, project, Usage.JAVA_API);
List<String> runtimeDependencies = componentVariant(origin, project, Usage.JAVA_RUNTIME);

List<String> compileDependencies = componentVariant(extension.getVersionsProvidingConfiguration(), project, Usage.JAVA_API);
List<String> runtimeDependencies = componentVariant(extension.getVersionsProvidingConfiguration(), project, Usage.JAVA_RUNTIME);

Stream.concat(compileDependencies.stream(), runtimeDependencies.stream()).distinct().forEach(ga -> {
if (compileDependencies.contains(ga) && runtimeDependencies.contains(ga)) {
Expand All @@ -60,26 +73,73 @@ public class PublishedMetadata implements Serializable {
});
}

private List<String> componentVariant(Configuration origin, Project project, String usage) {
private List<String> componentVariant(Provider<String> versionsProvidingConfiguration, Project project, String usage) {
Configuration versionsSource;
if (versionsProvidingConfiguration.isPresent()) {
versionsSource = project.getConfigurations().getByName(versionsProvidingConfiguration.get());
} else {
// version provider is not configured, create on adhoc based on ALL classpaths of the project
versionsSource = maybeCreateDefaultVersionSourcConfiguration(project.getConfigurations(), project.getObjects(),
project.getExtensions().findByType(SourceSetContainer.class));
}

Configuration singleComponentVariantResolver = project.getConfigurations().detachedConfiguration(project.getDependencies().create(gav));
singleComponentVariantResolver.setCanBeConsumed(false);
singleComponentVariantResolver.shouldResolveConsistentlyWith(origin);
origin.getAttributes().keySet().forEach(a -> {
singleComponentVariantResolver.shouldResolveConsistentlyWith(versionsSource);
versionsSource.getAttributes().keySet().forEach(a -> {
@SuppressWarnings("rawtypes") Attribute untypedAttributeKey = a;
//noinspection unchecked
singleComponentVariantResolver.getAttributes().attribute(untypedAttributeKey, requireNonNull(origin.getAttributes().getAttribute(a)));
singleComponentVariantResolver.getAttributes().attribute(untypedAttributeKey, requireNonNull(versionsSource.getAttributes().getAttribute(a)));
});
singleComponentVariantResolver.getAttributes().attribute(USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, usage));
return firstAndOnlyComponent(singleComponentVariantResolver).getDependencies().stream()
.filter(PublishedMetadata::filterComponentDependencies)
.map(PublishedMetadata::ga)
.collect(Collectors.toList());
return firstAndOnlyComponentDependencies(singleComponentVariantResolver);
}

private Configuration maybeCreateDefaultVersionSourcConfiguration(ConfigurationContainer configurations, ObjectFactory objects, SourceSetContainer sourceSets) {
String name = DEFAULT_VERSION_SOURCE_CONFIGURATION;
Configuration existing = configurations.findByName(name);
if (existing != null) {
return existing;
}

return configurations.create(name, c -> {
c.setCanBeResolved(true);
c.setCanBeConsumed(false);
c.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.JAVA_RUNTIME));
c.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.LIBRARY));
c.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.class, LibraryElements.JAR));
c.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.class, Bundling.EXTERNAL));
if (GradleVersion.current().compareTo(GradleVersion.version("7.0")) >= 0) {
c.getAttributes().attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
}

if (sourceSets != null) {
for (SourceSet sourceSet : sourceSets) {
Configuration implementation = configurations.getByName(sourceSet.getImplementationConfigurationName());
Configuration compileOnly = configurations.getByName(sourceSet.getCompileOnlyConfigurationName());
Configuration runtimeOnly = configurations.getByName(sourceSet.getRuntimeOnlyConfigurationName());
Configuration annotationProcessor = configurations.getByName(sourceSet.getAnnotationProcessorConfigurationName());
c.extendsFrom(implementation, compileOnly, runtimeOnly, annotationProcessor);
}
}
});
}

private ResolvedComponentResult firstAndOnlyComponent(Configuration singleComponentVariantResolver) {
ResolvedDependencyResult onlyResult = (ResolvedDependencyResult) singleComponentVariantResolver.getIncoming().getResolutionResult()
.getRoot().getDependencies().iterator().next();
return onlyResult.getSelected();
private List<String> firstAndOnlyComponentDependencies(Configuration singleComponentVariantResolver) {
DependencyResult result = singleComponentVariantResolver
.getIncoming().getResolutionResult().getRoot()
.getDependencies().iterator().next();

if (result instanceof UnresolvedDependencyResult) {
errorMessage = ((UnresolvedDependencyResult) result).getFailure().getMessage();
return emptyList();
} else {
return ((ResolvedDependencyResult) result).getSelected().getDependencies().stream()
.filter(PublishedMetadata::filterComponentDependencies)
.map(PublishedMetadata::ga)
.collect(Collectors.toList());
}
}

private static boolean filterComponentDependencies(DependencyResult d) {
Expand Down Expand Up @@ -113,4 +173,8 @@ public List<String> getRequiresTransitive() {
public List<String> getRequiresStaticTransitive() {
return requiresStaticTransitive;
}

public String getErrorMessage() {
return errorMessage;
}
}

0 comments on commit 7c8412a

Please sign in to comment.