I am new to Gradle and I am reading the documentation but I don't understand some parts of it. One of these parts is connected with buildscript block. What is its purpose?
If your build script needs to use external libraries, you can add them to the script's classpath in the build script itself. You do this using the buildscript() method, passing in a closure which declares the build script classpath.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
Ok but what is the difference with:
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
For example, why it is necessary to use buildscript?
The buildScript block determines which plugins, task classes, and other classes are available for use in the rest of the build script. Without a buildScript block, you can use everything that ships with Gradle out-of-the-box. If you additionally want to use third-party plugins, task classes, or other classes (in the build script!), you have to specify the corresponding dependencies in the buildScript block.
The global level dependencies and repositories sections list dependencies that required for building your source and running your source etc.
The buildscript is for the build.gradle file itself. So, this would contain dependencies for say creating RPMs, Dockerfile, and any other dependencies for running the tasks in all the dependent build.gradle
I appreciate Peter's answer... but it was not immediately obvious to me what the rest of the build script meant as emphasized in the answer and in the documentation.
Usually bringing in dependent functionality is for use in the Java program or whatever other program you might be writing. Bringing in Spring say, is not to be used in the build script, but in the Java program. Putting it in the buildscript closure ensures that the dependencies are available for use within the gradle build itself. Not the output program.
A bit more explanation by demonstrating Android top-level gradle file.
buildscript {
// this is where we are going to find the libraries defined in "dependencies block" at below
repositories {
google()
jcenter()
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
// everything listed in the dependencies is actually a plugin, which we'll do "apply plugin" in our module level gradle file.
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2' // this is android gradle plugin
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // kotlin gradle plugin
}
}
module level gradle file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
What is "plugin"? They are just java classes, which implement Plugin interface. Under the interface, it has a method "apply" to add multiple task objects with different names. Task is a class where we can implement the workflow. For instance, the build task consists of the flow of building the app.
So, what does buildscript do? It defines where to find the plugins. What does plugin do? It encompasses multiple tasks. What does task do? It provides us with the build, install, lint, etc.
My understanding might be wrong. Please don't hesitate to correct me if you find anything is misleading.
The "buildscript" configuration section is for gradle itself (i.e. changes to how gradle is able to perform the build). So this section will usually include the Android Gradle plugin.
It's a bit high level but hope helps.
For me, clear distinction started to shape once I start to understand what is a building block, method, and task. How the syntax looks like, how you can configure them etc. So I suggest you go through all these. After that, you can begin to make sense out of this syntax.
Then it's very important to know what's the type of the object build.gradle (an instance of Project class) so to know what can you have inside a build.gradle file. That would answer where that 'buildScript' and others come from. And to extend your capabilities/features (let's say android), see how plugins can help.
Last but not least, there's a very good tutorial here that talks about closures, delegates which are the concepts essential to understand the script.
buildscript block is used for the build script, NOT for the gradle build output (for example, an Android app apk). In the following code example, the encoding code is used in build script, not in the gradle build output program; so the dependecies should be added to buildscript block.
https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
External dependencies for the build script
Instead of manipulating the
script classpath directly, it is recommended to apply plugins that
come with their own classpath. For custom build logic, the
recommendation is to use a custom plugin. If your build script needs
to use external libraries, you can add them to the script’s classpath
in the build script itself. You do this using the buildscript()
method, passing in a block which declares the build script classpath.
The block passed to the buildscript() method configures a ScriptHandler instance.
You declare the build script classpath by adding dependencies to the
classpath configuration. This is the same way you declare, for
example, the Java compilation classpath. You can use any of the
dependency types except project dependencies.
Having declared the build script classpath, you can use the classes in
your build script as you would any other classes on the classpath. The
following example adds to the previous example, and uses classes from
the build script classpath.
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
tasks.register('encode') {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}
}
You can imagine the buildScript block as contents from the Gradle core, like plugins{} block that already goes into Gradle internally.
So all plugins from buildScript of parent build.gradle will be available in all nested build.gradle modules.
I believe that everything in the buildscript {} will be available for the current build script itself and then the all its subprojects.
And for the properties declared in the file itself outside of the buildscript {} it will not become immediately available to for buildscript of the given project itself, but to all its subprojects.
So if you want to declare something and use it for the buildscript itself right away (current buildscript and not just subproject's buildscript), declare them in the buildscript {} for the current project and it also has the side effect to let subproject use it later on.
If you just want to declare something globally (for sub-projects's buildscripts) you can declare them directly as ext {} in parent project. The parent project won't be able to use them for itself's buildscript but it will be available all the subproject to use, in or out of the buildscript clause.
For example in parent project:
ext {
kotlin_version_XX = '1.7.10'
}
buildscript {
ext {
kotlin_version = '1.7.10'
}
// kotlin_version will be available here since declared in buildscript{}
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// will NOT be available here -- error due to kotlin_version_XX declared in project
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version_XX"
}
And if you have a subproject:
dependencies {
// both kotlin_version and kotlin_version_XX can be used here, since it was declared in parent project
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version_XX"
}
buildscript {
// both kotlin_version and kotlin_version_XX can even be used here for subproject's script's use, since it was already declared in parent project
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version_XX"
}
We have a Gradle build that includes a buildSrc with some custom plugins. Those plugins apply yet other plugins. For example, our plugin applies com.android.tools.build:gradle. For annotation processing that library needs to be on Gradle's classpath during compilation. So, putting this in our main build.gradle works:
buildscript {
repositories {
google()
}
dependencies {
classpath "com.android.tools.build:gradle:$gToolsVersion"
}
}
However, that means that for a user to apply this plugin they must (1) apply our plugin and (2) add that buildscript boilerplate. It seems like that shouldn't be necessary. We can also add a project.buildscript block inside our plugin but that too seems unnecessary and, due to this bug is problematic: https://developer.android.com/studio/build/gradle-plugin-3-0-0.html?utm_source=android-studio#known_issues.
I added the com.android.tools.build:gradle dependency to buildSrc/build.gradle as a runtime dependency. It seems like that should work: I thought that tells Gradle that in order to run my plugin that library (and its dependencies) need to be on the classpath. However, gradle buildEnvironment (and the fact that our build fails) makes it clear that's not the case.
So, questions:
What's the difference between a runtime dependency specified in buildSrc/build.gradle and a classpath dependency specified in a buildscript block in a regular build.gradle?
How can I arrange things so that users can apply the plugin from buildSrc and not have to also add the buildscript block to their build.gradle?
I got a slightly different problem and found an acceptable solution that might help with for your second question: I wanted to apply the same repositories in the buildSrc/build.gradle and twice in the root build.gradle.
repositories.gradle in the project root:
repositories {
if (project.hasProperty('nexus')) {
maven {
url 'http://localhost:8081/repository/JCenter/'
}
maven {
url 'http://localhost:8081/repository/Maven_Google/'
}
} else {
jcenter()
google()
}
}
ext {
androidGradleBuildToolsDependency = 'com.android.tools.build:gradle:3.1.3'
}
buildSrc/build.gradle:
buildscript {
apply from: '../repositories.gradle'
}
allprojects {
apply from: '../repositories.gradle'
}
dependencies {
// androidGradleBuildToolsDependency is defined in repositories.gradle
implementation androidGradleBuildToolsDependency
}
Root build.gradle:
buildscript {
apply from: 'repositories.gradle'
}
allprojects {
// this line will also be executed from the build.gradles in subprojects, so the working
// directory isn't always the same, so we use the absolute path here
apply from: "${rootProject.projectDir}/repositories.gradle"
}
Note that you do not need the classpath dependency inside the buildscript block of the root build.gradle as you normally would. The implementation dependency in the repositories.gradle seems to auto apply it.
My solution probably doesn't work when the build.gradles are supplied via a dependency.
Stumbled upon this while digging into a tangential problem.
I've been successfully using buildSrc/build.gradle as the place to define dependencies that would normally belong in the root-project's buildscript classpath for a few of my projects.
You can see a working example here: https://github.com/episode6/chop/blob/develop/buildSrc/build.gradle
I used to use compile dependencie but just switched to runtimeClasspath which feels more appropriate and also works. I don't think your classpath dependencies were working because they would be on the classpath of the buildSrc project, but not compiled into or run along side it.
If you decide to go this route, you may run into the problem I was just digging into which only came up because of this approach.
When I tried this approach with the dokka plugin, I got the following error
Could not resolve all files for configuration ':detachedConfiguration1'.
> Cannot resolve external dependency org.jetbrains.dokka:dokka-fatjar:0.9.17 because no repositories are defined
I was able to workaround this by adding jcenter() to the root project's buildscript repositories: https://github.com/episode6/chop/blob/develop/build.gradle#L2
I have a webapp compilation subproject which is written in a separate script plugin and is being added to the root project via "apply from: scriptSrc" syntax. Top of my script plugin looks like this:
buildscript {
repositories {
jcenter()
}
dependencies{
classpath 'com.moowork.gradle:gradle-grunt-plugin:0.10'
}
}
apply plugin: "com.moowork.grunt"
However its not recognizing the plugin and fails on the initialization phase saying
Plugin with id 'com.moowork.grunt' not found.
I am following this: https://plugins.gradle.org/plugin/com.moowork.grunt/0.10.
Anyone else having issues with grunt plugin inclusion in script plugin?
Not sure exactly what exactly is causing the issue. Make sure you have the buildscript setup the following way.
apply plugin: 'com.moowork.grunt'
buildscript{
dependencies{
classpath 'com.moowork.gradle:gradle-grunt-plugin:0.13'
classpath 'com.moowork.gradle:gradle-node-plugin:0.12'
}
// In this section you declare where to find the dependencies of your project
repositories {
jcenter()
mavenCentral()
}
}
If you go plan to use plugin:0.13, you need to include the node-plugin as well or else you may get a NoClassDefFound Exception. This may not be necessary if you continue to use version 0.10.
Also, if you are working on a gradle project in eclipse and building the project from commandline, make sure you refresh the project so build reflects in your workspace.
I came across gradle plugin to help me to deal with dotted property names. It works fine in single project when used like this:
apply plugin: 'config'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.esyfur:gradle-config-plugin:0.4.+'
}
}
task printProps() {
println(config.grafana.url)
}
However, I want to use this plugin in multiple projects (multi-module) and would like idealy not to repeat such initialization in every project but rather inject it somehow to have it more manageable.
I have failed to find out how to do it or if it can be done. I tried e.g. in parent build.gradle use this:
allprojects {
apply plugin: 'config'
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.esyfur:gradle-config-plugin:0.4.+'
}
}
but it does not work. Gradle complains that it can not find property 'config' on task.
Update
After Peter's comment I started wondering around and creating an example project from the scratch and incrementally get it closer to my 'real project'. I was not completely precise in describing my setup. In settings.gradle I use
rootProject.children.each { project ->
project.buildFileName = "${project.name}.gradle"
}
which causes this problem. Everything works fine When switched back to build.gradle names.
The problem is that your settings.gradle doesn't configure rootProject.buildFileName to match the non-standard filename used for the build script. Therefore the build script doesn't get evaluated, and its buildscript block never takes effect.
I wrote a custom gradle plugin which comes with an additional compile step. For the compilation some classes of the plugin itself are needed, since it is an annotation processor.
I try to solve it by adding the plugin as a compile dependency this way:
// in the custom plugin
project.dependencies {
compile "com.thilko.spring:gradle-springdoc-plugin:0.1.SNAPSHOT"
compile localGroovy()
}
This solution is working but introduces duplication since I have to declare the same plugin version that is already declared in the build script section of the project that uses the plugin:
// build.gradle of the project that uses the plugin
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.thilko.spring:gradle-springdoc-plugin:0.1"
}
}
apply plugin: 'springdoc'
Is there a way to reuse the dependencies defined in the buildscript section?
If you think it's worth it, you can declare an extra property inside the buildscript block (e.g. ext.springdocPlugin = "com.thilko.spring:gradle-springdoc-plugin:0.1.SNAPSHOT"), and then reuse it from outside (e.g. dependencies { compile buildscript.springdocPlugin }).
If you add the dependency to the pom of the your plugin, it will be added to the buildscript dependencies of the project you apply your plugin to.