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

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'
}

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")
}

Can I create a Gradle plugin that adds a dependency or another plugin based on a Gradle extension value?

Can I create a Gradle plugin that adds a dependency based on an extension value?
I have a convention plugin that I use for libraries various projects, which brings in various dependencies, takes care of boilerplate configuration, configures other plugins etc etc. I want to add an extension to the plugin that can tell the plugin whether or not to add a certain dependency, in this case it happens to be Spock, as not every library module needs the Spock dependency.
So far, my plugin looks like this
interface BasePluginExtension {
Property<Boolean> getUseSpock()
}
class BasePlugin implements Plugin<Project> {
#Override
void apply(Project project) {
BasePluginExtension basePluginExtension = project.extensions.create('basePluginConfig', BasePluginExtension)
// If a value was supplied, use it, otherwise assume we want Spock
if (basePluginExtension?.useSpock?.get() ?: true) {
// Printing for debugging purposes
println "I'm using spock! ${basePluginExtension.useSpock.get()}"
// Currently apply a plugin that applies Spock but could also just add a dependency
project.plugins.apply("test-config")
}
}
}
Then in the build.gradle file that I want to pull my plugin into, I have
plugins {
id 'base-plugin'
}
basePluginConfig {
useSpock = true
}
I'm following the docs on configuring an extension but I am getting the following error:
Cannot query the value of extension 'basePluginConfig' property 'useSpock' because it has no value available.
I've also tried the method of making an abstract class for the extension but I want the ability to have multiple configurable parameters in the future.
Is adding a dependency after plugin extension values have been configured not allowed/out of order for how Gradle works? Or am I possibly missing something obvious?

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).

Using Gradle 5.1 "implementation platform" instead of Spring Dependency Management Plugin

I have written a Gradle Plugin that contains a bunch of common setup configuration so that all of our projects just need to apply that plugin and a set of dependencies. It uses the Spring Dependency Management Plugin to setup the BOM imports for Spring as shown in the code snippet below:
trait ConfigureDependencyManagement {
void configureDependencyManagement(final Project project) {
assert project != null
project.apply(plugin: "io.spring.dependency-management")
final DependencyManagementExtension dependencyManagementExtension = project.extensions.findByType(DependencyManagementExtension)
dependencyManagementExtension.imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE"
}
}
}
Whilst that still works in Gradle 5.1 I wanted to replace the Spring Dependency Management Plugin with the new dependency mechanism for BOM Imports so I updated the above to now be this:
trait ConfigureDependencyManagement {
void configureDependencyManagement(final Project project) {
assert project != null
project.dependencies.platform("org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE")
}
}
Unfortunately that change means none of the dependencies defined by these BOMs are being imported and I get errors like these when building projects?
Could not find org.springframework.boot:spring-boot-starter-web:.
Required by:
project :
Could not find org.springframework.boot:spring-boot-starter-data-jpa:.
Required by:
project :
Could not find org.springframework.boot:spring-boot-starter-security:.
Required by:
project :
Am I correct in thinking the Spring Dependency Management Plugin is no longer needed with Gradle 5.1 and if so then am I missing something for this to work?
The platform support in Gradle 5 can replace the Spring dependency management plugins for BOM consumption. However the Spring plugin offers features that are not covered by the Gradle support.
Regarding your issue, the problem comes from the following line:
project.dependencies.platform("org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE")
This will simply create a Dependency, it still needs to be added to a configuration. by doing something like:
def platform = project.dependencies.platform("org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE")
project.dependencies.add("configurationName", platform)
where configurationName is the name of the configuration that requires the BOM. Note that you may have to add this BOM to multiple configurations, depending on your project.

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

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.

Resources