Okay, let's make this question as simple as possible.
I am trying to build a Gradle plugin. I have an extension named MyExt.
open class MyExt {
/**
* <p>This is some sample docs</p>
*/
var myVar: String = "myVar is awesome"
}
and this is my plugin
class MyAwesomePlugin : Plugin<Project> {
override fun apply(project: Project) {
val myExt : MyExt = project.extensions.create("my-awesome-plugin", MyExt::class.java)
...
}
}
and when I try to implement this plugin in a project, the IntelliJ is not showing docs for myVar as it shows for other Gradle variables.
Not showing for myVar
But showing for Gradle variables
I've both sources and docs jar in my repo.
Why it's not showing the variable doc? What am I missing?
How to add docs to the extension variables in a Gradle plugin?
TLDR; It's a bug
Make sure you have correct documentation format: refer to Documenting Kotlin Code E.g. for me it works with this comment:
/**
* This is some sample docs
*/
Also make sure you have applied the plugin and re-imported Gradle project:
Related
I am very excited about the incubating Gradle's version catalogs and have been experimenting with it. I’ve found that the information in my gradle/libs.versions.toml is accessible in the build.gradle.kts scripts for my app and utility-lib projects.
However, I am unable to use the content of the toml file for buildSrc/build.gradle.kts or the convention files.
The only way that I could build was to hard-code the dependencies into those files, as I did before the version catalog feature.
In the buildSrc folder, I created a settings.gradle.kts file and inserted the dependencyResolutionManagement code for versionCatalogs, which is pointing to the same file as for my app and utility-lib projects.
Based on the Gradle7 docs, it seems that sharing a version catalog with buildSrc and modules is possible… I’d appreciate a nudge into getting it to work with buildSrc, if possible.
Here is a simple sample project, which I created via gradle init: my-version-catalog
Thank you for your time and help,
Mike
With Gradle 7.3.3, it is possible. Note version catalogs are GA since Gradle 7.4
The code snippet assumes Gradle is at least 7.4, but if you need them prior that version, insert enableFeaturePreview("VERSION_CATALOGS") at the beginning of each settings.gradle.kts.
Using buildSrc
buildSrc/settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
buildSrc/build.gradle.kts
dependencies {
implementation(libs.gradleplugin.intellij) // <- the lib reference
}
You can even use the version catalog for plugins
gradle/libs.versions.toml
...
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
jetbrains-changelog = { id = "org.jetbrains.changelog", version.ref = "changelog-plugin" }
jetbrains-intellij = { id = "org.jetbrains.intellij", version.ref = "intellij-plugin" }
hierynomus-license = { id = "com.github.hierynomus.license", version.ref = "license-plugin" }
nebula-integtest = { id = "nebula.integtest", version.ref = "nebula-integtest-plugin" }
build.gradle.kts
plugins {
id("java")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.nebula.integtest)
alias(libs.plugins.jetbrains.intellij)
alias(libs.plugins.jetbrains.changelog)
alias(libs.plugins.hierynomus.license)
}
Note for accessing the catalog within scripts, please refer to the below section, the trick is the same.
Using convention plugins and included build
In the main project include a the Gradle project that holds the convention plugins.
build.gradle.kts
includeBuild("convention-plugins") // here it's a subfolder
convention-plugins/settings.gradle.kts
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "convention-plugins"
The trick to enable convention plugins to access the version catalog is split in two part, add an ugly implementation dependency that locate where the version catalog generated classes are located.
libs.javaClass.superclass.protectionDomain.codeSource.location
Then in the convention plugin refer to the libs extension via Project::the.
val libs = the<LibrariesForLibs>()
This is tracked by gradle/gradle#15383.
convention-plugins/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
implementation(libs.gradleplugin.kotlin.jvm)
// https://github.com/gradle/gradle/issues/15383
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}
And in the actual convention plugin
import org.gradle.accessors.dm.LibrariesForLibs
plugins {
id("org.jetbrains.kotlin.jvm")
}
// https://github.com/gradle/gradle/issues/15383
val libs = the<LibrariesForLibs>()
dependencies {
detektPlugins(libs.bundles.kotlinStuff) // access catalog entries
}
The org.gradle.accessors.dm.LibrariesForLibs class is generated by gradle is somewhere in local gradle folder ./gradle/<version>/dependency-accessors/<hash>/classes
Quick note that older IntelliJ IDEA currently (2022.3) reports alias(libs.gradleplugin.thePlugin) as an error in the editor,
although the dependencies are correctly resolved.
This tracked by KTIJ-19369, the ticket indicates this is actually a bug in Gradle Kotlin DSL gradle/gradle#22797, and someone made a simple IntelliJ IDEA plugin to hide this error until resolved.
Brice, it looks like a can of worms to go down that path, particularly for my situation, where I'm trying to use a libs.version.toml file from an android project, but the custom plugin is of course from a java/kotlin project. I tried creating the libs file by hardwiring the path to the toml file in the custom plugin. It might work if both were java projects, but I never tried that since that's not what I'm after. The ideal solution would be for the plugin to use the libs file from the project it is applied to, but it looks like the version catalog needs to be created in the settings file, before you even have access to "Project", so that's why you would have to hardwire the path.
Short answer. No, but there are other techniques for a custom plugin to get project version data from the project it is applied to.
Say I'm using the palantir/gradle-git-version Gradle plugin, and have the following code in build.gradle.kts to determine the project version:
// If release branch, return after incrementing patch version.
// Else, return $lastTag-SNAPSHOT.
val projectVersion: String by lazy {
val versionDetails: groovy.lang.Closure<VersionDetails> by extra
with(versionDetails()) {
if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
}
// If it detached state, get branch name from GitLab CI env var
val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
if (branch?.startsWith("release/") == true) {
val tokens = lastTag.split('.')
"${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
} else "$lastTag-SNAPSHOT"
}
}
This works, but the code is duplicated across all the projects, which is difficult to maintain except for a very small number of projects.
This is just one example, the same applies for other Gradle tasks that assume certain conventions within the company/team, like creating a Dockerfile.
What is a good way to centralize such code so that all projects can use them? Note that code like this don't usually stand on their own, but rely on Gradle plugins.
What is a good way to centralize such code so that all projects can use them?
You'll want to create a custom Gradle plugin to hold your project's conventions.
If you have Gradle installed locally, you can use the Build Init Plugin to create a skeleton plugin project. With Gradle installed locally, simple run gradle init in a new project directory and follow the prompts to create the plugin project.
As a concrete example (assuming you generated a plugin project as mentioned earlier), to apply your versioning conventions, a plugin could be:
// Plugin's build.gradle.kts
dependencies {
// Add dependency for plugin, GAV can be found on the plugins page:
// https://plugins.gradle.org/plugin/com.palantir.git-version
implementation("com.palantir.gradle.gitversion:gradle-git-version:0.12.3")
}
Then a versioning conventions plugin could be:
import com.palantir.gradle.gitversion.VersionDetails
import groovy.lang.Closure
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
class VersioningConventionsPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Apply plugin to project as you would in the main Gradle build file.
project.pluginManager.apply("com.palantir.git-version")
// Configure version conventions
val projectVersion: String by lazy {
// Gradle generates some Kotlin DSL code on the fly, in a plugin implementation we don't have that.
// So we must convert the DSL to the Gradle API.
val versionDetails: Closure<VersionDetails> = project.extensions.extraProperties.get("versionDetails") as Closure<VersionDetails>
with(versionDetails.call()) {
if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
}
val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
if (branch?.startsWith("release/") == true) {
val tokens = lastTag.split('.')
"${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
} else "$lastTag-SNAPSHOT"
}
}
// Set the version as an extra property on the project
// Accessible via extra["projectVersion"]
project.extensions.extraProperties["projectVersion"] = projectVersion
}
}
I gave a Kotlin example since your sample used the Kotlin DSL. Once you've finished development work of your conventions plugin, then you would publish to a repository such as the Gradle Plugins repository. If it's an internal company plugin, then publish it to an internal Nexus Repository or similar.
Follow the docs for the maven-publish plugin for more details on publishing. Gradle plugins can be published like any other artifact/JAR.
I have a section that defines the environment variables in build.gradle and I want to pass this to my custom plugin.
Snippet of build.gradle as below:
apply "myplugin"
ext {
lombokVersion = '1.18.6'
setEnvironmnetVariables = {
environment -> environment.put{'RUNTIME_ENV', 'test')
}
I want to see this RUNTIME_ENV in my plugin 'myplugin'. I am new to this gradle plugin development. Could anyone help me out with this? I am using spring-boot project with groovy.
You can't set environment variables from Gradle nor Java in general.
You can however set dynamic project properties which is one way to convey information to your custom plugin.
Since you're already using the extra propeties, you can just set the values you need directly:
// Root project build.gradle
ext {
lombokVersion = "1.18.6"
RUNTIME_ENV = "test
}
Then your custom plugin, you access them like so:
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
String runtimeEnv = (String) project.getExtensions().getExtraProperties().get("RUNTIME_ENV");
// do something with variable
}
}
Gradle has great guides on building plugins, give them a look.
https://docs.gradle.org/current/userguide/java_gradle_plugin.html#java_gradle_plugin
https://docs.gradle.org/current/userguide/custom_plugins.html
https://guides.gradle.org/implementing-gradle-plugins/
Additional, if you have Gradle installed locally, you can create a skeleton Gradle plugin project with the init task:
gradle help --task init
I have a custom gradle.kts script I am building that will do our maven publishing for all of our various modules to our sonatype repository, but encountering a strange error. Here are the contents of my maven-deploy.gradle.kts file:
plugins {
`maven-publish`
signing
}
publishing {
//expression 'publishing' cannot be invoked as a function.
//The function invoke() is not found
}
I can run tasks and whatnot within the maven-deploy.gradle.kts file fine, but trying to use the publishing function from the gradle documentation is proving to be impossible. Any ideas? I'm using gradle version 4.10.3 (I need Android support). The maven-deploy.gradle.kts file is in buildSrc/src/main/kotlin and is being added by id("maven-deploy") in my main project's build.gradle.kts file.
This happens because Gradle only imports the generated type-safe accessors for Gradle Kotlin DSL into the main build script, but not into script plugins:
Only the main project build scripts have type-safe model accessors. Initialization scripts, settings scripts, script plugins (precompiled or otherwise) do not. These limitations will be removed in a future Gradle release.
See Understanding when type-safe model accessors are available
In the script you mentioned, you can access the publishing extension dynamically, for example, using configure<PublishingExtension> { ... }:
import org.gradle.api.publish.PublishingExtension
plugins {
`maven-publish`
signing
}
configure<PublishingExtension> {
// ...
}
This is described here: Project extensions and conventions
UPD: Gradle 5.3 RC1 seems to add a possibility to use the generated extensions in script plugins.
I earlier asked this question about configuring the jar task in a particular way. I now need to translate this into the code in a custom plugin.
The working config in the build.gradle was this:
jar {
exclude "yang"
from ("src/main/resources/yang") {
into ("META-INF/yang")
}
}
This correctly puts classes at the root, but puts the files from "src/main/resources/yang" into "META-INF/yang".
In my custom plugin's "apply" method, I tried doing this in my "project.afterEvaluate()" closure:
Jar jarTask = project.getTasks().getByName("jar")
jarTask.exclude("yang")
jarTask.from(task.yangFilesRootDir)
jarTask.into("META-INF/yang")
From the API doc for "Jar", I couldn't see any other way to do this that "looks like" what I had in the build script.
This doesn't work. It does put the files from "src/main/resources/yang" into "META-INF/yang", but it also puts all of the class files there.
Assuming your plugin is implemented in Groovy, you'll want to move the call to into() to a configuration closure passed to the from() method.
jarTask.from(task.yangFilesRootDir) {
into 'META-INF/yang'
}