mirror of
https://github.com/rife2/bld
synced 2026-08-05 07:07:48 +02:00
Compare commits
2 commits
2177fcb57c
...
dffe1cf6df
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dffe1cf6df | ||
|
|
3ced706fa7 |
|
|
@ -1669,6 +1669,9 @@ public class BaseProject extends BuildExecutor {
|
||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Map<String, List<File>> classpathJarsMemo_ = new HashMap<>();
|
||||||
|
private DependencySet resolvedExtensions_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the version resolution for a dependency scope, applying the
|
* Creates the version resolution for a dependency scope, applying the
|
||||||
* version overrides from the properties and the BOMs that are
|
* version overrides from the properties and the BOMs that are
|
||||||
|
|
@ -1708,16 +1711,39 @@ public class BaseProject extends BuildExecutor {
|
||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public List<File> dependencyClasspathJars(Scope scope, String groupId, String artifactId) {
|
public List<File> dependencyClasspathJars(Scope scope, String groupId, String artifactId) {
|
||||||
|
var coordinate = groupId + ":" + artifactId;
|
||||||
|
var memo_key = "dependency." + scope + "." + coordinate;
|
||||||
|
var memoized = classpathJarsMemo_.get(memo_key);
|
||||||
|
if (memoized != null) {
|
||||||
|
return memoized;
|
||||||
|
}
|
||||||
|
|
||||||
var declared = dependencies().scope(scope).get(new Dependency(groupId, artifactId));
|
var declared = dependencies().scope(scope).get(new Dependency(groupId, artifactId));
|
||||||
if (declared == null) {
|
if (declared == null) {
|
||||||
throw new IllegalArgumentException("Dependency '" + groupId + ":" + artifactId + "' isn't declared in the " + scope + " scope.");
|
throw new IllegalArgumentException("Dependency '" + coordinate + "' isn't declared in the " + scope + " scope.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = scopeLibDirectory(scope);
|
||||||
|
var cache = new BldCache(libBldDirectory(), new VersionResolution(properties()));
|
||||||
|
cache.cacheDependenciesHash(repositories(), dependencies());
|
||||||
|
if (cache.isDependenciesHashValid()) {
|
||||||
|
var cached = cache.getCachedDependencyClasspath(scope, coordinate);
|
||||||
|
if (cached != null) {
|
||||||
|
var jars = cached.stream().map(name -> new File(dir, name)).toList();
|
||||||
|
classpathJarsMemo_.put(memo_key, jars);
|
||||||
|
return jars;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolver = new DependencyResolver(versionResolution(scope), artifactRetriever(), repositories(), declared);
|
var resolver = new DependencyResolver(versionResolution(scope), artifactRetriever(), repositories(), declared);
|
||||||
var dir = scopeLibDirectory(scope);
|
var jars = resolver.getAllDependencies(Scope.compile, Scope.runtime).stream()
|
||||||
return resolver.getAllDependencies(Scope.compile, Scope.runtime).stream()
|
|
||||||
.map(dependency -> new File(dir, dependency.toFileName()))
|
.map(dependency -> new File(dir, dependency.toFileName()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
cache.cacheDependencyClasspath(scope, coordinate, jars.stream().map(File::getName).toList());
|
||||||
|
cache.writeCache();
|
||||||
|
classpathJarsMemo_.put(memo_key, jars);
|
||||||
|
return jars;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File scopeLibDirectory(Scope scope) {
|
private File scopeLibDirectory(Scope scope) {
|
||||||
|
|
@ -1751,6 +1777,13 @@ public class BaseProject extends BuildExecutor {
|
||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public List<File> extensionClasspathJars(String groupId, String artifactId) {
|
public List<File> extensionClasspathJars(String groupId, String artifactId) {
|
||||||
|
var coordinate = groupId + ":" + artifactId;
|
||||||
|
var memo_key = "extension." + coordinate;
|
||||||
|
var memoized = classpathJarsMemo_.get(memo_key);
|
||||||
|
if (memoized != null) {
|
||||||
|
return memoized;
|
||||||
|
}
|
||||||
|
|
||||||
var wrapper = new Wrapper();
|
var wrapper = new Wrapper();
|
||||||
wrapper.currentDir(workDirectory());
|
wrapper.currentDir(workDirectory());
|
||||||
try {
|
try {
|
||||||
|
|
@ -1762,25 +1795,45 @@ public class BaseProject extends BuildExecutor {
|
||||||
var properties = new HierarchicalProperties().parent(BuildExecutor.setupProperties(workDirectory()));
|
var properties = new HierarchicalProperties().parent(BuildExecutor.setupProperties(workDirectory()));
|
||||||
properties.putAll(wrapper.wrapperProperties());
|
properties.putAll(wrapper.wrapperProperties());
|
||||||
|
|
||||||
|
var resolution = new VersionResolution(properties);
|
||||||
|
var cache = new BldCache(libBldDirectory(), resolution);
|
||||||
|
cache.cacheExtensionsHash(wrapper.repositories(), wrapper.extensions());
|
||||||
|
if (cache.isExtensionsHashValid()) {
|
||||||
|
var cached = cache.getCachedExtensionClasspath(coordinate);
|
||||||
|
if (cached != null) {
|
||||||
|
var jars = cached.stream().map(name -> new File(libBldDirectory(), name)).toList();
|
||||||
|
classpathJarsMemo_.put(memo_key, jars);
|
||||||
|
return jars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var repositories = new ArrayList<Repository>();
|
var repositories = new ArrayList<Repository>();
|
||||||
for (var repository : wrapper.repositories()) {
|
for (var repository : wrapper.repositories()) {
|
||||||
repositories.add(Repository.resolveRepository(properties, repository));
|
repositories.add(Repository.resolveRepository(properties, repository));
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolution = new VersionResolution(properties);
|
// the resolved set of all the extensions is reused across
|
||||||
var extensions = wrapper.extensions().stream().map(Dependency::parse).toList();
|
// lookups of different dependencies in the same build
|
||||||
var resolved = new ParallelDependencyResolver(resolution, artifactRetriever(), repositories)
|
if (resolvedExtensions_ == null) {
|
||||||
.resolveAllDependencies(extensions, Scope.compile, Scope.runtime);
|
var extensions = wrapper.extensions().stream().map(Dependency::parse).toList();
|
||||||
|
resolvedExtensions_ = new ParallelDependencyResolver(resolution, artifactRetriever(), repositories)
|
||||||
|
.resolveAllDependencies(extensions, Scope.compile, Scope.runtime);
|
||||||
|
}
|
||||||
|
|
||||||
var dependency = resolved.get(new Dependency(groupId, artifactId));
|
var dependency = resolvedExtensions_.get(new Dependency(groupId, artifactId));
|
||||||
if (dependency == null) {
|
if (dependency == null) {
|
||||||
throw new IllegalArgumentException("Dependency '" + groupId + ":" + artifactId + "' isn't part of the extensions of this project.");
|
throw new IllegalArgumentException("Dependency '" + coordinate + "' isn't part of the extensions of this project.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolver = new DependencyResolver(resolution, artifactRetriever(), repositories, dependency);
|
var resolver = new DependencyResolver(resolution, artifactRetriever(), repositories, dependency);
|
||||||
return resolver.getAllDependencies(Scope.compile, Scope.runtime).stream()
|
var jars = resolver.getAllDependencies(Scope.compile, Scope.runtime).stream()
|
||||||
.map(d -> new File(libBldDirectory(), d.toFileName()))
|
.map(d -> new File(libBldDirectory(), d.toFileName()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
cache.cacheExtensionClasspath(coordinate, jars.stream().map(File::getName).toList());
|
||||||
|
cache.writeCache();
|
||||||
|
classpathJarsMemo_.put(memo_key, jars);
|
||||||
|
return jars;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package rife.bld;
|
package rife.bld;
|
||||||
|
|
||||||
import rife.bld.dependencies.DependencyScopes;
|
import rife.bld.dependencies.DependencyScopes;
|
||||||
|
import rife.bld.dependencies.Scope;
|
||||||
import rife.bld.dependencies.Repository;
|
import rife.bld.dependencies.Repository;
|
||||||
import rife.bld.dependencies.VersionResolution;
|
import rife.bld.dependencies.VersionResolution;
|
||||||
import rife.bld.wrapper.Wrapper;
|
import rife.bld.wrapper.Wrapper;
|
||||||
|
|
@ -40,12 +41,15 @@ public class BldCache {
|
||||||
private static final String WRAPPER_PROPERTIES_HASH = Wrapper.WRAPPER_PROPERTIES + PROPERTY_SUFFIX_HASH;
|
private static final String WRAPPER_PROPERTIES_HASH = Wrapper.WRAPPER_PROPERTIES + PROPERTY_SUFFIX_HASH;
|
||||||
private static final String BLD_BUILD_HASH = "bld-build" + PROPERTY_SUFFIX_HASH;
|
private static final String BLD_BUILD_HASH = "bld-build" + PROPERTY_SUFFIX_HASH;
|
||||||
|
|
||||||
|
private static final String PROPERTY_SUFFIX_CLASSPATH = ".classpath.";
|
||||||
|
|
||||||
private static final String PROPERTY_EXTENSIONS_PREFIX = "bld.extensions";
|
private static final String PROPERTY_EXTENSIONS_PREFIX = "bld.extensions";
|
||||||
private static final String PROPERTY_EXTENSIONS_HASH = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_HASH;
|
private static final String PROPERTY_EXTENSIONS_HASH = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_HASH;
|
||||||
private static final String PROPERTY_EXTENSIONS_LOCAL = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_LOCAL;
|
private static final String PROPERTY_EXTENSIONS_LOCAL = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_LOCAL;
|
||||||
private static final String PROPERTY_EXTENSIONS_DOWNLOAD_SOURCES = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DOWNLOAD_SOURCES;
|
private static final String PROPERTY_EXTENSIONS_DOWNLOAD_SOURCES = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DOWNLOAD_SOURCES;
|
||||||
private static final String PROPERTY_EXTENSIONS_DOWNLOAD_JAVADOC = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DOWNLOAD_JAVADOC;
|
private static final String PROPERTY_EXTENSIONS_DOWNLOAD_JAVADOC = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DOWNLOAD_JAVADOC;
|
||||||
private static final String PROPERTY_EXTENSIONS_DEPENDENCY_TREE = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
private static final String PROPERTY_EXTENSIONS_DEPENDENCY_TREE = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
||||||
|
private static final String PROPERTY_EXTENSIONS_CLASSPATH_PREFIX = PROPERTY_EXTENSIONS_PREFIX + PROPERTY_SUFFIX_CLASSPATH;
|
||||||
|
|
||||||
private static final String PROPERTY_DEPENDENCIES_PREFIX = "bld.dependencies";
|
private static final String PROPERTY_DEPENDENCIES_PREFIX = "bld.dependencies";
|
||||||
private static final String PROPERTY_DEPENDENCIES_HASH = PROPERTY_DEPENDENCIES_PREFIX + PROPERTY_SUFFIX_HASH;
|
private static final String PROPERTY_DEPENDENCIES_HASH = PROPERTY_DEPENDENCIES_PREFIX + PROPERTY_SUFFIX_HASH;
|
||||||
|
|
@ -55,6 +59,7 @@ public class BldCache {
|
||||||
private static final String PROPERTY_DEPENDENCIES_PROVIDED_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".provided" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
private static final String PROPERTY_DEPENDENCIES_PROVIDED_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".provided" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
||||||
private static final String PROPERTY_DEPENDENCIES_RUNTIME_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".runtime" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
private static final String PROPERTY_DEPENDENCIES_RUNTIME_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".runtime" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
||||||
private static final String PROPERTY_DEPENDENCIES_TEST_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".test" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
private static final String PROPERTY_DEPENDENCIES_TEST_DEPENDENCY_TREE = PROPERTY_DEPENDENCIES_PREFIX + ".test" + PROPERTY_SUFFIX_DEPENDENCY_TREE;
|
||||||
|
private static final String PROPERTY_DEPENDENCIES_CLASSPATH_PREFIX = PROPERTY_DEPENDENCIES_PREFIX + PROPERTY_SUFFIX_CLASSPATH;
|
||||||
|
|
||||||
private final File cacheDir_;
|
private final File cacheDir_;
|
||||||
private final VersionResolution resolution_;
|
private final VersionResolution resolution_;
|
||||||
|
|
@ -64,6 +69,8 @@ public class BldCache {
|
||||||
private List<File> extensionsLocalArtifacts_;
|
private List<File> extensionsLocalArtifacts_;
|
||||||
private String extensionsDependencyTree_;
|
private String extensionsDependencyTree_;
|
||||||
private String dependenciesHash_;
|
private String dependenciesHash_;
|
||||||
|
private final java.util.Map<String, String> extensionClasspaths_ = new java.util.LinkedHashMap<>();
|
||||||
|
private final java.util.Map<String, String> dependencyClasspaths_ = new java.util.LinkedHashMap<>();
|
||||||
private Boolean dependenciesDownloadSources_;
|
private Boolean dependenciesDownloadSources_;
|
||||||
private Boolean dependenciesDownloadJavadocs_;
|
private Boolean dependenciesDownloadJavadocs_;
|
||||||
private String dependenciesCompileDependencyTree_;
|
private String dependenciesCompileDependencyTree_;
|
||||||
|
|
@ -477,6 +484,76 @@ public class BldCache {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resolved classpath jar names of a single extension
|
||||||
|
* dependency, to store with {@link #writeCache()}.
|
||||||
|
* <p>
|
||||||
|
* The names are only meaningful while the extensions hash is valid,
|
||||||
|
* check {@link #isExtensionsHashValid()} before using them.
|
||||||
|
*
|
||||||
|
* @param coordinate the {@code groupId:artifactId} of the dependency
|
||||||
|
* @param jarNames the jar file names of its transitive classpath
|
||||||
|
* @see #getCachedExtensionClasspath
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public void cacheExtensionClasspath(String coordinate, List<String> jarNames) {
|
||||||
|
extensionClasspaths_.put(coordinate, String.join("\n", jarNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the previously stored classpath jar names of a single
|
||||||
|
* extension dependency.
|
||||||
|
*
|
||||||
|
* @param coordinate the {@code groupId:artifactId} of the dependency
|
||||||
|
* @return the stored jar file names; or {@code null} when nothing was
|
||||||
|
* stored for the coordinate
|
||||||
|
* @since 2.4.0
|
||||||
|
* @see #cacheExtensionClasspath
|
||||||
|
*/
|
||||||
|
public List<String> getCachedExtensionClasspath(String coordinate) {
|
||||||
|
var stored = hashProperties().getProperty(PROPERTY_EXTENSIONS_CLASSPATH_PREFIX + coordinate);
|
||||||
|
if (stored == null || stored.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return List.of(stored.split("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resolved classpath jar names of a single dependency in a
|
||||||
|
* scope, to store with {@link #writeCache()}.
|
||||||
|
* <p>
|
||||||
|
* The names are only meaningful while the dependencies hash is valid,
|
||||||
|
* check {@link #isDependenciesHashValid()} before using them.
|
||||||
|
*
|
||||||
|
* @param scope the scope the dependency is declared in
|
||||||
|
* @param coordinate the {@code groupId:artifactId} of the dependency
|
||||||
|
* @param jarNames the jar file names of its transitive classpath
|
||||||
|
* @since 2.4.0
|
||||||
|
* @see #getCachedDependencyClasspath
|
||||||
|
*/
|
||||||
|
public void cacheDependencyClasspath(Scope scope, String coordinate, List<String> jarNames) {
|
||||||
|
dependencyClasspaths_.put(scope + "." + coordinate, String.join("\n", jarNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the previously stored classpath jar names of a single
|
||||||
|
* dependency in a scope.
|
||||||
|
*
|
||||||
|
* @param scope the scope the dependency is declared in
|
||||||
|
* @param coordinate the {@code groupId:artifactId} of the dependency
|
||||||
|
* @return the stored jar file names; or {@code null} when nothing was
|
||||||
|
* stored for the coordinate
|
||||||
|
* @since 2.4.0
|
||||||
|
* @see #cacheDependencyClasspath
|
||||||
|
*/
|
||||||
|
public List<String> getCachedDependencyClasspath(Scope scope, String coordinate) {
|
||||||
|
var stored = hashProperties().getProperty(PROPERTY_DEPENDENCIES_CLASSPATH_PREFIX + scope + "." + coordinate);
|
||||||
|
if (stored == null || stored.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return List.of(stored.split("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the state of this {@code BldCache} instance to disk.
|
* Writes the state of this {@code BldCache} instance to disk.
|
||||||
*
|
*
|
||||||
|
|
@ -490,7 +567,9 @@ public class BldCache {
|
||||||
if (!extensionsHash_.equals(properties.get(PROPERTY_EXTENSIONS_HASH))) {
|
if (!extensionsHash_.equals(properties.get(PROPERTY_EXTENSIONS_HASH))) {
|
||||||
properties.put(PROPERTY_EXTENSIONS_HASH, extensionsHash_);
|
properties.put(PROPERTY_EXTENSIONS_HASH, extensionsHash_);
|
||||||
properties.remove(PROPERTY_EXTENSIONS_DEPENDENCY_TREE);
|
properties.remove(PROPERTY_EXTENSIONS_DEPENDENCY_TREE);
|
||||||
|
properties.keySet().removeIf(key -> key.toString().startsWith(PROPERTY_EXTENSIONS_CLASSPATH_PREFIX));
|
||||||
}
|
}
|
||||||
|
extensionClasspaths_.forEach((coordinate, jars) -> properties.put(PROPERTY_EXTENSIONS_CLASSPATH_PREFIX + coordinate, jars));
|
||||||
|
|
||||||
if (extensionsDependencyTree_ != null) {
|
if (extensionsDependencyTree_ != null) {
|
||||||
properties.put(PROPERTY_EXTENSIONS_DEPENDENCY_TREE, extensionsDependencyTree_);
|
properties.put(PROPERTY_EXTENSIONS_DEPENDENCY_TREE, extensionsDependencyTree_);
|
||||||
|
|
@ -525,7 +604,9 @@ public class BldCache {
|
||||||
properties.remove(PROPERTY_DEPENDENCIES_PROVIDED_DEPENDENCY_TREE);
|
properties.remove(PROPERTY_DEPENDENCIES_PROVIDED_DEPENDENCY_TREE);
|
||||||
properties.remove(PROPERTY_DEPENDENCIES_RUNTIME_DEPENDENCY_TREE);
|
properties.remove(PROPERTY_DEPENDENCIES_RUNTIME_DEPENDENCY_TREE);
|
||||||
properties.remove(PROPERTY_DEPENDENCIES_TEST_DEPENDENCY_TREE);
|
properties.remove(PROPERTY_DEPENDENCIES_TEST_DEPENDENCY_TREE);
|
||||||
|
properties.keySet().removeIf(key -> key.toString().startsWith(PROPERTY_DEPENDENCIES_CLASSPATH_PREFIX));
|
||||||
}
|
}
|
||||||
|
dependencyClasspaths_.forEach((coordinate, jars) -> properties.put(PROPERTY_DEPENDENCIES_CLASSPATH_PREFIX + coordinate, jars));
|
||||||
|
|
||||||
if (dependenciesCompileDependencyTree_ != null) {
|
if (dependenciesCompileDependencyTree_ != null) {
|
||||||
properties.put(PROPERTY_DEPENDENCIES_COMPILE_DEPENDENCY_TREE, dependenciesCompileDependencyTree_);
|
properties.put(PROPERTY_DEPENDENCIES_COMPILE_DEPENDENCY_TREE, dependenciesCompileDependencyTree_);
|
||||||
|
|
|
||||||
|
|
@ -426,14 +426,324 @@ public class TestClasspathJars {
|
||||||
FileUtils.writeString(properties, new File(lib_bld, "bld-wrapper.properties"));
|
FileUtils.writeString(properties, new File(lib_bld, "bld-wrapper.properties"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDependencyClasspathJarsCaching() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", dependency("liba", "1.1.0")),
|
||||||
|
"liba:1.1.0", pom("liba", "1.1.0", ""),
|
||||||
|
"tool:2.0.0", pom("tool", "2.0.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
project.dependencies().scope(provided)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
|
||||||
|
var jars = project.dependencyClasspathJars(provided, "com.example", "tool");
|
||||||
|
var resolution_requests = settledRequests(requests);
|
||||||
|
assertTrue(resolution_requests > 0);
|
||||||
|
|
||||||
|
// a repeated call in the same build resolves nothing
|
||||||
|
assertEquals(jars, project.dependencyClasspathJars(provided, "com.example", "tool"));
|
||||||
|
assertEquals(resolution_requests, requests.get());
|
||||||
|
|
||||||
|
// a fresh project instance reads the persisted cache instead
|
||||||
|
// of resolving, like a new build invocation would
|
||||||
|
var fresh = new JarsProject(tmp, serverRepository(server));
|
||||||
|
fresh.dependencies().scope(provided)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
assertEquals(jars, fresh.dependencyClasspathJars(provided, "com.example", "tool"));
|
||||||
|
assertEquals(resolution_requests, requests.get());
|
||||||
|
|
||||||
|
// changing the declared version invalidates the cache
|
||||||
|
var changed = new JarsProject(tmp, serverRepository(server));
|
||||||
|
changed.dependencies().scope(provided)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(2, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-2.0.0.jar"),
|
||||||
|
changed.dependencyClasspathJars(provided, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertTrue(requests.get() > resolution_requests);
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExtensionClasspathJarsCaching() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"ext:1.0.0", pom("ext", "1.0.0", dependency("tool", "1.0.0")),
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", dependency("liba", "1.1.0")),
|
||||||
|
"liba:1.1.0", pom("liba", "1.1.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
writeWrapperProperties(project, server, "com.example:ext:1.0.0");
|
||||||
|
|
||||||
|
var jars = project.extensionClasspathJars("com.example", "tool");
|
||||||
|
var resolution_requests = settledRequests(requests);
|
||||||
|
assertTrue(resolution_requests > 0);
|
||||||
|
|
||||||
|
// repeated lookups in the same build resolve nothing, also
|
||||||
|
// for another dependency out of the same extension universe
|
||||||
|
assertEquals(jars, project.extensionClasspathJars("com.example", "tool"));
|
||||||
|
assertEquals(resolution_requests, requests.get());
|
||||||
|
var liba_jars = project.extensionClasspathJars("com.example", "liba");
|
||||||
|
assertEquals(List.of("liba-1.1.0.jar"), liba_jars.stream().map(File::getName).toList());
|
||||||
|
var universe_requests = settledRequests(requests);
|
||||||
|
assertEquals(resolution_requests, universe_requests);
|
||||||
|
|
||||||
|
// a fresh project instance reads the persisted cache
|
||||||
|
var fresh = new JarsProject(tmp, serverRepository(server));
|
||||||
|
assertEquals(jars, fresh.extensionClasspathJars("com.example", "tool"));
|
||||||
|
assertEquals(universe_requests, requests.get());
|
||||||
|
|
||||||
|
// changing the declared extensions invalidates the cache
|
||||||
|
writeWrapperProperties(project, server, "com.example:tool:1.0.0");
|
||||||
|
var changed = new JarsProject(tmp, serverRepository(server));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar", "liba-1.1.0.jar"),
|
||||||
|
changed.extensionClasspathJars("com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertTrue(requests.get() > universe_requests);
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDependencyClasspathJarsBomChangeInvalidates() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"bom1:1.0.0", bomPom("bom1", "1.0.0", dependency("libb", "1.5.0")),
|
||||||
|
"bom1:2.0.0", bomPom("bom1", "2.0.0", dependency("libb", "2.0.0")),
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", dependency("libb", "3.0.0")),
|
||||||
|
"libb:1.5.0", pom("libb", "1.5.0", ""),
|
||||||
|
"libb:2.0.0", pom("libb", "2.0.0", ""),
|
||||||
|
"libb:3.0.0", pom("libb", "3.0.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
project.dependencies().scope(test)
|
||||||
|
.include(new Bom("com.example", "bom1", new VersionNumber(1, 0, 0)))
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar", "libb-1.5.0.jar"),
|
||||||
|
project.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
var warm_requests = requests.get();
|
||||||
|
|
||||||
|
// a different BOM version changes the hash and the pinned jar
|
||||||
|
var changed = new JarsProject(tmp, serverRepository(server));
|
||||||
|
changed.dependencies().scope(test)
|
||||||
|
.include(new Bom("com.example", "bom1", new VersionNumber(2, 0, 0)))
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar", "libb-2.0.0.jar"),
|
||||||
|
changed.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertTrue(requests.get() > warm_requests);
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDependencyClasspathJarsOverrideChangeInvalidates() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", dependency("libb", "2.0.0")),
|
||||||
|
"libb:2.0.0", pom("libb", "2.0.0", ""),
|
||||||
|
"libb:2.5.0", pom("libb", "2.5.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
project.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar", "libb-2.0.0.jar"),
|
||||||
|
project.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
var warm_requests = requests.get();
|
||||||
|
|
||||||
|
// a version override changes the hash and the resolved jar
|
||||||
|
var overridden = new JarsProject(tmp, serverRepository(server));
|
||||||
|
overridden.properties().put(VersionResolution.PROPERTY_OVERRIDE_PREFIX, "com.example:libb:2.5.0");
|
||||||
|
overridden.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar", "libb-2.5.0.jar"),
|
||||||
|
overridden.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertTrue(requests.get() > warm_requests);
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDependencyClasspathJarsRepositoryChangeInvalidates() throws Exception {
|
||||||
|
var requests1 = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var requests2 = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var poms = Map.of("tool:1.0.0", pom("tool", "1.0.0", ""));
|
||||||
|
var server1 = createArtifactServer(poms, requests1);
|
||||||
|
var server2 = createArtifactServer(poms, requests2);
|
||||||
|
server1.start();
|
||||||
|
server2.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server1));
|
||||||
|
project.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
project.dependencyClasspathJars(test, "com.example", "tool");
|
||||||
|
assertTrue(requests1.get() > 0);
|
||||||
|
|
||||||
|
// a different repository list changes the hash, the second
|
||||||
|
// server sees the resolution instead of the cache answering
|
||||||
|
var moved = new JarsProject(tmp, serverRepository(server2));
|
||||||
|
moved.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
moved.dependencyClasspathJars(test, "com.example", "tool");
|
||||||
|
assertTrue(requests2.get() > 0);
|
||||||
|
} finally {
|
||||||
|
server1.stop(0);
|
||||||
|
server2.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDependencyClasspathJarsScopeKeysIsolated() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", ""),
|
||||||
|
"tool:2.0.0", pom("tool", "2.0.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
project.dependencies().scope(provided)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
project.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(2, 0, 0)));
|
||||||
|
|
||||||
|
// the same coordinate is cached per scope, with its own version
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar"),
|
||||||
|
project.dependencyClasspathJars(provided, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertEquals(List.of("tool-2.0.0.jar"),
|
||||||
|
project.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
var warm_requests = settledRequests(requests);
|
||||||
|
|
||||||
|
var fresh = new JarsProject(tmp, serverRepository(server));
|
||||||
|
fresh.dependencies().scope(provided)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
fresh.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(2, 0, 0)));
|
||||||
|
assertEquals(List.of("tool-1.0.0.jar"),
|
||||||
|
fresh.dependencyClasspathJars(provided, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertEquals(List.of("tool-2.0.0.jar"),
|
||||||
|
fresh.dependencyClasspathJars(test, "com.example", "tool").stream().map(File::getName).toList());
|
||||||
|
assertEquals(warm_requests, requests.get());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testClasspathCacheCoexistsWithDependencyTrees() throws Exception {
|
||||||
|
var requests = new java.util.concurrent.atomic.AtomicInteger();
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", "")), requests);
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
project.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
project.dependencyClasspathJars(test, "com.example", "tool");
|
||||||
|
var warm_requests = settledRequests(requests);
|
||||||
|
|
||||||
|
// another BldCache user writing to the same file with the same
|
||||||
|
// hash preserves the classpath entries
|
||||||
|
var cache = new BldCache(project.libBldDirectory(), new VersionResolution(project.properties()));
|
||||||
|
cache.cacheDependenciesHash(project.repositories(), project.dependencies());
|
||||||
|
assertTrue(cache.isDependenciesHashValid());
|
||||||
|
cache.cacheDependenciesTestDependencyTree("tree placeholder");
|
||||||
|
cache.writeCache();
|
||||||
|
|
||||||
|
assertEquals("tree placeholder", cache.getCachedDependenciesTestDependencyTree());
|
||||||
|
var fresh = new JarsProject(tmp, serverRepository(server));
|
||||||
|
fresh.dependencies().scope(test)
|
||||||
|
.include(new Dependency("com.example", "tool", new VersionNumber(1, 0, 0)));
|
||||||
|
fresh.dependencyClasspathJars(test, "com.example", "tool");
|
||||||
|
assertEquals(warm_requests, requests.get());
|
||||||
|
|
||||||
|
// and a classpath write with the same hash preserves the tree
|
||||||
|
fresh.dependencyClasspathJars(test, "com.example", "tool");
|
||||||
|
assertEquals("tree placeholder", cache.getCachedDependenciesTestDependencyTree());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExtensionClasspathJarsUndeclaredAfterCached() throws Exception {
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"ext:1.0.0", pom("ext", "1.0.0", dependency("tool", "1.0.0")),
|
||||||
|
"tool:1.0.0", pom("tool", "1.0.0", ""),
|
||||||
|
"other:1.0.0", pom("other", "1.0.0", "")));
|
||||||
|
server.start();
|
||||||
|
var tmp = Files.createTempDirectory("classpathjars").toFile();
|
||||||
|
try {
|
||||||
|
var project = new JarsProject(tmp, serverRepository(server));
|
||||||
|
writeWrapperProperties(project, server, "com.example:ext:1.0.0");
|
||||||
|
assertEquals(1, project.extensionClasspathJars("com.example", "tool").size());
|
||||||
|
|
||||||
|
// the extension that brought the tool in is replaced, a stale
|
||||||
|
// cache entry must not keep answering for it
|
||||||
|
writeWrapperProperties(project, server, "com.example:other:1.0.0");
|
||||||
|
var changed = new JarsProject(tmp, serverRepository(server));
|
||||||
|
var exception = assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> changed.extensionClasspathJars("com.example", "tool"));
|
||||||
|
assertTrue(exception.getMessage().contains("isn't part of the extensions"));
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
FileUtils.deleteDirectory(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the POM prefetcher can still have a speculative request in flight
|
||||||
|
// when resolution returns, count requests only after they settle
|
||||||
|
private static int settledRequests(java.util.concurrent.atomic.AtomicInteger requests)
|
||||||
|
throws InterruptedException {
|
||||||
|
var last = requests.get();
|
||||||
|
var stable_since = System.currentTimeMillis();
|
||||||
|
while (System.currentTimeMillis() - stable_since < 300) {
|
||||||
|
Thread.sleep(50);
|
||||||
|
var current = requests.get();
|
||||||
|
if (current != last) {
|
||||||
|
last = current;
|
||||||
|
stable_since = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
private static Repository serverRepository(HttpServer server) {
|
private static Repository serverRepository(HttpServer server) {
|
||||||
return new Repository("http://localhost:" + server.getAddress().getPort() + "/");
|
return new Repository("http://localhost:" + server.getAddress().getPort() + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpServer createArtifactServer(Map<String, String> poms)
|
private static HttpServer createArtifactServer(Map<String, String> poms)
|
||||||
|
throws IOException {
|
||||||
|
return createArtifactServer(poms, new java.util.concurrent.atomic.AtomicInteger());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpServer createArtifactServer(Map<String, String> poms, java.util.concurrent.atomic.AtomicInteger requests)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
var server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
|
var server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
|
||||||
server.createContext("/", exchange -> {
|
server.createContext("/", exchange -> {
|
||||||
|
requests.incrementAndGet();
|
||||||
var segments = exchange.getRequestURI().getPath().split("/");
|
var segments = exchange.getRequestURI().getPath().split("/");
|
||||||
var filename = segments[segments.length - 1];
|
var filename = segments[segments.length - 1];
|
||||||
byte[] body = null;
|
byte[] body = null;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue