How to define a helper method for gradle repository? - java

Gradle scripts have shortcut functions to define common well known repositories, for example
repositories {
mavenCentral()
}
I would like to define my own shortcut function something like myPrivateMavenRepo() so that I can write something like
repositories {
mavenCentral()
myPrivateMavenRepo()
}
Rather than
repositories {
mavenCentral()
maven {
url "http://repo.mycompany.com/maven2"
}
}
Questions:
How can a custom repo function be developed?
Where is this custom function hosted?
I have lots of repos that I want to switch to gradle I don't want to hardcode each build.gradle file with the repo Url how do I centralize this in a way that is easy to bootstrap?

The methods in the repositories closure are defined by the RepositoryHandler interface. While mavenCentral(), mavenLocal() and jcenter() add predefined repositories, all other methods require a configuration closure, action or map, which will be applied to a new ArtifactRepository.
A simple approach would be to define such configuration closures, actions or maps and provide them via a plugin extension:
repositories {
mavenCentral()
maven myPluginExtension.myRepoClosure
}
Since RepositoryHandler is also a ArtifactRepositoryContainer, you could use its modification methods like add directly to create and register ArtifactRepository objects:
repositories {
jcenter()
add myPluginExtension.myRepo
}
If you want to add methods directly to the RepositoryHandler, you can alter its meta class. Please note that this is a Groovy feature, so you can only use this in a Groovy plugin, not a Java plugin.
project.repositories.metaClass.myRepo {
// implement logic to add repository (e.g. call maven(closure) ...)
}
The method will be available in your build script:
repositories {
mavenLocal()
myRepo()
}

I think you could likely do
allprojects {
repositories.ext.myPrivateMavenRepo = {
repositories.maven {
url: 'http://repo.mycompany.com/maven2'
}
}
}
Usage
repositories {
myPrivateMavenRepo()
}
You could easily turn that into a plugin

Related

Kotlin Gradle Plugin: How to access `Project` extensions such as `sourceSets`?

In a regular build script you can easily use extensions on Project like Project.sourceSets, for example build.gradle.kts:
sourceSets {
main {
...
}
}
But when I am developing a Gradle plugin in my buildSrc module, I cannot access these. For example:
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
class ExamplePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.sourceSets { // error because `sourceSets` can't be resolved.
}
}
}
This is happening despite including the kotlin-gradle-plugin module in my buildSrc dependencies:
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
}
So, how can I access these extensions from within my Gradle plugin?
class ExamplePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.configure<JavaPluginExtension> {
sourceSets {
println(names)
}
}
}
}
See additional notes here: https://docs.gradle.org/current/userguide/kotlin_dsl.html#project_extensions_and_conventions
Basically for plugins, or other times when the plugins applied are not known, the accessors (sourceSets, configurations, etc) of extensions added by other plugins will need to go through a method call which sort of 'retrieves' that scope or object. Further down the link there is also an example of how to get tasks created by other plugins:
val test by target.tasks.existing(Test::class)
test.configure { useJUnitPlatform() }
// or
val test by target.tasks.existing(Test::class) {
useJUnitPlatform()
}
note that if the 'sourceSet' object does not exist on the project (because the java plugin was not applied), an exception will be thrown .
tested with gradle version 7.2, kotlin-dsl version 2.1.6

Custom Jetbrains Plugin - Missing Dependency

I'm trying to build my own custom line marker for Clion following this tutorial - https://plugins.jetbrains.com/docs/intellij/line-marker-provider.html . My question is about the language attribute on the extension tag in the plugin.xml file ...
<extensions defaultExtensionNs="com.intellij">
<codeInsight.lineMarkerProvider language="JAVA"
implementationClass="org.intellij.sdk.language.SimpleLineMarkerProvider"/>
</extensions>
When i add this extension the language="JAVA" gets highlighted in red. What plugin/dependency do i need to add for this to be resolved?
My IDE is also not able to resolve PsiIdentifier and PsiMethod from the provided snippet ...
public class MyCorrectLineMarkerProvider implements LineMarkerProvider {
public LineMarkerInfo getLineMarkerInfo(#NotNull PsiElement element) {
if (element instanceof PsiIdentifier && element.getParent() instanceof PsiMethod) return new LineMarkerInfo(element, ...);
return null;
}
}
This is what my build.gradle looks like, i know there are some other dependencies i need to add but the tutorial i've been following isn't very clear about it.
plugins {
id 'org.jetbrains.intellij' version '1.2.0'
id 'java'
}
group 'com.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version = '2021.2.2'
}
runIde {
jvmArgs '--add-exports', 'java.base/jdk.internal.vm=ALL-UNNAMED'
}
patchPluginXml {
changeNotes = """
Add change notes here.<br>
<em>most HTML tags may be used</em>"""
}
test {
useJUnitPlatform()
}
You have to add a dependency on the java plugin in your build.gradle, like so
intellij {
plugins = ['java']
}
See the IntelliJ Dev Guide for more information on plugin dependencies.
The tutorial you are following is meant for adding support for a new language, instead you are adding a line marker to Java (existing language :p). Plugin dependencies are simply outside the scope of that tutorial.

Model cannot be cast to ModelRealmProxyInterface

I'm trying to use Realm-java library built from source, I have used the below JARs and AARs
a jar file for the Realm Gradle plugin
an aar file for the Realm library
a jar file for the annotations
a jar file for the annotations
processor
I have added Realm-annotations-processor as below in dependencies
kapt project(':realm-annotations-processor-5.8.0')
When we do
val realm = Realm.getDefaultInstance()
try {
realm.executeTransaction { realmInstance ->
realmInstance.copyToRealm(user)// ClassCastException thrown
}
} finally {
realm.refreshAndClose()
}
The precise exception we have encountered is:
UserModel cannot be cast to io.realm.com_example_mobile_test_auth_model_UserModelRealmProxyInterface
Finally Solved this myself , I had missed registering the Realm-Transformer which is responsible for generating the RealmProxy.
We need to add below code to build.gradle of required module/app
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath "io.realm:realm-transformer:5.8.0"
}
}
import io.realm.transformer.RealmTransformer
android.registerTransform(new RealmTransformer(rootProject))
I had this problem, I tried many solutions, but only this
realm.deleteAll();
could help me

Gradle 5 Kotlin DSL: Common Tasks & Maven Artifacts in multi-modules projects

I really would like to appreciate Gradle 5 especially in combination with the new Kotlin DSL, but I’m having a very hard time to get (in my eyes) a very, very simple and common build running with Gradle.
Task
Release a Java library with several interdependent submodules in Maven default directory layout as high-quality Maven artifacts/repository in a to-the-point, simple Gradle build (i.e. DRY).
Therefore: Have a root project as umbrella which defines & contains all the common configuration (practically all except the real dependencies).
My current struggles
I ported my current "results" to a sample project on Github and asked this question in the Gradle forum already.
Currently I'm failing to declare the necessary task to provide standard -sources and -javadoc artifacts in my central build.
For example these three "solutions" which you'll find on looking for a Kotlin DSL based solutions all do no (longer) work in a multi-module scenario:
https://stackoverflow.com/a/48070667
https://stackoverflow.com/a/52596969/1237653
and even the offical "Maven Publish" documentation is only working in a single-module scenario.
Incomplete solution (/build.gradle.kts)
Complete example see on Github: https://github.com/bentolor/gradle-maven-multimodule-kotlindsl
subprojects {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
group = "de.bentolor.sampleproject"
version = "0.1.0"
repositories {
jcenter()
}
dependencies {
// Dependencies used in EVERY module
"compile"("commons-logging:commons-logging:1.2")
"testImplementation"("junit:junit:4.12")
}
tasks {
// not working
/*register("sourcesJar", Jar::class.java) {
from(sourceSets.main.get().allJava)
classifier = "sources"
}*/
// not working, eiher
/* task<Jar>("sourcesJar") {
from(sourceSets.main.get().allJava)
classifier = "sources"
} */
}
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
configure<PublishingExtension> {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
// won't work, beause inaccessible declaration in `tasks{}`-Block
//add("archives", javadocJar)
//add("archives", sourcesJar)
}
}
repositories {
mavenLocal()
}
}
}
Example submodule /module2/build.gradle.kts
group = "de.bentolor.sampleproject.module2"
dependencies {
compile(project(":module1"))
}
Try this:
subprojects {
apply<JavaLibraryPlugin>()
apply<MavenPublishPlugin>()
group = "de.bentolor.sampleproject"
version = "0.1.0"
repositories {
jcenter()
}
dependencies {
val implementation by configurations
val testImplementation by configurations
implementation("commons-logging:commons-logging:1.2")
testImplementation("junit:junit:4.12")
}
// This will work, but as long as these tasks are need only for publishing you can declare them inplace later where you need
// tasks {
// val sourcesJar by creating(Jar::class) {
// val sourceSets: SourceSetContainer by project
// from(sourceSets["main"].allJava)
// classifier = "sources"
// }
// val javadoc by getting(Javadoc::class)
// val javadocJar by creating(Jar::class) {
// from(javadoc)
// classifier = "javadoc"
// }
// }
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
configure<PublishingExtension> {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
// If you configured them before
// val sourcesJar by tasks.getting(Jar::class)
// val javadocJar by tasks.getting(Jar::class)
val sourcesJar by tasks.creating(Jar::class) {
val sourceSets: SourceSetContainer by project
from(sourceSets["main"].allJava)
classifier = "sources"
}
val javadocJar by tasks.creating(Jar::class) {
from(tasks.get("javadoc"))
classifier = "javadoc"
}
artifact(sourcesJar)
artifact(javadocJar)
}
}
}
}
A few notes:
Why use String-based apply, when you can do a type-safe apply<T>()?
Why use invokes on stings in dependencies, when you can use delegates, which is less hacky and better refactorable.
Consider using implementation instead of compile
Why sourceSets is not working in a multi-module project?
When you're using Kotlin DSL it generates accessors for projects based on the applied plugins. It's a two-step process: first Gradle processes plugins (that's why it's recommended to put them in plugins block) and generates accessors and then you can use them in your code (accessors are generated as Kotlin extensions for Project, NamedDomainObjectContainer and so on). But if you're configuring subprojects there are two issues:
Parent project evaluates before child, so the extensions for child are not known in parent.
The set of plugins applied to parent and child is different and you need to use children accessors in parent.
sourceSets is one of the accessors generated by Kotlin DSL for children. And it's just not available in parent. You can try it yourself: apply only java plugin in subprojects. sourceSets will be available in children build scripts, but not in parent.
This is also why you can use java in children, but have to use configure<JavaPluginExtension> when configuring it in parent.
But you can use delegates to get references for domain objects, like tasks, source sets, configuration and so on.

buildSrc: Could not get unknown property for object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler

I am trying to reorganize this Android (Java based) library to use the buildSrc folder to define all versions and dependencies as described in this article.
I already set this successfully up for several times for Kotlin bases projects. This time the project is pure Java.
In the buildSrc folder I created the following buildSrc/src/main/java/org/ligi/snackengage/Dependencies.java file:
package org.ligi.snackengage;
public class Dependencies {
public static class Android { /* ... */ }
public static class GradlePlugins {
public static final String ANDROID = "com.android.tools.build:gradle:3.6.3";
// ...
}
public static class Libs { /* ... */ }
}
Then I refer to the definitions in the project root build.gradle among others:
import org.ligi.snackengage.Dependencies.GradlePlugins
apply plugin: "com.github.ben-manes.versions"
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath GradlePlugins.ANDROID
classpath GradlePlugins.MAVEN
classpath GradlePlugins.VERSIONS
}
}
allprojects {
repositories {
google()
jcenter()
}
}
Here is the work in progress branch. When I build the project then the following error occurs:
* Where:
Build file 'SnackEngage/build.gradle' line: 12
* What went wrong:
A problem occurred evaluating root project 'SnackEngage'.
> Could not get unknown property 'GradlePlugins' for object of type
org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
Here is the build log.
You have defined GradlePlugins class as an inner static class of Dependencies, so you need to use Dependencies.GradlePlugins to access it from your build script.
Change your dependencies block as follows:
import org.ligi.snackengage.Dependencies // do not import org.ligi.snackengage.Dependencies.GradlePlugins
buildscript {
// ...
dependencies {
classpath Dependencies.GradlePlugins.ANDROID
classpath Dependencies.GradlePlugins.MAVEN
classpath Dependencies.GradlePlugins.VERSIONS
}
}
EDIT you could also use a static import, as follows:
import static org.ligi.snackengage.Dependencies.*
buildscript {
// ...
dependencies {
classpath GradlePlugins.ANDROID
classpath GradlePlugins.MAVEN
classpath GradlePlugins.VERSIONS
}
}
You need to define variable GradlePlugins with def (in Gradle) or public class GradlePlugins (in Java), before attempting to access it. Kotlin class GradlePlugins should also work.
dependencies {
classpath GradlePlugins.ANDROID
classpath GradlePlugins.MAVEN
classpath GradlePlugins.VERSIONS
}
And I think the buildSrc directory belongs into the module directory, as the Gradle manual shows.

Categories

Resources