From c4ce3ec77cfb1c18dbcd6fe09ad6fb1ea924ba87 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Thu, 21 Sep 2023 13:59:53 +0200 Subject: [PATCH] Include compile-only dependencies in the ApplicationModel --- .../runnerjar/PackageAppTestBase.java | 15 ++- .../runnerjar/ProvidedExtensionDepsTest.java | 46 ++++++-- .../ProvidedExtensionDepsTestModeTest.java | 108 ++++++++++++++++++ .../GradleApplicationModelBuilder.java | 6 +- .../maven/DependencyTreeMojoTestBase.java | 4 +- .../bootstrap/model/ApplicationModel.java | 15 ++- .../model/DefaultApplicationModel.java | 48 +++++++- .../maven/dependency/DependencyFlags.java | 43 +++++-- .../resolver/BootstrapAppModelResolver.java | 9 +- .../ApplicationDependencyTreeResolver.java | 18 ++- .../maven/BuildDependencyGraphVisitor.java | 19 ++- .../builder/QuarkusModelBuilderTest.java | 4 +- 12 files changed, 288 insertions(+), 47 deletions(-) create mode 100644 core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/PackageAppTestBase.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/PackageAppTestBase.java index 497dbb331d6ed..3e99369b1c29d 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/PackageAppTestBase.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/PackageAppTestBase.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -115,7 +114,17 @@ public Path getPath(Path workDir) throws IOException { public static Collection getDeploymentOnlyDeps(ApplicationModel model) { return model.getDependencies().stream().filter(d -> d.isDeploymentCp() && !d.isRuntimeCp()) - .map(d -> new ArtifactDependency(d)).collect(Collectors.toSet()); + .map(ArtifactDependency::new).collect(Collectors.toSet()); + } + + public static Collection getDependenciesWithFlag(ApplicationModel model, int flag) { + var set = new HashSet(); + for (var d : model.getDependencies(flag)) { + if (d.isFlagSet(flag)) { + set.add(new ArtifactDependency(d)); + } + } + return set; } @Override @@ -159,7 +168,7 @@ protected void testBootstrap(QuarkusBootstrap creator) throws Exception { } } - List missingEntries = Collections.emptyList(); + List missingEntries = List.of(); for (String entry : expectedLib) { if (!actualMainLib.remove(entry)) { if (missingEntries.isEmpty()) { diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java index 4c7e59652eda5..7f612eb6534b3 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTest.java @@ -5,14 +5,16 @@ import java.util.HashSet; import java.util.Set; +import org.eclipse.aether.util.artifact.JavaScopes; + import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsDependency; import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactDependency; import io.quarkus.maven.dependency.Dependency; import io.quarkus.maven.dependency.DependencyFlags; -import io.quarkus.maven.dependency.GACTV; public class ProvidedExtensionDepsTest extends BootstrapFromOriginalJarTestBase { @@ -22,6 +24,10 @@ protected TsArtifact composeApplication() { final TsArtifact extADep = TsArtifact.jar("ext-a-dep"); addToExpectedLib(extADep); + final TsArtifact depC1 = TsArtifact.jar("dep-c"); + addToExpectedLib(depC1); + extADep.addDependency(depC1); + final TsArtifact extAProvidedDep = TsArtifact.jar("ext-a-provided-dep"); final TsArtifact extADeploymentDep = TsArtifact.jar("ext-a-deployment-dep"); @@ -30,32 +36,56 @@ protected TsArtifact composeApplication() { final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); addToExpectedLib(extA.getRuntime()); extA.getRuntime() - .addDependency(new TsDependency(extADep)) + .addDependency(extADep) .addDependency(new TsDependency(extAProvidedDep, "provided")); extA.getDeployment() - .addDependency(new TsDependency(extADeploymentDep)) + .addDependency(extADeploymentDep) .addDependency(new TsDependency(extAOptionalDeploymentDep, "provided")); final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); this.install(extB); - final TsArtifact someProvidedDep = TsArtifact.jar("some-provided-dep"); + final TsArtifact directProvidedDep = TsArtifact.jar("direct-provided-dep"); + + // TODO this is when provided leaks into runtime + //final TsArtifact depC2 = TsArtifact.jar("dep-c", "2"); + //addToExpectedLib(depC2); // in this case provided version will override the compile one + //directProvidedDep.addDependency(depC2); + + final TsArtifact transitiveProvidedDep = TsArtifact.jar("transitive-provided-dep"); + directProvidedDep.addDependency(transitiveProvidedDep); return TsArtifact.jar("app") .addManagedDependency(platformDescriptor()) .addManagedDependency(platformProperties()) .addDependency(extA) .addDependency(extB, "provided") - .addDependency(new TsDependency(someProvidedDep, "provided")); + .addDependency(new TsDependency(directProvidedDep, "provided")); } @Override protected void assertAppModel(ApplicationModel model) throws Exception { - final Set expected = new HashSet<>(); - expected.add(new ArtifactDependency(new GACTV("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), "compile", + Set expected = new HashSet<>(); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), DependencyFlags.DEPLOYMENT_CP)); - expected.add(new ArtifactDependency(new GACTV("io.quarkus.bootstrap.test", "ext-a-deployment-dep", "1"), "compile", + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment-dep", "1"), DependencyFlags.DEPLOYMENT_CP)); assertEquals(expected, getDeploymentOnlyDeps(model)); + + expected = new HashSet<>(); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b", "1"), + JavaScopes.PROVIDED, + DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, + DependencyFlags.DIRECT, + DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT, + DependencyFlags.COMPILE_ONLY)); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "direct-provided-dep", "1"), + JavaScopes.PROVIDED, + DependencyFlags.DIRECT, + DependencyFlags.COMPILE_ONLY)); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "transitive-provided-dep", "1"), + JavaScopes.PROVIDED, + DependencyFlags.COMPILE_ONLY)); + assertEquals(expected, getDependenciesWithFlag(model, DependencyFlags.COMPILE_ONLY)); } } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java new file mode 100644 index 0000000000000..4270ac9851b2d --- /dev/null +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java @@ -0,0 +1,108 @@ +package io.quarkus.deployment.runnerjar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.aether.util.artifact.JavaScopes; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsDependency; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactDependency; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.DependencyFlags; + +public class ProvidedExtensionDepsTestModeTest extends BootstrapFromOriginalJarTestBase { + + @Override + protected boolean isBootstrapForTestMode() { + return true; + } + + @Override + protected TsArtifact composeApplication() { + + final TsArtifact extADep = TsArtifact.jar("ext-a-dep"); + addToExpectedLib(extADep); + + final TsArtifact depC1 = TsArtifact.jar("dep-c"); + extADep.addDependency(depC1); + + final TsArtifact extAProvidedDep = TsArtifact.jar("ext-a-provided-dep"); + + final TsArtifact extADeploymentDep = TsArtifact.jar("ext-a-deployment-dep"); + final TsArtifact extAOptionalDeploymentDep = TsArtifact.jar("ext-a-provided-deployment-dep"); + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + addToExpectedLib(extA.getRuntime()); + extA.getRuntime() + .addDependency(extADep) + .addDependency(new TsDependency(extAProvidedDep, "provided")); + extA.getDeployment() + .addDependency(extADeploymentDep) + .addDependency(new TsDependency(extAOptionalDeploymentDep, "provided")); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + this.install(extB); + addToExpectedLib(extB.getRuntime()); // test mode enabled + + final TsArtifact directProvidedDep = TsArtifact.jar("direct-provided-dep"); + addToExpectedLib(directProvidedDep); // test mode enabled + + final TsArtifact depC2 = TsArtifact.jar("dep-c", "2"); + addToExpectedLib(depC2); // in this case provided version will override the compile one + directProvidedDep.addDependency(depC2); + + final TsArtifact transitiveProvidedDep = TsArtifact.jar("transitive-provided-dep"); + directProvidedDep.addDependency(transitiveProvidedDep); + addToExpectedLib(transitiveProvidedDep); // test mode enabled + + return TsArtifact.jar("app") + .addManagedDependency(platformDescriptor()) + .addManagedDependency(platformProperties()) + .addDependency(new TsDependency(directProvidedDep, "provided")) + .addDependency(extA) + .addDependency(extB, "provided"); + } + + @Override + protected void assertAppModel(ApplicationModel model) throws Exception { + + final Set compileOnly = new HashSet<>(); + compileOnly.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b", "1"), + JavaScopes.PROVIDED, + DependencyFlags.RUNTIME_CP, + DependencyFlags.DEPLOYMENT_CP, + DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, + DependencyFlags.DIRECT, + DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT, + DependencyFlags.COMPILE_ONLY)); + compileOnly.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "direct-provided-dep", "1"), + JavaScopes.PROVIDED, + DependencyFlags.RUNTIME_CP, + DependencyFlags.DEPLOYMENT_CP, + DependencyFlags.DIRECT, + DependencyFlags.COMPILE_ONLY)); + compileOnly.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "transitive-provided-dep", "1"), + JavaScopes.PROVIDED, + DependencyFlags.RUNTIME_CP, + DependencyFlags.DEPLOYMENT_CP, + DependencyFlags.COMPILE_ONLY)); + assertEquals(compileOnly, getDependenciesWithFlag(model, DependencyFlags.COMPILE_ONLY)); + + Set expected = new HashSet<>(); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment", "1"), + DependencyFlags.DEPLOYMENT_CP)); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-deployment-dep", "1"), + DependencyFlags.DEPLOYMENT_CP)); + expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-b-deployment", "1"), + JavaScopes.PROVIDED, + DependencyFlags.DEPLOYMENT_CP)); + + assertEquals(expected, getDeploymentOnlyDeps(model)); + } +} diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java index 834f95206d32b..378a06566f018 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java @@ -275,7 +275,8 @@ private void collectDependencies(ResolvedConfiguration configuration, .setVersion(version) .setResolvedPath(f.toPath()) .setDirect(true) - .setRuntimeCp(); + .setRuntimeCp() + .setDeploymentCp(); processQuarkusDependency(artifactBuilder, modelBuilder); modelBuilder.addDependency(artifactBuilder); } @@ -296,7 +297,8 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res final ArtifactCoords depCoords = toArtifactCoords(a); final ResolvedDependencyBuilder depBuilder = ResolvedDependencyBuilder.newInstance() .setCoords(depCoords) - .setRuntimeCp(); + .setRuntimeCp() + .setDeploymentCp(); if (isFlagOn(flags, COLLECT_DIRECT_DEPS)) { depBuilder.setDirect(true); flags = clearFlag(flags, COLLECT_DIRECT_DEPS); diff --git a/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java b/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java index 1ff2aad86c4df..bd99f28420ec7 100644 --- a/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java +++ b/devtools/maven/src/test/java/io/quarkus/maven/DependencyTreeMojoTestBase.java @@ -7,7 +7,6 @@ import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -98,7 +97,8 @@ public void test() throws Exception { System.setOut(defaultOut); } - assertEquals(readInLowCase(Paths.get("").toAbsolutePath().resolve("target").resolve("test-classes") + assertEquals(readInLowCase(Path.of("").normalize().toAbsolutePath() + .resolve("target").resolve("test-classes") .resolve(app.getArtifactFileName() + "." + mode())), readInLowCase(mojoLog)); } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModel.java index 2cb24beea18d0..95bd38dfc31d0 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModel.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModel.java @@ -26,12 +26,25 @@ public interface ApplicationModel { ResolvedDependency getAppArtifact(); /** - * All the dependencies of an application including runtime and build time dependencies. + * Returns application dependencies that are included into the runtime and augmentation (Quarkus build time) + * classpath. + *

+ * Note: in production bootstrap mode, {@link io.quarkus.maven.dependency.DependencyFlags#COMPILE_ONLY} dependencies + * will not be included in the result. However, they could still be queries by calling {@link #getDependencies(int)} + * passing in {@link io.quarkus.maven.dependency.DependencyFlags#COMPILE_ONLY} flag as an argument. * * @return application runtime and build time dependencies */ Collection getDependencies(); + /** + * Returns application dependencies with the requested flags set. + * + * @param flags dependency flags that must be set for a dependency to be included in the result + * @return application dependencies that have requested flags set + */ + Iterable getDependencies(int flags); + /** * Runtime dependencies of an application * diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java index 2b80f9cb4952f..2aab254d2db02 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java @@ -1,9 +1,12 @@ package io.quarkus.bootstrap.model; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.stream.Collectors; @@ -39,7 +42,50 @@ public ResolvedDependency getAppArtifact() { @Override public Collection getDependencies() { - return dependencies; + var result = new ArrayList(dependencies.size()); + for (var d : getDependencies(DependencyFlags.DEPLOYMENT_CP)) { + result.add(d); + } + return result; + } + + @Override + public Iterable getDependencies(int flags) { + return () -> new Iterator<>() { + + final Iterator i = dependencies.iterator(); + ResolvedDependency next; + + { + moveOn(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public ResolvedDependency next() { + if (next == null) { + throw new NoSuchElementException(); + } + var current = next; + moveOn(); + return current; + } + + private void moveOn() { + next = null; + while (i.hasNext()) { + var d = i.next(); + if ((d.getFlags() & flags) == flags) { + next = d; + break; + } + } + } + }; } @Override diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/DependencyFlags.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/DependencyFlags.java index 117a6d2730eb2..bc7475da3ae5e 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/DependencyFlags.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/DependencyFlags.java @@ -3,25 +3,44 @@ public interface DependencyFlags { /* @formatter:off */ - public static final int OPTIONAL = 0b000000000001; - public static final int DIRECT = 0b000000000010; - public static final int RUNTIME_CP = 0b000000000100; - public static final int DEPLOYMENT_CP = 0b000000001000; - public static final int RUNTIME_EXTENSION_ARTIFACT = 0b000000010000; - public static final int WORKSPACE_MODULE = 0b000000100000; - public static final int RELOADABLE = 0b000001000000; + public static final int OPTIONAL = 0b0000000000001; + public static final int DIRECT = 0b0000000000010; + public static final int RUNTIME_CP = 0b0000000000100; + public static final int DEPLOYMENT_CP = 0b0000000001000; + public static final int RUNTIME_EXTENSION_ARTIFACT = 0b0000000010000; + public static final int WORKSPACE_MODULE = 0b0000000100000; + public static final int RELOADABLE = 0b0000001000000; // A top-level runtime extension artifact is either a direct // dependency or a first extension dependency on the branch // navigating from the root to leaves - public static final int TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT = 0b000010000000; - public static final int CLASSLOADER_PARENT_FIRST = 0b000100000000; - public static final int CLASSLOADER_RUNNER_PARENT_FIRST = 0b001000000000; - public static final int CLASSLOADER_LESSER_PRIORITY = 0b010000000000; + public static final int TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT = 0b0000010000000; + public static final int CLASSLOADER_PARENT_FIRST = 0b0000100000000; + public static final int CLASSLOADER_RUNNER_PARENT_FIRST = 0b0001000000000; + public static final int CLASSLOADER_LESSER_PRIORITY = 0b0010000000000; // General purpose flag that could be re-used for various // kinds of processing indicating that a dependency has been // visited. This flag is meant to be cleared for all the nodes // once the processing of the whole tree has completed. - public static final int VISITED = 0b100000000000; + public static final int VISITED = 0b0100000000000; + + /** + * Compile-only dependencies are those that are configured in the project + * to be included only in the compile phase ({@code provided} dependency scope in Maven, + * {@code compileOnly} configuration in Gradle). + *

+ * These dependencies will not be present on the Quarkus application runtime or + * augmentation (deployment) classpath when the application is bootstrapped in production mode + * (io.quarkus.runtime.LaunchMode.NORMAL). + *

+ * Compile-only dependencies will be present on both the runtime and the augmentation classpath + * of a Quarkus application launched in test and dev modes. + *

+ * In any case though, these dependencies will be available during augmentation for processing + * using {@link io.quarkus.bootstrap.model.ApplicationModel#getDependencies(int)} by passing + * this flag as an argument. + */ + public static final int COMPILE_ONLY = 0b1000000000000; + /* @formatter:on */ } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java index 8d486d0c7a989..96febed333639 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java @@ -289,13 +289,7 @@ private ApplicationModel doResolveModel(ArtifactCoords coords, } private Set getExcludedScopes() { - if (test) { - return Set.of(); - } - if (devmode) { - return Set.of(JavaScopes.TEST); - } - return Set.of(JavaScopes.PROVIDED, JavaScopes.TEST); + return test ? Set.of() : Set.of(JavaScopes.TEST); } private ApplicationModel buildAppModel(ResolvedDependency appArtifact, CollectRequest collectRtDepsRequest, @@ -318,6 +312,7 @@ private ApplicationModel buildAppModel(ResolvedDependency appArtifact, CollectRe .setApplicationModelBuilder(appBuilder) .setCollectReloadableModules(collectReloadableDeps && reloadableModules.isEmpty()) .setBuildTreeConsumer(buildTreeConsumer) + .setIncludeCompileOnly(test || devmode) .resolve(collectRtDepsRequest); } catch (BootstrapDependencyProcessingException e) { throw new AppModelResolverException( diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java index 21d10db1cec82..f2a0c010e2705 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java @@ -99,6 +99,7 @@ public static Artifact getRuntimeArtifact(DependencyNode dep) { private ApplicationModelBuilder appBuilder; private boolean collectReloadableModules; private Consumer buildTreeConsumer; + private boolean includeCompileOnly; public ApplicationDependencyTreeResolver setArtifactResolver(MavenArtifactResolver resolver) { this.resolver = resolver; @@ -130,6 +131,11 @@ public ApplicationDependencyTreeResolver setBuildTreeConsumer(Consumer b return this; } + public ApplicationDependencyTreeResolver setIncludeCompileOnly(boolean includeCompileOnly) { + this.includeCompileOnly = includeCompileOnly; + return this; + } + public void resolve(CollectRequest collectRtDepsRequest) throws AppModelResolverException { DependencyNode root = resolveRuntimeDeps(collectRtDepsRequest); @@ -206,7 +212,7 @@ public void resolve(CollectRequest collectRtDepsRequest) throws AppModelResolver root = normalize(originalSession, root); final BuildDependencyGraphVisitor buildDepsVisitor = new BuildDependencyGraphVisitor(originalResolver, appBuilder, - buildTreeConsumer); + buildTreeConsumer, includeCompileOnly); buildDepsVisitor.visit(root); if (!CONVERGED_TREE_ONLY && collectReloadableModules) { @@ -354,11 +360,17 @@ private void visitRuntimeDependency(DependencyNode node) { artifact.getArtifactId(), artifact.getVersion()); } dep = toAppArtifact(artifact, module) - .setRuntimeCp() - .setDeploymentCp() .setOptional(node.getDependency().isOptional()) .setScope(node.getDependency().getScope()) .setDirect(isWalkingFlagOn(COLLECT_DIRECT_DEPS)); + if (JavaScopes.PROVIDED.equals(dep.getScope())) { + dep.setFlags(DependencyFlags.COMPILE_ONLY); + if (includeCompileOnly) { + dep.setRuntimeCp().setDeploymentCp(); + } + } else { + dep.setRuntimeCp().setDeploymentCp(); + } if (extDep != null) { dep.setRuntimeExtensionArtifact(); if (isWalkingFlagOn(COLLECT_TOP_EXTENSION_RUNTIME_NODES)) { diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java index 262a10365d701..a6c1c49fd9c03 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java @@ -10,6 +10,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.util.artifact.JavaScopes; import io.quarkus.bootstrap.model.ApplicationModelBuilder; import io.quarkus.bootstrap.util.DependencyUtils; @@ -21,24 +22,23 @@ public class BuildDependencyGraphVisitor { private final MavenArtifactResolver resolver; private final ApplicationModelBuilder appBuilder; - private final StringBuilder buf; private final Consumer buildTreeConsumer; private final List depth; + private final boolean includeCompileOnly; private DependencyNode currentDeployment; private DependencyNode currentRuntime; private Artifact runtimeArtifactToFind; public BuildDependencyGraphVisitor(MavenArtifactResolver resolver, ApplicationModelBuilder appBuilder, - Consumer buildTreeConsumer) { + Consumer buildTreeConsumer, boolean includeCompileOnly) { this.resolver = resolver; this.appBuilder = appBuilder; this.buildTreeConsumer = buildTreeConsumer; + this.includeCompileOnly = includeCompileOnly; if (buildTreeConsumer == null) { - buf = null; depth = null; } else { - buf = new StringBuilder(); depth = new ArrayList<>(); } } @@ -97,7 +97,12 @@ public void visit(DependencyNode node) throws BootstrapMavenException { } private void consume(DependencyNode node) { - buf.setLength(0); + if (!includeCompileOnly + && !depth.isEmpty() + && JavaScopes.PROVIDED.equals(node.getDependency().getScope())) { + return; + } + var buf = new StringBuilder(); if (!depth.isEmpty()) { for (int i = 0; i < depth.size() - 1; ++i) { if (depth.get(i)) { @@ -141,7 +146,9 @@ private void visitLeave(DependencyNode node) throws BootstrapMavenException { artifact = resolver.resolve(artifact, node.getRepositories()).getArtifact(); } - int flags = DependencyFlags.DEPLOYMENT_CP; + int flags = includeCompileOnly || !JavaScopes.PROVIDED.equals(node.getDependency().getScope()) + ? DependencyFlags.DEPLOYMENT_CP + : 0; if (node.getDependency().isOptional()) { flags |= DependencyFlags.OPTIONAL; } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java index 213ea5df6a14c..1239360b4b959 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java @@ -72,7 +72,7 @@ public void shouldLoadMultiModuleTestModel() throws URISyntaxException, IOExcept new File(projectDir, quarkusModel.getApplicationModule().getId().getArtifactId()), true); final Collection projectModules = quarkusModel.getWorkspaceModules(); - assertEquals(projectModules.size(), 2); + assertEquals(2, projectModules.size()); for (WorkspaceModule p : projectModules) { assertProjectModule(p, new File(projectDir, p.getId().getArtifactId()), quarkusModel.getApplicationModule().getId().equals(p.getId())); @@ -106,7 +106,7 @@ public void shouldLoadMultiModuleDevModel() throws URISyntaxException, IOExcepti new File(projectDir, quarkusModel.getApplicationModule().getId().getArtifactId()), true); final Collection projectModules = quarkusModel.getWorkspaceModules(); - assertEquals(projectModules.size(), 2); + assertEquals(2, projectModules.size()); for (WorkspaceModule p : projectModules) { assertProjectModule(p, new File(projectDir, p.getId().getArtifactId()), quarkusModel.getApplicationModule().getId().equals(p.getId()));