From 4749536b2ca221d61114da9615705734b76420e5 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Wed, 8 Sep 2021 17:20:02 +0200 Subject: [PATCH] Optimize conditional dependencies resolution in Gradle --- .../java/io/quarkus/gradle/QuarkusPlugin.java | 49 ++++++++------- .../gradle/builder/QuarkusModelBuilder.java | 40 ++++++++++--- ...ApplicationDeploymentClasspathBuilder.java | 6 +- .../ConditionalDependenciesEnabler.java | 59 ++++++++++++------- .../gradle/dependency/DependencyUtils.java | 15 ----- .../io/quarkus/gradle/tasks/QuarkusDev.java | 3 +- .../gradle/tasks/QuarkusGenerateConfig.java | 13 ++++ 7 files changed, 113 insertions(+), 72 deletions(-) diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java index 370a14f9533f8..1dc5149b41b73 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java @@ -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; @@ -82,11 +83,6 @@ public class QuarkusPlugin implements Plugin { 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 @@ -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) @@ -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 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 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 testExtensions = conditionalDependenciesEnabler + .declareConditionalDependencies(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME); + deploymentClasspathBuilder.createBuildClasspath(testExtensions, + JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, false); + }); + } + private Set getSourcesParents(SourceSet mainSourceSet) { Set srcDirs = mainSourceSet.getJava().getSrcDirs(); return srcDirs.stream() @@ -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 visited = new HashSet<>(); ConfigurationContainer configurations = project.getConfigurations(); @@ -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); diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java index 79f279557a901..1be45e0690719 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java @@ -55,14 +55,23 @@ public class QuarkusModelBuilder implements ParameterizedToolingModelBuilder 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; } diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java index f49fe94a7138f..6d9332c6d743d 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java @@ -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 extensions, String baseConfigurationName, boolean common) { String deploymentConfigurationName = toDeploymentConfigurationName(baseConfigurationName); project.getConfigurations().create(deploymentConfigurationName); DependencyHandler dependencies = project.getDependencies(); - Set 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)) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java index 39414d74335b7..bf621a7225b3c 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java @@ -17,24 +17,32 @@ public class ConditionalDependenciesEnabler { private final Map featureVariants = new HashMap<>(); - + private final Set allExtensions = new HashSet<>(); private final Project project; public ConditionalDependenciesEnabler(Project project) { this.project = project; } - public void declareConditionalDependencies(String baseConfigurationName) { + public Set 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 runtimeArtifacts = resolvedConfiguration.getResolvedConfiguration().getResolvedArtifacts(); + List extensions = collectExtensionsForResolution(runtimeArtifacts); + if (extensions.isEmpty()) { + return allExtensions; + } + featureVariants.putAll(extractFeatureVariants(extensions)); - resolveConditionalDependencies(extensions, resolvedConfiguration, baseConfigurationName); + resolveConditionalDependencies(extensions, runtimeArtifacts, baseConfigurationName); + return allExtensions; } private List collectExtensionsForResolution(Set runtimeArtifacts) { @@ -42,6 +50,7 @@ private List collectExtensionsForResolution(Set collectExtensionsForResolution(Set conditionalExtensions, - Configuration existingDependencies, String baseConfigurationName) { - final Configuration conditionalDeps = createConditionalDependenciesConfiguration(existingDependencies, - conditionalExtensions); + Set runtimeArtifacts, String baseConfigurationName) { + boolean hasChanged = false; List newConditionalDependencies = new ArrayList<>(); - newConditionalDependencies.addAll(conditionalExtensions); - for (ResolvedArtifact artifact : conditionalDeps.getResolvedConfiguration().getResolvedArtifacts()) { + + final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, + conditionalExtensions); + Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); + + Set 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))); } @@ -76,12 +93,13 @@ private void resolveConditionalDependencies(List 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 enhancedRuntimeArtifacts = enhancedDependencies.getResolvedConfiguration() + .getResolvedArtifacts(); + resolveConditionalDependencies(newConditionalDependencies, enhancedRuntimeArtifacts, baseConfigurationName); } } } @@ -96,11 +114,10 @@ private Map extractFeatureVariants(List extensions) { - Configuration newConfiguration = existingDeps.copy(); - newConfiguration.getDependencies().addAll(collectConditionalDependencies(extensions)); - return newConfiguration; + Set dependencies = collectConditionalDependencies(extensions); + return project.getConfigurations().detachedConfiguration(dependencies.toArray(new Dependency[0])); } private Set collectConditionalDependencies(List extensionDependencies) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java index 91e17dbe0bf19..0c0a1ceb647a3 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/dependency/DependencyUtils.java @@ -87,21 +87,6 @@ public static boolean exists(Set runtimeArtifacts, Dependency return false; } - public static Set loadQuarkusExtension(Project project, Configuration configuration) { - Set extensions = new HashSet<>(); - Configuration configurationCopy = duplicateConfiguration(project, configuration); - - Set 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; diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java index f28c8bca01aa5..ba5888e3c6b78 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java @@ -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 { @@ -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) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateConfig.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateConfig.java index 8c6ccbd33e21e..73a4764e7b64f 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateConfig.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateConfig.java @@ -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; @@ -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;