Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Loubyansky committed Nov 20, 2024
1 parent c872c3b commit 81e34a8
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ public TsArtifact addDependency(TsArtifact dep) {
return addDependency(new TsDependency(dep));
}

public TsArtifact addDependency(TsArtifact dep, boolean optional) {
return addDependency(new TsDependency(dep, optional));
}

public TsArtifact addDependency(TsArtifact dep, TsArtifact... excludes) {
return addDependency(new TsDependency(dep).exclude(excludes));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ public TsQuarkusExt setDependencyCondition(TsQuarkusExt... exts) {
return setDescriptorProp(BootstrapConstants.DEPENDENCY_CONDITION, buf.toString());
}

public TsQuarkusExt setDependencyCondition(TsArtifact... exts) {
final StringBuilder buf = new StringBuilder();
int i = 0;
buf.append(exts[i++].getKey());
while (i < exts.length) {
buf.append(' ').append(exts[i++].getKey());
}
return setDescriptorProp(BootstrapConstants.DEPENDENCY_CONDITION, buf.toString());
}

public TsQuarkusExt setDescriptorProp(String name, String value) {
rtDescr.set(name, value);
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.quarkus.bootstrap.resolver.test;

import org.junit.jupiter.api.Disabled;

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;

@Disabled
public class ConditionalDependenciesDirectDependencyOnTransitiveDeploymentArtifactTestCase extends CollectDependenciesBase {

@Override
protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception {
var resolver = super.newAppModelResolver(currentProject);
resolver.setIncubatingModelResolver(false);
return resolver;
}

@Override
protected void setupDependencies() {

TsArtifact nettyNioClient = TsArtifact.jar("netty-nio-client");
installAsDep(nettyNioClient);

final TsQuarkusExt nettyClientInternalExt = new TsQuarkusExt("netty-client-internal");
nettyClientInternalExt.getRuntime().addDependency(nettyNioClient, true);
nettyClientInternalExt.setDependencyCondition(nettyNioClient);
install(nettyClientInternalExt, false);
addCollectedDep(nettyClientInternalExt.getRuntime(),
DependencyFlags.RUNTIME_CP | DependencyFlags.DEPLOYMENT_CP | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT);
addCollectedDeploymentDep(nettyClientInternalExt.getDeployment());

final TsQuarkusExt commonExt = new TsQuarkusExt("common");
commonExt.getRuntime().addDependency(nettyNioClient, true);
commonExt.getRuntime().addDependency(nettyClientInternalExt, true);
commonExt.setConditionalDeps(nettyClientInternalExt);
install(commonExt, false);
addCollectedDep(commonExt.getRuntime(),
DependencyFlags.RUNTIME_CP | DependencyFlags.DEPLOYMENT_CP | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT);
addCollectedDeploymentDep(commonExt.getDeployment());

final TsQuarkusExt sqsExt = new TsQuarkusExt("sqs");
sqsExt.addDependency(commonExt);
sqsExt.getRuntime().addDependency(nettyNioClient, true);
addCollectedDep(sqsExt.getRuntime(),
DependencyFlags.RUNTIME_CP | DependencyFlags.DEPLOYMENT_CP | DependencyFlags.RUNTIME_EXTENSION_ARTIFACT);
addCollectedDeploymentDep(sqsExt.getDeployment());

final TsQuarkusExt messagingSqsExt = new TsQuarkusExt("messaging-sqs");
messagingSqsExt.getDeployment().addDependency(commonExt.getDeployment()); // this line breaks it
messagingSqsExt.addDependency(sqsExt);

installAsDep(messagingSqsExt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,15 @@ private void injectDeploymentDependencies(ExtensionDependency extDep)
clearReloadable(deploymentNode);
}

final List<DependencyNode> deploymentDeps = deploymentNode.getChildren();
if (!replaceDirectDepBranch(extDep, deploymentDeps)) {
var deploymentVisitor = new OrderedDependencyVisitor(deploymentNode);
// skip the root node
deploymentVisitor.next();
while (deploymentVisitor.hasNext()) {
deploymentVisitor.next();
extDep.replaceRuntimeNodes(deploymentVisitor);
}

if (!extDep.presentInTargetGraph) {
throw new BootstrapDependencyProcessingException(
"Quarkus extension deployment artifact " + deploymentNode.getArtifact()
+ " does not appear to depend on the corresponding runtime artifact "
Expand All @@ -634,7 +641,7 @@ private void injectDeploymentDependencies(ExtensionDependency extDep)
runtimeNode.setData(QUARKUS_RUNTIME_ARTIFACT, runtimeNode.getArtifact());
runtimeNode.setArtifact(deploymentNode.getArtifact());
runtimeNode.getDependency().setArtifact(deploymentNode.getArtifact());
runtimeNode.setChildren(deploymentDeps);
runtimeNode.setChildren(deploymentNode.getChildren());
}

private void clearReloadable(DependencyNode node) {
Expand All @@ -658,7 +665,7 @@ private boolean replaceDirectDepBranch(ExtensionDependency extDep, List<Dependen
}
if (isSameKey(extDep.info.runtimeArtifact, a)) {
// we are not comparing the version in the above condition because the runtime version
// may appear to be different then the deployment one and that's ok
// may appear to be different from the deployment one and that's ok
// e.g. the version of the runtime artifact could be managed by a BOM
// but overridden by the user in the project config. The way the deployment deps
// are resolved here, the deployment version of the runtime artifact will be the one from the BOM.
Expand Down Expand Up @@ -798,6 +805,7 @@ static ExtensionDependency get(DependencyNode node) {
final Collection<Exclusion> exclusions;
boolean conditionalDepsQueued;
private List<ExtensionDependency> runtimeExtensionDeps;
private boolean presentInTargetGraph;

ExtensionDependency(ExtensionInfo info, DependencyNode node, Collection<Exclusion> exclusions) {
this.runtimeNode = node;
Expand All @@ -820,6 +828,36 @@ void addExtensionDependency(ExtensionDependency dep) {
}
runtimeExtensionDeps.add(dep);
}

void replaceRuntimeNodes(OrderedDependencyVisitor depVisitor) {

/* @formatter:off
var sb = new StringBuilder();
sb.append(depVisitor.getCurrentDistance()).append("/").append(deploymentDistance).append(" ")
.append(depVisitor.getCurrent().getArtifact())
.append(runtimeNode.getArtifact()).append(" ").append(presentInTargetGraph);
System.out.println(sb);
@formatter:on */

if (!presentInTargetGraph && isSameKey(runtimeNode.getArtifact(), depVisitor.getCurrent().getArtifact())) {
// we are not comparing the version in the above condition because the runtime version
// may appear to be different from the deployment one and that's ok
// e.g. the version of the runtime artifact could be managed by a BOM
// but overridden by the user in the project config. The way the deployment deps
// are resolved here, the deployment version of the runtime artifact will be the one from the BOM.
var inserted = new DefaultDependencyNode(runtimeNode);
inserted.setChildren(runtimeNode.getChildren());
depVisitor.replaceCurrent(inserted);
presentInTargetGraph = true;
//stem.out.println(" replace");
return;
}
if (runtimeExtensionDeps != null) {
for (var d : runtimeExtensionDeps) {
d.replaceRuntimeNodes(depVisitor);
}
}
}
}

private class ConditionalDependency {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.quarkus.bootstrap.resolver.maven;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.NoSuchElementException;

import org.eclipse.aether.graph.DependencyNode;

/**
* Walks a dependency tree by visiting dependencies in the order of their priorities
* from the perspective of version conflict resolution.
*/
class OrderedDependencyVisitor {

private final Deque<List<DependencyNode>> stack = new ArrayDeque<>();
private List<DependencyNode> currentList;
private int currentIndex = -1;
private int currentDistance;
private int totalOnCurrentDistance = 1;
private int totalOnNextDistance;

/**
* The root of the dependency tree
*
* @param root the root of the dependency tree
*/
OrderedDependencyVisitor(DependencyNode root) {
currentList = List.of(root);
}

/**
* Current dependency.
*
* @return current dependency
*/
DependencyNode getCurrent() {
ensureNonNegativeIndex();
return currentList.get(currentIndex);
}

/**
* Returns the current distance (depth) from the root to the level on which the current node is.
*
* @return current depth
*/
int getCurrentDistance() {
ensureNonNegativeIndex();
return currentDistance;
}

private void ensureNonNegativeIndex() {
if (currentIndex < 0) {
throw new RuntimeException("The visitor has not been positioned on the first dependency node yet");
}
}

/**
* Whether there are still not visited dependencies.
*
* @return true if there are still not visited dependencies, otherwise - false
*/
boolean hasNext() {
return !stack.isEmpty()
|| currentIndex + 1 < currentList.size()
|| !currentList.get(currentIndex).getChildren().isEmpty();
}

/**
* Returns the next dependency.
*
* @return the next dependency
*/
DependencyNode next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
if (currentIndex >= 0) {
var children = currentList.get(currentIndex).getChildren();
if (!children.isEmpty()) {
stack.addLast(children);
totalOnNextDistance += children.size();
}
if (--totalOnCurrentDistance == 0) {
++currentDistance;
totalOnCurrentDistance = totalOnNextDistance;
totalOnNextDistance = 0;
}
}
if (++currentIndex == currentList.size()) {
currentList = stack.removeFirst();
currentIndex = 0;
}
return currentList.get(currentIndex);
}

/**
* Replaces the current dependency in the tree with the argument.
*
* @param newNode dependency node that should replace the current one in the tree
*/
void replaceCurrent(DependencyNode newNode) {
currentList.set(currentIndex, newNode);
}
}

0 comments on commit 81e34a8

Please sign in to comment.