When are two artifacts considered equal? - gradle

Are two artifacts considered the same for dependency resolution if they have two different values for their respective groups? For example, will
com.example:artifact
and
org.example:artifact
resolve to the same version of artifact on my classpath (where gradle by default will choose the latest)? Or will I get two copies of artifact (because Gradle considers the artifacts different and puts both on the classpath)?

Gradle considers dependencies unique if they have matching group, name and version. In your example, those two dependencies would not be considered the same since they have different groups, and would therefore be duplicated. If you know in advance that such a duplication exists, you can declare a module replacement.
dependencies {
modules {
module("com.example:artifact") {
replacedBy("org.example:artifact")
}
}
}

Related

How do I include a project dependency with a classifier

I have a sub-project with a classifier, test-fixtures, that I want to include into an adjacent sub-project:
dependencies {
implementation(project(":producer"))
}
I assumed that would be a trivial task, but I can't seem to find out how. Is that possible?
I found that this was not a classifier in the maven sense. The variant (what gradle calls them) was created by the java-test-fixtures-plugin. See user guide on testing.
It is used by importing the dependency like this:
dependencies {
testFixtures(project(":producer"))
}
I had a little problem on this since this inhibits my freedom to select the configuration I needed in order to include this jar in an ear-file. I found a way around this by adding it manually as the earlib(...)-method actually does:
dependencies {
add("earlib", testFixtures(project(":producer")))
}

Share configuration dependencies using extendsFrom with control over order

Received this warning in my gradle build this morning, and trying to figure out how to solve it
Adding a Configuration as a dependency is a confusing behavior
which isn't recommended. This behaviour has been deprecated
and is scheduled to be removed in Gradle 8.0. If you're
interested in inheriting the dependencies from the
Configuration you are adding, you should use extendsFrom
Following up on this answer... I was using the configuration as a dependency approach so I could control ordering.
For example:
configurations {
A
B {
extendsFrom A
}
}
dependencies {
A 'jar1'
B 'jar2'
}
Seems to result in the order of B's path being jar1;jar2
But if I want B to be like A, but override some classes from A, then I need B's dependencies first.
So I was using this approach:
configurations {
A
B
}
dependencies {
A 'jar1'
B 'jar2'
B A
}
Which results in B's path being jar2;jar1
I couldn't figure out a way to get this to work using extendsFrom.
Mainly I tried to use B.extendsFrom(A) with various syntax in the dependencies section but couldn't get that to compile.
Is there a way to get the override/ordering use-case to work using extendsFrom?
I believe it's not possible with extendsFrom.
I am not an expert on the dependencies area, but what could work for you or give you some ideas is something like:
def a = project.configurations.findByName("A")
def b = project.configurations.findByName("A")
b.getIncoming().beforeResolve {
// If you want to get also dependencies from
// configurations that "A" extends use getAlLDependencies
a.getDependencies().forEach{
b.dependencies.add(it)
}
}
Note: getDependencies() and getAllDependencies() don't return resolved dependencies, but just the dependencies that were added to a configuration in build script (e.g. A 'jar1'). If you want resolved dependencies you have to resolve A.
Note2: I don't recommend class shadowing as I think you want to achieve. It can bring a lot of unexpected troubles, e.g. app could compile but might not work at runtime. Maybe you should rather do a dependency substitution or resolve conflicts with capabilities (https://docs.gradle.org/7.2/userguide/dependency_capability_conflict.html#sub:declaring-component-capabilities).

Allways append the artifact id to the group id?

I have work with maven for some time but I am not sure if it is always recommended to write the artifact id at the end of the group id although it is repetitive as maven coordinates would include the concatenation of the group id, the artifact id, and the version.
For example, givin a java project with package com.example.helloword I always do:
<groupId>com.example.helloword</groupId>
<artifactId>hello-word</artifactID>
<version>0.0.1-SNAPSHOT</version>
It is there a case in which it is preferred not to put the artifact id in the group id, what come to my mind is if the project is a shared module. For instance, a utility module used with helloword:
<groupId>com.example.helloword</groupId>
<artifactId>utilities</artifactID>
<version>0.0.1-SNAPSHOT</version>
Is it correct? or should I append also the artifact id to the group id?
You should not append the artifactId to the groupId. GroupIds are used to gather different, related artifacts. You do not create your own groupId for each and every artifact.
It is usually a good idea to begin the package names with the groupId. But package names can (and usually are) longer.
Generally speaking you should not append the artifactId on the groupId.
But I think it depends a bit on the size of the project.
As the Maven documentation on naming says
A good way to determine the granularity of the groupId is to use the project structure. That is, if the current project is a multiple module project, it should append a new identifier to the parent's groupId. For example,
org.apache.maven, org.apache.maven.plugins, org.apache.maven.reporting

How to show which parent pom contains a plugin?

If my pom is a child a hierarchy of other poms, is there a way to show exactly which parent pom contains the definition of a plugin?
Short answer is most probably: No.
According to the way Maven builds its model before executing a certain build, the Maven Builder Model:
In phase 1 the hierarchy of poms is resolved
In phase 2 model normalization and plugins configurations are further resolved
But only at the end of phase 2 the effective model validation is performed, which is done on the final effective pom.xml file, as a result of merges, overriding, profiling, injections (of properties) and so on.
To have a look at the full pom, the effective-pom goal of the maven-help-plugin is definitely the right tool. It will show (or write to a given file) the final effective pom a build is gonna use.
The full definition of a plugin (its executions, its global configuration and so on) can only be created once we have the effective pom, because:
the pluginManagement section in any point in the hierarchy can influence a certain plugin
The plugin section in any point in the hierarchy can also influnce it
profiles declared in any point in the hierarchy can also influence it
properties, if used as placeholders, can also play an important role
Having a look at the official Maven POM reference, we can see many entry point to influence a certain plugin definition, which will only be effective to our build once merged through the whole pom hierarchy. A definition per se would not help much since it can then be overriden/influenced further on on the hierarchy chain.
Think about the inherited element of a plugin:
true or false, whether or not this plugin configuration should apply to POMs which inherit from this one. Default value is true.
Or merging of plugin configuration sections:
The default behavior is to merge the content of the configuration element according to element name. If the child POM has a particular element, that value becomes the effective value. if the child POM does not have an element, but the parent does, the parent value becomes the effective value.
You can control how child POMs inherit configuration from parent POMs by adding attributes to the children of the configuration element. The attributes are combine.children and combine.self. Use these attributes in a child POM to control how Maven combines plugin configuration from the parent with the explicit configuration in the child.
Or further down per execution of a plugin:
inherited: Like the inherited element above, setting this false will supress Maven from passing this execution onto its children. This element is only meaningful to parent POMs.
The overall management can then be influenced by pluginManagement:
Plugin Management contains plugin elements in much the same way, except that rather than configuring plugin information for this particular project build, it is intended to configure project builds that inherit from this one. However, this only configures plugins that are actually referenced within the plugins element in the children. The children have every right to override pluginManagement definitions.
As such, the plugin definition at a certain point of the pom hierarchy may not be meaningful to the final effective build.

What is the difference between allprojects and subprojects

On a multi-project gradle build, can someone tell me what exactly is the difference between the "allprojects" section and the "subprojects" one? Just the parent directory? Does anyone use both? If so, do you have general rules that determines what typically is put in each one?
Related question: what is the difference between the two syntaxes (really for allprojects AND subprojects):
subprojects { ...
}
and
configure(subprojects) { ...
}
When would you one over the other?
In a multi-project gradle build, you have a rootProject and the subprojects. The combination of both is allprojects. The rootProject is where the build is starting from. A common pattern is a rootProject has no code and the subprojects are java projects. In which case, you apply the java plugin to only the subprojects:
subprojects {
apply plugin: 'java'
}
This would be equivalent to a maven aggregate pom project that just builds the sub-modules.
Concerning the two syntaxes, they do the exact same thing. The first one just looks better.
Adding to Ryan's answer, the configure method becomes important when you want to configure custom subsets of objects. For example configure([project(":foo"), project(":bar")]) { ... } or configure(tasks.matching { it.name.contains("foo") }) { ... }.
When to use allprojects vs. subprojects depends on the circumstances. Often you'll use both. For example, code related plugins like the Java plugin are typically applied to subprojects, because in many builds the root project doesn't contain any code. The Eclipse and IDEA plugins, on the other hand, are typically applied to allprojects. If in doubt, look at examples and other builds and/or experiment. The general goal is to avoid irrelevant configuration. In that sense, subprojects is better than allprojects as long as it gives the expected results.

Resources