diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromOriginalJarTestBase.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromOriginalJarTestBase.java index d9886d3bdd8e00..b00fb467d212d1 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromOriginalJarTestBase.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromOriginalJarTestBase.java @@ -72,8 +72,17 @@ protected QuarkusBootstrap.Builder initBootstrapBuilder() throws Exception { final QuarkusBootstrap.Builder bootstrap = QuarkusBootstrap.builder() .setApplicationRoot(applicationRoot) .setProjectRoot(applicationRoot) - .setAppModelResolver(resolver) - .setTest(isBootstrapForTestMode()); + .setAppModelResolver(resolver); + + switch (getBootstrapMode()) { + case PROD: + break; + case TEST: + bootstrap.setTest(true); + break; + default: + throw new IllegalArgumentException("Not supported bootstrap mode " + getBootstrapMode()); + } if (createWorkspace() || !wsModules.isEmpty()) { System.setProperty("basedir", ws.toAbsolutePath().toString()); diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromWorkspaceModuleTestBase.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromWorkspaceModuleTestBase.java index 4b02780956a774..6b48998c16cb86 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromWorkspaceModuleTestBase.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/BootstrapFromWorkspaceModuleTestBase.java @@ -19,8 +19,16 @@ protected QuarkusBootstrap.Builder initBootstrapBuilder() .setApplicationRoot(applicationRoot) .setProjectRoot(applicationRoot) .setTargetDirectory(appModel.getAppArtifact().getWorkspaceModule().getBuildDir().toPath()) - .setAppModelResolver(resolver) - .setTest(isBootstrapForTestMode()); + .setAppModelResolver(resolver); + switch (getBootstrapMode()) { + case PROD: + break; + case TEST: + bootstrap.setTest(true); + break; + default: + throw new IllegalArgumentException("Not supported bootstrap mode " + getBootstrapMode()); + } return bootstrap; } } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsInTestModeTest.java b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsInTestModeTest.java index 95c7dcf563b713..cc36dc92e3c34d 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsInTestModeTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsInTestModeTest.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.Set; +import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsDependency; @@ -17,8 +18,8 @@ public class ProvidedExtensionDepsInTestModeTest extends BootstrapFromOriginalJarTestBase { @Override - protected boolean isBootstrapForTestMode() { - return true; + protected QuarkusBootstrap.Mode getBootstrapMode() { + return QuarkusBootstrap.Mode.TEST; } @Override 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 index 26b8f66d583c20..7f6d175f67e71e 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/runnerjar/ProvidedExtensionDepsTestModeTest.java @@ -7,6 +7,7 @@ import org.eclipse.aether.util.artifact.JavaScopes; +import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsDependency; @@ -19,8 +20,8 @@ public class ProvidedExtensionDepsTestModeTest extends BootstrapFromOriginalJarTestBase { @Override - protected boolean isBootstrapForTestMode() { - return true; + protected QuarkusBootstrap.Mode getBootstrapMode() { + return QuarkusBootstrap.Mode.TEST; } @Override diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/QuarkusExtensionConfiguration.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/QuarkusExtensionConfiguration.java index 56b0b4e72a8b33..9049b78a15c366 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/QuarkusExtensionConfiguration.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/QuarkusExtensionConfiguration.java @@ -22,6 +22,7 @@ public class QuarkusExtensionConfiguration { private ListProperty runnerParentFirstArtifacts; private ListProperty lesserPriorityArtifacts; private ListProperty conditionalDependencies; + private ListProperty conditionalDevDependencies; private ListProperty dependencyCondition; private RemovedResources removedResources = new RemovedResources(); private Capabilities capabilities = new Capabilities(); @@ -41,6 +42,7 @@ public QuarkusExtensionConfiguration(Project project) { runnerParentFirstArtifacts = project.getObjects().listProperty(String.class); lesserPriorityArtifacts = project.getObjects().listProperty(String.class); conditionalDependencies = project.getObjects().listProperty(String.class); + conditionalDevDependencies = project.getObjects().listProperty(String.class); dependencyCondition = project.getObjects().listProperty(String.class); } @@ -108,6 +110,14 @@ public void setConditionalDependencies(List conditionalDependencies) { this.conditionalDependencies.addAll(conditionalDependencies); } + public ListProperty getConditionalDevDependencies() { + return conditionalDevDependencies; + } + + public void setConditionalDevDependencies(List conditionalDependencies) { + this.conditionalDevDependencies.addAll(conditionalDependencies); + } + public ListProperty getDependencyConditions() { return dependencyCondition; } diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ExtensionDescriptorTask.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ExtensionDescriptorTask.java index 938f1d83199f8b..ac4cb6be23aa9f 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ExtensionDescriptorTask.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ExtensionDescriptorTask.java @@ -44,6 +44,7 @@ import io.quarkus.extension.gradle.dsl.Capability; import io.quarkus.extension.gradle.dsl.RemovedResource; import io.quarkus.fs.util.ZipUtils; +import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.GACT; @@ -109,16 +110,10 @@ private void generateQuarkusExtensionProperties(Path metaInfDir) { props.setProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT, deploymentArtifact); - List conditionalDependencies = quarkusExtensionConfiguration.getConditionalDependencies().get(); - if (conditionalDependencies != null && !conditionalDependencies.isEmpty()) { - final StringBuilder buf = new StringBuilder(); - int i = 0; - buf.append(AppArtifactCoords.fromString(conditionalDependencies.get(i++)).toString()); - while (i < conditionalDependencies.size()) { - buf.append(' ').append(AppArtifactCoords.fromString(conditionalDependencies.get(i++)).toString()); - } - props.setProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES, buf.toString()); - } + setConditionalDepsProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES, + quarkusExtensionConfiguration.getConditionalDependencies().get(), props); + setConditionalDepsProperty(BootstrapConstants.CONDITIONAL_DEV_DEPENDENCIES, + quarkusExtensionConfiguration.getConditionalDevDependencies().get(), props); List dependencyConditions = quarkusExtensionConfiguration.getDependencyConditions().get(); if (dependencyConditions != null && !dependencyConditions.isEmpty()) { @@ -222,6 +217,18 @@ private void generateQuarkusExtensionProperties(Path metaInfDir) { } } + private static void setConditionalDepsProperty(String propName, List conditionalDependencies, Properties props) { + if (conditionalDependencies != null && !conditionalDependencies.isEmpty()) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(ArtifactCoords.fromString(conditionalDependencies.get(i++))); + while (i < conditionalDependencies.size()) { + buf.append(' ').append(ArtifactCoords.fromString(conditionalDependencies.get(i++))); + } + props.setProperty(propName, buf.toString()); + } + } + private void generateQuarkusExtensionDescriptor(Path inputMetaInfDirectory, Path outputMetaInfDirectory) throws IOException { File extensionFile = new File(inputMetaInfDirectory.toFile(), BootstrapConstants.QUARKUS_EXTENSION_FILE_NAME); diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java index 523ab4d735cd7a..2a6cd65081a990 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java @@ -48,7 +48,7 @@ public ConditionalDependenciesEnabler(Project project, LaunchMode mode, if (!baseRuntimeConfig.getIncoming().getDependencies().isEmpty()) { // Gather all extensions from the full resolved dependency tree - collectConditionalDependencies(baseRuntimeConfig.getResolvedConfiguration().getResolvedArtifacts()); + collectConditionalDependencies(baseRuntimeConfig.getResolvedConfiguration().getResolvedArtifacts(), mode); // If there are any extensions which had unresolved conditional dependencies: while (!unsatisfiedConditionalDeps.isEmpty()) { boolean satisfiedConditionalDeps = false; @@ -58,7 +58,7 @@ public ConditionalDependenciesEnabler(Project project, LaunchMode mode, while (i < unsatisfiedConditionalDeps.size()) { final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i); // Try to resolve it with the latest evolved graph available - if (resolveConditionalDependency(conditionalDep)) { + if (resolveConditionalDependency(conditionalDep, mode)) { // Mark the resolution as a success so we know the graph evolved satisfiedConditionalDeps = true; unsatisfiedConditionalDeps.remove(i); @@ -86,7 +86,7 @@ private void reset() { unsatisfiedConditionalDeps.clear(); } - private void collectConditionalDependencies(Set runtimeArtifacts) { + private void collectConditionalDependencies(Set runtimeArtifacts, LaunchMode mode) { // For every artifact in the dependency graph: for (ResolvedArtifact artifact : runtimeArtifacts) { // Add to master list of artifacts: @@ -96,17 +96,28 @@ private void collectConditionalDependencies(Set runtimeArtifac if (extension != null) { // Add to master list of accepted extensions: allExtensions.put(extension.getExtensionId(), extension); - for (Dependency conditionalDep : extension.getConditionalDependencies()) { - // If the dependency is not present yet in the graph, queue it for resolution later - if (!exists(conditionalDep)) { - queueConditionalDependency(extension, conditionalDep); - } - } + queueConditionalDependencies(extension, mode); } } } - private boolean resolveConditionalDependency(Dependency conditionalDep) { + private void queueConditionalDependencies(ExtensionDependency extension, LaunchMode mode) { + queueConditionalDependencies(extension, extension.getConditionalDependencies()); + if (LaunchMode.DEVELOPMENT.equals(mode)) { + queueConditionalDependencies(extension, extension.getConditionalDevDependencies()); + } + } + + private void queueConditionalDependencies(ExtensionDependency extension, Collection conditionalDeps) { + for (Dependency conditionalDep : conditionalDeps) { + // If the dependency is not present yet in the graph, queue it for resolution later + if (!exists(conditionalDep)) { + queueConditionalDependency(extension, conditionalDep); + } + } + } + + private boolean resolveConditionalDependency(Dependency conditionalDep, LaunchMode mode) { final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep); Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); @@ -148,12 +159,7 @@ private boolean resolveConditionalDependency(Dependency conditionalDep) { extensionDependency.setConditional(true); // Add to the master list of accepted extensions allExtensions.put(extensionDependency.getExtensionId(), extensionDependency); - for (Dependency cd : extensionDependency.getConditionalDependencies()) { - // Add any unsatisfied/unresolved conditional dependencies of this dependency to the queue - if (!exists(cd)) { - queueConditionalDependency(extensionDependency, cd); - } - } + queueConditionalDependencies(extensionDependency, mode); } return satisfied; } diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/extension/ConfigurationUtils.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/extension/ConfigurationUtils.java index 5645a94bee2ee1..3b2f6515086822 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/extension/ConfigurationUtils.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/extension/ConfigurationUtils.java @@ -40,6 +40,11 @@ public static ListProperty getConditionalDependencies(@NotNull Object ex return (ListProperty) callGetter(extensionConfiguration, "getConditionalDependencies"); } + @SuppressWarnings("unchecked") + public static ListProperty getConditionalDevDependencies(@NotNull Object extensionConfiguration) { + return (ListProperty) callGetter(extensionConfiguration, "getConditionalDevDependencies"); + } + @SuppressWarnings("unchecked") public static ListProperty getDependencyConditions(@NotNull Object extensionConfiguration) { return (ListProperty) callGetter(extensionConfiguration, "getDependencyConditions"); diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ArtifactExtensionDependency.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ArtifactExtensionDependency.java index a9c48817bb80f2..81dc9235382b59 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ArtifactExtensionDependency.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ArtifactExtensionDependency.java @@ -12,7 +12,8 @@ public class ArtifactExtensionDependency extends ExtensionDependency conditionalDependencies, + List conditionalDevDeps, List dependencyConditions) { - super(extensionId, deploymentModule, conditionalDependencies, dependencyConditions); + super(extensionId, deploymentModule, conditionalDependencies, conditionalDevDeps, dependencyConditions); } } diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java index 15682e835114d6..98d78d38a47496 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java @@ -42,8 +42,6 @@ import io.quarkus.gradle.tooling.ToolingUtils; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; -import io.quarkus.maven.dependency.GACT; -import io.quarkus.maven.dependency.GACTV; public class DependencyUtils { @@ -238,7 +236,7 @@ private static ProjectExtensionDependency createExtensionDependency( + deploymentProjectPath); } } else if (extensionDescriptor.containsKey(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT)) { - final ArtifactCoords deploymentArtifact = GACTV + final ArtifactCoords deploymentArtifact = ArtifactCoords .fromString(extensionDescriptor.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT)); deploymentProject = ToolingUtils.findLocalProject(project, deploymentArtifact); @@ -249,42 +247,37 @@ private static ProjectExtensionDependency createExtensionDependency( } } - final List conditionalDependencies = new ArrayList<>(); - final List dependencyConditions = new ArrayList<>(); + final List conditionalDependencies = new ArrayList<>(0); + final List conditionalDevDependencies = new ArrayList<>(0); + final List dependencyConditions = new ArrayList<>(0); if (extensionConfiguration != null) { - final ListProperty conditionalDependenciesProp = ConfigurationUtils - .getConditionalDependencies(extensionConfiguration); - - if (conditionalDependenciesProp.isPresent()) { - for (String rawDep : conditionalDependenciesProp.get()) { - conditionalDependencies.add(create(project.getDependencies(), rawDep)); - } - } + collectConditionalDeps(project, ConfigurationUtils.getConditionalDependencies(extensionConfiguration), + conditionalDependencies); + collectConditionalDeps(project, ConfigurationUtils.getConditionalDevDependencies(extensionConfiguration), + conditionalDevDependencies); final ListProperty dependencyConditionsProp = ConfigurationUtils .getDependencyConditions(extensionConfiguration); - if (dependencyConditionsProp.isPresent()) { for (String rawCond : dependencyConditionsProp.get()) { - dependencyConditions.add(GACT.fromString(rawCond)); + dependencyConditions.add(ArtifactKey.fromString(rawCond)); } } } - if (extensionDescriptor != null && extensionDescriptor.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) { - final String[] deps = BootstrapUtils - .splitByWhitespace(extensionDescriptor.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES)); - - for (String condDep : deps) { - conditionalDependencies.add(create(project.getDependencies(), condDep)); - } - } + collectionConditionalDeps(project, + extensionDescriptor == null ? null + : extensionDescriptor.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES), + conditionalDependencies); + collectionConditionalDeps(project, + extensionDescriptor == null ? null + : extensionDescriptor.getProperty(BootstrapConstants.CONDITIONAL_DEV_DEPENDENCIES), + conditionalDevDependencies); if (extensionDescriptor != null && extensionDescriptor.containsKey(BootstrapConstants.DEPENDENCY_CONDITION)) { final ArtifactKey[] conditions = BootstrapUtils .parseDependencyCondition(extensionDescriptor.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); - dependencyConditions.addAll(Arrays.asList(conditions)); } @@ -293,58 +286,72 @@ private static ProjectExtensionDependency createExtensionDependency( deploymentProject, isIncludedBuild, conditionalDependencies, + conditionalDevDependencies, dependencyConditions); } + private static void collectionConditionalDeps(Project project, String conditionalDevDepsStr, + List conditionalDevDependencies) { + if (conditionalDevDepsStr != null) { + for (String condDep : BootstrapUtils.splitByWhitespace(conditionalDevDepsStr)) { + conditionalDevDependencies.add(create(project.getDependencies(), condDep)); + } + } + } + + private static void collectConditionalDeps(Project project, ListProperty conditionalDependenciesProp, + List conditionalDependencies) { + if (conditionalDependenciesProp.isPresent()) { + for (String rawDep : conditionalDependenciesProp.get()) { + conditionalDependencies.add(create(project.getDependencies(), rawDep)); + } + } + } + private static ArtifactExtensionDependency createExtensionDependency( Project project, ModuleVersionIdentifier extensionArtifactId, Path descriptorPath) { final Properties extensionProperties = loadLocalExtensionDescriptor(descriptorPath); - final ArtifactCoords deploymentArtifact = GACTV + final ArtifactCoords deploymentArtifact = ArtifactCoords .fromString(extensionProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT)); - final List conditionalDependencies; - if (extensionProperties.containsKey(BootstrapConstants.CONDITIONAL_DEPENDENCIES)) { - final String[] deps = BootstrapUtils - .splitByWhitespace(extensionProperties.getProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES)); - - if (deps.length > 0) { - conditionalDependencies = new ArrayList<>(deps.length); - for (String condDep : deps) { - conditionalDependencies.add(create(project.getDependencies(), condDep)); - } - } else { - conditionalDependencies = Collections.emptyList(); - } - } else { - conditionalDependencies = Collections.emptyList(); - } - - final List dependencyConditions; + List dependencyConditions = List.of(); if (extensionProperties.containsKey(BootstrapConstants.DEPENDENCY_CONDITION)) { final ArtifactKey[] conditions = BootstrapUtils .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); - if (conditions.length > 0) { dependencyConditions = Arrays.asList(conditions); - } else { - dependencyConditions = Collections.emptyList(); } - } else { - dependencyConditions = Collections.emptyList(); } return new ArtifactExtensionDependency( extensionArtifactId, deploymentArtifact, - conditionalDependencies, + parseConditionalDeps(project, extensionProperties, BootstrapConstants.CONDITIONAL_DEPENDENCIES), + parseConditionalDeps(project, extensionProperties, BootstrapConstants.CONDITIONAL_DEV_DEPENDENCIES), dependencyConditions); } + private static List parseConditionalDeps(Project project, Properties extensionProperties, String propertyName) { + var str = extensionProperties.getProperty(propertyName); + if (str == null) { + return List.of(); + } + final String[] deps = BootstrapUtils.splitByWhitespace(extensionProperties.getProperty(propertyName)); + if (deps.length == 0) { + return List.of(); + } + var list = new ArrayList(deps.length); + for (String condDep : deps) { + list.add(create(project.getDependencies(), condDep)); + } + return list; + } + public static Dependency create(DependencyHandler dependencies, String conditionalDependency) { - final ArtifactCoords dependencyCoords = GACTV.fromString(conditionalDependency); + final ArtifactCoords dependencyCoords = ArtifactCoords.fromString(conditionalDependency); return dependencies.create(String.join(":", dependencyCoords.getGroupId(), dependencyCoords.getArtifactId(), dependencyCoords.getVersion())); } diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ExtensionDependency.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ExtensionDependency.java index c10143b695ce7f..e8ce16efb50cb0 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ExtensionDependency.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ExtensionDependency.java @@ -15,15 +15,17 @@ public abstract class ExtensionDependency { private final ModuleVersionIdentifier extensionId; private final T deploymentModule; private final List conditionalDependencies; + private final List conditionalDevDeps; private final List dependencyConditions; private boolean isConditional; public ExtensionDependency(ModuleVersionIdentifier extensionId, T deploymentModule, - List conditionalDependencies, + List conditionalDependencies, List conditionalDevDeps, List dependencyConditions) { this.extensionId = extensionId; this.deploymentModule = deploymentModule; this.conditionalDependencies = conditionalDependencies; + this.conditionalDevDeps = conditionalDevDeps; this.dependencyConditions = dependencyConditions; } @@ -41,7 +43,15 @@ public void importConditionalDependency(DependencyHandler dependencies, ModuleVe } private Dependency findConditionalDependency(ModuleVersionIdentifier capability) { - for (Dependency conditionalDependency : conditionalDependencies) { + final Dependency dep = findConditionalDependency(capability, conditionalDependencies); + if (dep != null) { + return dep; + } + return findConditionalDependency(capability, conditionalDevDeps); + } + + private static Dependency findConditionalDependency(ModuleVersionIdentifier capability, List deps) { + for (Dependency conditionalDependency : deps) { if (conditionalDependency.getGroup().equals(capability.getGroup()) && conditionalDependency.getName().equals(capability.getName())) { return conditionalDependency; @@ -78,6 +88,10 @@ public List getConditionalDependencies() { return conditionalDependencies; } + public List getConditionalDevDependencies() { + return conditionalDevDeps; + } + public T getDeploymentModule() { return deploymentModule; } diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ProjectExtensionDependency.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ProjectExtensionDependency.java index 77ef259b116a0c..f362763462d716 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ProjectExtensionDependency.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/ProjectExtensionDependency.java @@ -16,6 +16,7 @@ public ProjectExtensionDependency( Project deploymentModule, Boolean isIncludedBuild, List conditionalDependencies, + List conditionalDevDeps, List dependencyConditions) { super(DefaultModuleVersionIdentifier.newId( extensionProject.getGroup().toString(), @@ -23,6 +24,7 @@ public ProjectExtensionDependency( extensionProject.getVersion().toString()), deploymentModule, conditionalDependencies, + conditionalDevDeps, dependencyConditions); this.isIncludedBuild = isIncludedBuild; diff --git a/docs/src/main/asciidoc/conditional-extension-dependencies.adoc b/docs/src/main/asciidoc/conditional-extension-dependencies.adoc index 576f4424f484e5..d806d20c702546 100644 --- a/docs/src/main/asciidoc/conditional-extension-dependencies.adoc +++ b/docs/src/main/asciidoc/conditional-extension-dependencies.adoc @@ -9,21 +9,21 @@ include::_attributes.adoc[] :summary: Trigger the inclusion on additional extensions based on certain conditions. :topics: extensions -Quarkus extension dependencies are usually configured in the same way as any other project dependencies in the project's build file, e.g. the Maven `pom.xml` or the Gradle build scripts. However, there are dependency types that aren't yet supported out-of-the-box by Maven and Gradle. What we refer here to as "conditional dependencies" is one example. +Quarkus extension dependencies are usually configured in the same way as any other project dependencies in a project's build file, e.g. the Maven `pom.xml` or the Gradle build scripts. However, Quarkus also supports types of dependencies that aren't supported out-of-the-box by Maven and Gradle. Conditional Quarkus extension dependencies falls into that category. == Conditional Dependencies -The idea behind the notion of the conditional dependency is that such a dependency must be activated only if a certain condition is satisfied. If the condition is not satisfied then the dependency **must not** be activated. In that regard, conditional dependencies can be categorized as optional, i.e. they may or may not appear in the resulting set of project dependencies. +A conditional dependency is a dependency that is activated only if a certain condition is satisfied. If the condition is not satisfied then the dependency **must not** be activated. In that regard, conditional dependencies can be categorized as optional, meaning they may or may not appear in the resulting dependency graph. -In which cases could conditional dependencies be useful? A typical example would be a component that should be activated **only** in case all of its required dependencies are available. If one or more of the component's required dependencies aren't available, instead of failing, the component should simply not be activated. +A typical example of a conditional dependency would be a component that should be added to the classpath **only** in case all of its required dependencies are present on the classpath. If one or more of the component's required dependencies aren't available, instead of failing, the component should simply not be added. -== Quarkus Conditional Extension Dependencies +== Conditional Quarkus Extension Dependencies -Quarkus supports conditional extension dependencies. I.e. one Quarkus extension may declare one or more conditional dependencies on other Quarkus extensions. Conditional dependencies on and from non-extension artifacts aren't supported. +A Quarkus extension may declare one or more conditional dependencies on other Quarkus extensions. Conditional dependencies on and from non-extension artifacts aren't supported. -Let's take the following scenario as an example: `quarkus-extension-a` has an optional dependency on `quarkus-extension-b` which should be included in a Quarkus application only if `quarkus-extension-c` is found among its dependencies (direct or transitive). In other words, the presence of `quarkus-extension-c` is the condition which, if satisfied, enables `quarkus-extension-b` during the build of a Quarkus application. +Let's consider the following scenario as an example: `quarkus-extension-a` has an optional dependency on `quarkus-extension-b` which should be included in a Quarkus application only if `quarkus-extension-c` is found among the application dependencies (direct or transitive). In this case, the presence of `quarkus-extension-c` is the condition which, if satisfied, will trigger inclusion of the `quarkus-extension-b` when Quarkus application dependencies are resolved. -The condition which triggers activation of an extension is configured in the extension's descriptor, which is included into the runtime artifact of the extension as `META-INF/quarkus-extension.properties`. Given that extension descriptor is generated by the Quarkus plugin at extension build time, extension developers can add the following configuration to express the condition which would have to be satisfied for the extension to be activated: +The condition which triggers activation of an extension is configured in the extension's `META-INF/quarkus-extension.properties`, which is included in the runtime artifact of the extension.Extension developers can add the following configuration to express the condition which would have to be satisfied for the extension to be activated: [source,xml] ---- @@ -61,20 +61,22 @@ The condition which triggers activation of an extension is configured in the ext ---- -<1> runtime Quarkus extension artifact ID, in our example `quarkus-extension-b`; +<1> runtime Quarkus extension artifact ID; <2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with; -<3> configuration of the condition which will have to be satisfied for this extension to be included into a Quarkus application expressed as a list of artifacts that must be present among the application dependencies; +<3> configuration of the dependency condition which will have to be satisfied for this extension to be added to a Quarkus application expressed as a list of artifacts that must be present among the application dependencies; <4> an artifact key (in the format of `groupId:artifactId[::]` but typically simply `:`) of the artifact that must be present among the application dependencies for the condition to be satisfied. -NOTE: In the example above the `artifact` used in the condition configuration happens to be a runtime Quarkus extension artifact but it could as well be any other artifact. There could also be more than one `artifact` element in the body of `dependencyCondition`. +NOTE: In the example above the `artifact` used in the condition configuration happens to be a runtime Quarkus extension artifact but it could as well be any other artifact. There could also be more than one `artifact` element in the body of the `dependencyCondition`. -Now, having a dependency activating condition in the descriptor of `quarkus-extension-b`, other extensions may declare a conditional dependency on it. +Now, having a dependency condition recorded in the descriptor of the `quarkus-extension-b`, other extensions may declare a conditional dependency on it. -A conditional dependency is configured in the runtime artifact of a Quarkus extension. In our example, it's the `quarkus-extension-a` that has a conditional dependency on `quarkus-extension-b`, which can be expressed in two ways. +NOTE: extensions with dependency conditions present in their metadata could still appear as regular dependencies in Maven `pom.xml` and Gradle build scripts. + +A conditional dependency is configured in the runtime artifact of a Quarkus extension. In this example, the `quarkus-extension-a` will declare a conditional dependency on the `quarkus-extension-b`, which can be done in the following two ways. === Declaring a dependency as `optional` -If an extension was configured with a dependency condition in its descriptor, other extensions may configure a conditional dependency on it by simply adding `true` to the dependency configuration. In our example it would look like this: +If an extension includes a dependency condition in its descriptor, other extensions may configure a conditional dependency on it by simply adding `true` to the dependency configuration. In our example it would look like this: [source,xml] ---- @@ -101,7 +103,9 @@ If an extension was configured with a dependency condition in its descriptor, ot <1> the runtime extension artifact `quarkus-extension-a` <2> declares an optional Maven dependency on the runtime extension artifact `quarkus-extension-b` -IMPORTANT: In general, for every runtime extension artifact dependency on another runtime extension artifact there must be a corresponding deployment extension artifact dependency on the other deployment extension artifact. And if the runtime dependency is declared as optional then the corresponding deployment dependency **must** also be configured as optional. +Given that `quarkus-extension-b` includes a dependency condition, Quarkus will interpret an optional dependency on the `quarkus-extension-b` as conditional. + +IMPORTANT: In general, for every runtime extension artifact dependency on another runtime extension artifact there must be the corresponding deployment extension artifact dependency on the other deployment extension artifact. And if the runtime dependency is declared as optional then the corresponding deployment dependency **must** also be configured as optional. [source,xml] ---- @@ -128,13 +132,11 @@ IMPORTANT: In general, for every runtime extension artifact dependency on anothe <1> the deployment extension artifact `quarkus-extension-a-deployment` <2> declares an optional Maven dependency on the deployment extension artifact `quarkus-extension-b-deployment` -Normally, optional Maven extension dependencies are ignored by the Quarkus dependency resolver at build time. In this case though, the optional dependency `quarkus-extension-b` includes a dependency condition in its extension descriptor, which turns this optional Maven dependency into a Quarkus conditional extension dependency. - -IMPORTANT: If `quarkus-extension-b` wasn't declared as `true` that would make `quarkus-extension-b` a required dependency of `quarkus-extension-a` and its dependency condition would be ignored. +IMPORTANT: If the `quarkus-extension-b` dependency wasn't declared as `true` it would make the `quarkus-extension-b` a required dependency of the `quarkus-extension-a` and its dependency condition would be ignored by the application dependency resolver. === Declaring a conditional dependency in the Quarkus extension descriptor -Conditional dependencies can also be configured in the Quarkus extension descriptor. The conditional dependency configured above could be expressed in the extension descriptor of `quarkus-extension-a` as: +Conditional dependencies can also be configured in the Quarkus extension descriptor directly. Here is an example of how it can be done in the Quarkus extension plugin configuration of the `quarkus-extension-a`: [source,xml] ---- @@ -171,10 +173,61 @@ Conditional dependencies can also be configured in the Quarkus extension descrip ---- +<1> the runtime Quarkus extension artifact ID; +<2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with; +<3> the conditional dependency configuration element; +<4> the artifact coordinates of conditional dependencies on other extensions. + +In this case, the Maven dependency is not at all required in the `pom.xml` file. + +== Dev mode-only extension dependencies + +Extensions can also declare conditional dependencies on other extensions using Dev mode as the condition or one of the conditions for those dependencies to be activated. + +Dev mode-only extension dependencies can be configured in the Quarkus extension plugin in the following way: + +[source,xml] +---- + + + + + quarkus-extension-a <1> + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + process-resources + + extension-descriptor <2> + + + <3> + org.acme:quarkus-extension-b:${b.version} <4> + + + + + + + +---- +<1> the runtime Quarkus extension artifact ID; +<2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with; +<3> the Dev mode conditional dependency configuration element; +<4> the artifact coordinates of conditional dependencies on extensions that should be evaluated only if an application is launched in Dev mode. + +The `quarkus-extension-b`, in this example, may or may not define its own condition to be evaluated. -<1> runtime Quarkus extension artifact ID, in our example `quarkus-extension-a` -<2> the goal that generates the extension descriptor which every Quarkus runtime extension project should be configured with -<3> conditional dependency configuration element -<4> artifact coordinates of conditional dependencies on other extensions. +If the `quarkus-extension-b` does not define a dependency condition on its own (there is no dependency condition recorded in its `META-INF/quarkus-extension.properties`), the `quarkus-extension-b` will only be added as a dependency of the `quarkus-extension-a` in Dev mode but not in other modes (prod or test). -In this case, the Maven dependency is not at all required in the `pom.xml`. +If the `quarkus-extension-b` does define a dependency condition on its own (a dependency condition recorded in its `META-INF/quarkus-extension.properties`), the `quarkus-extension-b` will be added as a dependency of the `quarkus-extension-a` in Dev mode only if its condition is satisfied (the artifacts it requires are present in the application dependency graph). \ No newline at end of file diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java index c5c84b316922bc..7155decb347665 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/BootstrapConstants.java @@ -10,6 +10,7 @@ public interface BootstrapConstants { String SERIALIZED_TEST_APP_MODEL = "quarkus-internal-test.serialized-app-model.path"; String DESCRIPTOR_FILE_NAME = "quarkus-extension.properties"; String CONDITIONAL_DEPENDENCIES = "conditional-dependencies"; + String CONDITIONAL_DEV_DEPENDENCIES = "conditional-dev-dependencies"; String DEPENDENCY_CONDITION = "dependency-condition"; /** diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java index d8861642c21714..e2e54d1c7c1a5f 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.IncubatingApplicationModelResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; @@ -143,15 +144,24 @@ protected boolean cleanWorkDir() { return true; } - protected boolean isBootstrapForTestMode() { - return false; + protected QuarkusBootstrap.Mode getBootstrapMode() { + return QuarkusBootstrap.Mode.PROD; } protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception { final BootstrapAppModelResolver appModelResolver = new BootstrapAppModelResolver(newArtifactResolver(currentProject)); appModelResolver.setIncubatingModelResolver(IncubatingApplicationModelResolver.isIncubatingEnabled(null)); - if (isBootstrapForTestMode()) { - appModelResolver.setTest(true); + switch (getBootstrapMode()) { + case PROD: + break; + case TEST: + appModelResolver.setTest(true); + break; + case DEV: + appModelResolver.setDevMode(true); + break; + default: + throw new IllegalArgumentException("Not supported bootstrap mode " + getBootstrapMode()); } return appModelResolver; } diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java index f6374e54a47de6..e5bc50e9a74895 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/TsQuarkusExt.java @@ -44,6 +44,16 @@ public TsQuarkusExt setConditionalDeps(TsQuarkusExt... exts) { return setDescriptorProp(BootstrapConstants.CONDITIONAL_DEPENDENCIES, buf.toString()); } + public TsQuarkusExt setConditionalDevDeps(TsQuarkusExt... exts) { + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(exts[i++].getRuntime().toString()); + while (i < exts.length) { + buf.append(' ').append(exts[i++].getRuntime().toString()); + } + return setDescriptorProp(BootstrapConstants.CONDITIONAL_DEV_DEPENDENCIES, buf.toString()); + } + public TsQuarkusExt setDependencyCondition(TsQuarkusExt... exts) { final StringBuilder buf = new StringBuilder(); int i = 0; diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesDevModelTestCase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesDevModelTestCase.java new file mode 100644 index 00000000000000..20c22683e39fa9 --- /dev/null +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesDevModelTestCase.java @@ -0,0 +1,88 @@ +package io.quarkus.bootstrap.resolver.test; + +import io.quarkus.bootstrap.app.QuarkusBootstrap; +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; +import io.quarkus.bootstrap.resolver.CollectDependenciesBase; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; +import io.quarkus.maven.dependency.DependencyFlags; + +public class ConditionalDependenciesDevModelTestCase extends CollectDependenciesBase { + + @Override + protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception { + var resolver = super.newAppModelResolver(currentProject); + resolver.setIncubatingModelResolver(false); + return resolver; + } + + @Override + protected QuarkusBootstrap.Mode getBootstrapMode() { + return QuarkusBootstrap.Mode.DEV; + } + + @Override + protected void setupDependencies() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + install(extA, false); + addCollectedDeploymentDep(extA.getDeployment()); + + installAsDep(extA.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + install(extB, false); + addCollectedDep(extB.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extB.getDeployment()); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setDependencyCondition(extB); + install(extC, false); + addCollectedDep(extC.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extC.getDeployment()); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + install(extD, false); + installAsDep(extD.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extD.getDeployment()); + + final TsArtifact libE = TsArtifact.jar("lib-e"); + install(libE, true); + final TsArtifact libEBuildTIme = TsArtifact.jar("lib-e-build-time"); + install(libEBuildTIme); + addCollectedDeploymentDep(libEBuildTIme); + + final TsQuarkusExt extE = new TsQuarkusExt("ext-e"); + extE.setDependencyCondition(extD); + extE.getRuntime().addDependency(libE); + extE.getDeployment().addDependency(libEBuildTIme); + install(extE, false); + addCollectedDep(extE.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extE.getDeployment()); + + final TsQuarkusExt extF = new TsQuarkusExt("ext-f"); + extF.setConditionalDeps(extC, extE); + install(extF, false); + installAsDep(extF.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extF.getDeployment()); + + final TsQuarkusExt extG = new TsQuarkusExt("ext-g"); + extG.setConditionalDevDeps(extB); + install(extG, false); + installAsDep(extG.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extG.getDeployment()); + } +} diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesProdModelTestCase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesProdModelTestCase.java new file mode 100644 index 00000000000000..7a470e4f666cc8 --- /dev/null +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesProdModelTestCase.java @@ -0,0 +1,78 @@ +package io.quarkus.bootstrap.resolver.test; + +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; +import io.quarkus.bootstrap.resolver.CollectDependenciesBase; +import io.quarkus.bootstrap.resolver.TsArtifact; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; +import io.quarkus.maven.dependency.DependencyFlags; + +public class ConditionalDependenciesProdModelTestCase extends CollectDependenciesBase { + + @Override + protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception { + var resolver = super.newAppModelResolver(currentProject); + resolver.setIncubatingModelResolver(false); + return resolver; + } + + @Override + protected void setupDependencies() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + install(extA, false); + addCollectedDeploymentDep(extA.getDeployment()); + + installAsDep(extA.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + install(extB, false); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setDependencyCondition(extB); + install(extC, false); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + install(extD, false); + installAsDep(extD.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extD.getDeployment()); + + final TsArtifact libE = TsArtifact.jar("lib-e"); + install(libE, true); + final TsArtifact libEBuildTIme = TsArtifact.jar("lib-e-build-time"); + install(libEBuildTIme); + addCollectedDeploymentDep(libEBuildTIme); + + final TsQuarkusExt extE = new TsQuarkusExt("ext-e"); + extE.setDependencyCondition(extD); + extE.getRuntime().addDependency(libE); + extE.getDeployment().addDependency(libEBuildTIme); + install(extE, false); + addCollectedDep(extE.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extE.getDeployment()); + + final TsQuarkusExt extF = new TsQuarkusExt("ext-f"); + extF.setConditionalDeps(extC, extE); + install(extF, false); + installAsDep(extF.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extF.getDeployment()); + + final TsQuarkusExt extG = new TsQuarkusExt("ext-g"); + extG.setConditionalDevDeps(extB); + install(extG, false); + installAsDep(extG.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extG.getDeployment()); + } +} diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/RuntimeOnlyApplicationModelTestCase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesRuntimeOnlyProdModelTestCase.java similarity index 96% rename from independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/RuntimeOnlyApplicationModelTestCase.java rename to independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesRuntimeOnlyProdModelTestCase.java index f0ddf15c9ede3a..cf246b9a85f2f4 100644 --- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/RuntimeOnlyApplicationModelTestCase.java +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/ConditionalDependenciesRuntimeOnlyProdModelTestCase.java @@ -7,7 +7,7 @@ import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; import io.quarkus.maven.dependency.DependencyFlags; -public class RuntimeOnlyApplicationModelTestCase extends CollectDependenciesBase { +public class ConditionalDependenciesRuntimeOnlyProdModelTestCase extends CollectDependenciesBase { private static final boolean runtimeOnly = true; diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/DevModeConditionalDependencyWithExtraConditionTestCase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/DevModeConditionalDependencyWithExtraConditionTestCase.java new file mode 100644 index 00000000000000..77ecac97bf92f3 --- /dev/null +++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/test/DevModeConditionalDependencyWithExtraConditionTestCase.java @@ -0,0 +1,63 @@ +package io.quarkus.bootstrap.resolver.test; + +import io.quarkus.bootstrap.app.QuarkusBootstrap; +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; +import io.quarkus.bootstrap.resolver.CollectDependenciesBase; +import io.quarkus.bootstrap.resolver.TsQuarkusExt; +import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; +import io.quarkus.maven.dependency.DependencyFlags; + +public class DevModeConditionalDependencyWithExtraConditionTestCase extends CollectDependenciesBase { + + @Override + protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception { + var resolver = super.newAppModelResolver(currentProject); + resolver.setIncubatingModelResolver(false); + return resolver; + } + + @Override + protected QuarkusBootstrap.Mode getBootstrapMode() { + return QuarkusBootstrap.Mode.DEV; + } + + @Override + protected void setupDependencies() { + + final TsQuarkusExt extA = new TsQuarkusExt("ext-a"); + install(extA, false); + addCollectedDeploymentDep(extA.getDeployment()); + + installAsDep(extA.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + + final TsQuarkusExt extB = new TsQuarkusExt("ext-b"); + install(extB, false); + addCollectedDep(extB.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extB.getDeployment()); + + final TsQuarkusExt extC = new TsQuarkusExt("ext-c"); + extC.setDependencyCondition(extA); + install(extC, false); + addCollectedDep(extC.getRuntime(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extC.getDeployment()); + + final TsQuarkusExt extD = new TsQuarkusExt("ext-d"); + install(extD, false); + + final TsQuarkusExt extE = new TsQuarkusExt("ext-e"); + extE.setDependencyCondition(extD); + install(extE, false); + + final TsQuarkusExt extG = new TsQuarkusExt("ext-g"); + extG.setConditionalDevDeps(extB, extC, extE); + install(extG, false); + installAsDep(extG.getRuntime(), + DependencyFlags.DIRECT + | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT + | DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT); + addCollectedDeploymentDep(extG.getDeployment()); + } +} 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 d0f1b3a3139f97..2bc13f5a18ccdf 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 @@ -378,6 +378,7 @@ private ApplicationModel buildAppModel(ResolvedDependencyBuilder appArtifact, .setCollectCompileOnly(filteredProvidedDeps) .setDependencyLogging(depLogConfig) .setRuntimeModelOnly(runtimeModelOnly) + .setDevMode(devmode) .resolve(collectRtDepsRequest); } else { ApplicationDependencyTreeResolver.newInstance() @@ -387,6 +388,7 @@ private ApplicationModel buildAppModel(ResolvedDependencyBuilder appArtifact, .setCollectCompileOnly(filteredProvidedDeps) .setBuildTreeConsumer(depLogConfig == null ? null : depLogConfig.getMessageConsumer()) .setRuntimeModelOnly(runtimeModelOnly) + .setDevMode(devmode) .resolve(collectRtDepsRequest); } if (logTime) { 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 e65df264dbdac9..6505fab69b4cd2 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 @@ -2,7 +2,6 @@ import static io.quarkus.bootstrap.util.DependencyUtils.getKey; import static io.quarkus.bootstrap.util.DependencyUtils.newDependencyBuilder; -import static io.quarkus.bootstrap.util.DependencyUtils.toArtifact; import java.io.BufferedReader; import java.io.IOException; @@ -52,10 +51,8 @@ import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.BootstrapDependencyProcessingException; import io.quarkus.bootstrap.model.ApplicationModelBuilder; -import io.quarkus.bootstrap.model.CapabilityContract; import io.quarkus.bootstrap.model.PlatformImportsImpl; import io.quarkus.bootstrap.resolver.AppModelResolverException; -import io.quarkus.bootstrap.util.BootstrapUtils; import io.quarkus.bootstrap.util.DependencyUtils; import io.quarkus.bootstrap.util.PropertyUtils; import io.quarkus.bootstrap.workspace.WorkspaceModule; @@ -107,6 +104,7 @@ public static Artifact getRuntimeArtifact(DependencyNode dep) { private Consumer buildTreeConsumer; private List collectCompileOnly; private boolean runtimeModelOnly; + private boolean devMode; public ApplicationDependencyTreeResolver setArtifactResolver(MavenArtifactResolver resolver) { this.resolver = resolver; @@ -140,11 +138,28 @@ public ApplicationDependencyTreeResolver setCollectCompileOnly(List return this; } + /** + * Whether to limit the resulting {@link io.quarkus.bootstrap.model.ApplicationModel} to the runtime dependencies. + * + * @param runtimeModelOnly whether to limit the resulting application model to the runtime dependencies + * @return self + */ public ApplicationDependencyTreeResolver setRuntimeModelOnly(boolean runtimeModelOnly) { this.runtimeModelOnly = runtimeModelOnly; return this; } + /** + * Whether an application model is resolved for dev mode + * + * @param devMode whether an application model is resolved for dev mode + * @return self + */ + public ApplicationDependencyTreeResolver setDevMode(boolean devMode) { + this.devMode = devMode; + return this; + } + public void resolve(CollectRequest collectRtDepsRequest) throws AppModelResolverException { this.managedDeps = collectRtDepsRequest.getManagedDependencies(); @@ -473,7 +488,7 @@ private void visitRuntimeDependency(DependencyNode node) { clearWalkingFlag(COLLECT_DIRECT_DEPS); if (extDep != null) { - extDep.info.ensureActivated(); + extDep.info.ensureActivated(appBuilder); visitExtensionDependency(extDep); } visitRuntimeDependencies(node.getChildren()); @@ -584,7 +599,7 @@ private ExtensionInfo getExtensionInfoOrNull(Artifact artifact, List boolean contains(T[] arr, T item) { + for (int i = 0; i < arr.length; ++i) { + if (item.equals(arr[i])) { + return true; + } + } + return false; + } + + private static String toCompactCoords(Artifact a) { + final StringBuilder b = new StringBuilder(); + b.append(a.getGroupId()).append(':').append(a.getArtifactId()).append(':'); + if (!a.getClassifier().isEmpty()) { + b.append(a.getClassifier()).append(':'); + } + if (!ArtifactCoords.TYPE_JAR.equals(a.getExtension())) { + b.append(a.getExtension()).append(':'); + } + b.append(a.getVersion()); + return b.toString(); + } +} diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java index 747c976b5eda30..95e1a938211199 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java @@ -5,7 +5,6 @@ import static io.quarkus.bootstrap.util.DependencyUtils.getWinner; import static io.quarkus.bootstrap.util.DependencyUtils.hasWinner; import static io.quarkus.bootstrap.util.DependencyUtils.newDependencyBuilder; -import static io.quarkus.bootstrap.util.DependencyUtils.toArtifact; import java.io.BufferedReader; import java.io.IOException; @@ -51,10 +50,8 @@ import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.BootstrapDependencyProcessingException; import io.quarkus.bootstrap.model.ApplicationModelBuilder; -import io.quarkus.bootstrap.model.CapabilityContract; import io.quarkus.bootstrap.model.PlatformImportsImpl; import io.quarkus.bootstrap.resolver.AppModelResolverException; -import io.quarkus.bootstrap.util.BootstrapUtils; import io.quarkus.bootstrap.util.DependencyUtils; import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.maven.dependency.ArtifactCoords; @@ -140,6 +137,7 @@ public static IncubatingApplicationModelResolver newInstance() { private DependencyLoggingConfig depLogging; private List collectCompileOnly; private boolean runtimeModelOnly; + private boolean devMode; public IncubatingApplicationModelResolver setArtifactResolver(MavenArtifactResolver resolver) { this.resolver = resolver; @@ -173,11 +171,28 @@ public IncubatingApplicationModelResolver setCollectCompileOnly(List return this; } + /** + * Whether to limit the resulting {@link io.quarkus.bootstrap.model.ApplicationModel} to the runtime dependencies. + * + * @param runtimeModelOnly whether to limit the resulting application model to the runtime dependencies + * @return self + */ public IncubatingApplicationModelResolver setRuntimeModelOnly(boolean runtimeModelOnly) { this.runtimeModelOnly = runtimeModelOnly; return this; } + /** + * Whether an application model is resolved for dev mode + * + * @param devMode whether an application model is resolved for dev mode + * @return self + */ + public IncubatingApplicationModelResolver setDevMode(boolean devMode) { + this.devMode = devMode; + return this; + } + public void resolve(CollectRequest collectRtDepsRequest) throws AppModelResolverException { this.managedDeps = collectRtDepsRequest.getManagedDependencies(); // managed dependencies will be a bit augmented with every added extension, so let's load the properties early @@ -610,7 +625,7 @@ void setFlags(byte walkingFlags) { } parentExtDep = parentExtDep.parent; } - ext.info.ensureActivated(); + ext.info.ensureActivated(appBuilder); } var existingDep = appBuilder.getDependency(resolvedDep.getKey()); @@ -758,7 +773,7 @@ private ExtensionInfo getExtensionInfoOrNull(Artifact artifact, List depth = new ArrayList<>(); diff --git a/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java b/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java index 3f31cf926c9259..27acf1122cb66e 100644 --- a/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java +++ b/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java @@ -220,6 +220,13 @@ public static class RemovedResources { @Parameter private List conditionalDependencies = new ArrayList<>(0); + /** + * Conditional dependencies that should be + * enabled in case an application is launched in dev mode and certain classpath conditions have been satisfied. + */ + @Parameter + private List conditionalDevDependencies = new ArrayList<>(0); + /** * Extension dependency condition that should be * satisfied for this extension to be enabled @@ -469,15 +476,8 @@ private void recordCapabilities(Properties props) { private void recordConditionalDeps(Properties props) { lookForConditionalDeps(); - if (!conditionalDependencies.isEmpty()) { - final StringBuilder buf = new StringBuilder(); - int i = 0; - buf.append(ArtifactCoords.fromString(conditionalDependencies.get(i++))); - while (i < conditionalDependencies.size()) { - buf.append(' ').append(ArtifactCoords.fromString(conditionalDependencies.get(i++))); - } - props.setProperty(BootstrapConstants.CONDITIONAL_DEPENDENCIES, buf.toString()); - } + setConditionalDepsProperty(props, BootstrapConstants.CONDITIONAL_DEPENDENCIES, conditionalDependencies); + setConditionalDepsProperty(props, BootstrapConstants.CONDITIONAL_DEV_DEPENDENCIES, conditionalDevDependencies); if (!dependencyCondition.isEmpty()) { final StringBuilder buf = new StringBuilder(); int i = 0; @@ -486,8 +486,20 @@ private void recordConditionalDeps(Properties props) { buf.append(' ').append(ArtifactKey.fromString(dependencyCondition.get(i++)).toGacString()); } props.setProperty(BootstrapConstants.DEPENDENCY_CONDITION, buf.toString()); + } + } + private void setConditionalDepsProperty(Properties props, String propertyName, List list) { + if (list.isEmpty()) { + return; + } + final StringBuilder buf = new StringBuilder(); + int i = 0; + buf.append(ArtifactCoords.fromString(list.get(i++))); + while (i < list.size()) { + buf.append(' ').append(ArtifactCoords.fromString(list.get(i++))); } + props.setProperty(propertyName, buf.toString()); } private void lookForConditionalDeps() { diff --git a/integration-tests/gradle/src/main/resources/conditional-dependencies/ext-a/runtime/build.gradle b/integration-tests/gradle/src/main/resources/conditional-dependencies/ext-a/runtime/build.gradle index 526ddde33e17b4..85410fe59eb39c 100644 --- a/integration-tests/gradle/src/main/resources/conditional-dependencies/ext-a/runtime/build.gradle +++ b/integration-tests/gradle/src/main/resources/conditional-dependencies/ext-a/runtime/build.gradle @@ -15,6 +15,7 @@ dependencies { quarkusExtension { deploymentArtifact = "org.acme:ext-a-deployment:1.0-SNAPSHOT" conditionalDependencies = ["org.acme:ext-b::jar:1.0-SNAPSHOT"] + conditionalDevDependencies = ["org.acme:ext-d::jar:1.0-SNAPSHOT", "org.acme:ext-n::jar:1.0-SNAPSHOT", "org.acme:ext-s::jar:1.0-SNAPSHOT"] } publishing { diff --git a/integration-tests/gradle/src/main/resources/conditional-test-project/runner/build.gradle b/integration-tests/gradle/src/main/resources/conditional-test-project/runner/build.gradle index 267a13ee7fb86a..e62a8d1c3476b3 100644 --- a/integration-tests/gradle/src/main/resources/conditional-test-project/runner/build.gradle +++ b/integration-tests/gradle/src/main/resources/conditional-test-project/runner/build.gradle @@ -17,10 +17,11 @@ repositories { dependencies { implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") - implementation 'io.quarkus:quarkus-resteasy' + implementation 'io.quarkus:quarkus-rest' implementation 'org.acme:ext-a:1.0-SNAPSHOT' implementation 'org.acme:ext-c:1.0-SNAPSHOT' + implementation 'org.acme:ext-t:1.0-SNAPSHOT' testImplementation 'io.quarkus:quarkus-junit5' testImplementation 'io.rest-assured:rest-assured' diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDependenciesTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDependenciesTest.java index d487b3b7ff5cdb..b260aca10bb01f 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDependenciesTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDependenciesTest.java @@ -6,12 +6,16 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import io.quarkus.bootstrap.utils.BuildToolHelper; + /** * This class uses test order because all tests depend on extension publication which can be done once. */ @@ -73,6 +77,7 @@ public void shouldImportConditionalDependency() throws IOException, URISyntaxExc // A -> B?(C) -> E?(C) // C + // T final File projectDir = getProjectDir("conditional-test-project"); @@ -85,6 +90,7 @@ public void shouldImportConditionalDependency() throws IOException, URISyntaxExc assertThat(mainLib.resolve("org.acme.ext-b-1.0-SNAPSHOT.jar")).exists(); assertThat(mainLib.resolve("org.acme.ext-c-1.0-SNAPSHOT.jar")).exists(); assertThat(mainLib.resolve("org.acme.ext-e-1.0-SNAPSHOT.jar")).exists(); + assertThat(mainLib.resolve("org.acme.ext-t-1.0-SNAPSHOT.jar")).exists(); assertThat(mainLib.resolve("org.acme.ext-d-1.0-SNAPSHOT.jar")).doesNotExist(); assertThat(mainLib.resolve("org.acme.transitive-dependency-1.0-SNAPSHOT.jar")).doesNotExist(); @@ -177,4 +183,35 @@ public void scenarioTwo() throws Exception { assertThat(deploymentLib.resolve("org.acme.ext-t-deployment-1.0-SNAPSHOT.jar")).exists(); assertThat(deploymentLib.resolve("org.acme.ext-u-deployment-1.0-SNAPSHOT.jar")).exists(); } + + @Test + @Order(6) + public void conditionalDevDependencies() throws Exception { + + // A -> B?(C) -> E?(C) + // A -> D?(dev) + // A -> N?(dev, G) + // A -> S? (dev, T) -> U + // C + // T + + var appModel = BuildToolHelper + .enableGradleAppModelForDevMode(getProjectDir("conditional-test-project/runner").toPath()); + final Set acmeArtifacts = new HashSet<>(); + for (var d : appModel.getDependencies()) { + if (d.getGroupId().equals("org.acme")) { + acmeArtifacts.add(d.getArtifactId()); + } + } + assertThat(acmeArtifacts).containsExactlyInAnyOrder( + "ext-a", "ext-a-deployment", + "ext-b", "ext-b-deployment", + "ext-e", "ext-e-deployment", + "ext-d", "ext-d-deployment", + "ext-s", "ext-s-deployment", + "ext-u", "ext-u-deployment", + "ext-c", "ext-c-deployment", + "ext-t", "ext-t-deployment", + "simple-dependency", "transitive-dependency"); + } } diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDevDependencyTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDevDependencyTest.java new file mode 100644 index 00000000000000..442aa33f8cb890 --- /dev/null +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/ConditionalDevDependencyTest.java @@ -0,0 +1,15 @@ +package io.quarkus.gradle; + +import org.junit.jupiter.api.Test; + +import io.quarkus.bootstrap.utils.BuildToolHelper; + +public class ConditionalDevDependencyTest extends QuarkusGradleTestBase { + + @Test + public void test() throws Exception { + + var appModel = BuildToolHelper.enableGradleAppModelForDevMode(getProjectDir("basic-java-application-project").toPath()); + appModel.getDependencies().forEach(e -> System.out.println(e.toCompactCoords())); + } +} diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleTestBase.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleTestBase.java index 5ca8dcbba35a94..fac7e9776e1f72 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleTestBase.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleTestBase.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -20,7 +19,7 @@ public class QuarkusGradleTestBase { protected static File getProjectDir(final String projectName) - throws URISyntaxException, IOException, FileNotFoundException { + throws URISyntaxException, IOException { final URL projectUrl = Thread.currentThread().getContextClassLoader().getResource(projectName); if (projectUrl == null) { throw new IllegalStateException("Failed to locate test project " + projectName); @@ -30,13 +29,16 @@ protected static File getProjectDir(final String projectName) throw new IllegalStateException(projectDir + " is not a directory"); } - final File projectProps = new File(projectDir, "gradle.properties"); - if (!projectProps.exists()) { - throw new IllegalStateException("Failed to locate " + projectProps); - } final Properties props = new Properties(); - try (InputStream is = new FileInputStream(projectProps)) { - props.load(is); + final File projectProps = new File(projectDir, "gradle.properties"); + if (projectProps.exists()) { + try (InputStream is = new FileInputStream(projectProps)) { + props.load(is); + } + } else { + props.setProperty("quarkusPlatformGroupId", "io.quarkus"); + props.setProperty("quarkusPlatformArtifactId", "quarkus-bom"); + props.setProperty("org.gradle.logging.level", "INFO"); } final String quarkusVersion = getQuarkusVersion(); props.setProperty("quarkusPlatformVersion", quarkusVersion);