diff --git a/devtools/gradle/gradle-application-plugin/build.gradle b/devtools/gradle/gradle-application-plugin/build.gradle index c7bba6e4c532f..2f24773c33488 100644 --- a/devtools/gradle/gradle-application-plugin/build.gradle +++ b/devtools/gradle/gradle-application-plugin/build.gradle @@ -33,7 +33,3 @@ pluginBundle { vcsUrl = 'https://github.com/quarkusio/quarkus' tags = ['quarkus', 'quarkusio', 'graalvm'] } - -test { - systemProperty 'kotlin_version', project.kotlin_version -} \ No newline at end of file diff --git a/devtools/gradle/gradle-application-plugin/pom.xml b/devtools/gradle/gradle-application-plugin/pom.xml index fa6bf4c85e9a5..cefcb1fc66b3c 100644 --- a/devtools/gradle/gradle-application-plugin/pom.xml +++ b/devtools/gradle/gradle-application-plugin/pom.xml @@ -54,18 +54,6 @@ quarkus-devmode-test-utils test - - org.jetbrains.kotlin - kotlin-gradle-plugin - ${kotlin.version} - test - - - org.checkerframework - checker-qual - - - diff --git a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java index 4a18868121e34..0f29e3f3c2e51 100644 --- a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java +++ b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java @@ -1,15 +1,10 @@ package io.quarkus.gradle; import static org.assertj.core.api.Assertions.assertThat; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -21,18 +16,12 @@ import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskContainer; import org.gradle.testfixtures.ProjectBuilder; -import org.gradle.testkit.runner.BuildResult; -import org.gradle.testkit.runner.GradleRunner; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import io.quarkus.gradle.extension.QuarkusPluginExtension; public class QuarkusPluginTest { - @TempDir - Path testProjectDir; - @Test public void shouldCreateTasks() { Project project = ProjectBuilder.builder().build(); @@ -78,7 +67,7 @@ public void shouldMakeQuarkusDevAndQuarkusBuildDependOnClassesTask() { } @Test - public void shouldReturnMultipleOutputSourceDirectories() { + public void shouldReturnMutlipleOutputSourceDirectories() { Project project = ProjectBuilder.builder().build(); project.getPluginManager().apply(QuarkusPlugin.ID); project.getPluginManager().apply("java"); @@ -95,68 +84,7 @@ public void shouldReturnMultipleOutputSourceDirectories() { } - @Test - public void shouldNotFailOnProjectDependenciesWithoutMain() throws IOException { - var kotlinVersion = System.getProperty("kotlin_version", "1.7.20"); - var settingFile = testProjectDir.resolve("settings.gradle.kts"); - var mppProjectDir = testProjectDir.resolve("mpp"); - var quarkusProjectDir = testProjectDir.resolve("quarkus"); - var mppBuild = mppProjectDir.resolve("build.gradle.kts"); - var quarkusBuild = quarkusProjectDir.resolve("build.gradle.kts"); - Files.createDirectory(mppProjectDir); - Files.createDirectory(quarkusProjectDir); - Files.writeString(settingFile, - "rootProject.name = \"quarkus-mpp-sample\"\n" + - "\n" + - "include(\n" + - " \"mpp\",\n" + - " \"quarkus\"\n" + - ")"); - - Files.writeString(mppBuild, - "buildscript {\n" + - " repositories {\n" + - " mavenLocal()\n" + - " mavenCentral()\n" + - " }\n" + - " dependencies {\n" + - " classpath(\"org.jetbrains.kotlin:kotlin-gradle-plugin:" + kotlinVersion + "\")\n" + - " }\n" + - "}\n" + - "\n" + - "apply(plugin = \"org.jetbrains.kotlin.multiplatform\")\n" + - "\n" + - "repositories {\n" + - " mavenCentral()\n" + - "}\n" + - "\n" + - "configure{\n" + - " jvm()\n" + - "}"); - - Files.writeString(quarkusBuild, - "plugins {\n" + - " id(\"io.quarkus\")\n" + - "}\n" + - "\n" + - "repositories {\n" + - " mavenCentral()\n" + - "}\n" + - "\n" + - "dependencies {\n" + - " implementation(project(\":mpp\"))\n" + - "}"); - - BuildResult result = GradleRunner.create() - .withPluginClasspath() - .withProjectDir(testProjectDir.toFile()) - .withArguments("quarkusGenerateCode") - .build(); - - assertEquals(SUCCESS, result.task(":quarkus:quarkusGenerateCode").getOutcome()); - } - - private static List getDependantProvidedTaskName(Task task) { + private static final List getDependantProvidedTaskName(Task task) { List dependantTaskNames = new ArrayList<>(); for (Object t : task.getDependsOn()) { try { diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java index 1c01060da4c65..5508c2e8e1a7f 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java @@ -43,7 +43,9 @@ public void exportDeploymentClasspath(String configurationName) { dependencies); } else { DependencyUtils.requireDeploymentDependency(deploymentConfigurationName, extension, dependencies); - alreadyProcessed.add(extension.getExtensionId()); + if (!alreadyProcessed.add(extension.getExtensionId())) { + continue; + } } } }); @@ -71,9 +73,9 @@ private Set collectQuarkusExtensions(ResolvedDependency dep } Set extensions = new LinkedHashSet<>(); for (ResolvedArtifact moduleArtifact : dependency.getModuleArtifacts()) { - var optionalExtension = DependencyUtils.getOptionalExtensionInfo(project, moduleArtifact); - if (optionalExtension.isPresent()) { - extensions.add(optionalExtension.get()); + ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, moduleArtifact); + if (extension != null) { + extensions.add(extension); return extensions; } } diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java index f2a1bb28cac0a..be96802daf78d 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java @@ -2,9 +2,7 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import javax.inject.Inject; @@ -20,6 +18,7 @@ import io.quarkus.bootstrap.model.AppArtifactKey; import io.quarkus.extension.gradle.QuarkusExtensionConfiguration; import io.quarkus.gradle.tooling.dependency.DependencyUtils; +import io.quarkus.gradle.tooling.dependency.ExtensionDependency; public class ValidateExtensionTask extends DefaultTask { @@ -63,7 +62,13 @@ public void validateExtension() { deploymentModuleKeys); deploymentModuleKeys.removeAll(existingDeploymentModuleKeys); - boolean hasErrors = !invalidRuntimeArtifacts.isEmpty() || !deploymentModuleKeys.isEmpty(); + boolean hasErrors = false; + if (!invalidRuntimeArtifacts.isEmpty()) { + hasErrors = true; + } + if (!deploymentModuleKeys.isEmpty()) { + hasErrors = true; + } if (hasErrors) { printValidationErrors(invalidRuntimeArtifacts, deploymentModuleKeys); @@ -71,13 +76,15 @@ public void validateExtension() { } private List collectRuntimeExtensionsDeploymentKeys(Set runtimeArtifacts) { - return runtimeArtifacts.stream() - .map(resolvedArtifact -> DependencyUtils.getOptionalExtensionInfo(getProject(), resolvedArtifact)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(extension -> new AppArtifactKey(extension.getDeploymentModule().getGroupId(), - extension.getDeploymentModule().getArtifactId())) - .collect(Collectors.toList()); + List runtimeExtensions = new ArrayList<>(); + for (ResolvedArtifact resolvedArtifact : runtimeArtifacts) { + ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(getProject(), resolvedArtifact); + if (extension != null) { + runtimeExtensions.add(new AppArtifactKey(extension.getDeploymentModule().getGroupId(), + extension.getDeploymentModule().getArtifactId())); + } + } + return runtimeExtensions; } private List findExtensionInConfiguration(Set deploymentArtifacts, diff --git a/devtools/gradle/gradle-model/build.gradle b/devtools/gradle/gradle-model/build.gradle index 29e30073f46cf..15e648b26cae7 100644 --- a/devtools/gradle/gradle-model/build.gradle +++ b/devtools/gradle/gradle-model/build.gradle @@ -3,7 +3,6 @@ dependencies { implementation "io.quarkus:quarkus-bootstrap-gradle-resolver:${version}" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:${kotlin_version}" testImplementation "io.quarkus:quarkus-devtools-testing:${version}" - testImplementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" } task sourcesJar(type: Jar, dependsOn: classes) { 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 7cd6180ccbfd6..6ab5650d7b5a9 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 @@ -6,10 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -40,7 +37,8 @@ public class ConditionalDependenciesEnabler { private final Set existingArtifacts = new HashSet<>(); private final List unsatisfiedConditionalDeps = new ArrayList<>(); - public ConditionalDependenciesEnabler(Project project, LaunchMode mode, Configuration platforms) { + public ConditionalDependenciesEnabler(Project project, LaunchMode mode, + Configuration platforms) { this.project = project; this.enforcedPlatforms = platforms; @@ -61,7 +59,7 @@ public ConditionalDependenciesEnabler(Project project, LaunchMode mode, Configur final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i); // Try to resolve it with the latest evolved graph available if (resolveConditionalDependency(conditionalDep)) { - // Mark the resolution as a success, so we know the graph has evolved + // Mark the resolution as a success so we know the graph evolved satisfiedConditionalDeps = true; unsatisfiedConditionalDeps.remove(i); } else { @@ -90,25 +88,23 @@ private void reset() { } private void collectConditionalDependencies(Set runtimeArtifacts) { - addToMasterList(runtimeArtifacts); - var artifactExtensions = getArtifactExtensions(runtimeArtifacts); - allExtensions.putAll(artifactExtensions); - artifactExtensions.forEach((ignored, extension) -> queueAbsentExtensionConditionalDependencies(extension)); - } - - private void addToMasterList(Set artifacts) { - artifacts.stream().map(ConditionalDependenciesEnabler::getKey).forEach(existingArtifacts::add); - } - - private Map getArtifactExtensions(Set runtimeArtifacts) { - return runtimeArtifacts.stream() - .flatMap(artifact -> DependencyUtils.getOptionalExtensionInfo(project, artifact).stream()) - .collect(Collectors.toMap(ExtensionDependency::getExtensionId, Function.identity())); - } - - private void queueAbsentExtensionConditionalDependencies(ExtensionDependency extension) { - extension.getConditionalDependencies().stream().filter(dep -> !exists(dep)) - .forEach(dep -> queueConditionalDependency(extension, dep)); + // For every artifact in the dependency graph: + for (ResolvedArtifact artifact : runtimeArtifacts) { + // Add to master list of artifacts: + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // If this artifact represents an extension: + 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); + } + } + } + } } private boolean resolveConditionalDependency(Dependency conditionalDep) { @@ -116,31 +112,51 @@ private boolean resolveConditionalDependency(Dependency conditionalDep) { final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep); Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); - boolean isConditionalDependencyResolved = resolvedArtifacts.stream() - .filter(artifact -> areEquals(conditionalDep, artifact)) - .flatMap(artifact -> DependencyUtils.getOptionalExtensionInfo(project, artifact).stream()) - .filter(extension -> extension.getDependencyConditions().isEmpty() - || exist(extension.getDependencyConditions())) - .findFirst().map(extension -> { - enableConditionalDependency(extension.getExtensionId()); - return true; - }).orElse(false); - - if (isConditionalDependencyResolved) { - addToMasterList(resolvedArtifacts); - var artifactExtensions = getArtifactExtensions(resolvedArtifacts); - artifactExtensions.forEach((id, extension) -> extension.setConditional(true)); - allExtensions.putAll(artifactExtensions); - artifactExtensions.forEach((ignored, extension) -> queueAbsentExtensionConditionalDependencies(extension)); + boolean satisfied = false; + // Resolved artifacts don't have great linking back to the original artifact, so I think + // this loop is trying to find the artifact that represents the original conditional + // dependency + for (ResolvedArtifact artifact : resolvedArtifacts) { + if (conditionalDep.getName().equals(artifact.getName()) + && conditionalDep.getVersion().equals(artifact.getModuleVersion().getId().getVersion()) + && artifact.getModuleVersion().getId().getGroup().equals(conditionalDep.getGroup())) { + // Once the dependency is found, reload the extension info from within + final ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + // Now check if this conditional dependency is resolved given the latest graph evolution + if (extensionDependency != null && (extensionDependency.getDependencyConditions().isEmpty() + || exist(extensionDependency.getDependencyConditions()))) { + satisfied = true; + enableConditionalDependency(extensionDependency.getExtensionId()); + break; + } + } } - return isConditionalDependencyResolved; - } + // No resolution (yet); give up. + if (!satisfied) { + return false; + } - private boolean areEquals(Dependency dependency, ResolvedArtifact artifact) { - return dependency.getName().equals(artifact.getName()) - && Objects.equals(dependency.getVersion(), artifact.getModuleVersion().getId().getVersion()) - && artifact.getModuleVersion().getId().getGroup().equals(dependency.getGroup()); + // The conditional dependency resolved! Let's now add all of /its/ dependencies + for (ResolvedArtifact artifact : resolvedArtifacts) { + // First add the artifact to the master list + existingArtifacts.add(getKey(artifact)); + ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); + if (extensionDependency == null) { + continue; + } + // If this artifact represents an extension, mark this one as a conditional extension + 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); + } + } + } + return satisfied; } private void queueConditionalDependency(ExtensionDependency extension, Dependency conditionalDep) { 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 d68064a42277b..5d0f37b1793e3 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 @@ -10,7 +10,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.Properties; import org.gradle.api.GradleException; @@ -74,59 +73,48 @@ public static String asDependencyNotation(ArtifactCoords artifactCoords) { return String.join(":", artifactCoords.getGroupId(), artifactCoords.getArtifactId(), artifactCoords.getVersion()); } - public static Optional getOptionalExtensionInfo(Project project, ResolvedArtifact artifact) { - return loadExtensionDependencyFromProject(artifact, project) - .or(() -> loadExtensionDependencyFromDir(artifact, project)) - .or(() -> loadExtensionDependencyFromJar(artifact, project)); - } - - private static Optional loadExtensionDependencyFromProject(ResolvedArtifact artifact, - Project project) { - Optional projectDep = Optional.of(artifact.getId().getComponentIdentifier()) - .filter(ProjectComponentIdentifier.class::isInstance) - .map(ProjectComponentIdentifier.class::cast) - .map(ProjectComponentIdentifier::getProjectPath) - .map(projectPath -> project.getRootProject().findProject(projectPath)); - - return projectDep - .map(Project::getExtensions) - .map(container -> container.findByType(SourceSetContainer.class)) - .map(container -> container.findByName(SourceSet.MAIN_SOURCE_SET_NAME)) - .map(it -> it.getOutput().getResourcesDir()) - .map(File::toPath) - .flatMap(resourceDir -> loadOptionalExtensionInfo(project, resourceDir, artifact.getModuleVersion().getId(), - projectDep.get())); - } - - private static Optional loadExtensionDependencyFromDir(ResolvedArtifact artifact, Project project) { - return Optional.of(artifact.getFile().toPath()).filter(Files::exists) - .flatMap(path -> loadOptionalExtensionInfo(project, path, artifact.getModuleVersion().getId(), null)); - } - - private static Optional loadExtensionDependencyFromJar(ResolvedArtifact artifact, Project project) { - return Optional.of(artifact) - .filter(it -> ArtifactCoords.TYPE_JAR.equals(it.getExtension())) - .filter(it -> Files.exists(it.getFile().toPath())) - .flatMap(it -> { - try (FileSystem artifactFs = ZipUtils.newFileSystem(it.getFile().toPath())) { - return loadOptionalExtensionInfo(project, artifactFs.getPath(""), artifact.getModuleVersion().getId(), - null); - } catch (IOException e) { - throw new GradleException("Failed to read " + it.getFile(), e); - } - }); - } + public static ExtensionDependency getExtensionInfoOrNull(Project project, ResolvedArtifact artifact) { + ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId(); + File artifactFile = artifact.getFile(); + + if (artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { + final Project projectDep = project.getRootProject().findProject( + ((ProjectComponentIdentifier) artifact.getId().getComponentIdentifier()).getProjectPath()); + SourceSetContainer sourceSets = projectDep == null ? null + : projectDep.getExtensions().findByType(SourceSetContainer.class); + if (sourceSets != null) { + SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); + File resourcesDir = mainSourceSet.getOutput().getResourcesDir(); + Path descriptorPath = resourcesDir.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, projectDep); + } + } + } - private static Optional loadOptionalExtensionInfo(Project project, Path resourcePath, - ModuleVersionIdentifier extensionId, Project extensionProject) { - return Optional.of(resourcePath) - .map(path -> path.resolve(BootstrapConstants.DESCRIPTOR_PATH)) - .filter(Files::exists) - .map(descriptorPath -> loadExtensionInfo(project, descriptorPath, extensionId, extensionProject)); + if (!artifactFile.exists()) { + return null; + } + if (artifactFile.isDirectory()) { + Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null); + } + } else if (ArtifactCoords.TYPE_JAR.equals(artifact.getExtension())) { + try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) { + Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH); + if (Files.exists(descriptorPath)) { + return loadExtensionInfo(project, descriptorPath, artifactId, null); + } + } catch (IOException e) { + throw new GradleException("Failed to read " + artifactFile, e); + } + } + return null; } private static ExtensionDependency loadExtensionInfo(Project project, Path descriptorPath, - ModuleVersionIdentifier extensionId, Project extensionProject) { + ModuleVersionIdentifier exentionId, Project extensionProject) { final Properties extensionProperties = new Properties(); try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) { extensionProperties.load(reader); @@ -150,10 +138,10 @@ private static ExtensionDependency loadExtensionInfo(Project project, Path descr final ArtifactKey[] constraints = BootstrapUtils .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); if (extensionProject != null) { - return new LocalExtensionDependency(extensionProject, extensionId, deploymentModule, conditionalDependencies, + return new LocalExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); } - return new ExtensionDependency(extensionId, deploymentModule, conditionalDependencies, + return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies, constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); }