Xeno
08/05/2025, 3:54 PMbeforeSettings
phase and for regular dependencies in the projectsLoaded
phase. However, I have been struggling for a while to determine in which hook I should add transitive dependencies for plugins. For example, the plugin (com.gradleup.shadow
) has the following dependencies:
- org.vafer:jdependency
- org.apache.logging.log4j:log4j-core
- org.codehaus.plexus:plexus-xml
- org.codehaus.plexus:plexus-utils
- commons-io:commons-io
- org.jdom:jdom2
- org.apache.ant:ant
All of these dependencies are present in my system, including their artifacts and POM files. Currently, my plugin is unable to resolve transitive dependencies for plugins because I am unsure about the correct hook to use for adding them to the configuration and the precise method to do so. As a result, the build process fails with an error.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':shadowJar'.
org/vafer/jdependency/Clazzpath* Try:
Run with --stacktrace option to get the stack trace.
Run with --debug option to get more log output.
Run with --scan to get full insights.
Get more help at https://help.gradle.org.BUILD FAILED in 1s 5 actionable tasks: 1 executed, 4 up-to-date and if I add this to build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.apache.ant:ant:1.10.14")
classpath("org.ow2.asm:asm-commons:9.8")
classpath("org.ow2.asm:asm:9.8")
classpath("commons-io:commons-io:2.18.0")
classpath("org.vafer:jdependency:2.13")
}
}
then I get
BUILD SUCCESSFUL in 1s
6 actionable tasks: 6 up-to-date
Moreover, both attempts were made in an environment without Internet access and with a cleared cache.
Please tell me how to correctly add such dependencies to the configuration using the API so that they fall into the plugin classpath, I will be very grateful
Xeno
08/05/2025, 4:08 PMprivate void configurePluginResolution(Settings settings) {
settings.getPluginManagement().getResolutionStrategy().eachPlugin(requested -> {
String pluginId = requested.getRequested().getId().getId();
if (pluginId.startsWith("org.gradle.") || !pluginId.contains(".")) return;
MavenCoordinate coord = versionScanner.findPluginArtifact(pluginId, logger);
if (coord != null && coord.isValid()) {
String module = coord.getGroupId() + ":" + coord.getArtifactId() + ":" + coord.getVersion();
requested.useModule(module);
requested.useVersion(coord.getVersion());
resolvedPlugins.put(pluginId, coord);
logger.lifecycle("Resolved plugin: {} → {}", pluginId, module);
collectPluginDependencies(coord);
} else {
logger.warn("Plugin not resolved: {}", pluginId);
}
});
}
it is used in such a hook
PluginsDependenciesHandler pluginsHandler = new PluginsDependenciesHandler();
gradle.beforeSettings(pluginsHandler::handle);
there is also a method for direct dependencies
ProjectDependenciesHandler projectHandler = new ProjectDependenciesHandler();
gradle.projectsLoaded(projectHandler::addRepository);
gradle.projectsEvaluated(projectHandler::handleAfterConfiguration);
But I don't understand in which hook the dependencies that plugins require should be processed and how exactly they should be added to the configurationVampire
08/05/2025, 4:10 PMXeno
08/05/2025, 4:23 PMThe thing is that I am a Linux distribution maintainer and we have jar files and pom files separated into different directories in the system, and in this case gradle will not take into account transitive dependencies as far as I understand. It often happens that the number of dependencies in a project can be downloaded insanely, but not all of them are actually required, so I need to have full control over dependency resolution
Vampire
08/05/2025, 4:27 PMand in this case gradle will not take into account transitive dependencies as far as I understandI think if you set up an Ivy repository with according patterns it should work just fine. But again, if you only have POMs but not GMMs, you potentially change what is resolved and might break builds and this can be an obvious break or also some silent one that you do not right away notice!
Xeno
08/05/2025, 4:37 PMVampire
08/05/2025, 4:41 PMVampire
08/05/2025, 4:42 PMXeno
08/05/2025, 4:45 PMVampire
08/05/2025, 4:47 PMXeno
08/05/2025, 4:50 PMXeno
08/05/2025, 4:50 PMVampire
08/05/2025, 4:51 PMVampire
08/05/2025, 4:52 PMJulien Plissonneau Duquène
08/05/2025, 7:30 PMprojectsLoaded
hook (link to doc, see the example there, it looks very much like what you're trying to do) but it's also possible that you get some error before your hook is even called.
Anyway I think that adding transitive dependencies as direct dependencies is the wrong approach here and is likely to cause hard to solve issues in more complex projects.
FI I'm currently working on upgrading the Gradle package in Debian. In this distribution and its derivatives there is a "system Maven repository" under /usr/share/maven-repo
that can be used directly by Maven and Gradle:
$ ls -l /usr/share/maven-repo/log4j/log4j/1.2.17/
total 4
lrwxrwxrwx 1 root root 37 Jan 31 2022 log4j-1.2.17.jar -> ../../../../java/log4j-1.2-1.2.17.jar
-rw-r--r-- 1 root root 3395 Jan 31 2022 log4j-1.2.17.pom
My own init scripts are (among other things) replacing external repositories with this system repository, and no specific handling is necessary for transitive dependencies, they are automatically looked up and found there.Vampire
08/06/2025, 6:50 AMJulien Plissonneau Duquène
08/06/2025, 11:00 AMVampire
08/06/2025, 11:06 AMJulien Plissonneau Duquène
08/06/2025, 11:32 AMVampire
08/06/2025, 11:33 AMVampire
08/06/2025, 11:39 AMmavenLocal()
.
If you have library X which provides GMM and a Gradle build that requires the GMM to work properly and have unfiltered mavenLocal()
in that build and before the real repository, it works fine if the dependency is fully present in mavenLocal()
because you for example did publishToMavenLocal
for X.
And it also works fine if it is not present in mavenLocal()
at all.
But if you then execute any Maven build that also uses X, Maven uses the hermaphrodite mavenLocal()
as download-cache and you only find those files that the Maven build needed, which of course does not include GMM files or also some classified artrifacts and so on.
If you then run said Gradle build again, it sees the POM in mavenLocal()
, expects the dependency to be completely present there and lives with what it finds, failing (or misbehaving) as the GMM file is not found and thus resolution suddenly changed.Julien Plissonneau Duquène
08/06/2025, 11:47 AM.module
files that should have been there and then having random issues further down is not too helpful.
Also I don't know if repository managers / mirrors / proxies / caches are automatically pulling these files nowadays, but I suspect this could be, or has been an issue for folks depending on internal mirrors.Vampire
08/06/2025, 11:55 AMVampire
08/06/2025, 12:01 PMVampire
08/06/2025, 12:03 PMJulien Plissonneau Duquène
08/06/2025, 12:06 PMthere are libs out there that have the marker but don't publish the GMM file alongside🤦 that had to happen, obviously ...
Vampire
08/06/2025, 12:11 PMXeno
08/08/2025, 12:05 AMVampire
08/08/2025, 6:51 AMMoreover, the structure of the system directories here does not correspond to the structure of the maven or ivy repository,
Ivy repos might have a default layout, the patterns to retrieve artifacts and metadata files are configurable, so you should be able to configure it.
Julien Plissonneau Duquène
08/08/2025, 8:43 AMthere can only be 1 version of the library in the systemThat's the same in Debian (though there are sometimes exceptions for major versions), but I still think that adding the dependency explicitly is going to be more brittle in the long term than getting the transitive dependency resolution to work. Anyway you are the maintainer 😉 so it's your call. In Debian we install the JARs with a version and add a symlink without the version, but you could also do the reverse.
$ ls -l /usr/share/java/assertj-core*
-rw-r--r-- 1 root root 1373614 Oct 29 2024 /usr/share/java/assertj-core-3.26.3.jar
lrwxrwxrwx 1 root root 23 Oct 29 2024 /usr/share/java/assertj-core.jar -> assertj-core-3.26.3.jar
the patterns to retrieve artifacts and metadata files are configurableI haven't tried this, but I would expect that not to work as Gradle won't find the Ivy metadata that isn't provided. But a
flatDir
should work according to the doc:
To resolve a dependency, this resolver looks for one of the following files. It will return the first match it finds:
• …
• [artifact].[ext]
• …... though with no metadata thus no transitive dependencies, which is not great.
Vampire
08/08/2025, 8:54 AMI haven't tried this, but I would expect that not to work as Gradle won't find the Ivy metadata that isn't provided.I didn't actually try that yet, but you can configure for a repository which kind of metadata it provides, so I would hope you can confiugre that it contains POM even for an Ivy repository.
Vampire
08/08/2025, 9:01 AMVampire
08/08/2025, 9:05 AMartifact()
metadata source.
That would work similar to flatDir
then without the requirement that all artifacts are in the same directory.
But still you would miss the metadata then of course. 😞Vampire
08/08/2025, 9:10 AMmaven
repository that points to the directory where the POMs lie
and add an artifact URL that points to the directory where the JARs lie, then it would work.
But as far as I understood that is not the case either.Julien Plissonneau Duquène
08/08/2025, 9:15 AM