How to apply plugin to allprojects with new Gradle plugins mechanism? - gradle

Before Gradle 2.1 I could apply plugin to all projects by using allProjects closure (by prevoisly resolving the jar, of course):
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:3.0.1"
}
}
allprojects {
apply plugin: "com.jfrog.artifactory"
}
With new publishing mechanism it looks like the plugins closure can't be used inside allprojects:
allprojects {
plugins {
id "com.jfrog.artifactory" version "3.0.1"
}
}
fails with:
"Could not find method plugins() for arguments [build_xxxx_run_closure1_closure4#yyyyy] on root project"
What are the rules of using plugins closure? Is the plugin applied to current project only? If so, how can I apply it to all projects without repeating the plugins closure inside each build?

The new plugins {...} syntax cannot be used within a allprojects {...} or subprojects {...} closure. Additionally, it can only be used within build scripts (no script plugins, init scripts, etc). If you want to avoid having to apply the plugin to each project individually I'd suggest using the old notation. This is an issue the Gradle team is aware of and a solution will be introduced in future versions.
Update: Starting with Gradle 3.0 you can do this in a slightly modified way. You still have to explicitly use apply() but you no longer have to deal with all the buildscript { } nonsense to get the plugin on your classpath. This also allows you to conditionally apply plugins. Check out the Gradle 3.0 release notes for more information.
plugins {
id 'my.special.plugin' version '1.0' apply false
}
allprojects {
apply plugin: 'java'
apply plugin: 'my.special.plugin'
}

Related

Add Android plugin to gradle init script

I'm currently working on creating my own Gradle custom distribution and I want to add some default settings to all my Android projects. I tried to do this with the following init script:
initscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath "com.android.tools.build:gradle:7.1.1"
}
}
apply plugin: 'com.android.application'
gradle.allprojects {
android {
...
}
dependencies {
implementation 'junit:junit:4.12'
}
}
but with this I get Plugin with id 'com.android.application' not found. What am I missing here?
For gradle init script, you must use the fully qualified class name of the plugin instead of the id. So you will have to apply the plugin this way:
apply plugin: com.android.build.gradle.AppPlugin
Change the initscript to buildscript.
And also, does this build.gradle in root project/directy? Because you Android Gradle Plugin is applied in the gradle module not in the root.
So apply:
apply plugin: "com.android.application"
inside your module's build.gradle.
Make sure your Gradle version is compatible with your Android Gradle Plugin.
For more information you can follow the link below, if it helps you
plugin with id com.android.application not found

Can I specify in gradle that I only want to use a certain plugin for tests?

I have a project and for testing purposes only I want to standup a quick websocket server. Spring Boot seems like the simplest way to do that, but I don't want to include the plugin in my implementation, only for tests. I can't seem to find documentation on something like this, but in general I've found the answer to the question "can gradle do this" is usually yes.
So how would I go about specifying to only use the 'org.springframework.boot' plugin for test builds?
I've tried placing the plugins {} block inside a test {} block but that doesn't work.
I'm pretty sure you may just apply the org.springframework.boot plugin in the regular way and it won't effect your build artifacts.
However, it is possible to apply plugins dynamically, if you are afraid that they might have undesired side-effects on build artifacts. The old plugin mechanism actually worked this way by default and separated the resolution of plugins from their application to the Project instance:
// This part resolves the plugin
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.3.2.RELEASE'
}
}
// This part applies the plugin
apply plugin: 'org.springframework.boot'
This way it was possible to apply a plugin based on a condition:
if (testMode) {
apply plugin: 'org.springframework.boot'
}
Using the new plugins block to apply plugins, this is not possible directly, as the plugins block is a special block that does not allow custom code:
// This is not allowed!
plugins {
if (testMode) {
id 'org.springframework.boot' version '2.3.2.RELEASE'
}
}
The solution is to tell the plugins block to resolve a plugin without applying it automatically. This can then be done dynamically using apply plugin::
plugins {
id 'org.springframework.boot' version '2.3.2.RELEASE' apply false
}
if (testMode) {
apply plugin: 'org.springframework.boot'
}
You don't have to use org.springframework.boot plugin: just use io.spring.dependency-management plugin. This way, you will be able to declare spring-* related dependencies in your testImplementation configuration, without any impact on the implementation configuration.
plugins {
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-websocket'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:2.2.0.RELEASE"
}
}
Then in your test sources, you will be able to declare SpringBoot application and other websocket-related stuff (controllers)
EDIT what org.springframework.boot is actually doing, is to change the packaging of the main jar artifact by declaring the bootJar task, see Springboot plugin reference. In your case you don't need this, as far as I understand, if you just want to run some Spring application in test sourceset.

Different ways to apply plugins ? (Gradle Kotlin DSL)

Trying to migrate this one project's build to GSK.
We have this in Groovy:
allprojects {
apply plugin: 'java'
...
sourceSets {
...
}
sourceCompatibility = ...
}
So while figuring out how to access the plugin convention in Kotlin, I found out that:
allprojects {
plugins {
java apply true
}
...
println("Project $name, plugins: ${plugins}") // empty list
val java = the<JavaPluginConvention>() // throws exception
}
but if you do it like this:
allprojects {
apply {
plugin(JavaPlugin::class.java)
}
}
plugin is applied and convention becomes accessible
WTH?
This issue isn't specific to Kotlin, and is due to a race condition. While the script is being evaluated, it may not have added the plugin to the classpath yet. This is one of many reasons why the plugins block was created, as it's specifically evaluated prior to the rest of the scripts evaluation during a buildscript phase. That said however, this special treatment is only done if this block is at the top of the script, and not when it's within a subprojects or allprojects block, as those blocks are technically arbitrary and are evaluated later to ensure the buildscript is idempotent. In your case, you are just moving up the race by placing it in allprojects block, and are getting lucky.
When dealing with multi-project builds, this is problematic, however if possible, the best is to declare the plugin in the plugins block with the apply false constrained syntax to add it to your build's classpath in the buildscript phase. You will then be able to apply the plugin later via the plugin's id during script evaluation (version isn't necessary, as it's used for fetching the dependency only).
An example:
plugins {
id("org.gradle.sample.hello") version "1.0.0" apply false
}
subprojects {
apply(plugin = "org.gradle.sample.hello")
}
The Gradle User Guide does a great job at explaining how these should be used, and the balance you will need to consider in multi-module projects.
Due to the nature of how some plugins are written, there may be cases where other issues will arise, but if plugin authors are following best practice guidelines, you'll be fine.
If your plugins are for Kotlin, then the best way for now is:
plugins {
kotlin("jvm") version "1.7.21"
kotlin("plugin.serialization") version "1.7.21"
}

Spring Boot Gradle Plugin "Blessed" Dependencies in a multi-project environment

In a multi-project Gradle environment, I have the usual buildscript block in my parent build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.7.RELEASE")
}
}
However, the dependencies which are supposed to be "blessed" (See 54.2 Declaring dependencies without versions) does not seem to be, since Gradle does not search for the correct version (because there is not version at all). I wonder if I am missing some additional configuration or it's just not possible at this moment to have "blessed" dependencies for a multi-project Gradle environment.
Applying spring-boot plugin to all projects (not just parent project) should help.
Try:
allprojects {
apply plugin: 'spring-boot'
}
EDIT: Andy and I posted the answers almost at the same time. But yes, either allprojects or subprojects should work fine, depending whether you parent project needs the plugin, or not.
You need to apply the Spring Boot plugin to every project that you want to use the Boot-provided dependency versions. As it's a multi-project build (and assuming you want to apply the plugin to every subproject), add the following to your build.gradle:
subprojects {
apply plugin: 'spring-boot'
}

How to configure a plugin to depend on a specific version of gradle?

I am writing a set of Gradle plugins, but I want to control the specific versions of groovy and gradle that are used.
I don't want the plugins to depend on whatever versions of Gradle/Groovy are installed, like the following would do:
dependencies {
compile localGroovy()
compile gradleApi()
}
Another reason I don't want to use the local method - when you use a proper dependency specification, Gradle then knows about the source code for those libs and the IDE plugins can hookup the source automatically.
Below are the relevant sections of my build script:
allprojects { Project iProject ->
apply plugin: 'idea'
apply plugin: 'maven'
repositories {
jcenter()
}
}
subprojects { Project iProject ->
apply plugin: 'groovy'
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.2'
}
}
project(':eclipsei2g') {
group = 'eclipsei2g'
version = '0.0.1-SNAPSHOT'
dependencies {
compile 'org.gradle:gradle-core:2.0'
}
}
project(':g2idea13') {
group = 'g2idea13'
version = '0.0.1-SNAPSHOT'
dependencies {
compile 'org.gradle:gradle-core:2.0'
compile 'org.gradle-plugins:gradle-ide:2.0'
}
}
When I run this I get an error resolving the gradle-ide dependency:
Could not resolve all dependencies for configuration ':g2idea13:compile'.
> Could not find org.gradle:gradle-ide:2.0.
Searched in the following locations:
http://jcenter.bintray.com/org/gradle/gradle-ide/2.0/gradle-ide-2.0.pom
http://jcenter.bintray.com/org/gradle/gradle-ide/2.0/gradle-ide-2.0.jar
Required by:
g2idea13:g2idea13:0.0.1-SNAPSHOT
There doesn't seem to be anything on the jcenter repository since 0.9 for the plugins stuff.
I also tried 'org.gradle:gradle-ide:2.0'.
Is this even how I should be doing this? Is there another way to specify a specific gradle version? Am I just using the wrong repository? I couldn't even get gradle-core to resolve on mavenCentral(). Is there an official Gradle repository somewhere that I should be using?
gradleApi() is the way to go. There isn't currently a public list of dependencies for Gradle plugins.

Resources