I have a build script where a task adds dependencies to a configuration. Then compileJava depends on that task.
plugins {
`java-library`
}
repositories {
mavenCentral()
}
val setupDeps = tasks.register("setupDeps") {
doLast {
dependencies.add("implementation", "org.clojure:clojure:1.10.3")
}
}
tasks.named("compileJava") {
dependsOn(setupDeps)
}
From the shell this works. But in intellij the tooling api of gradle is used to build the dependency model. And the tooling api does not know that it has to execute this task first. Is there any way to configure this? I could of course always dynamically add the dependencies during configuration phase but i don't want to do that because to find out what needs to be added is slow.
I found the answer myself.
The task name to dependOn is :prepareKotlinBuildScriptModel
So this solves the problem:
tasks.findByPath(":prepareKotlinBuildScriptModel")?.dependsOn(setupDeps)
I have a project with two subprobjects: gradle-plugin and plugin-consumer. I want to apply the plugin from gradle-plugin to plugin-consumer. I tried to do this:
// plugin-consumer/build.gradle
buildscript {
dependencies {
classpath project(':gradle-plugin')
}
}
apply plugin: 'my.plugin.id'
But I was greeted with the following error:
A problem occurred configuring project ':plugin-consumer'.
> Cannot use project dependencies in a script classpath definition.
I assume this is not supported because it'd require fully building gradle-plugin before plugin-consumer can be configured.
Fortunately I can use a fileTree dependency to accomplish my goal:
// plugin-consumer/build.gradle
buildscript {
dependencies {
classpath fileTree(includes: ['*.jar'], dir: '../gradle-plugin/build/libs')
}
}
apply plugin: 'my.plugin.id'
This works, but it feels like a massive hack and leads to "bootstrapping problems".
For example, I can't clean gradle-plugin because the (deleted) jar file is necessary for configuring plugin-consumer, which must be done to (re)build gradle-plugin.
Fortunately this can be avoided by always running build immediately after clean (in the same 'run' so to speak). This can be done manually (gradle clean build) or automatically (with clean.finalizedBy(build)). Again, this works, but feels like a hack.
At last, my actual question: is there a better way to do this?
Please note that gradle-plugin is an independent plugin that's not only used by plugin-consumer, therefore buildSrc is unfortunately not an appropriate solution here.
You can publish the plugin to your local Maven repository with the Maven Publish plugin. Then simply consume it like any other artifact.
Assuming you have something similar in your plugin project:
plugins {
`maven-publish`
`java-gradle-plugin`
}
Simply publish it locally:
./gradlew :my-plugin-project:publishToMavenLocal
Then in your consuming project, something like:
buildscript {
repositories {
mavenLocal()
}
dependencies {
"classpath"("com.example:my-plugin-gav:1.0.0-SNAPSHOT")
}
}
// apply plugin
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"
}
I have a Kotlin Gradle project.
If I create a .kts file it runs in InteliJ alright except when it is in the /src/main/kotlin folder.
IDEA highlights the whole file in red.
Gradle throws out compilation exception.
The exception is
...src/main/kotlin/test.kts: (3, 1): Cannot access script base class 'kotlin.script.templates.standard.ScriptTemplateWithArgs'. Check your module classpath for missing or conflicting dependencies`.
What is the problem?
My build.gradle:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.0-rc-131'
}
group 'kotlin.tutorials.coroutines'
version '1.0-SNAPSHOT'
repositories {
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap' }
mavenCentral()
jcenter()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
ext.ktor_version = '1.0.0-alpha-1'
ext.coroutines_version = '0.30.2-eap13'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "ch.qos.logback:logback-classic:1.2.3"
//KTOR features
compile "io.ktor:ktor-jackson:$ktor_version"
compile "io.ktor:ktor-auth:$ktor_version"
compile "io.ktor:ktor-auth-jwt:$ktor_version"
compile "io.ktor:ktor-freemarker:$ktor_version"
compile "io.ktor:ktor-html-builder:$ktor_version"
}
compileKotlin.kotlinOptions.jvmTarget = "1.8"
compileTestKotlin.kotlinOptions.jvmTarget = "1.8"
.kts files should go to the src/main/resources folder since src/main/kotlin is for .kt files.
Scripts are a completely different animal in this sense, and you should use something like KtsRunner to execute them.
Related question is here.
If you just want to use scripts from IDEA, then you should use Scratch files which are supported out of the box.
The solution turned out to be very straight forward.
The compiler could not find utility classes that are usually added to any Kotlin script classpath. Adding one dependency to my build.gradle fixed it:
dependencies {
compile "org.jetbrains.kotlin:kotlin-scripting-jvm"
}
P.S.
I created 2 tickets to improve Kotlin script support in InteliJ:
https://youtrack.jetbrains.com/issue/KT-27542
https://youtrack.jetbrains.com/issue/KT-27544
If you care about those features, please vote them up!
I had quite good gradle configuration, that built everything just fine. But one of the projects of my multi-project build derived from the rest of them so much, that I would gladly move it to another git repo and configure submodules to handle it.
First, I moved Project and its resources to subfolder Libraries/MovedProject. After altering some lines in gradle configurations it worked fine. But then I decided to write a new build.gradle just for this project, and move all configurations there from the main one.
And this is where everything stopped working. When I try to call any task it always ends
with Could not find property 'sourceSets' on project ':Libraries/MovedProject'. Line which is responsible for it is:
dependencies {
...
if (noEclipseTask) {
testCompile project(':Libraries/MovedLibrary').sourceSets.test.output
}
}
which I use for running tests in which I use classes from other projects. If I remove that line, the build fails only when it reaches compileTestJava task of projects that make use of MovedProject. If I remove that line and call gradle :Libraries/MovedLibrary:properties I can see :
...
sourceCompatibility: 1.7
sourceSets: [source set main, source set test]
standardOutputCapture: org.gradle.logging.internal.DefaultLoggingManager#1e263938
...
while gradle :Libraries/MovedLibrary:build builds correctly.
Currently I've got everything set up as following:
directories:
/SomeMainProject1
/SomeMainProject2
/SomeMainProject3
/Libraries
/MovedProject
build.gradle
dependencies.gradle
project.gradle
tasks.gradle
/Builder
dependencies.gradle
project.gradle
tasks.gradle
build.gradle
settings.gradle
settings.gradle
include Libraries/MovedProject,
SomeMainProject1,
SomeMainProject2,
SomeMainProject3
sourceSets for MovedProject are defined in Libraries/MovedProject/project.gradle:
sourceSets {
main {
java {
srcDir 'src'
srcDir 'resources'
}
resources { srcDir 'resources' }
}
test { java {
srcDir 'test/unit'
} }
}
dependencies that makes use of sourceSets.test.output are stored in Builder/dependancies.gradle, and set for each project that needs MovedProject to run tests:
project(':SomeMainProject1') {
dependencies {
...
if (noEclipseTask) {
testCompile project(':Libraries/net.jsdpu').sourceSets.test.output
}
}
}
What would be the easiest way to get rid of that error and make gradle build projects with current directory structure? I would like to understand why gradle cannot see that property.
The line in question is problematic because it makes the assumption that project :Libraries/MovedLibrary is evaluated (not executed) before the current project, which may not be the case. And if it's not, the source sets of the other project will not have been configured yet. (There won't even be a sourceSets property because the java-base plugin hasn't been applied yet.)
In general, it's best not to reach out into project models of other projects, especially if they aren't children of the current project. In the case of project A using project B's test code, the recommended solution is to have project B expose a test Jar (via an artifacts {} block) that is then consumed by project A.
If you want to keep things as they are, you may be able to work around the problem by using gradle.projectsEvaluated {} or project.evaluationDependsOn(). See the Gradle Build Language Reference for more information.
I had a similar error happen to me in a multimodule project, but for me the cause was as simple as I had forgotten to apply the java-library plugin within the configurations, I only had maven-publish plugin in use.
Once I added the plugin, sourceSets was found normally:
configure(subprojects) {
apply plugin: 'maven-publish'
apply plugin: 'java-library'
....