mirror of
https://github.com/rife2/bld
synced 2026-08-04 23:07:46 +02:00
Compare commits
7 commits
cddd3ec429
...
734ac585ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
734ac585ab | ||
|
|
27c45033fb | ||
|
|
b2987b6b23 | ||
|
|
1e76ee31c1 | ||
|
|
4447610d02 | ||
|
|
f3f4799190 | ||
|
|
5859a07a4c |
|
|
@ -29,7 +29,7 @@ public class BaseProject extends BuildExecutor {
|
||||||
/**
|
/**
|
||||||
* The CLI option to trigger automatic dependency download and purge.
|
* The CLI option to trigger automatic dependency download and purge.
|
||||||
*
|
*
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public static final String AUTO_DOWNLOAD_PURGE_OPTION = "--auto-download-purge";
|
public static final String AUTO_DOWNLOAD_PURGE_OPTION = "--auto-download-purge";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ public class BuildExecutor {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the execution is verbose;
|
* @return {@code true} if the execution is verbose;
|
||||||
* or {@code false} otherwise
|
* or {@code false} otherwise
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public boolean verbose() {
|
public boolean verbose() {
|
||||||
return verbose_;
|
return verbose_;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import java.net.URLConnection;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static rife.tools.HttpUtils.HEADER_AUTHORIZATION;
|
import static rife.tools.HttpUtils.HEADER_AUTHORIZATION;
|
||||||
import static rife.tools.HttpUtils.basicAuthorizationHeader;
|
import static rife.tools.HttpUtils.basicAuthorizationHeader;
|
||||||
|
|
@ -35,6 +36,8 @@ import static rife.tools.StringUtils.encodeHexLower;
|
||||||
* @since 1.5.18
|
* @since 1.5.18
|
||||||
*/
|
*/
|
||||||
public abstract class ArtifactRetriever {
|
public abstract class ArtifactRetriever {
|
||||||
|
private static final int TRANSFER_CHUNK_SIZE = 128 * 1024;
|
||||||
|
|
||||||
private final static ArtifactRetriever UNCACHED = new ArtifactRetriever() {
|
private final static ArtifactRetriever UNCACHED = new ArtifactRetriever() {
|
||||||
String getCached(RepositoryArtifact artifact) {
|
String getCached(RepositoryArtifact artifact) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -42,6 +45,10 @@ public abstract class ArtifactRetriever {
|
||||||
|
|
||||||
void cache(RepositoryArtifact artifact, String content) {
|
void cache(RepositoryArtifact artifact, String content) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isCaching() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,7 +70,7 @@ public abstract class ArtifactRetriever {
|
||||||
*/
|
*/
|
||||||
public static ArtifactRetriever cachingInstance() {
|
public static ArtifactRetriever cachingInstance() {
|
||||||
return new ArtifactRetriever() {
|
return new ArtifactRetriever() {
|
||||||
private final Map<RepositoryArtifact, String> artifactCache = new HashMap<>();
|
private final Map<RepositoryArtifact, String> artifactCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
String getCached(RepositoryArtifact artifact) {
|
String getCached(RepositoryArtifact artifact) {
|
||||||
return artifactCache.get(artifact);
|
return artifactCache.get(artifact);
|
||||||
|
|
@ -73,6 +80,9 @@ public abstract class ArtifactRetriever {
|
||||||
artifactCache.put(artifact, content);
|
artifactCache.put(artifact, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isCaching() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,6 +93,8 @@ public abstract class ArtifactRetriever {
|
||||||
|
|
||||||
abstract void cache(RepositoryArtifact artifact, String content);
|
abstract void cache(RepositoryArtifact artifact, String content);
|
||||||
|
|
||||||
|
abstract boolean isCaching();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the contents of an artifact as a string.
|
* Reads the contents of an artifact as a string.
|
||||||
*
|
*
|
||||||
|
|
@ -141,17 +153,17 @@ public abstract class ArtifactRetriever {
|
||||||
|
|
||||||
var download_filename = artifact.location().substring(artifact.location().lastIndexOf("/") + 1);
|
var download_filename = artifact.location().substring(artifact.location().lastIndexOf("/") + 1);
|
||||||
var download_file = new File(directory, download_filename);
|
var download_file = new File(directory, download_filename);
|
||||||
System.out.print("Downloading: " + artifact.location() + " ... ");
|
var transfer = TransferOutput.instance().start(artifact.location());
|
||||||
System.out.flush();
|
var status = "";
|
||||||
try {
|
try {
|
||||||
if (artifact.repository().isLocal()) {
|
if (artifact.repository().isLocal()) {
|
||||||
var source = new File(artifact.location());
|
var source = new File(artifact.location());
|
||||||
if (source.exists()) {
|
if (source.exists()) {
|
||||||
FileUtils.copy(source, download_file);
|
FileUtils.copy(source, download_file);
|
||||||
System.out.print("done");
|
status = "done";
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
System.out.print("not found");
|
status = "not found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -159,7 +171,7 @@ public abstract class ArtifactRetriever {
|
||||||
if (download_file.exists() && download_file.canRead()) {
|
if (download_file.exists() && download_file.canRead()) {
|
||||||
if (checkHash(artifact, download_file, ".sha256", "SHA-256") ||
|
if (checkHash(artifact, download_file, ".sha256", "SHA-256") ||
|
||||||
checkHash(artifact, download_file, ".md5", "MD5")) {
|
checkHash(artifact, download_file, ".md5", "MD5")) {
|
||||||
System.out.print("exists");
|
status = "exists";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,23 +182,29 @@ public abstract class ArtifactRetriever {
|
||||||
HEADER_AUTHORIZATION,
|
HEADER_AUTHORIZATION,
|
||||||
basicAuthorizationHeader(artifact.repository().username(), artifact.repository().password()));
|
basicAuthorizationHeader(artifact.repository().username(), artifact.repository().password()));
|
||||||
}
|
}
|
||||||
|
var content_length = connection.getContentLengthLong();
|
||||||
try (var input_stream = connection.getInputStream()) {
|
try (var input_stream = connection.getInputStream()) {
|
||||||
var readableByteChannel = Channels.newChannel(input_stream);
|
var readableByteChannel = Channels.newChannel(input_stream);
|
||||||
try (var fileOutputStream = new FileOutputStream(download_file)) {
|
try (var fileOutputStream = new FileOutputStream(download_file)) {
|
||||||
var fileChannel = fileOutputStream.getChannel();
|
var fileChannel = fileOutputStream.getChannel();
|
||||||
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
|
var position = 0L;
|
||||||
|
long transferred;
|
||||||
|
while ((transferred = fileChannel.transferFrom(readableByteChannel, position, TRANSFER_CHUNK_SIZE)) > 0) {
|
||||||
|
position += transferred;
|
||||||
|
transfer.progress(position, content_length);
|
||||||
|
}
|
||||||
|
|
||||||
System.out.print("done");
|
status = "done";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
System.out.print("not found");
|
status = "not found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
System.out.println();
|
transfer.finish(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,17 @@ public class DependencyResolver {
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
public DependencySet getAllDependencies(Scope... scopes) {
|
public DependencySet getAllDependencies(Scope... scopes) {
|
||||||
|
var prefetcher = PomPrefetcher.create(resolution_, retriever_, repositories_);
|
||||||
|
try {
|
||||||
|
return getAllDependencies(prefetcher, scopes);
|
||||||
|
} finally {
|
||||||
|
if (prefetcher != null) {
|
||||||
|
prefetcher.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencySet getAllDependencies(PomPrefetcher prefetcher, Scope... scopes) {
|
||||||
var result = new DependencySet();
|
var result = new DependencySet();
|
||||||
var overridden = resolution_.overrideDependency(dependency_);
|
var overridden = resolution_.overrideDependency(dependency_);
|
||||||
result.add(overridden);
|
result.add(overridden);
|
||||||
|
|
@ -138,6 +149,11 @@ public class DependencyResolver {
|
||||||
next_dependencies.removeIf(it -> matchesExclusions(exclusion_context, it));
|
next_dependencies.removeIf(it -> matchesExclusions(exclusion_context, it));
|
||||||
// add all next dependencies to the queue
|
// add all next dependencies to the queue
|
||||||
dependency_queue.addAll(next_dependencies);
|
dependency_queue.addAll(next_dependencies);
|
||||||
|
// speculatively retrieve the POMs of the queued dependencies in
|
||||||
|
// parallel so that they are cached when they're processed in order
|
||||||
|
if (prefetcher != null) {
|
||||||
|
prefetcher.prefetch(next_dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
// unless we find a next set of dependencies to add, stop resolving
|
// unless we find a next set of dependencies to add, stop resolving
|
||||||
parent = null;
|
parent = null;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package rife.bld.dependencies;
|
||||||
|
|
||||||
import rife.ioc.HierarchicalProperties;
|
import rife.ioc.HierarchicalProperties;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -147,15 +148,14 @@ public class DependencyScopes extends LinkedHashMap<Scope, DependencySet> {
|
||||||
|
|
||||||
private DependencySet resolveScopedDependencies(HierarchicalProperties properties, ArtifactRetriever retriever, List<Repository> repositories, Scope[] resolvedScopes, Scope[] transitiveScopes, DependencySet excluded) {
|
private DependencySet resolveScopedDependencies(HierarchicalProperties properties, ArtifactRetriever retriever, List<Repository> repositories, Scope[] resolvedScopes, Scope[] transitiveScopes, DependencySet excluded) {
|
||||||
var resolution = new VersionResolution(properties);
|
var resolution = new VersionResolution(properties);
|
||||||
var dependencies = new DependencySet();
|
var roots = new ArrayList<Dependency>();
|
||||||
for (var scope : resolvedScopes) {
|
for (var scope : resolvedScopes) {
|
||||||
var scoped_dependencies = get(scope);
|
var scoped_dependencies = get(scope);
|
||||||
if (scoped_dependencies != null) {
|
if (scoped_dependencies != null) {
|
||||||
for (var dependency : scoped_dependencies) {
|
roots.addAll(scoped_dependencies);
|
||||||
dependencies.addAll(new DependencyResolver(resolution, retriever, repositories, dependency).getAllDependencies(transitiveScopes));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var dependencies = new ParallelDependencyResolver(resolution, retriever, repositories).resolveAllDependencies(roots, transitiveScopes);
|
||||||
if (excluded != null) {
|
if (excluded != null) {
|
||||||
dependencies.removeAll(excluded);
|
dependencies.removeAll(excluded);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,11 @@ public class DependencySet extends AbstractSet<Dependency> implements Set<Depend
|
||||||
* including other classifiers.
|
* including other classifiers.
|
||||||
* <p>
|
* <p>
|
||||||
* The destination directory must exist and be writable.
|
* The destination directory must exist and be writable.
|
||||||
|
* <p>
|
||||||
|
* The artifacts of different dependencies are transferred in parallel,
|
||||||
|
* the {@value VersionResolution#PROPERTY_TRANSFER_PARALLELISM} property
|
||||||
|
* can be used to change the number of simultaneous transfers, setting it
|
||||||
|
* to {@code 1} makes the transfers sequential.
|
||||||
*
|
*
|
||||||
* @param resolution the version resolution state that can be cached
|
* @param resolution the version resolution state that can be cached
|
||||||
* @param retriever the retriever to use to get artifacts
|
* @param retriever the retriever to use to get artifacts
|
||||||
|
|
@ -140,42 +145,9 @@ public class DependencySet extends AbstractSet<Dependency> implements Set<Depend
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public List<RepositoryArtifact> transferIntoDirectory(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories, File directory, File modulesDirectory, String... classifiers) {
|
public List<RepositoryArtifact> transferIntoDirectory(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories, File directory, File modulesDirectory, String... classifiers) {
|
||||||
var result = new ArrayList<RepositoryArtifact>();
|
return new DependencyTransferBatch()
|
||||||
for (var dependency : this) {
|
.add(this, directory, modulesDirectory, classifiers)
|
||||||
var transfer_directory = directory;
|
.transfer(resolution, retriever, repositories);
|
||||||
if (dependency.isModularJar()) {
|
|
||||||
if (modulesDirectory == null) {
|
|
||||||
throw new DependencyTransferException(dependency, "modules directory is not provided");
|
|
||||||
}
|
|
||||||
transfer_directory = modulesDirectory;
|
|
||||||
}
|
|
||||||
else if (directory == null) {
|
|
||||||
throw new DependencyTransferException(dependency, "artifacts directory is not provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!transfer_directory.exists()) {
|
|
||||||
if (!transfer_directory.mkdirs()) {
|
|
||||||
throw new DependencyTransferException(dependency, transfer_directory, "couldn't create directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var artifact = new DependencyResolver(resolution, retriever, repositories, dependency).transferIntoDirectory(transfer_directory);
|
|
||||||
if (artifact != null) {
|
|
||||||
result.add(artifact);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classifiers != null) {
|
|
||||||
for (var classifier : classifiers) {
|
|
||||||
if (classifier != null && !dependency.excludedClassifiers().contains(classifier)) {
|
|
||||||
var classifier_artifact = new DependencyResolver(resolution, retriever, repositories, dependency.withClassifier(classifier)).transferIntoDirectory(transfer_directory);
|
|
||||||
if (classifier_artifact != null) {
|
|
||||||
result.add(classifier_artifact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -206,11 +178,7 @@ public class DependencySet extends AbstractSet<Dependency> implements Set<Depend
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public String generateTransitiveDependencyTree(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories, Scope... scopes) {
|
public String generateTransitiveDependencyTree(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories, Scope... scopes) {
|
||||||
var compile_dependencies = new DependencySet();
|
return new ParallelDependencyResolver(resolution, retriever, repositories).resolveAllDependencies(this, scopes).generateDependencyTree();
|
||||||
for (var dependency : this) {
|
|
||||||
compile_dependencies.addAll(new DependencyResolver(resolution, retriever, repositories, dependency).getAllDependencies(scopes));
|
|
||||||
}
|
|
||||||
return compile_dependencies.generateDependencyTree();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
129
src/main/java/rife/bld/dependencies/DependencyTransferBatch.java
Normal file
129
src/main/java/rife/bld/dependencies/DependencyTransferBatch.java
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import rife.bld.dependencies.exceptions.DependencyTransferException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the artifact transfers of one or more dependency sets so that
|
||||||
|
* they can be transferred together in a single parallel batch, instead of
|
||||||
|
* separate consecutive batches per dependency set.
|
||||||
|
* <p>
|
||||||
|
* The batch itself is a passive collector, the resolution context is only
|
||||||
|
* provided when the transfers are {@linkplain #transfer performed}. The
|
||||||
|
* parallelism is determined by {@link VersionResolution#transferParallelism()},
|
||||||
|
* setting it to {@code 1} makes the transfers sequential. Identical
|
||||||
|
* transfers into the same directory are only performed once.
|
||||||
|
*
|
||||||
|
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public class DependencyTransferBatch {
|
||||||
|
private record TransferRequest(Dependency dependency, File directory, String[] classifiers) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<TransferRequest> requests_ = new ArrayList<>();
|
||||||
|
private final Set<String> transferTargets_ = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the artifact transfers for a dependency set to this batch.
|
||||||
|
* <p>
|
||||||
|
* The destination directory must exist and be writable.
|
||||||
|
*
|
||||||
|
* @param dependencies the dependencies whose artifacts to transfer
|
||||||
|
* @param directory the directory to transfer the artifacts into
|
||||||
|
* @param modulesDirectory the directory to download the modules into
|
||||||
|
* @param classifiers the additional classifiers to transfer
|
||||||
|
* @return this batch instance
|
||||||
|
* @throws DependencyTransferException when the transfer couldn't be prepared
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public DependencyTransferBatch add(DependencySet dependencies, File directory, File modulesDirectory, String... classifiers) {
|
||||||
|
for (var dependency : dependencies) {
|
||||||
|
var transfer_directory = directory;
|
||||||
|
if (dependency.isModularJar()) {
|
||||||
|
if (modulesDirectory == null) {
|
||||||
|
throw new DependencyTransferException(dependency, "modules directory is not provided");
|
||||||
|
}
|
||||||
|
transfer_directory = modulesDirectory;
|
||||||
|
}
|
||||||
|
else if (directory == null) {
|
||||||
|
throw new DependencyTransferException(dependency, "artifacts directory is not provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transfer_directory.exists()) {
|
||||||
|
if (!transfer_directory.mkdirs()) {
|
||||||
|
throw new DependencyTransferException(dependency, transfer_directory, "couldn't create directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip transfers that are already batched for the same directory
|
||||||
|
if (!transferTargets_.add(dependency + " -> " + transfer_directory.getAbsolutePath())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
requests_.add(new TransferRequest(dependency, transfer_directory, classifiers));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs all the collected artifact transfers in a single parallel
|
||||||
|
* batch, in the order they were added.
|
||||||
|
* <p>
|
||||||
|
* This empties the batch, transfers can be collected and transferred
|
||||||
|
* again with the same instance.
|
||||||
|
*
|
||||||
|
* @param resolution the version resolution state that can be cached
|
||||||
|
* @param retriever the retriever to use to get artifacts
|
||||||
|
* @param repositories the repositories to use for the transfer
|
||||||
|
* @return the list of artifacts that were transferred successfully
|
||||||
|
* @throws DependencyTransferException when an error occurred during the transfer
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public List<RepositoryArtifact> transfer(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories) {
|
||||||
|
final var repos = (repositories == null ? List.<Repository>of() : repositories);
|
||||||
|
try {
|
||||||
|
var transfers = new ArrayList<Supplier<List<RepositoryArtifact>>>(requests_.size());
|
||||||
|
for (var request : requests_) {
|
||||||
|
transfers.add(() -> {
|
||||||
|
var artifacts = new ArrayList<RepositoryArtifact>();
|
||||||
|
var artifact = new DependencyResolver(resolution, retriever, repos, request.dependency()).transferIntoDirectory(request.directory());
|
||||||
|
if (artifact != null) {
|
||||||
|
artifacts.add(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.classifiers() != null) {
|
||||||
|
for (var classifier : request.classifiers()) {
|
||||||
|
if (classifier != null && !request.dependency().excludedClassifiers().contains(classifier)) {
|
||||||
|
var classifier_artifact = new DependencyResolver(resolution, retriever, repos, request.dependency().withClassifier(classifier)).transferIntoDirectory(request.directory());
|
||||||
|
if (classifier_artifact != null) {
|
||||||
|
artifacts.add(classifier_artifact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return artifacts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new ArrayList<RepositoryArtifact>();
|
||||||
|
for (var artifacts : ParallelExecution.execute(transfers, resolution.transferParallelism())) {
|
||||||
|
result.addAll(artifacts);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
requests_.clear();
|
||||||
|
transferTargets_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves multiple dependencies in parallel within a list of
|
||||||
|
* Maven-compatible repositories.
|
||||||
|
* <p>
|
||||||
|
* The parallelism is determined by {@link VersionResolution#resolutionParallelism()},
|
||||||
|
* setting it to {@code 1} makes the resolution sequential. The results are
|
||||||
|
* always identical to resolving each dependency sequentially with a
|
||||||
|
* {@link DependencyResolver}, in the same order.
|
||||||
|
*
|
||||||
|
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public class ParallelDependencyResolver {
|
||||||
|
private final VersionResolution resolution_;
|
||||||
|
private final ArtifactRetriever retriever_;
|
||||||
|
private final List<Repository> repositories_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new parallel resolver.
|
||||||
|
* <p>
|
||||||
|
* The repositories will be checked in the order they're listed.
|
||||||
|
*
|
||||||
|
* @param resolution the version resolution state that can be cached
|
||||||
|
* @param retriever the retriever to use to get artifacts
|
||||||
|
* @param repositories the repositories to use for the resolution
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public ParallelDependencyResolver(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories) {
|
||||||
|
resolution_ = resolution;
|
||||||
|
retriever_ = retriever;
|
||||||
|
if (repositories == null) {
|
||||||
|
repositories = List.of();
|
||||||
|
}
|
||||||
|
repositories_ = repositories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the transitive dependencies of multiple root dependencies,
|
||||||
|
* merging the results in the order of the provided roots.
|
||||||
|
* <p>
|
||||||
|
* The roots are resolved in parallel while a shared prefetcher
|
||||||
|
* speculatively warms the retriever cache across all of them.
|
||||||
|
*
|
||||||
|
* @param roots the root dependencies to resolve
|
||||||
|
* @param scopes the scopes to return the transitive dependencies for
|
||||||
|
* @return the merged transitive dependencies of all the roots
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public DependencySet resolveAllDependencies(Collection<Dependency> roots, Scope... scopes) {
|
||||||
|
var result = new DependencySet();
|
||||||
|
if (roots.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefetcher = PomPrefetcher.create(resolution_, retriever_, repositories_);
|
||||||
|
try {
|
||||||
|
var resolutions = new ArrayList<Supplier<DependencySet>>(roots.size());
|
||||||
|
for (var root : roots) {
|
||||||
|
resolutions.add(() -> new DependencyResolver(resolution_, retriever_, repositories_, root).getAllDependencies(prefetcher, scopes));
|
||||||
|
}
|
||||||
|
for (var dependencies : ParallelExecution.execute(resolutions, resolution_.resolutionParallelism())) {
|
||||||
|
result.addAll(dependencies);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (prefetcher != null) {
|
||||||
|
prefetcher.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the latest versions of multiple dependencies, returning them
|
||||||
|
* in the same order as the provided dependencies.
|
||||||
|
*
|
||||||
|
* @param dependencies the dependencies to resolve the latest versions of
|
||||||
|
* @return the latest versions in the order of the provided dependencies
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public List<Version> resolveLatestVersions(List<Dependency> dependencies) {
|
||||||
|
var resolutions = new ArrayList<Supplier<Version>>(dependencies.size());
|
||||||
|
for (var dependency : dependencies) {
|
||||||
|
resolutions.add(() -> new DependencyResolver(resolution_, retriever_, repositories_, dependency).latestVersion());
|
||||||
|
}
|
||||||
|
return ParallelExecution.execute(resolutions, resolution_.resolutionParallelism());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version resolution state that can be cached.
|
||||||
|
*
|
||||||
|
* @return the version resolution state
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public VersionResolution resolution() {
|
||||||
|
return resolution_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the repositories that are used by this resolver.
|
||||||
|
*
|
||||||
|
* @return the resolver's repositories
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public List<Repository> repositories() {
|
||||||
|
return repositories_;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/java/rife/bld/dependencies/ParallelExecution.java
Normal file
64
src/main/java/rife/bld/dependencies/ParallelExecution.java
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes tasks in parallel while preserving the order of their results
|
||||||
|
* and the sequential semantics of failures: the first task that fails in
|
||||||
|
* order will have its exception rethrown.
|
||||||
|
*
|
||||||
|
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
final class ParallelExecution {
|
||||||
|
private ParallelExecution() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> List<T> execute(List<Supplier<T>> tasks, int parallelism) {
|
||||||
|
var result = new ArrayList<T>(tasks.size());
|
||||||
|
|
||||||
|
parallelism = Math.min(tasks.size(), parallelism);
|
||||||
|
if (parallelism <= 1) {
|
||||||
|
for (var task : tasks) {
|
||||||
|
result.add(task.get());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var executor = Executors.newFixedThreadPool(parallelism);
|
||||||
|
try {
|
||||||
|
var futures = new ArrayList<Future<T>>(tasks.size());
|
||||||
|
for (var task : tasks) {
|
||||||
|
futures.add(executor.submit(task::get));
|
||||||
|
}
|
||||||
|
for (var future : futures) {
|
||||||
|
try {
|
||||||
|
result.add(future.get());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IllegalStateException("Parallel execution was interrupted", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (e.getCause() instanceof RuntimeException runtime) {
|
||||||
|
throw runtime;
|
||||||
|
}
|
||||||
|
if (e.getCause() instanceof Error error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/main/java/rife/bld/dependencies/PomPrefetcher.java
Normal file
75
src/main/java/rife/bld/dependencies/PomPrefetcher.java
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speculatively retrieves POMs in parallel so that they are already cached
|
||||||
|
* by the artifact retriever when the sequential dependency resolution
|
||||||
|
* processes them, without influencing the resolution semantics.
|
||||||
|
* <p>
|
||||||
|
* A single instance can be shared by multiple concurrent resolutions, the
|
||||||
|
* POM of each unique dependency will only be prefetched once.
|
||||||
|
*
|
||||||
|
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
class PomPrefetcher {
|
||||||
|
private final VersionResolution resolution_;
|
||||||
|
private final ArtifactRetriever retriever_;
|
||||||
|
private final List<Repository> repositories_;
|
||||||
|
private final ExecutorService executor_;
|
||||||
|
private final Set<Dependency> submitted_ = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a prefetcher when it can be beneficial.
|
||||||
|
*
|
||||||
|
* @return the prefetcher; or {@code null} when the retriever doesn't
|
||||||
|
* cache the retrieved POMs or when the resolution parallelism disables it
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
static PomPrefetcher create(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories) {
|
||||||
|
// prefetching only has benefits when the retrieved POMs are
|
||||||
|
// cached for the sequential resolution that follows
|
||||||
|
if (!retriever.isCaching() || resolution.resolutionParallelism() <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new PomPrefetcher(resolution, retriever, repositories);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PomPrefetcher(VersionResolution resolution, ArtifactRetriever retriever, List<Repository> repositories) {
|
||||||
|
resolution_ = resolution;
|
||||||
|
retriever_ = retriever;
|
||||||
|
repositories_ = repositories;
|
||||||
|
executor_ = Executors.newFixedThreadPool(resolution.resolutionParallelism());
|
||||||
|
}
|
||||||
|
|
||||||
|
void prefetch(Collection<PomDependency> candidates) {
|
||||||
|
for (var candidate : candidates) {
|
||||||
|
var dependency = resolution_.overrideDependency(candidate.convertToDependency());
|
||||||
|
if (submitted_.add(dependency)) {
|
||||||
|
executor_.submit(() -> {
|
||||||
|
try {
|
||||||
|
new DependencyResolver(resolution_, retriever_, repositories_, dependency).getMavenPom(dependency);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// failures are ignored since they will resurface
|
||||||
|
// with the proper context when the dependency is
|
||||||
|
// resolved in order
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() {
|
||||||
|
executor_.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
185
src/main/java/rife/bld/dependencies/TransferOutput.java
Normal file
185
src/main/java/rife/bld/dependencies/TransferOutput.java
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs the status of artifact transfers to the console.
|
||||||
|
* <p>
|
||||||
|
* When the console is an interactive terminal, the transfers that are in
|
||||||
|
* progress are displayed in a live updating block at the bottom of the
|
||||||
|
* output, with each transfer reporting its progress as it happens.
|
||||||
|
* Otherwise, a single line is printed when each transfer finishes, keeping
|
||||||
|
* the output stable for CI logs, pipes and IDE consoles, while still never
|
||||||
|
* interleaving the lines of parallel transfers.
|
||||||
|
*
|
||||||
|
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
abstract class TransferOutput {
|
||||||
|
private static final TransferOutput INSTANCE = create();
|
||||||
|
|
||||||
|
static TransferOutput instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TransferOutput create() {
|
||||||
|
if (System.console() != null && !"dumb".equals(System.getenv("TERM"))) {
|
||||||
|
return new AnsiTransferOutput();
|
||||||
|
}
|
||||||
|
return new PlainTransferOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts tracking a single artifact transfer.
|
||||||
|
*
|
||||||
|
* @param location the location of the artifact that is being transferred
|
||||||
|
* @return the transfer to report progress and completion on
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
abstract Transfer start(String location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single artifact transfer whose progress is being tracked.
|
||||||
|
*
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
abstract static class Transfer {
|
||||||
|
protected final String location_;
|
||||||
|
|
||||||
|
protected Transfer(String location) {
|
||||||
|
location_ = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports transfer progress.
|
||||||
|
*
|
||||||
|
* @param transferred the number of bytes transferred so far
|
||||||
|
* @param total the total number of bytes; or {@code -1} when unknown
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
abstract void progress(long transferred, long total);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports that the transfer finished.
|
||||||
|
*
|
||||||
|
* @param status the final status of the transfer, like
|
||||||
|
* {@code done}, {@code exists} or {@code not found}
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
abstract void finish(String status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String describe(String location, String status) {
|
||||||
|
return "Downloading: " + location + " ... " + status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PlainTransferOutput extends TransferOutput {
|
||||||
|
Transfer start(String location) {
|
||||||
|
return new Transfer(location) {
|
||||||
|
void progress(long transferred, long total) {
|
||||||
|
// no-op, only the finished state is printed
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish(String status) {
|
||||||
|
System.out.println(describe(location_, status));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnsiTransferOutput extends TransferOutput {
|
||||||
|
private static final long REPAINT_INTERVAL_MS = 100;
|
||||||
|
private static final int MAX_LINE_WIDTH = 79;
|
||||||
|
|
||||||
|
private final List<AnsiTransfer> active_ = new ArrayList<>();
|
||||||
|
private int paintedLines_ = 0;
|
||||||
|
private long lastRepaint_ = 0;
|
||||||
|
|
||||||
|
synchronized Transfer start(String location) {
|
||||||
|
var transfer = new AnsiTransfer(location);
|
||||||
|
active_.add(transfer);
|
||||||
|
repaint(null);
|
||||||
|
return transfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void reportProgress() {
|
||||||
|
if (System.currentTimeMillis() - lastRepaint_ >= REPAINT_INTERVAL_MS) {
|
||||||
|
repaint(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void reportFinished(AnsiTransfer transfer, String status) {
|
||||||
|
active_.remove(transfer);
|
||||||
|
repaint(describe(transfer.location_, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaint(String finishedLine) {
|
||||||
|
var output = new StringBuilder();
|
||||||
|
if (paintedLines_ > 0) {
|
||||||
|
// move the cursor to the beginning of the live block
|
||||||
|
// and clear everything below it
|
||||||
|
output.append("\u001B[").append(paintedLines_).append("F\u001B[0J");
|
||||||
|
}
|
||||||
|
if (finishedLine != null) {
|
||||||
|
output.append(finishedLine).append('\n');
|
||||||
|
}
|
||||||
|
for (var transfer : active_) {
|
||||||
|
output.append(transfer.statusLine()).append('\n');
|
||||||
|
}
|
||||||
|
paintedLines_ = active_.size();
|
||||||
|
lastRepaint_ = System.currentTimeMillis();
|
||||||
|
System.out.print(output);
|
||||||
|
System.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AnsiTransfer extends Transfer {
|
||||||
|
private volatile long transferred_ = 0;
|
||||||
|
private volatile long total_ = -1;
|
||||||
|
|
||||||
|
AnsiTransfer(String location) {
|
||||||
|
super(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
void progress(long transferred, long total) {
|
||||||
|
transferred_ = transferred;
|
||||||
|
total_ = total;
|
||||||
|
reportProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish(String status) {
|
||||||
|
reportFinished(this, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
String statusLine() {
|
||||||
|
var filename = location_.substring(location_.lastIndexOf('/') + 1);
|
||||||
|
var line = new StringBuilder("Downloading: ").append(filename).append(" ... ");
|
||||||
|
if (total_ > 0) {
|
||||||
|
line.append(transferred_ * 100 / total_).append('%');
|
||||||
|
} else if (transferred_ > 0) {
|
||||||
|
line.append(humanBytes(transferred_));
|
||||||
|
}
|
||||||
|
// prevent lines from wrapping since that would break
|
||||||
|
// the cursor repositioning of the repaints
|
||||||
|
if (line.length() > MAX_LINE_WIDTH) {
|
||||||
|
line.setLength(MAX_LINE_WIDTH);
|
||||||
|
}
|
||||||
|
return line.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String humanBytes(long bytes) {
|
||||||
|
if (bytes >= 1024 * 1024) {
|
||||||
|
return (bytes / (1024 * 1024)) + " MB";
|
||||||
|
}
|
||||||
|
if (bytes >= 1024) {
|
||||||
|
return (bytes / 1024) + " KB";
|
||||||
|
}
|
||||||
|
return bytes + " B";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import rife.ioc.HierarchicalProperties;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is responsible for managing version overrides for dependencies.
|
* This class is responsible for managing version overrides for dependencies.
|
||||||
|
|
@ -27,6 +28,10 @@ import java.util.Map;
|
||||||
* bld.override-tests=com.uwyn.rife2:bld-tests-badge:1.4.7
|
* bld.override-tests=com.uwyn.rife2:bld-tests-badge:1.4.7
|
||||||
* bld.override-h2=com.h2database:h2:2.2.222
|
* bld.override-h2=com.h2database:h2:2.2.222
|
||||||
* </pre>
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* It also captures other dependency resolution preferences, like the number
|
||||||
|
* of parallel artifact transfers through the "{@code bld.transferParallelism}"
|
||||||
|
* property.
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public class VersionResolution {
|
public class VersionResolution {
|
||||||
|
|
@ -36,7 +41,26 @@ public class VersionResolution {
|
||||||
*/
|
*/
|
||||||
public static final String PROPERTY_OVERRIDE_PREFIX = "bld.override";
|
public static final String PROPERTY_OVERRIDE_PREFIX = "bld.override";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property key that determines how many artifact transfers are
|
||||||
|
* performed in parallel, {@code 1} makes them sequential.
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY_TRANSFER_PARALLELISM = "bld.transferParallelism";
|
||||||
|
private static final int DEFAULT_TRANSFER_PARALLELISM = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property key that determines how many POMs are speculatively
|
||||||
|
* retrieved in parallel during transitive dependency resolution,
|
||||||
|
* {@code 1} disables the parallel retrieval.
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY_RESOLUTION_PARALLELISM = "bld.resolutionParallelism";
|
||||||
|
private static final int DEFAULT_RESOLUTION_PARALLELISM = 6;
|
||||||
|
|
||||||
private final Map<String, Version> versionOverrides_ = new HashMap<>();
|
private final Map<String, Version> versionOverrides_ = new HashMap<>();
|
||||||
|
private final int transferParallelism_;
|
||||||
|
private final int resolutionParallelism_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a dummy {@code VersionResolution} instance that doesn't override anything.
|
* Returns a dummy {@code VersionResolution} instance that doesn't override anything.
|
||||||
|
|
@ -74,6 +98,22 @@ public class VersionResolution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
transferParallelism_ = parseParallelism(properties, PROPERTY_TRANSFER_PARALLELISM, DEFAULT_TRANSFER_PARALLELISM);
|
||||||
|
resolutionParallelism_ = parseParallelism(properties, PROPERTY_RESOLUTION_PARALLELISM, DEFAULT_RESOLUTION_PARALLELISM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseParallelism(HierarchicalProperties properties, String property, int defaultValue) {
|
||||||
|
if (properties != null) {
|
||||||
|
var parallelism = properties.getValueString(property);
|
||||||
|
if (parallelism != null && !parallelism.isBlank()) {
|
||||||
|
try {
|
||||||
|
return Math.max(1, Integer.parseInt(parallelism.trim()));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Logger.getLogger("rife.bld").warning("Unable to parse the " + property + " property as an integer: '" + parallelism + "', using " + defaultValue + " instead");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -122,4 +162,27 @@ public class VersionResolution {
|
||||||
public Map<String, Version> versionOverrides() {
|
public Map<String, Version> versionOverrides() {
|
||||||
return versionOverrides_;
|
return versionOverrides_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of artifact transfers that are performed in parallel,
|
||||||
|
* {@code 1} means transfers are sequential.
|
||||||
|
*
|
||||||
|
* @return the number of parallel artifact transfers
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public int transferParallelism() {
|
||||||
|
return transferParallelism_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of POMs that are speculatively retrieved in parallel
|
||||||
|
* during transitive dependency resolution, {@code 1} means the parallel
|
||||||
|
* retrieval is disabled.
|
||||||
|
*
|
||||||
|
* @return the number of parallel POM retrievals
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public int resolutionParallelism() {
|
||||||
|
return resolutionParallelism_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ public abstract class AbstractCreateOperation<T extends AbstractCreateOperation<
|
||||||
* parents, outputting the location when the operation is {@link #verbose()}.
|
* parents, outputting the location when the operation is {@link #verbose()}.
|
||||||
*
|
*
|
||||||
* @param directory the directory to create
|
* @param directory the directory to create
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
protected void executeCreateDirectory(File directory) {
|
protected void executeCreateDirectory(File directory) {
|
||||||
if (verbose()) {
|
if (verbose()) {
|
||||||
|
|
@ -194,7 +194,7 @@ public abstract class AbstractCreateOperation<T extends AbstractCreateOperation<
|
||||||
*
|
*
|
||||||
* @param content the content to write
|
* @param content the content to write
|
||||||
* @param file the file to write the content into
|
* @param file the file to write the content into
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
protected void executeWriteProjectFile(String content, File file)
|
protected void executeWriteProjectFile(String content, File file)
|
||||||
throws FileUtilsErrorException {
|
throws FileUtilsErrorException {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public abstract class AbstractOperation<T extends AbstractOperation<T>> {
|
||||||
* @param verbose {@code true} if the operation should be verbose;
|
* @param verbose {@code true} if the operation should be verbose;
|
||||||
* {@code false} otherwise
|
* {@code false} otherwise
|
||||||
* @return this operation instance
|
* @return this operation instance
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public T verbose(boolean verbose) {
|
public T verbose(boolean verbose) {
|
||||||
verbose_ = verbose;
|
verbose_ = verbose;
|
||||||
|
|
@ -68,7 +68,7 @@ public abstract class AbstractOperation<T extends AbstractOperation<T>> {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the operation should be verbose;
|
* @return {@code true} if the operation should be verbose;
|
||||||
* {@code false} otherwise
|
* {@code false} otherwise
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public boolean verbose() {
|
public boolean verbose() {
|
||||||
return verbose_;
|
return verbose_;
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ public abstract class AbstractProcessOperation<T extends AbstractProcessOperatio
|
||||||
*
|
*
|
||||||
* @param classpath classpath entries for the operation
|
* @param classpath classpath entries for the operation
|
||||||
* @return this operation instance
|
* @return this operation instance
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public T classpath(File... classpath) {
|
public T classpath(File... classpath) {
|
||||||
|
|
@ -269,7 +269,7 @@ public abstract class AbstractProcessOperation<T extends AbstractProcessOperatio
|
||||||
*
|
*
|
||||||
* @param classpath a list of classpath entries for the operation
|
* @param classpath a list of classpath entries for the operation
|
||||||
* @return this operation instance
|
* @return this operation instance
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public T classpath(Collection<File> classpath) {
|
public T classpath(Collection<File> classpath) {
|
||||||
classpath_.addAll(classpath.stream().map(File::getAbsolutePath).toList());
|
classpath_.addAll(classpath.stream().map(File::getAbsolutePath).toList());
|
||||||
|
|
@ -293,7 +293,7 @@ public abstract class AbstractProcessOperation<T extends AbstractProcessOperatio
|
||||||
*
|
*
|
||||||
* @param modulePath module path entries for the operation
|
* @param modulePath module path entries for the operation
|
||||||
* @return this operation instance
|
* @return this operation instance
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public T modulePath(File... modulePath) {
|
public T modulePath(File... modulePath) {
|
||||||
return modulePath(List.of(modulePath));
|
return modulePath(List.of(modulePath));
|
||||||
|
|
@ -320,7 +320,7 @@ public abstract class AbstractProcessOperation<T extends AbstractProcessOperatio
|
||||||
*
|
*
|
||||||
* @param modulePath a list of module path entries for the operation
|
* @param modulePath a list of module path entries for the operation
|
||||||
* @return this operation instance
|
* @return this operation instance
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public T modulePath(Collection<File> modulePath) {
|
public T modulePath(Collection<File> modulePath) {
|
||||||
modulePath_.addAll(modulePath.stream().map(File::getAbsolutePath).toList());
|
modulePath_.addAll(modulePath.stream().map(File::getAbsolutePath).toList());
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,13 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
private File libTestModulesDirectory_;
|
private File libTestModulesDirectory_;
|
||||||
private boolean downloadSources_ = false;
|
private boolean downloadSources_ = false;
|
||||||
private boolean downloadJavadoc_ = false;
|
private boolean downloadJavadoc_ = false;
|
||||||
|
private final DependencyTransferBatch transfers_ = new DependencyTransferBatch();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the download operation.
|
* Performs the download operation.
|
||||||
|
* <p>
|
||||||
|
* The artifact transfers of all the scopes are collected first and then
|
||||||
|
* performed together in a single parallel batch.
|
||||||
*
|
*
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
|
|
@ -54,11 +58,15 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!silent()) {
|
||||||
|
System.out.println("Analyzing dependencies...");
|
||||||
|
}
|
||||||
executeDownloadCompileDependencies();
|
executeDownloadCompileDependencies();
|
||||||
executeDownloadProvidedDependencies();
|
executeDownloadProvidedDependencies();
|
||||||
executeDownloadRuntimeDependencies();
|
executeDownloadRuntimeDependencies();
|
||||||
executeDownloadStandaloneDependencies();
|
executeDownloadStandaloneDependencies();
|
||||||
executeDownloadTestDependencies();
|
executeDownloadTestDependencies();
|
||||||
|
executeTransferDependencies();
|
||||||
if (!silent()) {
|
if (!silent()) {
|
||||||
System.out.println("Downloading finished successfully.");
|
System.out.println("Downloading finished successfully.");
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +118,11 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the {@link #execute} operation, download the artifacts for a particular dependency scope.
|
* Part of the {@link #execute} operation, collect the artifact transfers
|
||||||
|
* for a particular dependency scope into the {@linkplain #transfers()
|
||||||
|
* transfer batch}.
|
||||||
|
* <p>
|
||||||
|
* The transfers are performed by {@link #executeTransferDependencies}.
|
||||||
*
|
*
|
||||||
* @param destinationDirectory the directory in which the artifacts should be downloaded
|
* @param destinationDirectory the directory in which the artifacts should be downloaded
|
||||||
* @param modulesDirectory the directory in which the modules should be downloaded
|
* @param modulesDirectory the directory in which the modules should be downloaded
|
||||||
|
|
@ -128,7 +140,29 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
additional_classifiers = classifiers.toArray(new String[0]);
|
additional_classifiers = classifiers.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies.transferIntoDirectory(new VersionResolution(properties()), artifactRetriever(), repositories(), destinationDirectory, modulesDirectory, additional_classifiers);
|
transfers().add(dependencies, destinationDirectory, modulesDirectory, additional_classifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of the {@link #execute} operation, perform all the collected
|
||||||
|
* artifact transfers of the {@linkplain #transfers() transfer batch}
|
||||||
|
* together in parallel.
|
||||||
|
*
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
protected void executeTransferDependencies() {
|
||||||
|
transfers().transfer(new VersionResolution(properties()), artifactRetriever(), repositories());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the batch that collects the artifact transfers of this
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* @return the artifact transfer batch of this operation
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
protected DependencyTransferBatch transfers() {
|
||||||
|
return transfers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* of other-module is ALL-UNNAMED
|
* of other-module is ALL-UNNAMED
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions addExports(String... modules) {
|
public JavacOptions addExports(String... modules) {
|
||||||
if (isNotEmpty(modules)) {
|
if (isNotEmpty(modules)) {
|
||||||
|
|
@ -151,7 +151,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* of other-module is ALL-UNNAMED
|
* of other-module is ALL-UNNAMED
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions addExports(Collection<String> modules) {
|
public JavacOptions addExports(Collection<String> modules) {
|
||||||
return addCommaSeparatedOption("--add-exports", modules);
|
return addCommaSeparatedOption("--add-exports", modules);
|
||||||
|
|
@ -161,7 +161,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specifies additional modules to be considered as required by a given module
|
* Specifies additional modules to be considered as required by a given module
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions addReads(String... modules) {
|
public JavacOptions addReads(String... modules) {
|
||||||
if (isNotEmpty(modules)) {
|
if (isNotEmpty(modules)) {
|
||||||
|
|
@ -174,7 +174,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specifies additional modules to be considered as required by a given module
|
* Specifies additional modules to be considered as required by a given module
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions addReads(Collection<String> modules) {
|
public JavacOptions addReads(Collection<String> modules) {
|
||||||
return addCommaSeparatedOption("--add-reads", modules);
|
return addCommaSeparatedOption("--add-reads", modules);
|
||||||
|
|
@ -222,7 +222,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* if none specified or inferred
|
* if none specified or inferred
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions defaultModuleForCreatedFiles(String module) {
|
public JavacOptions defaultModuleForCreatedFiles(String module) {
|
||||||
add("--default-module-for-created-files");
|
add("--default-module-for-created-files");
|
||||||
|
|
@ -417,7 +417,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Overrides or augments a module with classes and resources in JAR files or directories
|
* Overrides or augments a module with classes and resources in JAR files or directories
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions patchModule(String module) {
|
public JavacOptions patchModule(String module) {
|
||||||
add("--patch-module");
|
add("--patch-module");
|
||||||
|
|
@ -441,7 +441,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Provide source compatibility with the specified Java SE release
|
* Provide source compatibility with the specified Java SE release
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions source(int version) {
|
public JavacOptions source(int version) {
|
||||||
add("--source");
|
add("--source");
|
||||||
|
|
@ -453,7 +453,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Generate class files suitable for the specified Java SE release
|
* Generate class files suitable for the specified Java SE release
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions target(int version) {
|
public JavacOptions target(int version) {
|
||||||
add("--target");
|
add("--target");
|
||||||
|
|
@ -645,7 +645,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find input source files for multiple modules
|
* Specify where to find input source files for multiple modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions moduleSourcePathStrings(Collection<String> paths) {
|
public JavacOptions moduleSourcePathStrings(Collection<String> paths) {
|
||||||
return addPathOption("--module-source-path", paths);
|
return addPathOption("--module-source-path", paths);
|
||||||
|
|
@ -655,7 +655,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find input source files for multiple modules
|
* Specify where to find input source files for multiple modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions moduleSourcePathPaths(Collection<Path> paths) {
|
public JavacOptions moduleSourcePathPaths(Collection<Path> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -668,7 +668,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find input source files for multiple modules
|
* Specify where to find input source files for multiple modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions moduleSourcePath(Collection<File> paths) {
|
public JavacOptions moduleSourcePath(Collection<File> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -827,7 +827,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify a module path where to find annotation processors
|
* Specify a module path where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorModulePathPaths(Collection<Path> paths) {
|
public JavacOptions processorModulePathPaths(Collection<Path> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -840,7 +840,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify a module path where to find annotation processors
|
* Specify a module path where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorModulePathStrings(Collection<String> paths) {
|
public JavacOptions processorModulePathStrings(Collection<String> paths) {
|
||||||
return addPathOption("--processor-module-path", paths);
|
return addPathOption("--processor-module-path", paths);
|
||||||
|
|
@ -850,7 +850,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify a module path where to find annotation processors
|
* Specify a module path where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorModulePath(Collection<File> paths) {
|
public JavacOptions processorModulePath(Collection<File> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -902,7 +902,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find annotation processors
|
* Specify where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorPathStrings(Collection<String> paths) {
|
public JavacOptions processorPathStrings(Collection<String> paths) {
|
||||||
return addPathOption("--processor-path", paths);
|
return addPathOption("--processor-path", paths);
|
||||||
|
|
@ -912,7 +912,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find annotation processors
|
* Specify where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorPath(Collection<File> paths) {
|
public JavacOptions processorPath(Collection<File> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -925,7 +925,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Specify where to find annotation processors
|
* Specify where to find annotation processors
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions processorPathPaths(Collection<Path> paths) {
|
public JavacOptions processorPathPaths(Collection<Path> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -1036,7 +1036,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Override location of upgradeable modules
|
* Override location of upgradeable modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions upgradeModulePathStrings(Collection<String> paths) {
|
public JavacOptions upgradeModulePathStrings(Collection<String> paths) {
|
||||||
return addPathOption("--upgrade-module-path", paths);
|
return addPathOption("--upgrade-module-path", paths);
|
||||||
|
|
@ -1046,7 +1046,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Override location of upgradeable modules
|
* Override location of upgradeable modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions upgradeModulePath(Collection<File> paths) {
|
public JavacOptions upgradeModulePath(Collection<File> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -1059,7 +1059,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Override location of upgradeable modules
|
* Override location of upgradeable modules
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions upgradeModulePathPaths(Collection<Path> paths) {
|
public JavacOptions upgradeModulePathPaths(Collection<Path> paths) {
|
||||||
if (isNotEmpty(paths)) {
|
if (isNotEmpty(paths)) {
|
||||||
|
|
@ -1083,7 +1083,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Enable recommended warning categories
|
* Enable recommended warning categories
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions xLint() {
|
public JavacOptions xLint() {
|
||||||
add("-Xlint");
|
add("-Xlint");
|
||||||
|
|
@ -1094,7 +1094,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Warning categories to enable
|
* Warning categories to enable
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions xLint(XLintKey... keys) {
|
public JavacOptions xLint(XLintKey... keys) {
|
||||||
if (isNotEmpty(keys)) {
|
if (isNotEmpty(keys)) {
|
||||||
|
|
@ -1107,7 +1107,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Warning categories to enable
|
* Warning categories to enable
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions xLint(Collection<XLintKey> keys) {
|
public JavacOptions xLint(Collection<XLintKey> keys) {
|
||||||
if (isNotEmpty(keys)) {
|
if (isNotEmpty(keys)) {
|
||||||
|
|
@ -1120,7 +1120,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Warning categories to disable
|
* Warning categories to disable
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions xLintDisable(XLintKey... keys) {
|
public JavacOptions xLintDisable(XLintKey... keys) {
|
||||||
if (isNotEmpty(keys)) {
|
if (isNotEmpty(keys)) {
|
||||||
|
|
@ -1133,7 +1133,7 @@ public class JavacOptions extends ArrayList<String> {
|
||||||
* Warning categories to disable
|
* Warning categories to disable
|
||||||
*
|
*
|
||||||
* @return this list of options
|
* @return this list of options
|
||||||
* @since 2.3.1
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public JavacOptions xLintDisable(Collection<XLintKey> keys) {
|
public JavacOptions xLintDisable(Collection<XLintKey> keys) {
|
||||||
if (isNotEmpty(keys)) {
|
if (isNotEmpty(keys)) {
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,26 @@ public class UpdatesOperation extends AbstractOperation<UpdatesOperation> {
|
||||||
*/
|
*/
|
||||||
public void execute() {
|
public void execute() {
|
||||||
var resolution = new VersionResolution(properties());
|
var resolution = new VersionResolution(properties());
|
||||||
var result = new DependencyScopes();
|
|
||||||
|
var scopes = new ArrayList<Scope>();
|
||||||
|
var dependencies = new ArrayList<Dependency>();
|
||||||
for (var entry : dependencies_.entrySet()) {
|
for (var entry : dependencies_.entrySet()) {
|
||||||
var scope = entry.getKey();
|
|
||||||
for (var dependency : entry.getValue()) {
|
for (var dependency : entry.getValue()) {
|
||||||
var latest = new DependencyResolver(resolution, artifactRetriever(), repositories(), dependency).latestVersion();
|
scopes.add(entry.getKey());
|
||||||
if (latest.compareTo(dependency.version()) > 0) {
|
dependencies.add(dependency);
|
||||||
var latest_dependency = new Dependency(dependency.groupId(), dependency.artifactId(), latest,
|
}
|
||||||
dependency.classifier(), dependency.type());
|
}
|
||||||
result.scope(scope).include(latest_dependency);
|
|
||||||
}
|
var latest_versions = new ParallelDependencyResolver(resolution, artifactRetriever(), repositories()).resolveLatestVersions(dependencies);
|
||||||
|
|
||||||
|
var result = new DependencyScopes();
|
||||||
|
for (var i = 0; i < dependencies.size(); ++i) {
|
||||||
|
var dependency = dependencies.get(i);
|
||||||
|
var latest = latest_versions.get(i);
|
||||||
|
if (latest.compareTo(dependency.version()) > 0) {
|
||||||
|
var latest_dependency = new Dependency(dependency.groupId(), dependency.artifactId(), latest,
|
||||||
|
dependency.classifier(), dependency.type());
|
||||||
|
result.scope(scopes.get(i)).include(latest_dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,16 @@ public class WrapperExtensionResolver {
|
||||||
|
|
||||||
private Set<String> transferExtensionDependencies() {
|
private Set<String> transferExtensionDependencies() {
|
||||||
var filenames = new HashSet<String>();
|
var filenames = new HashSet<String>();
|
||||||
var dependencies = new DependencySet();
|
var roots = new ArrayList<Dependency>();
|
||||||
for (var d : dependencies_) {
|
for (var d : dependencies_) {
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
dependencies.addAll(new DependencyResolver(resolution_, retriever_, repositories_, d).getAllDependencies(Scope.compile, Scope.runtime));
|
roots.add(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!roots.isEmpty()) {
|
||||||
|
System.out.println("Analyzing bld extension dependencies...");
|
||||||
|
}
|
||||||
|
var dependencies = new ParallelDependencyResolver(resolution_, retriever_, repositories_).resolveAllDependencies(roots, Scope.compile, Scope.runtime);
|
||||||
if (!dependencies.isEmpty()) {
|
if (!dependencies.isEmpty()) {
|
||||||
ensurePrintedHeader();
|
ensurePrintedHeader();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
2.3.1-SNAPSHOT
|
2.4.0-SNAPSHOT
|
||||||
|
|
@ -4,15 +4,20 @@
|
||||||
*/
|
*/
|
||||||
package rife.bld.dependencies;
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import rife.ioc.HierarchicalProperties;
|
import rife.ioc.HierarchicalProperties;
|
||||||
import rife.tools.FileUtils;
|
import rife.tools.FileUtils;
|
||||||
import rife.tools.StringUtils;
|
import rife.tools.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static rife.bld.dependencies.Dependency.CLASSIFIER_JAVADOC;
|
import static rife.bld.dependencies.Dependency.CLASSIFIER_JAVADOC;
|
||||||
|
|
@ -2341,4 +2346,98 @@ public class TestDependencyResolver {
|
||||||
FileUtils.deleteDirectory(tmp2);
|
FileUtils.deleteDirectory(tmp2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetAllDependenciesParallelPomPrefetching() throws Exception {
|
||||||
|
var max_concurrent_retrievals = new AtomicInteger();
|
||||||
|
var server = createPomServer(max_concurrent_retrievals);
|
||||||
|
server.start();
|
||||||
|
try {
|
||||||
|
var repositories = List.of(new Repository("http://localhost:" + server.getAddress().getPort() + "/"));
|
||||||
|
var root = new Dependency("com.example", "root", new VersionNumber(1, 0, 0));
|
||||||
|
|
||||||
|
// the caching retriever enables parallel POM prefetching
|
||||||
|
var resolver = new DependencyResolver(VersionResolution.dummy(), ArtifactRetriever.cachingInstance(), repositories, root);
|
||||||
|
assertEquals(StringUtils.convertLineSeparator("""
|
||||||
|
com.example:root:1.0.0
|
||||||
|
com.example:child1:1.0.0
|
||||||
|
com.example:child2:1.0.0
|
||||||
|
com.example:child3:1.0.0
|
||||||
|
com.example:child4:1.0.0
|
||||||
|
com.example:child5:1.0.0
|
||||||
|
com.example:child6:1.0.0
|
||||||
|
com.example:shared:1.0.0"""), StringUtils.join(resolver.getAllDependencies(compile), System.lineSeparator()));
|
||||||
|
assertTrue(max_concurrent_retrievals.get() > 1, "expected concurrent POM retrievals, max was " + max_concurrent_retrievals.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetAllDependenciesSequentialWithoutCachingRetriever() throws Exception {
|
||||||
|
var max_concurrent_retrievals = new AtomicInteger();
|
||||||
|
var server = createPomServer(max_concurrent_retrievals);
|
||||||
|
server.start();
|
||||||
|
try {
|
||||||
|
var repositories = List.of(new Repository("http://localhost:" + server.getAddress().getPort() + "/"));
|
||||||
|
var root = new Dependency("com.example", "root", new VersionNumber(1, 0, 0));
|
||||||
|
|
||||||
|
// the uncached retriever disables prefetching, retrievals stay sequential
|
||||||
|
var resolver = new DependencyResolver(VersionResolution.dummy(), ArtifactRetriever.instance(), repositories, root);
|
||||||
|
assertEquals(8, resolver.getAllDependencies(compile).size());
|
||||||
|
assertEquals(1, max_concurrent_retrievals.get(), "expected sequential POM retrievals, max was " + max_concurrent_retrievals.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpServer createPomServer(AtomicInteger maxConcurrentRetrievals)
|
||||||
|
throws IOException {
|
||||||
|
var graph = new HashMap<String, List<String>>();
|
||||||
|
graph.put("root", List.of("child1", "child2", "child3", "child4", "child5", "child6"));
|
||||||
|
graph.put("child1", List.of("shared"));
|
||||||
|
|
||||||
|
var active_retrievals = new AtomicInteger();
|
||||||
|
var server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
|
||||||
|
server.createContext("/", exchange -> {
|
||||||
|
var active = active_retrievals.incrementAndGet();
|
||||||
|
maxConcurrentRetrievals.accumulateAndGet(active, Math::max);
|
||||||
|
try {
|
||||||
|
// delay the response so that parallel retrievals overlap
|
||||||
|
Thread.sleep(100);
|
||||||
|
|
||||||
|
// serve the POM of the artifact in the request path
|
||||||
|
var segments = exchange.getRequestURI().getPath().split("/");
|
||||||
|
var artifact = segments[segments.length - 3];
|
||||||
|
var body = buildPom(artifact, graph.getOrDefault(artifact, List.of())).getBytes();
|
||||||
|
exchange.sendResponseHeaders(200, body.length);
|
||||||
|
exchange.getResponseBody().write(body);
|
||||||
|
exchange.close();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
active_retrievals.decrementAndGet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.setExecutor(Executors.newCachedThreadPool());
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildPom(String artifact, List<String> children) {
|
||||||
|
var dependencies = new StringBuilder();
|
||||||
|
for (var child : children) {
|
||||||
|
dependencies.append("<dependency><groupId>com.example</groupId><artifactId>")
|
||||||
|
.append(child)
|
||||||
|
.append("</artifactId><version>1.0.0</version></dependency>");
|
||||||
|
}
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>%s</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<dependencies>%s</dependencies>
|
||||||
|
</project>""".formatted(artifact, dependencies);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,16 @@
|
||||||
package rife.bld.dependencies;
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import rife.ioc.HierarchicalProperties;
|
||||||
|
import rife.tools.FileUtils;
|
||||||
import rife.tools.StringUtils;
|
import rife.tools.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static rife.bld.dependencies.TransferTestHelper.*;
|
||||||
import static rife.bld.dependencies.RepositoryTestHelper.getNextRepository;
|
import static rife.bld.dependencies.RepositoryTestHelper.getNextRepository;
|
||||||
import static rife.bld.dependencies.Scope.compile;
|
import static rife.bld.dependencies.Scope.compile;
|
||||||
import static rife.bld.dependencies.Scope.runtime;
|
import static rife.bld.dependencies.Scope.runtime;
|
||||||
|
|
@ -373,4 +378,50 @@ public class TestDependencySet {
|
||||||
└─ org.json:json:20250107
|
└─ org.json:json:20250107
|
||||||
"""), dependencies.generateTransitiveDependencyTree(VersionResolution.dummy(), ArtifactRetriever.instance(), RepositoryTestHelper.getNextRepositories(), compile, runtime));
|
"""), dependencies.generateTransitiveDependencyTree(VersionResolution.dummy(), ArtifactRetriever.instance(), RepositoryTestHelper.getNextRepositories(), compile, runtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTransferIntoDirectoryParallel() throws Exception {
|
||||||
|
var max_concurrent_transfers = new AtomicInteger();
|
||||||
|
var server = createTransferServer(max_concurrent_transfers);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("transfers").toFile();
|
||||||
|
try {
|
||||||
|
var dependencies = createTransferDependencies(1, 6);
|
||||||
|
var repositories = List.of(transferRepository(server));
|
||||||
|
|
||||||
|
var artifacts = dependencies.transferIntoDirectory(new VersionResolution(null), ArtifactRetriever.instance(), repositories, tmp, tmp);
|
||||||
|
|
||||||
|
assertTransferredArtifacts(dependencies, artifacts, tmp);
|
||||||
|
assertTrue(max_concurrent_transfers.get() > 1, "expected concurrent transfers, max was " + max_concurrent_transfers.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTransferIntoDirectorySequential() throws Exception {
|
||||||
|
var max_concurrent_transfers = new AtomicInteger();
|
||||||
|
var server = createTransferServer(max_concurrent_transfers);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("transfers").toFile();
|
||||||
|
try {
|
||||||
|
var properties = new HierarchicalProperties();
|
||||||
|
properties.put(VersionResolution.PROPERTY_TRANSFER_PARALLELISM, "1");
|
||||||
|
var resolution = new VersionResolution(properties);
|
||||||
|
assertEquals(1, resolution.transferParallelism());
|
||||||
|
|
||||||
|
var dependencies = createTransferDependencies(1, 6);
|
||||||
|
var repositories = List.of(transferRepository(server));
|
||||||
|
|
||||||
|
var artifacts = dependencies.transferIntoDirectory(resolution, ArtifactRetriever.instance(), repositories, tmp, tmp);
|
||||||
|
|
||||||
|
assertTransferredArtifacts(dependencies, artifacts, tmp);
|
||||||
|
assertEquals(1, max_concurrent_transfers.get(), "expected sequential transfers, max was " + max_concurrent_transfers.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import rife.bld.dependencies.exceptions.DependencyTransferException;
|
||||||
|
import rife.tools.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static rife.bld.dependencies.TransferTestHelper.*;
|
||||||
|
|
||||||
|
public class TestDependencyTransferBatch {
|
||||||
|
@Test
|
||||||
|
void testTransferAcrossSets() throws Exception {
|
||||||
|
var max_concurrent_transfers = new AtomicInteger();
|
||||||
|
var server = createTransferServer(max_concurrent_transfers);
|
||||||
|
server.start();
|
||||||
|
var tmp1 = Files.createTempDirectory("transfers1").toFile();
|
||||||
|
var tmp2 = Files.createTempDirectory("transfers2").toFile();
|
||||||
|
try {
|
||||||
|
var set1 = createTransferDependencies(1, 3);
|
||||||
|
var set2 = createTransferDependencies(4, 6);
|
||||||
|
var repositories = List.of(transferRepository(server));
|
||||||
|
|
||||||
|
var batch = new DependencyTransferBatch();
|
||||||
|
batch.add(set1, tmp1, tmp1)
|
||||||
|
.add(set2, tmp2, tmp2)
|
||||||
|
// identical transfers into the same directory are only performed once
|
||||||
|
.add(set1, tmp1, tmp1);
|
||||||
|
var artifacts = batch.transfer(new VersionResolution(null), ArtifactRetriever.instance(), repositories);
|
||||||
|
|
||||||
|
assertEquals(6, artifacts.size());
|
||||||
|
assertTransferredArtifacts(set1, artifacts.subList(0, 3), tmp1);
|
||||||
|
assertTransferredArtifacts(set2, artifacts.subList(3, 6), tmp2);
|
||||||
|
assertTrue(max_concurrent_transfers.get() > 1, "expected concurrent transfers, max was " + max_concurrent_transfers.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp1);
|
||||||
|
FileUtils.deleteDirectory(tmp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTransferEmptiesTheBatch() throws Exception {
|
||||||
|
var server = createTransferServer(new AtomicInteger());
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("transfers").toFile();
|
||||||
|
try {
|
||||||
|
var repositories = List.of(transferRepository(server));
|
||||||
|
var resolution = new VersionResolution(null);
|
||||||
|
var batch = new DependencyTransferBatch();
|
||||||
|
|
||||||
|
var set1 = createTransferDependencies(1, 3);
|
||||||
|
batch.add(set1, tmp, tmp);
|
||||||
|
assertTransferredArtifacts(set1, batch.transfer(resolution, ArtifactRetriever.instance(), repositories), tmp);
|
||||||
|
|
||||||
|
// the batch was emptied, nothing is transferred again
|
||||||
|
assertTrue(batch.transfer(resolution, ArtifactRetriever.instance(), repositories).isEmpty());
|
||||||
|
|
||||||
|
// the same instance can collect and transfer again,
|
||||||
|
// including targets that were transferred before
|
||||||
|
var set2 = createTransferDependencies(3, 5);
|
||||||
|
batch.add(set2, tmp, tmp);
|
||||||
|
assertTransferredArtifacts(set2, batch.transfer(resolution, ArtifactRetriever.instance(), repositories), tmp);
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTransferModularJarsIntoModulesDirectory() throws Exception {
|
||||||
|
var server = createTransferServer(new AtomicInteger());
|
||||||
|
server.start();
|
||||||
|
var artifacts_dir = Files.createTempDirectory("artifacts").toFile();
|
||||||
|
var modules_dir = Files.createTempDirectory("modules").toFile();
|
||||||
|
try {
|
||||||
|
var dependencies = new DependencySet()
|
||||||
|
.include(new Dependency("com.example", "artifact1", new VersionNumber(1, 0, 0)))
|
||||||
|
.include(new Dependency("com.example", "module1", new VersionNumber(1, 0, 0), "", Dependency.TYPE_MODULAR_JAR));
|
||||||
|
|
||||||
|
var artifacts = new DependencyTransferBatch()
|
||||||
|
.add(dependencies, artifacts_dir, modules_dir)
|
||||||
|
.transfer(new VersionResolution(null), ArtifactRetriever.instance(), List.of(transferRepository(server)));
|
||||||
|
|
||||||
|
assertEquals(2, artifacts.size());
|
||||||
|
assertTrue(new File(artifacts_dir, "artifact1-1.0.0.jar").exists());
|
||||||
|
assertTrue(new File(modules_dir, "module1-1.0.0.jar").exists());
|
||||||
|
assertFalse(new File(artifacts_dir, "module1-1.0.0.jar").exists());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(artifacts_dir);
|
||||||
|
FileUtils.deleteDirectory(modules_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTransferClassifiers() throws Exception {
|
||||||
|
var server = createTransferServer(new AtomicInteger());
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("transfers").toFile();
|
||||||
|
try {
|
||||||
|
var dependencies = createTransferDependencies(1, 2);
|
||||||
|
var artifacts = new DependencyTransferBatch()
|
||||||
|
.add(dependencies, tmp, tmp, "sources")
|
||||||
|
.transfer(new VersionResolution(null), ArtifactRetriever.instance(), List.of(transferRepository(server)));
|
||||||
|
|
||||||
|
assertEquals(4, artifacts.size());
|
||||||
|
for (var i = 1; i <= 2; i++) {
|
||||||
|
assertTrue(new File(tmp, "artifact" + i + "-1.0.0.jar").exists());
|
||||||
|
assertTrue(new File(tmp, "artifact" + i + "-1.0.0-sources.jar").exists());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMissingDirectories() {
|
||||||
|
var regular = new DependencySet()
|
||||||
|
.include(new Dependency("com.example", "artifact1", new VersionNumber(1, 0, 0)));
|
||||||
|
var modular = new DependencySet()
|
||||||
|
.include(new Dependency("com.example", "module1", new VersionNumber(1, 0, 0), "", Dependency.TYPE_MODULAR_JAR));
|
||||||
|
|
||||||
|
assertThrows(DependencyTransferException.class, () -> new DependencyTransferBatch().add(regular, null, null));
|
||||||
|
assertThrows(DependencyTransferException.class, () -> new DependencyTransferBatch().add(modular, Files.createTempDirectory("artifacts").toFile(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyBatch() {
|
||||||
|
var artifacts = new DependencyTransferBatch()
|
||||||
|
.transfer(new VersionResolution(null), ArtifactRetriever.instance(), List.of());
|
||||||
|
assertTrue(artifacts.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/test/java/rife/bld/dependencies/TransferTestHelper.java
Normal file
71
src/test/java/rife/bld/dependencies/TransferTestHelper.java
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com)
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||||
|
*/
|
||||||
|
package rife.bld.dependencies;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a local artifact server and dependency fixtures for testing
|
||||||
|
* transfers without relying on remote repositories.
|
||||||
|
*/
|
||||||
|
abstract class TransferTestHelper {
|
||||||
|
static HttpServer createTransferServer(AtomicInteger maxConcurrentTransfers)
|
||||||
|
throws IOException {
|
||||||
|
var active_transfers = new AtomicInteger();
|
||||||
|
var server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
|
||||||
|
server.createContext("/", exchange -> {
|
||||||
|
var active = active_transfers.incrementAndGet();
|
||||||
|
maxConcurrentTransfers.accumulateAndGet(active, Math::max);
|
||||||
|
try {
|
||||||
|
// delay the response so that parallel transfers overlap
|
||||||
|
Thread.sleep(200);
|
||||||
|
|
||||||
|
var body = exchange.getRequestURI().getPath().getBytes();
|
||||||
|
exchange.sendResponseHeaders(200, body.length);
|
||||||
|
exchange.getResponseBody().write(body);
|
||||||
|
exchange.close();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
active_transfers.decrementAndGet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.setExecutor(Executors.newCachedThreadPool());
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Repository transferRepository(HttpServer server) {
|
||||||
|
return new Repository("http://localhost:" + server.getAddress().getPort() + "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
static DependencySet createTransferDependencies(int from, int to) {
|
||||||
|
var dependencies = new DependencySet();
|
||||||
|
for (var i = from; i <= to; i++) {
|
||||||
|
dependencies.include(new Dependency("com.example", "artifact" + i, new VersionNumber(1, 0, 0)));
|
||||||
|
}
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertTransferredArtifacts(DependencySet dependencies, List<RepositoryArtifact> artifacts, File directory) {
|
||||||
|
assertEquals(dependencies.size(), artifacts.size());
|
||||||
|
var index = 0;
|
||||||
|
for (var dependency : dependencies) {
|
||||||
|
var filename = dependency.artifactId() + "-" + dependency.version() + ".jar";
|
||||||
|
assertTrue(artifacts.get(index).location().endsWith(filename), "expected artifact " + filename + " at index " + index);
|
||||||
|
assertTrue(new File(directory, filename).exists(), "expected file " + filename + " to be transferred");
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue