I'm developing Gradle custom plugin. I want to add dependency to the existing configuration. I'm trying to do it like this:
open class MyApplicationExtension #Inject constructor(objects: ObjectFactory) {
val version: Property<String> = objects.property(String::class)
}
class MyApplicationPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.apply(ApplicationPlugin::class)
val extension = project.extensions.create<MyApplicationExtension>("myApp")
val implConfig = project.configurations["implementation"]
implConfig.defaultDependencies {
add(project.dependencies.create("com:my-app:${extension.version.get()}"))
}
}
}
But when I try to use application in gradle project the added dependency is not added. I'm trying to use it like this:
apply<MyApplicationPlugin>()
the<MyApplicationExtension>().version.set("0.1.0")
dependencies {
// This overrides the default dependencies
implementation("com:another:0.2.0")
}
And when I invoke dependencies task my dependency is not shown there. So how to add configurable dependency to the implementation configuration from custom plugin? Running with Gradle 5.3.1 in Kotlin DSL.
Default dependencies are only used if no other dependency is added to the configuration.
Since that does not seem to be your use case, you should simply add the dependency in a normal fashion.
implConfig.defaultDependencies {
add(project.dependencies.create("com:my-app:${extension.version.get()}"))
}
needs to become
implConfig.dependencies.add(project.dependencies.create("com:my-app:${extension.version.get()}"))
Related
I'm writing a custom gradle plugin that uses an extension. Here's what the relevant part look like:
class Aar2Jar implements org.gradle.api.Plugin<Project> {
void apply (Project project) {
println('applying Android Jar')
def ext = project.extensions.create('jarSpec', JarSpecExtension)
if (ext.name.get() == "") {
ext.name.set(project.name)
}
and Here's what the extension looks like:
package test.android.gradle
import org.gradle.api.provider.Property
abstract class JarSpecExtension {
abstract Property<String> getName()
abstract Property<String> getVersion()
abstract Property<String> getInclude()
abstract Property<String> getExclude()
JarSpecExtension() {
name.convention("")
version.convention("1.0.0")
include.convention("*")
exclude.convention("")
}
}
And here's what my build.gradle script that uses this custom plugin looks like:
plugins {
id 'com.android.library'
id 'test.android.jar'
}
jarSpec {
name = 'myJar'
}
The problem is that doesn't work and I get the following error:
* What went wrong:
An exception occurred applying plugin request [id: 'test.android.jar']
> Failed to apply plugin 'test.android.jar'.
> No such property: extensions for class: test.android.gradle.Aar2Jar
However, if I change the build.gradle file to this:
plugins {
id 'com.android.library'
id 'test.android.jar'
}
jarSpec.name = 'myJar'
It works perfect. The question is why can I not refer to the extension using the closure style. I'm using gradle version 7.2.
I have a custom annotation processor that does roughly this:
generate an annotation type (classes using this type are deferred until later rounds)
in a later round, process the classes using this type and generate some more files for them
This has worked well so far in Java. But recently, I've been converting some of my classes from Java to Kotlin. The setup is still the same.
But when I converted a class using the generated annotation type to Kotlin, this error popped up:
error: incompatible types: NonExistentClass cannot be converted to Annotation
#error.NonExistentClass()
It's worth noting that this only happens when the file is in Kotlin - other (Java) files also use the same annotation, and they don't produce an error when I remove the annotation from the Kotlin file.
After some googling, I found out that the recommended solution is to add an option to the build.gradle:
kapt {
correctErrorTypes = true
}
However, using this did not fix the problem. I looked at the generated stub file, and it's clear that kapt keeps putting error.NonExistentClass in there despite the setting:
package my.pckg;
import my.pckg.GeneratedAnnotation;
#kotlin.Metadata(...)
public final class MyClass {
private int myAnnotatedField;
#error.NonExistentClass()
public static void myAnnotatedField$annotations() {
}
public final int getMyAnnotatedField() {
return 0;
}
public final void setMyAnnotatedField(int p0) {
}
//...
}
I think it's worth noting that:
the stub kept the correct import for the generated annotation
kapt moved the annotation to a static method, while the original generated annotation supports only fields
MyClass looks like this:
package my.pckg
import my.pckg.GeneratedAnnotation
class MyClass {
#GeneratedAnnotation
var myAnnotatedField: Int = 0
//...
}
And here's the build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
//...
kotlinOptions {
jvmTarget = '1.8'
}
}
kapt {
correctErrorTypes = true
}
dependencies {
kapt 'custom.annotation.processor'
implementation "androidx.core:core-ktx:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41"
//...
}
There have been other people with this problem on SO, but none of the solutions suggested there have fixed it for me.
Edit: I have done some more testing, and discovered that the generated type is only replaced by error.NonExistentClass when the type is used as an annotation (as an object type or even generics argument it works fine). At this point I believe this may simply be a kapt bug.
I configured kotlin-noarg plugin in my Spring application:
buildscript {
ext {
kotlinVersion = '1.2.51'
}
dependencies {
// ...
classpath "org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}"
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: "kotlin-noarg"
noArg {
annotation("com.example.demo.DefaultConstructor")
invokeInitializers = true
}
Then I have my annotation declared:
package com.example.demo
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.CLASS)
annotation class DefaultConstructor
And then I have my class:
#DefaultConstructor
data class UserForm(#field:NotNull
#field:DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") val dateTime: LocalDateTime)
Here is how I'm trying to use UserForm class:
#GetMapping("")
public String userForm(UserForm userForm) {
userForm.copy(LocalDateTime.now());
return "/users/form";
}
So Spring is trying here to initialize my UserForm class using reflection, but there is no default constructor being generated, despite kotlin-noarg plugin being configured(correctly, I believe).
Where is the problem here ? Is it because of misconfiguration of the kotlin-noarg plugin ? And how to fix it ?
I created demo project
I'm not fully sure with the answer but:
kotlin-noarg provides a syntactic constructor(only available with reflection)
If spring is trying to instantiate UserForm directly (without reflection) it will fail.
There is a way to avoid the problem.
Mark UserForm as #Entity after this it will be recognized as something that belongs to spring. After this no-arg constructor will be either generated by kotlin-spring plugin or you need to add kotlin-jpa plugin
I want to exclude particular class from a jar in gradle dependency
dependencies {
compile("com.example:myapp") {
exclude("org/springframework/**")
}
}
Any suggestions? I am stuck with the dirty class.
You can unzip a jar using the Copy task, exclude the desired class and then add a file dependency on the extracted classes.
For example:
task unzipJar(type: Copy) {
from zipTree('commons-collections-3.2.jar')
into ("$buildDir/libs/commons-collection")
include "**/*.class"
exclude "**/Unmodifiable.class"
}
dependencies {
compile files("$buildDir/libs/commons-collection") {
builtBy "unzipJar"
}
}
I am in the process of building a custom Gradle plugin using the following guide. I want to define a property in the settings.gradle file and read it in the plugin class, but every attempt failed so far.
Here is the error I am getting:
Could not find property 'MyProperty' on root Project 'MyProject'
Here is my settings.gradle file:
gradle.ext.MyProperty = "The Value"
and here is the file containing my plugin:
package myplugin.gradle
import org.gradle.api.Project
import org.gradle.api.Plugin
class FirstPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('myTask') << {
def instance = project.MyProperty
println "VALUE : " + instance
}
}
}
I can access MyProperty if I put project.ext.set("MyProperty", "Property Value") in build.gradle, but I can't find a way to set it in the settings.gradle file.
I guess one way to fix this issue would be to read the content of settings.gradle in build.gradle and send it to the plugin using project.ext.set(...), but is there a more direct way?
Apparently, the way to go (which is better anyways) is to use an extension object. This extension object allows to send data from the build.gradle file to the plugin. The build.gradle file reads the content of the variable in the settings.gradle file and puts it in the extension object.
In the file containing the plugin:
package myplugin.gradle
import org.gradle.api.Project
import org.gradle.api.Plugin
class MyExtension {
String myProperty
}
class FirstPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myExtensionName', MyExtension)
project.task('myTask') << {
def instance = project.myExtensionName.myProperty
println "VALUE : " + instance
}
}
}
In the build.gradle file:
apply plugin: 'myPluginName'
...
myExtensionName {
myProperty = gradle.ext.myPropertyInSettings
}
And finally, in the settings.gradle file :
gradle.ext.myPropertyInSettings = "The Value"
Also, I recommend this tutorial instead of the one provided by Gradle.
I have a gradle project where a property set in settings.gradle is accessed from 3 places:
settings.gradle itself
build.gradle (including subprojects {} )
a gradle plugin in ./buildSrc
Accessing from settings.gradle and build.gradle:
gradle.ext.MY_PROPERTY_NAME
Accessing from a gradle plugin's apply() method:
(plugin is applied from subprojects {} closure)
project.rootProject.gradle.ext.MY_PROPERTY_NAME
This seems to work fine in Gradle 4.7 and Gradle 5.2