What order does Grails evaluate its dependency repositories? - maven

Does Grails evaluate its Maven repos top-down or bottom-up?
I would assume top-down but some manual testing might indicate bottom-up. I can provide test results if need be.
grails.project.dependency.resolution = {
repositories {
grailsPlugins()
grailsHome()
mavenLocal()
grailsCentral()
mavenCentral()
}
}

Related

Moving transitive dependency from implementation to testImplementation

I have few dependencies, that have the same transitive dependency for tests, resulting in test dependencies ending up in my 'implementation'.
Can I somehow smoothly redirect said dependency to 'testImplementation' or do I have to perform something like:
implementation(A){exclude T}
implementation(B){exclude T}
implementation(C){exclude T}
testImplementation(T)
Something a bit better would be:
configurations {
implementation {
exclude(T)
}
}
testImplementation(T)
Then you won't need to explicitly exclude T for each implementation dependency that might pull it in.

Applying platform constraints to all configurations

How do I inherit constraints from BOMs for all configurations in an ergonomic way ? The following is how I am currently doing it. I am on Gradle 6.6.1.
dependencies {
compileOnly(platform('x:y:z'))
implementation(platform('x:y:z'))
annotationProcessor(platform('x:y:z'))
testAnnotationProcessor(platform('x:y:z'))
testImplementation(platform('x:y:z'))
testCompileOnly(platform('x:y:z'))
}
Well, you could do it by abusing the configurations.all method like this:
// Groovy DSL
configurations.all { config ->
project.dependencies.add(config.name, project.dependencies.platform('x:y:z'))
}
But you don't need to add the platform to all those configurations in the first place. Because most of them are resolvable and extend both api and implementation, you typically only need to add it to one of those. The only exception is annotationProcessor, which is isolated (but is extended by testAnnotationProcessor). So you can still reduce it to:
// Groovy DSL
dependencies {
implementation platform('x:y:z') // or api
annotationProcessor platform('x:y:z')
}
This is in my opinion more readable and more precise.
A common use case is for Spring Boot. It could look like this:
// Groovy DSL
import org.springframework.boot.gradle.plugin.SpringBootPlugin
plugins {
id 'java'
id 'org.springframework.boot' version '2.4.2'
}
dependencies {
// BOMS (Note that using the "BOM_COORDINATES" variable makes it match the version of the plugin)
implementation platform(SpringBootPlugin.BOM_COORDINATES)
annotationProcessor platform(SpringBootPlugin.BOM_COORDINATES)
// Actual dependencies
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
}
Interestingly, there is a Gradle issue on this exact use case. Here they explained that typically you don't need this functionality, and where you do it is better to be explicit about it rather than just "hammer" a set of dependency versions onto everything.

What is the difference between simple and source set dependencies when working with the Kotlin MPP plugin?

I'm using the Kotlin MPP plugin (with .kts support) and while I've been reading some code I came upon build.gradle.kts files like this:
kotlin {
sourceSets {
commonMain {
dependencies {
api(kotlinxCollectionsImmutable)
}
}
}
dependencies {
with(Libs) {
commonMainApi(kotlinStdLibCommon)
commonMainApi(kotlinxCoroutinesCommon)
}
}
}
What is the difference between declaring an api dependency within a sourceSet compared to declaring a commonMainApi dependency? Is there any?
No difference. The commonMainApi is just an alternative way of doing the same, and doesn't look to be recommended any more. Link - https://kotlinlang.ru/docs/reference/building-mpp-with-gradle.html
Альтернативным способом указания зависимостей является использование
встроенного DSL Gradle на верхнем уровне с именами конфигурации,
следующими за шаблоном : [translation:
Alternatively, dependencies can be declared by specifying
configuration names at the top level using the built-in Gradle DSL]
dependencies {
commonMainApi 'com.example:foo-common:1.0'
jvm6MainApi 'com.example:foo-jvm6:1.0'
}
Interestingly, this document is described as a translation of https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html where this paragraph (about the alternative syntax) is completely missing, so one can only deduce that the English version has been updated and the alternative syntax removed as either not recommended or obsolete at this point.

Find all transitive artifacts with used classes

I am using jqassistant to create a transitive dependency list of artifacts. My aim is to base this on the type dependencies because I want to find architecture deficits by comparing the Maven transitive dependency list with the one given by type dependencies (all artifacts of which at least one type is used in any transitive form).
Unfortunately, the following query only gave back the original artifact basis-vs-bo. The query was applied to a database that I created by scanning a Maven repository.
What did I miss?
MATCH (basis:Artifact{name:"basis-vs-bo",version:"4.0.0"})
WHERE NOT( HAS (basis.classifier))
WITH basis
MATCH (basis) -[:CONTAINS]-> (t:Type) -[:DEPENDS_ON*]-> (s:Type) <-[:CONTAINS]- (a:Artifact)
RETURN DISTINCT a
The Maven repository scanner treats all artifacts in isolation, i.e. dependencies between them are not evaluated while scanning classes.
This is the same behavior as scanning several JAR files from a directory, so dependencies between classes (method invocations, inheritance hierarchies, etc.) are only resolved if the involved classes are located within the same artifact.
As the dependencies between the artifacts are known in the case of the Maven repository it should be possible to apply queries which resolve these dependencies similar to the "classpath:*" concepts but this needs to be evaluated.
Update
The following queries should help getting class based dependencies (DEPENDS_ON) between classes in different artifacts:
Build DEPENDS_ON relations between artifacts based on declared dependencies in POMs:
MATCH
(r:Repository)-[:CONTAINS_POM]->(p1:Pom)-[:DESCRIBES]->(a1:Java:Archive),
(r:Repository)-[:CONTAINS_POM]->(p2:Pom)-[:DESCRIBES]->(a2:Java:Archive),
(p1)-[:DECLARES_DEPENDENCY]->(a2)
MERGE
(a1)-[:DEPENDS_ON]->(a2)
RETURN
a1.fqn, collect(a2.fqn)
Resolve types required by an artifact to those contained in an artifact which is a dependency, a relation RESOLVES_TO is created:
MATCH
(a1:Artifact)-[:DEPENDS_ON]->(a2:Artifact),
(a1)-[:REQUIRES]->(requiredType:Type),
(a2)-[:CONTAINS]->(containedType:Type)
WHERE
requiredType.fqn = containedType.fqn
MERGE
(requiredType)-[:RESOLVES_TO]->(containedType)
RETURN
a1.fqn, a2.fqn, collect(requiredType.fqn)
Propagate DEPENDS_ON relations between types based on the RESOLVES_TO relation:
MATCH
(a1:Artifact)-[:REQUIRES]->(requiredType:Type),
(a1)-[:CONTAINS]->(type:Type)-[:DEPENDS_ON]->(requiredType),
(a2:Artifact)-[:CONTAINS]->(resolvedType:Type),
(requiredType)-[:RESOLVES_TO]->(resolvedType:Type)
MERGE
(type)-[dependsOn:DEPENDS_ON]->(resolvedType)
SET
dependsOn.resolved=true
RETURN
a1.fqn, type.fqn, a2.fqn, collect(resolvedType.fqn)
Does this work for you?

When to use plugins.withType(somePlugin).whenPluginAdded

A gradle build has several submodules. Some of them have the java plugin applied, some don't. I'm trying to configure the plugin only when it's applied. To do this, I add the following in my top-level build.gradle file:
allprojects {
plugins.withType(JavaPlugin) {
//some configuration on the JavaPlugin
}
}
However, I also noticed the following style:
allprojects {
plugins.withType(JavaPlugin).whenPluginAdded {
//some configuration on the JavaPlugin
}
}
What's the difference between the 2. When do I use the withType(){}-style configuration and when do I use the withType().whenPluginAdded{}-style?
When you use whenPluginAdded() it invokes whenObjectAdded() on the current collection. And when you call withType() and pass a Closure, it invokes all() on the current collection, which in its turn calls whenObjectAdded() on a copied collection.
So both these methods do the same thing but the former makes a defensive copy of a plugin collection.

Resources