DependencyVersionsListMojo.java
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.basepom.mojo.dvc.mojo;
import static com.google.common.base.Preconditions.checkState;
import org.basepom.mojo.dvc.AbstractDependencyVersionsMojo;
import org.basepom.mojo.dvc.QualifiedName;
import org.basepom.mojo.dvc.dependency.DependencyMap;
import org.basepom.mojo.dvc.version.VersionResolutionCollection;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.shared.utils.logging.MessageBuilder;
import org.apache.maven.shared.utils.logging.MessageUtils;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.version.Version;
/**
* Resolves all dependencies of a project and lists all versions for any artifact and what version was chosen by the resolver.
*
* @since 2.0.0
*/
@Mojo(name = "list", requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.NONE, defaultPhase = LifecyclePhase.VERIFY)
public final class DependencyVersionsListMojo
extends AbstractDependencyVersionsMojo {
/**
* List only dependencies in conflict or all dependencies.
*/
@Parameter(defaultValue = "false", property = "dvc.conflicts-only")
public boolean conflictsOnly = false;
@Override
protected void doExecute(final ImmutableSetMultimap<QualifiedName, VersionResolutionCollection> resolutionMap, final DependencyMap rootDependencyMap) {
final var filteredMap = ImmutableMap.copyOf(Maps.filterValues(
resolutionMap.asMap(),
v -> {
// report if no condition is set.
boolean report = true;
if (conflictsOnly) {
// do not report if conflicts are requested but none exists
report &= v.stream().anyMatch(VersionResolutionCollection::hasConflict);
}
if (directOnly) {
// do not report if only directs are requested but it is not direct
report &= v.stream().anyMatch(VersionResolutionCollection::hasDirectDependencies);
}
if (managedOnly) {
report &= v.stream().anyMatch(VersionResolutionCollection::hasManagedDependencies);
}
return report;
}));
log.report(quiet, "%s%s dependencies%s for '%s' scope%s:",
(directOnly ? "Direct" : "All"),
(managedOnly ? " managed" : ""),
(deepScan ? " using deep scan" : ""),
scope,
(conflictsOnly ? ", reporting only conflicts" : ""));
if (filteredMap.isEmpty()) {
return;
}
final var rootDependencies = rootDependencyMap.getAllDependencies();
// calculate padding for columnar output
final int namePadding = filteredMap.keySet().stream()
.map(QualifiedName::length).reduce(0, Math::max);
final int scopePadding = rootDependencies.entrySet().stream()
.filter(e -> filteredMap.containsKey(e.getKey()))
.map(e -> e.getValue().getDependency().getScope().length()).reduce(0, Math::max);
for (final var entry : filteredMap.entrySet()) {
final QualifiedName dependencyName = entry.getKey();
final DependencyNode currentDependency = rootDependencies.get(dependencyName);
assert currentDependency != null;
final ImmutableSortedSet<VersionResolutionCollection> resolutions = ImmutableSortedSet.copyOf(entry.getValue());
final MessageBuilder mb = MessageUtils.buffer();
mb.a(Strings.padEnd(dependencyName.getShortName() + ": ", namePadding + 2, ' '))
.a(Strings.padEnd(currentDependency.getDependency().getScope(), scopePadding + 1, ' '));
// fetch the resolved version from the dependency in the tree.
final Version dependencyVersion = currentDependency.getVersion();
checkState(dependencyVersion != null, "Dependency Version for %s is null! File a bug!", currentDependency);
final ComparableVersion actualVersion = new ComparableVersion(dependencyVersion.toString());
final boolean isDirect = resolutions.stream().anyMatch(VersionResolutionCollection::hasDirectDependencies);
final boolean isManaged = (currentDependency.getManagedBits() & DependencyNode.MANAGED_VERSION) != 0;
if (isManaged) {
mb.warning(actualVersion);
} else if (isDirect) {
mb.strong(actualVersion);
} else {
mb.a(actualVersion);
}
final var unselectedVersions = ImmutableSortedSet.copyOf(Sets.filter(resolutions, v -> !v.isMatchFor(actualVersion)));
if (!unselectedVersions.isEmpty()) {
mb.a(" (");
for (final var it = unselectedVersions.iterator(); it.hasNext(); ) {
final VersionResolutionCollection resolution = it.next();
final String result = resolution.getExpectedVersion().toString();
if (resolution.hasConflict()) {
mb.failure("!" + result + "!");
} else if (resolution.isMatchFor(actualVersion)) {
mb.success(result);
} else if (resolution.hasDirectDependencies()) {
mb.strong("*" + result + "*");
} else {
mb.a(result);
}
if (it.hasNext()) {
mb.a(", ");
}
}
mb.a(")");
}
log.info("%s", mb);
}
}
}