Skip to content

Commit a0cb56b

Browse files
authored
Rework dirty-tree (#318)
The original implementation was very blunt and OOM prone. For start, split the two, as dirty needs some tricks in place. Also, right now this merely implements "show adjascent nodes of leaves that were eliminated".
1 parent 3c857d7 commit a0cb56b

File tree

8 files changed

+206
-32
lines changed

8 files changed

+206
-32
lines changed

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/ToolboxCommando.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,8 @@ Result<Map<CollectResult, CollectResult>> treeDiff(
396396
Result<CollectResult> dirtyTree(
397397
ResolutionScope resolutionScope,
398398
ResolutionRoot resolutionRoot,
399-
int maxLevel,
400-
boolean verboseTree,
399+
int dirtyLevelPast,
400+
boolean conflictResolve,
401401
DependencyMatcher dependencyMatcher)
402402
throws Exception;
403403

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/ToolboxResolver.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ CollectResult collect(
8181
boolean verbose)
8282
throws DependencyCollectionException;
8383

84+
CollectResult collectDirty(
85+
ResolutionScope resolutionScope,
86+
Artifact root,
87+
List<Dependency> dependencies,
88+
List<Dependency> managedDependencies,
89+
int dirtyLevelPast,
90+
boolean conflictResolve)
91+
throws DependencyCollectionException;
92+
93+
CollectResult collectDirty(
94+
ResolutionScope resolutionScope,
95+
Dependency root,
96+
List<Dependency> dependencies,
97+
List<Dependency> managedDependencies,
98+
int dirtyLevelPast,
99+
boolean conflictResolve)
100+
throws DependencyCollectionException;
101+
84102
CollectResult collectDm(Artifact root, List<Dependency> managedDependencies, boolean verbose)
85103
throws ArtifactDescriptorException, ArtifactResolutionException, VersionResolutionException;
86104

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.toolbox.shared.internal;
9+
10+
import static java.util.Objects.requireNonNull;
11+
12+
import java.util.concurrent.ConcurrentHashMap;
13+
import org.eclipse.aether.collection.DependencyCollectionContext;
14+
import org.eclipse.aether.collection.DependencySelector;
15+
import org.eclipse.aether.graph.Dependency;
16+
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
17+
18+
/**
19+
* A special dependency selector, that selects all until unless delegate unselected is met, and then it selects only
20+
* one level below of their children.
21+
*/
22+
public class DirtyTreeDependencySelector implements DependencySelector {
23+
private final DependencySelector delegate;
24+
private final DependencySelector filter;
25+
private final int maxLevelPast;
26+
private final ConcurrentHashMap<String, Boolean> stoppers;
27+
28+
public DirtyTreeDependencySelector(DependencySelector delegate, DependencySelector filter, int maxLevelPast) {
29+
this.delegate = requireNonNull(delegate);
30+
this.filter = requireNonNull(filter);
31+
if (maxLevelPast < 0) {
32+
throw new IllegalArgumentException("maxLevelPast must be greater than or equal to 0");
33+
}
34+
this.maxLevelPast = maxLevelPast;
35+
this.stoppers = new ConcurrentHashMap<>();
36+
}
37+
38+
@Override
39+
public boolean selectDependency(Dependency dependency) {
40+
boolean selected = delegate.selectDependency(dependency);
41+
if (!selected) {
42+
stoppers.put(ArtifactIdUtils.toId(dependency.getArtifact()), Boolean.TRUE);
43+
}
44+
return filter.selectDependency(dependency);
45+
}
46+
47+
@Override
48+
public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
49+
if (context.getDependency() != null
50+
&& stoppers.containsKey(
51+
ArtifactIdUtils.toId(context.getDependency().getArtifact()))) {
52+
return new LevelDependencySelector(maxLevelPast);
53+
} else {
54+
return new DirtyTreeDependencySelector(
55+
delegate.deriveChildSelector(context), filter.deriveChildSelector(context), maxLevelPast);
56+
}
57+
}
58+
}

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/LevelDependencySelector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public class LevelDependencySelector implements DependencySelector {
1717
private final int currentLevel;
1818

1919
public LevelDependencySelector(int maxLevel) {
20-
if (maxLevel < 1) {
21-
throw new IllegalArgumentException("maxLevel must be greater than 0");
20+
if (maxLevel < 0) {
21+
throw new IllegalArgumentException("maxLevel must be greater than or equal to 0");
2222
}
2323
this.maxLevel = maxLevel;
2424
this.currentLevel = 0;
@@ -31,7 +31,7 @@ private LevelDependencySelector(int maxLevel, int currentLevel) {
3131

3232
@Override
3333
public boolean selectDependency(Dependency dependency) {
34-
return true;
34+
return currentLevel < maxLevel;
3535
}
3636

3737
@Override

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/ToolboxCommandoImpl.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,20 +1109,20 @@ public boolean accept(DependencyNode node, List<DependencyNode> parents) {
11091109
public Result<CollectResult> dirtyTree(
11101110
ResolutionScope resolutionScope,
11111111
ResolutionRoot resolutionRoot,
1112-
int maxLevel,
1113-
boolean verboseTree,
1112+
int dirtyLevelPast,
1113+
boolean conflictResolve,
11141114
DependencyMatcher dependencyMatcher)
11151115
throws Exception {
11161116
output.suggest("Loading root of: {}", resolutionRoot.getArtifact());
11171117
ResolutionRoot root = toolboxResolver.loadRoot(resolutionRoot);
11181118
output.suggest("Collecting graph of: {}", resolutionRoot.getArtifact());
1119-
CollectResult collectResult = toolboxResolver.collect(
1119+
CollectResult collectResult = toolboxResolver.collectDirty(
11201120
resolutionScope,
11211121
root.getArtifact(),
11221122
root.getDependencies(),
11231123
root.getManagedDependencies(),
1124-
maxLevel,
1125-
verboseTree);
1124+
dirtyLevelPast,
1125+
conflictResolve);
11261126
CloningDependencyVisitor cloningDependencyVisitor = new CloningDependencyVisitor();
11271127
collectResult.getRoot().accept(new FilteringDependencyVisitor(cloningDependencyVisitor, new DependencyFilter() {
11281128
@Override

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/ToolboxResolverImpl.java

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
7474
import org.eclipse.aether.util.artifact.JavaScopes;
7575
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
76+
import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
7677
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
7778
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
7879
import org.eclipse.aether.version.InvalidVersionSpecificationException;
@@ -315,6 +316,46 @@ public CollectResult collect(
315316
verbose);
316317
}
317318

319+
@Override
320+
public CollectResult collectDirty(
321+
ResolutionScope resolutionScope,
322+
Artifact root,
323+
List<Dependency> dependencies,
324+
List<Dependency> managedDependencies,
325+
int dirtyLevelPast,
326+
boolean conflictResolve)
327+
throws DependencyCollectionException {
328+
return doCollectDirty(
329+
resolutionScope,
330+
null,
331+
root,
332+
dependencies,
333+
managedDependencies,
334+
remoteRepositories,
335+
dirtyLevelPast,
336+
conflictResolve);
337+
}
338+
339+
@Override
340+
public CollectResult collectDirty(
341+
ResolutionScope resolutionScope,
342+
Dependency root,
343+
List<Dependency> dependencies,
344+
List<Dependency> managedDependencies,
345+
int dirtyLevelPast,
346+
boolean conflictResolve)
347+
throws DependencyCollectionException {
348+
return doCollectDirty(
349+
resolutionScope,
350+
root,
351+
null,
352+
dependencies,
353+
managedDependencies,
354+
remoteRepositories,
355+
dirtyLevelPast,
356+
conflictResolve);
357+
}
358+
318359
@Override
319360
public CollectResult collectDm(Artifact root, List<Dependency> managedDependencies, boolean verbose)
320361
throws ArtifactDescriptorException, ArtifactResolutionException, VersionResolutionException {
@@ -569,6 +610,63 @@ protected CollectResult doCollect(
569610
return result;
570611
}
571612

613+
protected CollectResult doCollectDirty(
614+
ResolutionScope resolutionScope,
615+
Dependency rootDependency,
616+
Artifact root,
617+
List<Dependency> dependencies,
618+
List<Dependency> managedDependencies,
619+
List<RemoteRepository> remoteRepositories,
620+
int dirtyLevelPast,
621+
boolean conflictResolve)
622+
throws DependencyCollectionException {
623+
requireNonNull(resolutionScope);
624+
if (rootDependency == null && root == null) {
625+
throw new NullPointerException("one of rootDependency or root must be non-null");
626+
}
627+
628+
DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(this.session);
629+
session.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
630+
session.setDependencySelector(new DirtyTreeDependencySelector(
631+
session.getDependencySelector(), new ScopeDependencySelector(JavaScopes.TEST), dirtyLevelPast));
632+
if (!conflictResolve) {
633+
session.setDependencyGraphTransformer(null);
634+
}
635+
CollectRequest collectRequest = new CollectRequest();
636+
if (rootDependency != null) {
637+
root = rootDependency.getArtifact();
638+
}
639+
collectRequest.setRootArtifact(root);
640+
if (conflictResolve) {
641+
collectRequest.setDependencies(dependencies.stream()
642+
.filter(d -> !resolutionScope.isEliminateTest() || !JavaScopes.TEST.equals(d.getScope()))
643+
.collect(Collectors.toList()));
644+
} else {
645+
collectRequest.setDependencies(dependencies);
646+
}
647+
collectRequest.setManagedDependencies(managedDependencies);
648+
collectRequest.setRepositories(remoteRepositories);
649+
collectRequest.setRequestContext(CTX_TOOLBOX);
650+
collectRequest.setTrace(RequestTrace.newChild(null, collectRequest));
651+
652+
output.chatter("Collecting {} @ {}", collectRequest, resolutionScope.name());
653+
CollectResult result = repositorySystem.collectDependencies(session, collectRequest);
654+
if (resolutionScope != ResolutionScope.TEST) {
655+
ArrayList<DependencyNode> childrenToRemove = new ArrayList<>();
656+
for (DependencyNode node : result.getRoot().getChildren()) {
657+
if (!resolutionScope
658+
.getDirectInclude()
659+
.contains(node.getDependency().getScope())) {
660+
childrenToRemove.add(node);
661+
}
662+
}
663+
if (!childrenToRemove.isEmpty()) {
664+
childrenToRemove.forEach(c -> c.setChildren(Collections.emptyList()));
665+
}
666+
}
667+
return result;
668+
}
669+
572670
protected CollectResult doCollectDm(
573671
Dependency rootDependency,
574672
Artifact root,

toolbox/src/main/java/eu/maveniverse/maven/toolbox/plugin/gav/GavDirtyTreeMojo.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,25 +58,25 @@ public class GavDirtyTreeMojo extends GavMojoSupport {
5858
private String dependencyMatcher;
5959

6060
/**
61-
* Set it {@code true} for verbose tree.
61+
* The level up to you want to see dirty tree. Note: keep it low, otherwise this call is OOM prone.
62+
* Default: 0 (direct siblings only)
6263
*/
6364
@CommandLine.Option(
64-
names = {"--verboseTree"},
65-
defaultValue = "false",
66-
description = "Make it true for verbose tree")
67-
@Parameter(property = "verboseTree", defaultValue = "false", required = true)
68-
private boolean verboseTree;
65+
names = {"--dirtyLevel"},
66+
defaultValue = "0",
67+
description = "Set the level you want to see dirty tree up to")
68+
@Parameter(property = "dirtyLevel", defaultValue = "0", required = true)
69+
private int dirtyLevel;
6970

7071
/**
71-
* The level up to you want to see dirty tree. Note: keep it low, otherwise this call is OOM pronect.
72-
* Default: 3
72+
* Set it {@code true} to conflict resolve the tree.
7373
*/
7474
@CommandLine.Option(
75-
names = {"--dirtyLevel"},
76-
defaultValue = "3",
77-
description = "Set the level you want to see dirty tree up to")
78-
@Parameter(property = "dirtyLevel", defaultValue = "3", required = true)
79-
private int dirtyLevel;
75+
names = {"--conflictResolve"},
76+
defaultValue = "false",
77+
description = "Make it true tp conflict resolve the tree")
78+
@Parameter(property = "conflictResolve", defaultValue = "false", required = true)
79+
private boolean conflictResolve;
8080

8181
@Override
8282
protected Result<CollectResult> doExecute() throws Exception {
@@ -85,7 +85,7 @@ protected Result<CollectResult> doExecute() throws Exception {
8585
ResolutionScope.parse(scope),
8686
toolboxCommando.loadGav(gav, slurp(boms)),
8787
dirtyLevel,
88-
verboseTree,
88+
conflictResolve,
8989
toolboxCommando.parseDependencyMatcherSpec(dependencyMatcher));
9090
}
9191
}

toolbox/src/main/java/eu/maveniverse/maven/toolbox/plugin/mp/DirtyTreeMojo.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ public class DirtyTreeMojo extends MPMojoSupport {
3333
private String dependencyMatcher;
3434

3535
/**
36-
* Set it {@code true} for verbose tree.
36+
* The level up to you want to see dirty tree. Note: keep it low, otherwise this call is OOM prone.
37+
* Default: 0 (direct siblings only)
3738
*/
38-
@Parameter(property = "verboseTree", defaultValue = "false", required = true)
39-
private boolean verboseTree;
39+
@Parameter(property = "dirtyLevel", defaultValue = "0", required = true)
40+
private int dirtyLevel;
4041

4142
/**
42-
* The level up to you want to see dirty tree. Note: keep it low, otherwise this call is OOM pronect.
43-
* Default: 3
43+
* Set it {@code true} to conflict resolve the tree.
4444
*/
45-
@Parameter(property = "dirtyLevel", defaultValue = "3", required = true)
46-
private int dirtyLevel;
45+
@Parameter(property = "conflictResolve", defaultValue = "false", required = true)
46+
private boolean conflictResolve;
4747

4848
@Override
4949
protected Result<CollectResult> doExecute() throws Exception {
@@ -52,7 +52,7 @@ protected Result<CollectResult> doExecute() throws Exception {
5252
ResolutionScope.parse(scope),
5353
projectAsResolutionRoot(),
5454
dirtyLevel,
55-
verboseTree,
55+
conflictResolve,
5656
toolboxCommando.parseDependencyMatcherSpec(dependencyMatcher));
5757
}
5858
}

0 commit comments

Comments
 (0)