Custom Configuration dependency declaration - gradle

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

Related

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?

Adding gradle dependencies

I have a simple gradle 7.2 project, with a simple kotlin file, running java 11, on ubuntu 20.04 in vs code
For my project, I need to add some simple dependencies to java.security such that I'll be able to encrypt and hash some things.
So I need to add it as a dependency.
The project is created by running gradle init and picking all the default options.
I then want to be able to do an import like: import java.security.MessageDigest and use the java.security package.
I guess I'll have to add the dependency in the build file, which currently looks like this:
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
id("org.jetbrains.kotlin.jvm") version "1.5.0"
// Apply the application plugin to add support for building a CLI application in Java.
application
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// This dependency is used by the application.
implementation("com.google.guava:guava:30.1.1-jre")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
// Define the main class for the application.
mainClass.set("com.what.isthis.AppKt")
}
I now search google high and low for how a reference to the java.security package can be added in gradle, but find absolutely nothing anywhere.
Following a guide like this it looks like I could just add a in this manner:
implementation 'org.springframework.boot:spring-boot-starter-validation:2.4.0'
If what I wanted was a reference to this validation library. But I can never get to test it, because I can't find any info on how I would target java.security anywhere.
Looking at the docsI tried to just grab the names I could find here, but this did not compile:
implementation 'java.security Package'
So yes, how do I get thjis dependency. And in general how do find the names that I need for getting dependencies in general?
java.security package is part of the Java language itself as you can see from the documentation, for this reason you don't need to include it explicitly it should already be available to you.
Please make sure you have proper Java SDK set up in IDE. Try to configure different distribution/type than you use currently.
Even if you have logic in Kotlin class it should properly resolve an import and compile.
import java.security.MessageDigest
fun main() {
val test = MessageDigest.getInstance("SHA-256")
println("Test Succeeded")
}
You're not finding examples of declaring java.security packages in Gradle because you don't need to declare them; they're included in the JDK so you can import them natively in any class without declaring them in gradle. Try creating this class in any given package within your project and running it. It should succeed.
import java.security.MessageDigest;
public class Test {
public static void main(String[] args) throws Exception {
MessageDigest test = MessageDigest.getInstance("SHA-256");
System.out.println("Test Succeeded");
}
}

How to access the project version from within Gradle Kotlin DSL expressions?

I have a Gradle 5.3 build script using Kotlin DSL, similar to this:
plugins {
`kotlin-dsl`
`java-library`
}
group = "my.company"
version = "1.2.3"
Here, version= resolves to org.gradle.api.Project.setVersion.
Now, farther down, I'd like to do this (porting from a Groovy DSL build file):
tasks.named<Jar>("jar") {
manifest {
attributes(
"Product-Version" to version
)
}
}
Here, version resolves to AbstractArchiveTask.getVersion -- not what I want (and also deprecated)!
Figuring I could use Kotlin's qualified this, I tried to use
"${this#Project.version}"
instead (NB: the extra string wrapping gets rid of an additional type error), but I get Unresolved reference: #Project now.
How can I access the project version from within a Kotlin DSL expression?
The Gradle script doesn't seem to be nested inside Project but instead delegates accessors to its relevant properties. In fact, the top-level this is of type Build_gradle.
When those accessors are shadowed, variable project can be used; that is,
project.version
solves the issue at hand. As an alternative,
this#Build_gradle.version
is also valid but less readable.

Gradle Kotlin DSL: get sourceSet of another project

Currently, we're trying to migrate our existing build.gradle scripts to the new Kotlin DSL. Right now, we are struggling with the jar task configuration.
Our project is a simple multi-project. Let's say we've Core and Plugin and Plugin uses classes from Core. Now, when building Plugin, the target jar should include any used classes from Core.
This is how it looked like before:
jar {
from sourceSets.main.output
from project(':Core').sourceSets.main.output
}
And this is the current solution we've with Kotlin DSL:
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").the<SourceSetContainer>()["main"].allSource)
}
However, the above example just gives me an Extension of type 'SourceSetContainer' does not exist. Currently registered extension types: [ExtraPropertiesExtension] error. I've also tried other code snippets I've found, but none of them have been working so far.
I have also tried this (like suggested in the first answer):
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").sourceSets.getByName("main").allSource)
}
But then the IDE (and also the jar task) argues that sourceSets is not available: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: public val KotlinJvmProjectExtension.sourceSets: NamedDomainObjectContainer<DefaultKotlinSourceSet> defined in org.gradle.kotlin.dsl.
I hope that someone can help us, because it is very frustrating to spend hours in configuration instead of writing any usefull code.
Thank you very much in advance.
You can access the SourceSetContainer by
project(":Core").extensions.getByType(SourceSetContainer::class)
it seems <T : Any> Project.the(extensionType: KClass<T>): T looks in the convention of the project, while val Project.sourceSets: SourceSetContainer get() looks in the extensions ExtensionContaier instead. This is somewhat odd, as the documentation for the says "Returns the plugin convention or extension of the specified type."
Note that you may need to do your sourceSet manipulation in gradle.projectsEvaluated, because otherwise the sourceSet in question may not be configured yet if the corresponding project is not yet evaluated.
If you get access to the project, then everything should looks like your actual groovy gradle script:
project(":Core").sourceSets.getByName("main").allSource
So regarding your actual code:
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").sourceSets.getByName("main").allSource)
}

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