Skip to content

Commit

Permalink
Merge pull request #20112 from glefloch/fix/19022
Browse files Browse the repository at this point in the history
Optimize conditional dependencies resolution in Gradle
  • Loading branch information
aloubyansky authored Sep 16, 2021
2 parents 67b5f67 + 4749536 commit 4111085
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 72 deletions.
49 changes: 28 additions & 21 deletions devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import io.quarkus.gradle.builder.QuarkusModelBuilder;
import io.quarkus.gradle.dependency.ApplicationDeploymentClasspathBuilder;
import io.quarkus.gradle.dependency.ConditionalDependenciesEnabler;
import io.quarkus.gradle.dependency.ExtensionDependency;
import io.quarkus.gradle.extension.QuarkusPluginExtension;
import io.quarkus.gradle.extension.SourceSetExtension;
import io.quarkus.gradle.tasks.QuarkusAddExtension;
Expand Down Expand Up @@ -82,11 +83,6 @@ public class QuarkusPlugin implements Plugin<Project> {
public static final String NATIVE_TEST_IMPLEMENTATION_CONFIGURATION_NAME = "nativeTestImplementation";
public static final String NATIVE_TEST_RUNTIME_ONLY_CONFIGURATION_NAME = "nativeTestRuntimeOnly";

private static final String[] CONDITIONAL_DEPENDENCY_LOOKUP = new String[] {
JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME,
DEV_MODE_CONFIGURATION_NAME
};

private final ToolingModelBuilderRegistry registry;

@Inject
Expand Down Expand Up @@ -193,9 +189,7 @@ public void execute(Task test) {
.plus(testSourceSet.getOutput()));

// create a custom configuration for devmode
configurations.create(DEV_MODE_CONFIGURATION_NAME).extendsFrom(
configurations.getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME),
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
configurations.create(DEV_MODE_CONFIGURATION_NAME);

// create a custom configuration to be used for the dependencies of the testNative task
configurations.maybeCreate(NATIVE_TEST_IMPLEMENTATION_CONFIGURATION_NAME)
Expand Down Expand Up @@ -239,6 +233,31 @@ public void execute(Task test) {
});
}

private void registerConditionalDependencies(Project project) {
ConditionalDependenciesEnabler conditionalDependenciesEnabler = new ConditionalDependenciesEnabler(project);
ApplicationDeploymentClasspathBuilder deploymentClasspathBuilder = new ApplicationDeploymentClasspathBuilder(
project);
project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).getIncoming()
.beforeResolve((dependencies) -> {
Set<ExtensionDependency> implementationExtensions = conditionalDependenciesEnabler
.declareConditionalDependencies(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
deploymentClasspathBuilder.createBuildClasspath(implementationExtensions,
JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, true);
});
project.getConfigurations().getByName(DEV_MODE_CONFIGURATION_NAME).getIncoming().beforeResolve((devDependencies) -> {
Set<ExtensionDependency> devModeExtensions = conditionalDependenciesEnabler
.declareConditionalDependencies(DEV_MODE_CONFIGURATION_NAME);
deploymentClasspathBuilder.createBuildClasspath(devModeExtensions, DEV_MODE_CONFIGURATION_NAME, false);
});
project.getConfigurations().getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).getIncoming()
.beforeResolve((testDependencies) -> {
Set<ExtensionDependency> testExtensions = conditionalDependenciesEnabler
.declareConditionalDependencies(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME);
deploymentClasspathBuilder.createBuildClasspath(testExtensions,
JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, false);
});
}

private Set<Path> getSourcesParents(SourceSet mainSourceSet) {
Set<File> srcDirs = mainSourceSet.getJava().getSrcDirs();
return srcDirs.stream()
Expand Down Expand Up @@ -270,18 +289,7 @@ private void configureBuildNativeTask(Project project) {

private void afterEvaluate(Project project) {

ConditionalDependenciesEnabler conditionalDependenciesEnabler = new ConditionalDependenciesEnabler(project);
ApplicationDeploymentClasspathBuilder deploymentClasspathBuilder = new ApplicationDeploymentClasspathBuilder(project);

conditionalDependenciesEnabler
.declareConditionalDependencies(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
deploymentClasspathBuilder.createBuildClasspath(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, true);

for (String baseConfiguration : CONDITIONAL_DEPENDENCY_LOOKUP) {
conditionalDependenciesEnabler
.declareConditionalDependencies(baseConfiguration);
deploymentClasspathBuilder.createBuildClasspath(baseConfiguration, false);
}
registerConditionalDependencies(project);

final HashSet<String> visited = new HashSet<>();
ConfigurationContainer configurations = project.getConfigurations();
Expand All @@ -297,7 +305,6 @@ private void afterEvaluate(Project project) {
.sourceSetExtension();

if (sourceSetExtension.extraNativeTest() != null) {

SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets();
SourceSet nativeTestSourceSets = sourceSets.getByName(NATIVE_TEST_SOURCE_SET_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,23 @@ public class QuarkusModelBuilder implements ParameterizedToolingModelBuilder<Mod

private static final String MAIN_RESOURCES_OUTPUT = "build/resources/main";
private static final String CLASSES_OUTPUT = "build/classes";
private static final String DEPLOYMENT_CONFIGURATION = "deploymentConfiguration";
private static final String DEPLOYMENT_CONFIGURATION = "quarkusDeploymentConfiguration";
private static final String CLASSPATH_CONFIGURATION = "quarkusClasspathConfiguration";

private static Configuration classpathConfig(Project project, LaunchMode mode) {
if (LaunchMode.TEST.equals(mode)) {
return project.getConfigurations().getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
}
if (LaunchMode.DEVELOPMENT.equals(mode)) {
return project.getConfigurations().getByName(QuarkusPlugin.DEV_MODE_CONFIGURATION_NAME);
Configuration classpathConfiguration = project.getConfigurations().findByName(CLASSPATH_CONFIGURATION);
if (classpathConfiguration != null) {
project.getConfigurations().remove(classpathConfiguration);
}

return project.getConfigurations().create(CLASSPATH_CONFIGURATION).extendsFrom(
project.getConfigurations().getByName(QuarkusPlugin.DEV_MODE_CONFIGURATION_NAME),
project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME),
project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
}
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
}
Expand All @@ -76,16 +85,29 @@ private static Configuration deploymentClasspathConfig(Project project, LaunchMo
}

deploymentConfiguration = project.getConfigurations().create(DEPLOYMENT_CONFIGURATION)
.withDependencies(ds -> ds.addAll(platforms))
.extendsFrom(project.getConfigurations().getByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME)));
.withDependencies(ds -> ds.addAll(platforms));
Configuration implementationDeployment = project.getConfigurations().findByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME));
if (implementationDeployment != null) {
deploymentConfiguration.extendsFrom(implementationDeployment);
}

if (LaunchMode.TEST.equals(mode)) {
deploymentConfiguration.extendsFrom(project.getConfigurations().getByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME)));
Configuration testDeploymentConfiguration = project.getConfigurations()
.findByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME));
if (testDeploymentConfiguration != null) {
deploymentConfiguration.extendsFrom(testDeploymentConfiguration);
}
}
if (LaunchMode.DEVELOPMENT.equals(mode)) {
deploymentConfiguration.extendsFrom(project.getConfigurations().getByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(QuarkusPlugin.DEV_MODE_CONFIGURATION_NAME)));
Configuration devDeploymentConfiguration = project.getConfigurations()
.findByName(ApplicationDeploymentClasspathBuilder
.toDeploymentConfigurationName(QuarkusPlugin.DEV_MODE_CONFIGURATION_NAME));
if (devDeploymentConfiguration != null) {
deploymentConfiguration.extendsFrom(devDeploymentConfiguration);
}

}
return deploymentConfiguration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ public static String toDeploymentConfigurationName(String baseConfigurationName)
return baseConfigurationName + DEPLOYMENT_CONFIGURATION_SUFFIX;
}

public void createBuildClasspath(String baseConfigurationName, boolean common) {
public void createBuildClasspath(Set<ExtensionDependency> extensions, String baseConfigurationName, boolean common) {
String deploymentConfigurationName = toDeploymentConfigurationName(baseConfigurationName);
project.getConfigurations().create(deploymentConfigurationName);

DependencyHandler dependencies = project.getDependencies();
Set<ExtensionDependency> firstLevelExtensions = DependencyUtils.loadQuarkusExtension(project,
project.getConfigurations().findByName(baseConfigurationName));
for (ExtensionDependency extension : firstLevelExtensions) {
for (ExtensionDependency extension : extensions) {
if (common) {
commonExtensions.add(extension);
} else if (commonExtensions.contains(extension)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,40 @@
public class ConditionalDependenciesEnabler {

private final Map<String, ExtensionDependency> featureVariants = new HashMap<>();

private final Set<ExtensionDependency> allExtensions = new HashSet<>();
private final Project project;

public ConditionalDependenciesEnabler(Project project) {
this.project = project;
}

public void declareConditionalDependencies(String baseConfigurationName) {
public Set<ExtensionDependency> declareConditionalDependencies(String baseConfigurationName) {
featureVariants.clear();

Configuration resolvedConfiguration = DependencyUtils.duplicateConfiguration(project,
project.getConfigurations().getByName(baseConfigurationName));

allExtensions.clear();
Configuration baseConfiguration = project.getConfigurations().getByName(baseConfigurationName);
if (baseConfiguration.getIncoming().getDependencies().isEmpty()) {
return Collections.emptySet();
}
Configuration resolvedConfiguration = DependencyUtils.duplicateConfiguration(project, baseConfiguration);
Set<ResolvedArtifact> runtimeArtifacts = resolvedConfiguration.getResolvedConfiguration().getResolvedArtifacts();

List<ExtensionDependency> extensions = collectExtensionsForResolution(runtimeArtifacts);
if (extensions.isEmpty()) {
return allExtensions;
}

featureVariants.putAll(extractFeatureVariants(extensions));

resolveConditionalDependencies(extensions, resolvedConfiguration, baseConfigurationName);
resolveConditionalDependencies(extensions, runtimeArtifacts, baseConfigurationName);
return allExtensions;
}

private List<ExtensionDependency> collectExtensionsForResolution(Set<ResolvedArtifact> runtimeArtifacts) {
List<ExtensionDependency> firstLevelExtensions = new ArrayList<>();
for (ResolvedArtifact artifact : runtimeArtifacts) {
ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact);
if (extension != null) {
allExtensions.add(extension);
if (!extension.conditionalDependencies.isEmpty()) {
if (extension.needsResolution(runtimeArtifacts)) {
firstLevelExtensions.add(extension);
Expand All @@ -53,18 +62,26 @@ private List<ExtensionDependency> collectExtensionsForResolution(Set<ResolvedArt
}

private void resolveConditionalDependencies(List<ExtensionDependency> conditionalExtensions,
Configuration existingDependencies, String baseConfigurationName) {
final Configuration conditionalDeps = createConditionalDependenciesConfiguration(existingDependencies,
conditionalExtensions);
Set<ResolvedArtifact> runtimeArtifacts, String baseConfigurationName) {

boolean hasChanged = false;
List<ExtensionDependency> newConditionalDependencies = new ArrayList<>();
newConditionalDependencies.addAll(conditionalExtensions);
for (ResolvedArtifact artifact : conditionalDeps.getResolvedConfiguration().getResolvedArtifacts()) {

final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project,
conditionalExtensions);
Set<ResolvedArtifact> resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts();

Set<ResolvedArtifact> availableRuntimeArtifacts = new HashSet<>();
availableRuntimeArtifacts.addAll(runtimeArtifacts);
availableRuntimeArtifacts.addAll(resolvedArtifacts);

for (ResolvedArtifact artifact : resolvedArtifacts) {
ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact);
if (extensionDependency != null) {
if (DependencyUtils.exist(conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(),
if (DependencyUtils.exist(availableRuntimeArtifacts,
extensionDependency.dependencyConditions)) {
enableConditionalDependency(extensionDependency.extensionId);
allExtensions.add(extensionDependency);
if (!extensionDependency.conditionalDependencies.isEmpty()) {
featureVariants.putAll(extractFeatureVariants(Collections.singletonList(extensionDependency)));
}
Expand All @@ -76,12 +93,13 @@ private void resolveConditionalDependencies(List<ExtensionDependency> conditiona
}
}

Configuration enhancedDependencies = DependencyUtils.duplicateConfiguration(project,
project.getConfigurations().getByName(baseConfigurationName));

if (hasChanged) {
if (!newConditionalDependencies.isEmpty()) {
resolveConditionalDependencies(newConditionalDependencies, enhancedDependencies, baseConfigurationName);
Configuration enhancedDependencies = DependencyUtils.duplicateConfiguration(project,
project.getConfigurations().getByName(baseConfigurationName));
Set<ResolvedArtifact> enhancedRuntimeArtifacts = enhancedDependencies.getResolvedConfiguration()
.getResolvedArtifacts();
resolveConditionalDependencies(newConditionalDependencies, enhancedRuntimeArtifacts, baseConfigurationName);
}
}
}
Expand All @@ -96,11 +114,10 @@ private Map<String, ExtensionDependency> extractFeatureVariants(List<ExtensionDe
return possibleVariant;
}

private Configuration createConditionalDependenciesConfiguration(Configuration existingDeps,
private Configuration createConditionalDependenciesConfiguration(Project project,
List<ExtensionDependency> extensions) {
Configuration newConfiguration = existingDeps.copy();
newConfiguration.getDependencies().addAll(collectConditionalDependencies(extensions));
return newConfiguration;
Set<Dependency> dependencies = collectConditionalDependencies(extensions);
return project.getConfigurations().detachedConfiguration(dependencies.toArray(new Dependency[0]));
}

private Set<Dependency> collectConditionalDependencies(List<ExtensionDependency> extensionDependencies) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,6 @@ public static boolean exists(Set<ResolvedArtifact> runtimeArtifacts, Dependency
return false;
}

public static Set<ExtensionDependency> loadQuarkusExtension(Project project, Configuration configuration) {
Set<ExtensionDependency> extensions = new HashSet<>();
Configuration configurationCopy = duplicateConfiguration(project, configuration);

Set<ResolvedArtifact> resolvedArtifacts = configurationCopy.getResolvedConfiguration().getResolvedArtifacts();
for (ResolvedArtifact artifact : resolvedArtifacts) {
ExtensionDependency extension = getExtensionInfoOrNull(project, artifact);
if (extension != null) {
extensions.add(extension);
}
}

return extensions;
}

public static boolean isTestFixtureDependency(Dependency dependency) {
if (!(dependency instanceof ModuleDependency)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.QuarkusDevModeLauncher;
import io.quarkus.gradle.QuarkusPlugin;
import io.quarkus.runtime.LaunchMode;

public class QuarkusDev extends QuarkusTask {
Expand Down Expand Up @@ -353,7 +352,7 @@ private void addSelfWithLocalDeps(Project project, GradleDevModeLauncher.Builder
if (!visited.add(project.getPath())) {
return;
}
final Configuration compileCp = project.getConfigurations().findByName(QuarkusPlugin.DEV_MODE_CONFIGURATION_NAME);
final Configuration compileCp = project.getConfigurations().findByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
if (compileCp != null) {
compileCp.getIncoming().getDependencies().forEach(d -> {
if (d instanceof ProjectDependency) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import java.util.Collections;

import org.gradle.api.GradleException;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.CompileClasspath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option;

Expand All @@ -30,6 +33,16 @@ public String getFile() {
return file;
}

/**
* Create a dependency on classpath resolution. This makes sure included build are build this task runs.
*
* @return resolved compile classpath
*/
@CompileClasspath
public FileCollection getClasspath() {
return QuarkusGradleUtils.getSourceSet(getProject(), SourceSet.MAIN_SOURCE_SET_NAME).getCompileClasspath();
}

@Option(description = "The name of the file to generate", option = "file")
public void setFile(String file) {
this.file = file;
Expand Down

0 comments on commit 4111085

Please sign in to comment.