Use Gradle's DependencySet in depencencies block or other way to use multiple modules from one group - gradle

Gradle has an interface called DependencySet that Spring's dependency-management-plugin can use in a dependencies block in a dependencyManagement block like below. (Code from here for reference.)
dependencyManagement {
dependencies {
dependencySet(group:'org.slf4j', version: '1.7.7') {
entry 'slf4j-api'
entry 'slf4j-simple'
}
}
}
This is a very pretty to group dependencies that are in the same group. The more dependencies in the group the prettier it gets. Projects tend to have a lot of dependencies from the same group, especially if you are using Spring. Is there any way to use a similar notation in the "real" dependencies block?

I am afraid there isn't a way how to use dependencySet outside of the dependencyManagement section added by the Spring dependency-management-plugin plugin.
You can take a look at the following classes to see how the dependencies are being read from the script:
DefaultProject - method dependencies()
DefaultDependencyHandler
The interface for dependencySet is used differently in Gradle. For example it is returned when you want to know all dependencies from a configuration.

Related

Custom Configuration dependency declaration

I am trying to convert build.gradle to kotlin dsl. Using gradle 7.4.1.What the right way to declare custom configuration. For custom configuration like
configurations { grafana }
sourceSets { grafana }
and within dependencies block
grafanaImplementation "org.slf4j:slf4j-simple:1.7.36"
grafanaImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
grafanaRuntimeOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
While I am in kotlin-dsl I am doing
val grafana by configurations.creating
val grafanaSourceSet = sourceSets.create("grafana")
and within dependency block
grafana("org.slf4j:slf4j-simple:1.7.36")
grafana("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
When I try to put grafanaImplementation/ grafanaRuntimeOnly within kotlin dsl, it fails.
What is the equivalent of grafanaImplementation/ grafanaRuntimeOnly within kotlin dsl
Quick fix
When you do
val grafanaSourceSet = sourceSets.create("grafana")
behind the scenes Gradle will create the required configurations, grafanaImplementation, grafanaRuntimeOnly, etc, so you can use them without error like this:
val grafanaSourceSet = sourceSets.create("grafana")
dependencies {
"grafanaImplementation"("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
"grafanaRuntimeOnly"("org.slf4j:slf4j-simple:1.7.36")
}
This approach is more like how Groovy works - it basically disables type-checking and the strings will be evaluated during Gradle execution.
Generated DSL accessors
However, string-typing is not why we like Kotlin! We want type-safety and auto completion hints. That's exactly what we see with the implementation() and runtimeOnly(). So how do we get them for grafanaImplementation() and grafanaRuntimeOnly()?
Basically, Gradle will scan the registered config and when it sees that a plugin creates an implementation configuration, it generates Kotlin DSL accessors. However, it can't generate accessors for the build.gradle.kts that contains the definition for the accessors... that's too late. So we need to define the config earlier. We can do that with a buildSrc plugin.
buildSrc Grafana convention plugin
Set up a buildSrc project (this is covered more in the Gradle docs or other StackOverflow answers)
Create a pre-compiled script plugin for Grafana config
// $projectRoot/buildSrc/src/main/kotlin/grafana.convention.gradle.kts
plugins {
// using 'sourceSets' requires the Java plugin, so we must apply it
java
}
val grafanaSourceSet = sourceSets.create("grafana")
Note that this convention plugin is quite opinionated as it applies the Java plugin. In more complex setups you might want to instead react to the Java plugin, rather than always applying it.
Now apply the convention plugin, and Gradle will generate the Kotlin DSL accessors!
// $projectRoot/build.gradle.kts
plugins {
id("grafana.convention")
}
dependencies {
// no string-typing needed!
grafanaImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
grafanaRuntimeOnly("org.slf4j:slf4j-simple:1.7.36")
}

How can I use a dependencyManagement version with a classifier with Spring's Gradle dependency management plugin?

Per dependency-management-plugin#67, a classifier cannot be specified within the dependencySet block. This limitation is discussed in a related question (Is it possible to set a dependencySet entry's classifier using Spring's Gradle dependency management plugin).
dependencyManagement {
dependencies {
dependencySet(group:'com.querydsl', version: '4.2.2') {
entry 'querydsl-apt' // This needs to use the "general" classifier
entry 'querydsl-mongodb'
}
}
}
dependencies {
annotationProcessor 'com.querydsl:querydsl-apt:4.2.2:general' // Version needed to use a classifier
implementation 'com.querydsl:querydsl-mongodb'
}
One workaround to this limitation would be to use an ext block, and define a version variable:
ext {
querydslVersion = 4.2.2
}
dependencyManagement {
dependencies {
dependencySet(group:'com.querydsl', version: querydslVersion) {
entry 'querydsl-mongodb'
}
}
}
dependencies {
annotationProcessor "com.querydsl:querydsl-apt:$querydslVersion:general" // Version needed to use a classifier
implementation 'com.querydsl:querydsl-mongodb'
}
A downside to this approach are that it requires setting a property that wouldn't otherwise be set (and might need to be made available between modules in a multi-module plugin, making the build less obvious). It also doesn't work particularly well if the dependency version is transitively pulled in via a BOM (e.g. imports { mavenBom 'org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE'}, since that requires manually keeping the version in sync with the BOM's value.
Is there still a way to use the version defined in the dependency management section, despite this limitation?
The Spring Dependency Management Plugin gives programmatic access to the managed versions by way of the managedVersions map. This can be used to get the dependency version of the artifact, which can be used to specify the version to use:
dependencies {
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.managedVersions['com.querydsl:querydsl-apt']}:general"
implementation 'com.querydsl:querydsl-mongodb'
}

Gradle Custom String Notation in dependency

I'm creating a global repository for all the artifacts that I'm producing. Right now I've created an S3 bucket as repository where I'm storing the artifacts.
As there is no group (artifacts are global), I ended up duplicating the name as the group so the current notation for a dependency is Name:Name:Version. For this, I've created an extension function that takes two arguments, a name (the dependency that I want to add to the project) and the version.
For example, I would add implementation("Name", "1.0") if I wanted the dependency Name in my project. This is translated to implementation("Name:Name:1.0") and works fine but I feel that it is a little bit ugly and can be confusing, the dependency in the External Libraries tree in IntelliJ shows the dependency as Gradle: Name:Name:1.0, the longer the name is, the uglier it is.
The question is, is it possible to write a custom Notation that let me just do implementation("Name", "1.0") without an extension function so it only shows Gradle: Name:1.0 and everything else is handled in the background?
I have looked at the class ParsedModuleStringNotation and it seems the thing that I would need to change (create my own), but the creation of the objects is hardcoded, I am unsure how to proceed from there.
if you declare S3 repository as flatDir - there is an option for what you are asking
https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html
flatDir(args)
Adds a resolver that looks into a number of directories for artifacts. The artifacts are expected to be located in the root of the specified directories. The resolver ignores any group/organization information specified in the dependency section of your build script. If you only use this kind of resolver you might specify your dependencies like ":junit:4.4" instead of "junit:junit:4.4".
was able to work as below
repositories {
def s = project.rootDir.toString() + "/lib/"
flatDir dirs: s
mavenCentral()
}
//compileOnly 'javax.jms:jms-api:1.1-rev-1'
compileOnly ':my-jms:1.2'

update gradle from 4.4 to 5.4 make joda-time dependancy issue

HI I've migrated a project to gradle version 5.4 from 4.4. Since then gradlew build returns error as below.
....ConvTable.java:6: error: package org.joda.time does not exist
import org.joda.time.DateTime;
...ConvetService.java:5: error: package org.joda.time does not exist
import org.joda.time.DateTime;
...ConvetService.java:34: error: cannot find symbol
ConvTableP getLastCononTableDate(String fromCurrency, String toCurrency, DateTime dateTimeZone) throws IOException;
symbol: class DateTime
location: interface ConvetService
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details
* What went wrong:
Execution failed for task ':cur-api:compileJava'
gradle file looks like below. and its a sub project of bigger one
apply plugin: "j-library"
apply plugin: "m-publish"
group = "com.t.cur"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
publishing { publications { mavenJava(MavenPublication) { } }
repositories {
maven { url "${mv_repo_url}" } }
}
dependencies {
implementation "com.t.com:x-core:1.0.0"
implementation "commons-lang:commons-lang:2.6"
}
My guess is that as part of the upgrade, you changed compile configurations with implementation. One of the differences with the new configuration is that the dependencies will not be exposed to consuming projects as part of the compilation classpath. The idea is that the dependencies you put into implementation are implementation specific and should not "leak" onto the consuming projects. This speeds up the build when using incremental compilation as dependent classes are only recompiled if the public API changes but not the internal implementation. There is also a case to be made for providing looser coupling between projects, though this is a bit more subjective. The implementation dependencies will still be part of, and resolved, in the runtimeClasspath configuration though.
So (assuming this is the underlying issue of cause), the dependency x-core used to provide Joda as a transitive dependency for compilation. But this is no longer the case.
There are two ways to fix it. If you use Joda as part of the public API of x-core, you need to declare it using the api configuration instead of implementation (and use the java-library plugin if you don't already). This will make Joda part of the compilation classpath of dependent projects.
On the other hand, if this sub-project just happens to use Joda as well, but in a completely unrelated way to x-core, you should declare it as dependency here as well (either as implementation or api using the same rules as before).

Depend on multiple configurations from the same dependency

I'm developing a gradle plugin that adds 3 configurations. one, two, and three
When I use this plugin I can add dependencies to these configurations and the plugin then used those dependencies in a certain way depending on the configuration. This works great in a single project, however if in a multi-project build I'm forced to add the following to get the dependencies from my dependent projects:
dependencies {
one project(path: ':projectA', configuration: 'one')
two project( path: ':projectA', configuration: 'two')
three project( path: ':projectA', configuration: 'three')
}
Is there a way to make is so that when I declare a dependency on my other project it automatically pulls in my three custom configurations: one, two, and three
In my plugin I create a task that uses the different configurations:
project.getTasks().create("my-task", MyTask.class, t -> {
t.getOnes().setFrom(
project.getConfigurations().getByName("one"));
t.getTwos().setFrom(
project.getConfigurations().getByName("two"));
t.getThrees().setFrom(
project.getConfigurations().getByName("three"));
});
Maybe the method DomainObjectCollection.whenObjectAdded can help you.
Find the docs here:
https://docs.gradle.org/current/javadoc/org/gradle/api/DomainObjectCollection.html
You could try to get a DependencySet from all configurations of your project, which is a DomainObjectCollection, and call whenObjectAdded on it, and then check if what is being added is your "projectA", and then do your thing.
Alternatively, you could use project.afterEvaluate and then search through all the dependencies at that point in time.
The same problem in both cases is to get a collection of all the dependencies, and inspect them.
Something like this could work:
configurations.all().each {
if (it.allDependencies.contains(project(path: ':projectA'))) {
// do something
}
}

Resources