My organization uses the same Gradle plugins and dependencies for a lot of our projects. My custom plugin knowledge is pretty weak, but what I'd like to do is wrap these plugins and dependencies into a single, standalone plugin. I'm stuck on understanding how to separate the plugins/dependencies required for the plugin versus the ones that I want to use in the consuming project. Here's a simple example that I put together based on the gradle custom plugin docs, and some information about storing the plugin in a maven repo to allow it to automatically download dependencies:
// build.gradle from standalone plugin
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
// these ones I don't need in the plugin, just in the project where I apply the plugin
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
group = 'org.sample'
version = '1.0.0'
publishing {
repositories {
maven {
url "../maven-repo"
}
}
}
gradlePlugin {
plugins {
greeting {
id = "org.sample.greeter"
implementationClass = "org.sample.GreetingPlugin"
}
}
}
dependencies {
implementation gradleApi() // I think I need this for the imports in GreetingPlugin.java
implementation localGroovy() // I think I would need this if GreetingPlugin was written in Groovy
// these ones I don't need in the plugin, just in the project where I apply the plugin
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test' {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
// this is only needed in the project where I apply the plugin
// I believe this should be in the GreetingPlugin.java file though
test {
useJUnitPlatform()
}
and the backing class...
package org.sample;
import org.gradle.api.DefaultTask;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
class Greeting Plugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getTasks().create("hello", MyTask.class);
}
public static class MyTask extends DefaultTask {
#TaskAction
public void myTask() {
System.out.println("Hello, World!");
}
}
}
In the project I'm trying to consume the plugin, I have the following files:
// settings.gradle
pluginManagement {
repositories {
maven {
url "../maven-repo"
}
gradlePluginPortal()
}
}
// build.gradle
plugins {
id 'org.sample.greeter' version '1.0.0'
}
My thinking is that using the plugin in this way, the project inherits the plugins and dependencies listed in the plugin code. I think I'm close, as when I ./gradlew publish I can see the plugin being applied, but it doesn't like that the spring-starter-web dependency doesn't have a version (I know that when I do a multi-project gradle repo, I need to include the dependencyManagement block with mavenBOM, so maybe that's the key?) I'm trying to follow the SpringBoot gradle plugin for insight, but it's a bit too complicated for me.
So, is this the correct way to create a standalone plugin that includes plugins/dependencies baked in? And why isn't the spring dependency manager applying the versioning?
EDIT: I followed the link from #Alan Hay, and instead of a custom plugin, I tried to use the 'apply from'. However, it still doesn't work. Here's files based on that approach:
// build.gradle from 'parent' build.gradle
plugins {
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test' {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
and attempting to reference from another project, it's the only line in the file:
apply from: '<path-to-above>/build.gradle'
This error I get is the following:
script '<path-to-above>/build.gradle': 15: Only Project build scripts can contain plugins {} blocks
See https://docs.gradle.org/5.5.1/userguide/plugins.html#sec:plugins_block for information on the plugins {} block
# line 15, column 1.
plugins {
^
1 error
A standalone, binary plugin is the preferred approach when you need to share the same build logic across multiple independent projects. Additionally, good plugin design separates capabilities from convention. In this case, the capabilities are provided by Gradle and some third-party plugins, but you're adding your own conventions on top in this plugin.
When you're implementing this, you essentially need to push the code down one level. Anything that would be configuration in the build.gradle needs to be in your plugin's source code. Anything that would impact the classpath of the buildscript (i.e. buildscript { } or plugins { }) belongs in the dependencies of your plugin. The plugins { } block in your plugin should only have the build plugins required the build the plugin itself.
// build.gradle from standalone plugin
// plugins {} should contain only plugins you need in the build of the plugin itself
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
}
group = 'org.sample'
version = '1.0.0'
dependencies {
implementation gradleApi()
// Dependencies for plugins you will apply to the target build
implementation 'io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE'
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:2.4.0'
implementation 'org.springframework.boot:spring-boot-gradle-plugin:2.2.4.RELEASE'
}
gradlePlugin {
plugins {
greeting {
id = "org.sample.greeter"
implementationClass = "org.sample.GreetingPlugin"
}
}
}
publishing {
repositories {
maven {
url "../maven-repo"
}
}
}
package org.sample;
import org.gradle.api.DefaultTask;
import org.gradle.api.Plugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.testing.Test;
class Greeting Plugin implements Plugin<Project> {
#Override
public void apply(Project project) {
// Apply plugins to the project (already on the classpath)
project.getPluginManager().apply("war");
project.getPluginManager().apply("org.springframework.boot");
project.getPluginManager().apply("io.spring.dependency-management");
project.getPluginManager().apply(" org.asciidoctor.convert");
// Dependencies that you need for the code in the project that this plugin is applied
DependencyHandler dependencies = project.getDependencies();
dependencies.add(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, "org.springframework.boot:spring-boot-starter-web");
dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.junit.jupiter:junit-jupiter-engine");
dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, springBootStarterTest(dependencies));
projects.getTasks().withType(Test.class, test -> {
test.useJUnitPlatform();
});
}
private Dependency springBootStarterTest(DependencyHandler dependencies) {
Map<String, String> exclude = new HashMap<>();
exclude.put("group", "org.junit.vintage");
exclude.put("module", "junit-vintage-engine");
return ((ModuleDependency) dependencies.module("org.springframework.boot:spring-boot-starter-test")).exclude(exclude);
}
}
This is more verbose due to being written in Java, but it is functionally equivalent to putting this in your project's build.gradle:
plugins {
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test' {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
Related
Cannot understand how to configure build.gradle for using querydsl annotation processor without any jpa/jdo/mongo. I want to use #QueryEntity annotation to generate Q classes so then I will be able to compose dynamic SQL queries using DSL support then convert query to plain text and provide it to Spring R2DBC DatabaseClient executor.
Is there a way what gradle querydsl apt plugin and querydsl annotation processor to use for generating Q classes with #QueryEntity annotations in build.gradle file?
I'm using gradle 5, Spring Data R2DBC, Spring Boot, plan to integrate queryDsl with annotation processsor.
That's my currect build.gradle:
plugins {
id 'java'
id 'org.springframework.boot' version '2.2.1.RELEASE'
id "com.ewerk.gradle.plugins.querydsl" version "1.0.8"
}
apply plugin: 'io.spring.dependency-management'
group = 'com.whatever'
repositories {
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
}
ext {
springR2dbcVersion = '1.0.0.RELEASE'
queryDslVersion = '4.2.2'
}
dependencies {
implementation("com.querydsl:querydsl-sql:${queryDslVersion}")
implementation("com.querydsl:querydsl-apt:${queryDslVersion}")
implementation('org.springframework.boot:spring-boot-starter-webflux')
compileOnly('org.projectlombok:lombok')
annotationProcessor('org.projectlombok:lombok')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}")
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation('io.projectreactor:reactor-test')
}
test {
useJUnitPlatform()
}
Generally speaking, you shouldn't use the QueryDSL plugin.
In order to configure QueryDSL generation you just need the relevant querydsl module, the annotation processors and the generated source dir. For instance, with lombok integration, this configuration should work (you might need to play with the exact QueryDSL modules you need):
buildscript {
ext {
springBootVersion = '${springBootVersion}'
queryDslVersion = '4.2.2'
javaxVersion = '1.3.2'
}
}
plugins {
id 'idea'
}
idea {
module {
sourceDirs += file('generated/')
generatedSourceDirs += file('generated/')
}
}
dependencies {
// QueryDSL
compile "com.querydsl:querydsl-sql:${queryDslVersion}"
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:general")
// Lombok
compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
implementation("org.projectlombok:lombok:${lombokVersion}")
// Possibly annotation processors for additional Data annotations
annotationProcessor("javax.annotation:javax.annotation-api:${javaxVersion}")
/* TEST */
// Querydsl
testCompile "com.querydsl:querydsl-sql:${queryDslVersion}"
testAnnotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:general")
// Lombok
testImplementation("org.projectlombok:lombok:${lombokVersion}")
testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}")
testCompileOnly("org.projectlombok:lombok:${lombokVersion}")
}
Additional information: https://github.com/querydsl/querydsl/issues/2444#issuecomment-489538997
I want to leave this answer here as I struggled for several hours finding a solution that works for Kotlin (The question doesn't have a Java restriction as far as I can tell and there is really little information around for this). Kotlin supports annotation processing with the kapt plugin. In order to use this plugin for QueryDSL you need to 1. define the kapt plugin, and 2. specify the dependency that will do the processing, in this case com.querydsl:querydsl-apt. I used the general task for my plugin execution, but according to the documentation there are other options available (probably this can be useful for JPA, JDO, Hiberante with some extra tweaks)
plugins {
kotlin("kapt") version "1.4.10"
}
dependencies {
implementation("com.querydsl:querydsl-mongodb:4.4.0")
kapt("com.querydsl:querydsl-apt:4.4.0:general")
}
Now, whenever you run gradle build the annotation processing will trigger the DSL query class generation for your classes annotated with #QueryEntity. I hope it helps in case someone needs this for Kotlin.
This worked for me (Please follow the exact same order in the dependency)
sourceSets {
generated {
java {
srcDirs = ['build/generated/sources/annotationProcessor/java/main']
}
}
}
dependencies {
api 'com.querydsl:querydsl-jpa:4.4.0'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor('com.querydsl:querydsl-apt:4.4.0:jpa')
annotationProcessor('javax.annotation:javax.annotation-api')
}
This works!!!
ext {
queryDslVersion = '4.2.1'
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', 'build/generated/sources/annotationProcessor/java/main']
}
}
}
dependencies {
compile("com.querydsl:querydsl-core:${queryDslVersion}")
compile("com.querydsl:querydsl-jpa:${queryDslVersion}")
}
dependencies {
compile "com.querydsl:querydsl-jpa:${queryDslVersion}"
compileOnly 'org.projectlombok:lombok:1.16.18'
annotationProcessor(
"com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
"javax.annotation:javax.annotation-api:1.3.2",
"org.projectlombok:lombok"
)
for sinle gradle project just add next lines to the same build.gradle
for multi module gradle project add next lines to build.gradle of module where are jpa entities:
implementation("com.querydsl:querydsl-core:${queryDslVersion}")
annotationProcessor(
"com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
"javax.annotation:javax.annotation-api:1.3.2")
and next line to build.gradle of module where are jpa repositories:
implementation("com.querydsl:querydsl-jpa:${queryDslVersion}")
I have a multi-project in Gradle. The build.gradle script looks like:
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:2.0.4"
classpath "io.franzbecker:gradle-lombok:1.14"
}
}
allprojects {
//apply plugin: "base"
}
subprojects {
apply plugin: "com.github.johnrengelman.plugin-shadow"
apply plugin: "idea"
apply plugin: "java"
apply plugin: "io.franzbecker.gradle-lombok"
group = "io.shido"
version = "0.1.0-SNAPSHOT"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
repositories {
jcenter()
mavenCentral()
}
dependencies {
// [start] Research
//compileOnly "org.projectlombok:lombok:1.18.2"
// [end] Research
testCompile "nl.jqno.equalsverifier:equalsverifier:2.4.5"
testCompile "org.junit.jupiter:junit-jupiter-api:$junit_version"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_version"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_version"
}
//=================================================================================================
// P L U G I N S
//=================================================================================================
lombok {
version = "1.18.2"
}
//=================================================================================================
// T A S K S
//=================================================================================================
// shadowJar { ... }
test {
useJUnitPlatform()
}
}
I have a messages project then with this build.script:
plugins {
id "java-library"
}
repositories {
jcenter()
mavenCentral()
}
...and a core project with this build.script:
plugins {
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
dependencies {
compile project(":messages")
}
All of that should be OK.
If I write a simple class in messages:
package io.shido.event;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
#Getter
#Builder
#ToString
#EqualsAndHashCode(of = "name")
class Prototype {
private String id;
private String name;
}
...and then a unit test for the same:
package io.shido.event;
import org.junit.jupiter.api.Test;
final class PrototypeTest {
#Test
void instantiate() {
final Prototype event = Prototype.???
}
}
I'm expecting I can use a builder for that class right there, but there is nothing generated.
Am I missing something in the setup? Everything compiles, but I can't see anything being generated for Lombok. Not sure what else to try.
If you are using IDEA and recent version of Gradle (I think >= 4.7) you could use the following setup which is working fine in my different projects:
install Lombok plugin for IDEA , from Settings->Plugins configuration panel.
In your Gradle build script you can get rid of the lombok plugin declaration and lombok block : you just need to add the following dependencies on your project(s).
ext{
lombokVersion = '1.16.20'
junitVersion = '4.12'
}
dependencies {
compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
// other libs ...
// test dependencies
testCompile group: 'junit', name: 'junit', version: "${junitVersion}"
}
After project re-import, make sure to enable Annotation Processing in IDEA, from Settings -> Build,Execution,Deployment -> Compiler-> AnnotationProcessors menu : there is a checkbox "Enable annotation processing" which is disabled by default.
This should work fine, and you'll be able to use Lombok features in your main code and in unit test as well.
I tried to copy the Spring Boot Kotlin sample project https://github.com/JetBrains/kotlin-examples/tree/master/tutorials/spring-boot-restful. I Added some more dependencies and when I tried to build the executable jar and run it, I got the error:
Could not find or load main class...
Gradle build script:
buildscript {
ext.kotlin_version = '1.1.3' // Required for Kotlin integration
ext.spring_boot_version = '1.5.4.RELEASE'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Required for Kotlin integration
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
}
}
/*plugins {
id 'org.springframework.boot' version '2.0.0.RELEASE'
}*/
apply plugin: 'kotlin' // Required for Kotlin integration
apply plugin: "kotlin-spring" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin
apply plugin: 'org.springframework.boot'
jar {
baseName = 'gs-rest-service'
version = '0.1.0'
from {
(configurations.runtime).collect {
it.isDirectory() ? it : zipTree(it)
}
}
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.Applicationkt'
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin/'
test.java.srcDirs += 'src/test/kotlin/'
}
repositories {
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Required for Kotlin integration
compile("org.springframework.boot:spring-boot-starter-web")
compile group: 'org.apache.camel', name: 'camel-quartz2', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-http4', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-docker', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-aws', version: '2.20.2'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Change Applicationkt to ApplicationKt will work, and BTW you may upgrade Kotlin version to 1.3.50.
By Applicationkt I mean the one in this line:
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.Applicationkt'
Kotlin compiles the Application file in two different files:
one file called Application.class with the Springboot things
another file called ApplicationKt.class with the main method
In this second file is where the main function is located at, so you have to use this name in the build.gradle file.
mainClassName = 'org.jetbrains.kotlin.demo.ApplicationKt'
Update your build.gradle to
jar {
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.ApplicationKt'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
with an upper case K in ApplicationKt.
This is required because of the way Kotlin compiles to Java Bytecode. The fun main() function in Kotlin is not attached to any class, but Java always requires a class and does not support classless functions.
The Kotlin compiler has to create a Java class. Because you already defined a class Application it created one with the suffix Kt for the functions in your Kotlin file org/jetbrains/kotlin/demo/Application.kt. You have to set this class so that the JVM can find it.
BTW a Jar file is just a Zip file, you can unpack it and see for yourself if the ApplicationKt.class is there.
For me the main function needed to be outside the class body
#SpringBootApplication
#Configuration
class Application
(private val locationRepository: LocationRepository,
) : CommandLineRunner {
override fun run(vararg args: String?) {
whatever()
}
}
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
Indeed, Kotlin create file ApplicationKt.class in the jar if your main class file is named Application.kt. You have to add the following lines:
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = 'org.jetbrains.kotlin.demo.ApplicationKt'
If you use the classic jar plugin, you can do as below (which is described in previous responses):
jar {
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.ApplicationKt'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
However, my preference is to use bootJar plugin which is much clear and which allow me to use layered jars for example:
bootJar {
layered() // Not useful if you don't want to use layered jars
}
I was trying to build a jar out of my first groovy script. My project structure is as follows:
- build.gradle
- src\main\groovy\app\Test.groovy
My original gradle script:
apply plugin: 'groovy'
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
testCompile group: 'junit', name: 'junit', version: '4.11'
}
sourceSets.main.groovy.srcDirs = ["src/main/groovy"]
jar {
manifest {
attributes('Main-Class': 'app.Test')
}
}
From the guides I read, this should create a runnable jar. When I try to run it though I always get the error
Error: Could not find or load main class app.Test
I found out now that I need to add these two lines to the jar task:
from files(sourceSets.main.output.classesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
The weird thing is that if I replace the groovy script with a Test.java class (same content), I don't need those two extra lines to run the jar.
I couldn't find out why I need them or what exactly they do. Can anyone explain that, or offer a documentation link?
I'm new to SO, please help me with my mistakes.
EDIT
The code suggested by tim_yates is translated to test.jar with the following content:
META-INF/MANIFEST.MF
Manifest-Version: 1.0
Main-Class: app.Test
app/Test.class
package app;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class Test implements GroovyObject {
public Test() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].callStatic(Test.class, "Hi!");
}
}
I execute with the following statement:
java -jar test.jar
Which results in the error message stated above.
You've got to remember that this jar contains Groovy compiled classes. The answer is in your decompiled source that you showed in the beginning. It imports Groovy runtime classes.
If you just run java -jar test.jar those classes are not on the classpath.
Either include groovy on the classpath of your command line or use the gradle application plugin to build a fat JAR (which is probably better for runnable jars) that contain all your application dependencies.
apply plugin: 'groovy'
apply plugin: 'application'
mainClassName='app.Test'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.7'
testCompile 'junit:junit:4.12'
}
task uberjar(type: Jar,dependsOn:[':compileJava',':compileGroovy']) {
from files(sourceSets.main.output.classesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
manifest {
attributes 'Main-Class': mainClassName
}
}
Then build your jar with gradle uberjar
Assuming Test.groovy looks something like:
package app
class Test {
static main(args) {
println "Hi!"
}
}
Then you only need the following build script:
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.7'
testCompile 'junit:junit:4.12'
}
jar {
manifest {
attributes('Main-Class': 'app.Test')
}
}
I'm trying to encapsulate android plugin in my own plugin, but when I'm trying to apply my plugin build fails with an exception:
A problem occurred evaluating root project 'myproj'.
> Failed to apply plugin [id 'com.mycomp.build']
> Failed to apply plugin [id 'android-library']
> Plugin with id 'android-library' not found.
Here is how I'm applying android plugin inside my own plugin's implementation:
// build.gradle
apply plugin: 'groovy'
version = '1.0'
group = 'com.mycomp'
dependencies {
compile gradleApi()
compile localGroovy()
}
// Build.groovy
package com.mycomp
import org.gradle.api.Plugin
import org.gradle.api.Project
class Build implements Plugin<Project> {
void apply(Project project) {
println 'Hello from com.mycomp.Build'
project.beforeEvaluate {
buildscript.configurations.classpath +=
'com.android.tools.build:gradle:1.0.0-rc1'
}
project.configure(project) {
buildscript.repositories.mavenCentral()
apply plugin: 'android-library'
}
}
}
For some reason a classpath is not being properly loaded, what am I doing wrong?
I guess that at the time you'd like to add the plugin dependencies for the build script have been already resolved, thus it won't work that way. You need to specify the plugin You'd like to apply as a script dependency itself.
It will work that way:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0-rc1'
}
}
apply plugin: 'groovy'
apply plugin: Build
version = '1.0'
group = 'com.mycomp'
dependencies {
compile gradleApi()
compile localGroovy()
}
import org.gradle.api.Plugin
import org.gradle.api.Project
class Build implements Plugin<Project> {
void apply(Project project) {
project.configure(project) {
apply plugin: 'android-library'
}
}
}
Now, android-plugin is found but it fails because of the fact that groovy plugin had been applied earlier and there's a conflict.
Use the project's PluginManager. For example, the war plugin pulls in the java plugin like this:
public class WarPlugin implements Plugin<Project> {
// ...
public void apply(final Project project) {
project.getPluginManager().apply(org.gradle.api.plugins.JavaPlugin.class);
// ...
}
// ...
}