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
Related
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.
I need to apply a gradle plugin, in this case errorprone from a custom Gradle plugin.
My Plugin has a build.gradle that looks like this:
gradlePlugin {
plugins {
myErrorprone {
id = 'my-errorprone'
implementationClass = 'com.my.MyErrorpronePlugin'
}
}
}
And the plugin code is:
public class MyErrorpronePlugin implements Plugin<Project> {
List<String> compilerArgs =
Arrays.asList(
"-XepExcludedPaths:.*/proto/.*|.*/protoGeneratedSrc/.*",
"-XepDisableWarningsInGeneratedCode");
#Override
public void apply(Project project) {
project.getPluginManager().apply("net.ltgt.errorprone:");
for (JavaCompile task : project.getTasks().withType(JavaCompile.class)) {
task.getOptions().setCompilerArgs(compilerArgs);
}
}
}
Then, when in another project I apply this plugin (after getting the dependencies in the buildscript)
like this:
apply plugin: 'my-errorprone'
A problem occurred evaluating root project 'my-project.
Failed to apply plugin [id 'my-errorprone']
Plugin with id 'net.ltgt.errorprone' not found.
And it only resolved if i add to buildscript classpath
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.16"
How can I make my plugin work in such way that the project that consumes my plugin will not have to add this direct dependency in the classpath in "net.ltgt.gradle:gradle-errorprone-plugin:0.0.16" ?
In order to solve it, one should apply the plugin by class rather than by ID. This will force you to include error prone dependency in your plugin’s dependency list as it won’t compile until you add the dependency.
import net.ltgt.gradle.errorprone.ErrorPronePlugin.class;
public class MyPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPluginManager().apply(ErrorPronePlugin.class);
// custom logic here
}
}
and in the build.gradle file:
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
api "net.ltgt.gradle:gradle-errorprone-plugin:1.1.1"
}
I found the answer in Gradle's forum under this topic:
https://discuss.gradle.org/t/apply-a-gradle-plugin-errorprone-from-a-custom-gradle-java-plugin/34645/4
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.
I'm trying to get a simple non-Android Java project working with Dagger2 in IntelliJ (2016.3.3, not AndroidStudio) and Gradle (3.1), but nothing seems to get generated at all, I get no DaggerXXX classes output. Not sure if it's the Dagger annotation processing that doesn't work, the source set configuration in IntelliJ that's wrong, or something else. When building the project I get the following output:
build
|_classes
|_main
|_com
| |_<test classes correctly built>
|_generated
|_<empty, but should contain generated Dagger classes?>
What I've done is:
Configured the project to use annotation processing in the IntelliJ settings.
Used the following config:
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url("https://plugins.gradle.org/m2/")
}
}
dependencies {
classpath "net.ltgt.gradle:gradle-apt-plugin:0.9"
}
}
apply plugin: 'net.ltgt.apt'
apply plugin: 'java'
apply plugin: 'idea'
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.dagger:dagger:2.8'
apt 'com.google.dagger:dagger-compiler:2.8'
}
sourceSets {
main {
java {
srcDirs 'src/main/java'
}
}
}
Test program:
public class Test {
#Inject
TestPojo testPojo;
public static void main(String[] args){
<no generated Dagger component available for building>
}
}
#Component(modules = Module.class)
public interface Component {
Pojo get();
}
#Module
public class Module {
#Provides
public Pojo get(){
return new Pojo();
}
}
public class Pojo {}
Update:
It's working if I build the project through the command line and not through the IntelliJ gui. That points to the issue being within the IntelliJ environment or its Gradle integration somehow, rather than in the build script itself.
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.