how to call a gradle task which inside a jar with a parameter - gradle

I want to run a gradle task embedded in a jar, the jar which I am not producing myself. Is this even possible? I could also paste in the gradle task into my local build.gradle and run it but all the code would still need to reference the jar to actually run the main class.

You can add the jar which contains the Task you want to use in your buildscript classpath, using buildscript block. This way, you will be able to import and use this Task class in your build script. The buildscript block is generally used to add external Gradle Plugin jars into the script classpath, so that they can be applied, but you can also reference jars that provide no plugin implementation but only Task implementation classes.
Example
Assuming that:
the coordinates of the external jar is org.company.gradle:custom-tasks:0.1
the Task implementation class is org.company.gradle.tasks.MyTask
the Task implementation has a configurable property message
Then you can implement your build script as follows:
buildscript {
repositories {
// define repositories
}
dependencies {
// make the external jar available in the build script classpath
classpath "org.company.gradle:custom-tasks:0.1"
}
}
// use the Task
task 'myTask'(type: org.company.gradle.tasks.MyTask) {
message = "custom message"
}

Related

Why dont I always see `buildscript` in gradle files? [duplicate]

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"
}

Gradle task vs plugin vs task type?

apply plugin: 'war'
war {
...
}
From what I understand:
There is a gradle war plugin which I get available from the apply plugin: 'war' line, that allows me to use the war task declared via the war { ... } which is of type war?
If you apply the Gradle War plugin, a task with the name war is created automatically. When you refer to war { ... } in your build script, you are effectively configuring the war task instance with the help of a closure.
war {
baseName = 'example'
}
does the same thing as
war.baseName = 'example'
The benefit of the closure syntax is that you can configure the task instance through multiple statements. It also becomes more readable and looks more like a DSL.
The actual type of the task named war is War. You can always find out the type of task from the command line via gradle help --task <task-name>.
You can also get some information about the type of variable in your build script:
task printType {
doLast {
println war
}
}
Executing the task will print out the following:
$ gradle printType
:printType
task ':war'
The "war" block below the "apply" isn't defining a task, it's simply a configuration block used by the plugin.

call java function in gradle script

I have a java class which does some kind of functionality, one of these function returns something that I need to use it into gradle script to set the project properties.
I had achieved it by creating an artifact of project and used that artifact by adding it into classpath, that gave me accessibility of that class and function.
buildscript {
repositories {
jcenter()
maven{
url 'http:localhost:8081/artifactory/temp'
}
}
dependencies {
classpath "utility:sampleutility:1.0"
}
}
import com.polsys.utility.MyUtil
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile 'HRP:'+new MyUtil().callMe()+':1.0'
//callme function returns the name of artifact.
testCompile 'junit:junit:4.12'
}
Now, I had achieved it by the way as mentioned above that is by creating artifact, add that artifact into classpath, then import classes and use function. Is this any way by which I can call functions of current project? so I can merge that functionality which is available in the artifact into current project.
Simple way is to put your Java/Groovy code under buildSrc dir. Gradle will compile it and you'll be able to call this code from your buildscript. Check https://docs.gradle.org/current/userguide/custom_plugins.html and related docs.
To make your java code available to gradle script you need to have your java code under the directory hierarchy given below:
ProjectRootDirectory
buildSrc
src
main
groovy/java
YourPackages(in my case java packages and classes)
This is the path where gradle script looking for external plugins. Now you can import and access classes into gradle script(you will not end up with "unable to resolve class" error).

Gradle: Task does not execute another task based on the configuration it uses

I've got a very simple multiproject build like below:
module1, which generates a public API jar and exposes it through "publicAPI" configuration:
configurations {
publicAPI
}
task generatePublicAPI(type: Jar) {
outputs.upToDateWhen { false }
baseName 'public-api'
from sourceSets.main.output
}
artifacts {
publicAPI generatePublicAPI
}
module2, which uses the public API jar (by referencing 'publicAPI' configuration defined in module1) to generate a application jar:
configurations {
generateApplication
}
dependencies {
generateApplication project(path: ':module1', configuration: 'publicAPI')
}
task jarApp(type: Jar) {
baseName 'app'
from configurations.generateApplication.collect {
it.isDirectory() ? it : zipTree(it)
}
}
Now, when I execute 'gradle :module2:jarApp" task, I got the following error:
Cannot expand ZIP
'/home/picasso/Documents/GradlePlayground/module1/build/libs/public-api.jar'
as it does not exist
and I can see that gradle was not trying to execute 'generatePublicAPI' of module1.
However, if I make "jarApp" task depends on "generatePublicAPI" task explicitly,
task jarApp(dependsOn: 'module1:generatePublicAPI', type: Jar) {...}
then everything's fine.
BUT, wouldn't this approach against one of the purpose of using dependency configuration so that I don't have to worry about the details of how module1 is built, e.g. which task generates the jar and what artifacts it produces?
I thought gradle is able to work out the tasks it needs to execute by following along the "route" of the referenced dependency configuration.
Am I missing something here so that "generatePublicAPI" task can be executed automatically without have to declare "dependsOn" explicitly for "createApp" task?
I asked the same question on Gradleware's forum and got the answer from one of the core developer, here's the link.
Basically, the issue is that collect method returns a new collection but gradle has no way to know that this new collection was generated from the configuration, therefore it couldn't infer which task to execute.
The solution is instead of declaring a dependency on a task, declare a dependency on the actual configuration instead like the following:
task jarApp(dependsOn: configurations.generateApplication, type: Jar)

Gradle : Generating sources from WSDL & XSD and adding it to main classpath for compilation

I am failry new to gradle and have a multiproject gradle build to which want to add a WSDL2Java related tasks to one of the project.
I have coded the necessary tasks to generate,compile,package the generated stubs into a jar and add it to the classpath.
Now , i want to perform these tasks before the java compilation is started.
Below is how i coded the new tasks
task genClasses(type: JavaExec) {
//Run WSDL2Java and generate java source files.
}
task compileClasses(dependsOn:'genClasses'){
//Use ant.javac or add type:JavaCompile in task defination as shown below
// task compileClasses(dependsOn:'genClasses',type:JavaCompile)
}
task packageClasses(dependsOn:'compileClasses',type:Jar){
//package my jar
}
task createStubs(dependsOn: 'packageClasses'){
//add created jar to the classpath
}
compileJava.dependsOn createStubs
The build fails with exception and shows below message
Circular dependency between tasks. Cycle includes [task ':projectx:genWsdlClasses', task ':projectx:classes'].
I figured out that the compileClasses tasks somehow is causing this circular dependency, but not sure how to get rid of it?
Are there any other better or idiomatic ways to perform this source generation, compilation of generated source,packaging and adding it to the main sourceset classpath before the main source gets build?
I use the Jaxb-Plugin available here. My Gradle build file looks like this:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'no.entitas.gradle.jaxb:gradle-jaxb-plugin:2.0'
}
}
dependencies {
jaxb 'com.sun.xml.bind:jaxb-xjc:2.2.5-1'
jaxb 'com.sun.xml.bind:jaxb-impl:2.2.5-1'
}
generateSchemaSource.destinationPackage = "my.custom.package"
I believe that this will create the jaxb classes that you want. Does that help?
Use the plugin "no.nils.wsdl2java"
https://plugins.gradle.org/plugin/no.nils.wsdl2java
The plugin takes care of hooking it in to the build and clean tasks.

Resources