Gradle: using task's member as an other task's input - gradle

setVersionTask sets a member that I want to be used as input by getVersionTask. Here's my code:
class TaskA extends DefaultTask {
#InputFile
File pbxprojectFile
#Optional
String version
#TaskAction
void exec() {
this.version "version_set"
}
}
class TaskB extends DefaultTask {
#Input
String version
}
task setVersionTask(type: TaskA){
pbxprojectFile project.file('foo.txt')
}
task getVersionTask(type: TaskB){
doFirst{
println('version ' + setVersionTask.version)
}
version setVersionTask.version
dependsOn 'setVersionTask'
}
When I go
./gradlew -q getVersion
I get
A problem was found with the configuration of task ':getVersionTask'.
No value has been specified for property 'version'.
How do I accomplish that?
Thanks a bunch!

The trick was to set TaskB's version from TaskA's doLast:
task setVersionTask(type: TaskA){
pbxprojectFile project.file('foo.txt')
doLast{
getVersionTask.version = version
}
}
task getVersionTask(type: TaskB){
doFirst{
println('version ' + getVersionTask.version)
}
dependsOn 'setVersionTask'
}

Related

In a custom gradle plugin, how to add task depends on task which is defined in other plugin?

for example, I have a plugin:
public class PluginA implements Plugin.
and I will create a task taskA. Now I want to set: taskA.dependsOn taskB, but taskB is defined in PluginB.
Does it possible to do this, if can, how to do it?
You don't need to know that taskB comes from PluginB to create the dependency on taskA: you can simply reference taskB by its name, as follows:
class PluginA implements Plugin<Project> {
void apply(Project project) {
Task taskA = project.task('taskA') {
doLast {
println 'Executing task A from plugin A'
}
}
// create dependency from taskA to taskB
project.tasks.matching { it.name == 'taskB'}.each {
taskA.dependsOn it
}
}
}
Please note that if your consuming project which applies PluginA does not apply PluginB, the dependency will not be created. Maybe you want/need to automatically apply PluginB when applying PluginA.
Another way would be to create this dependency only when pluginB is applied, using pluginManager.withPlugin method:
class PluginA implements Plugin<Project> {
void apply(Project project) {
Task taskA = project.task('taskA') {
doLast {
println 'Executing task A from plugin A'
}
}
project.pluginManager.withPlugin('pluginB'){
println "pluginB applied => adding dependency from taskA to taskB"
project.afterEvaluate{
taskA.dependsOn project.tasks.getByName('taskB')
}
}
}
}

how to proguard with spring-boot gradle plugin

How to setup proguard obfuscation with spring boot 2 and gradle build?
Hello. Trying to setup code obfuscation of Spring Boot app with its gradle plugin and Proguard gradle plugin. Google mostly gives some approaches for older spring-boot-gradle-plugin version (i.e. this closest one using non-existing bootRepackage task), or using maven plugin (having repackage goal).
Idea is to obfuscate classes before jar packaging, as I understand, but I don't see any entry points in current gradle plugin version, and would like to avoid manual extraction and zipping back.
Is anyone using that combo at all? Spring Boot version >=2.0.3.
I think that today is not possible and you have to do it with manual extraction and zipping back.
An example of manual extraction and zipping back which could be helpful:
build.gradle
version = '0.1.0'
buildscript {
dependencies {
classpath 'net.sf.proguard:proguard-gradle:6.0.3'
classpath 'net.sf.proguard:proguard-base:6.0.3'
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
task extractJar(type: Copy) {
def zipFile = file("${buildDir}/libs/your_project_name-${version}.jar")
def outputDir = file("${buildDir}/unpacked/")
from zipTree(zipFile)
into outputDir
}
task proguard(type: proguard.gradle.ProGuardTask) {
doFirst {
tasks.extractJar.execute();
}
configuration 'proguard.conf'
injars "${buildDir}/unpacked/BOOT-INF/classes"
outjars "${buildDir}/obfClasses"
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
libraryjars "${buildDir}/unpacked/BOOT-INF/lib"
doLast {
tasks.deleteClasses.execute();
}
}
task deleteClasses(type: Delete) {
delete "${buildDir}/unpacked/BOOT-INF/classes/"
doLast {
tasks.copyObfuscatedClasses.execute()
}
}
task copyObfuscatedClasses(type: Copy) {
from "${buildDir}/obfClasses"
into "${buildDir}/unpacked/BOOT-INF/classes/"
include 'com/**'
include '*.properties'
doLast {
tasks.copyObfuscatedJars.execute()
}
}
task copyObfuscatedJars(type: Copy) {
from "${buildDir}/obfClasses"
into "${buildDir}/unpacked/BOOT-INF/lib/"
include '*.jar'
doLast {
tasks.deleteObfuscated.execute()
}
}
task deleteObfuscated(type: Delete) {
delete 'build/obfClasses'
doLast {
tasks.repackage.execute()
}
}
task repackage(type: Zip) {
from "${buildDir}/unpacked"
entryCompression ZipEntryCompression.STORED
archiveName "your_project_name-${version}-obf.jar"
destinationDir(file("${buildDir}/libs"))
}
proguard.conf
-ignorewarnings
-keepdirectories
-keep interface com.your_package.** { *; }
-keep class com.your_package.main{ *; }
-keep class com.your_package.model.** { *; }
-keepparameternames
-keepclassmembers #org.springframework.** class * {
*;
}
-keepclassmembers #org.springframework.** interface * {
*;
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep #org.springframework.** class *
-keepclassmembers #javax.** class * { *; }
-dontwarn org.springframework.**
-dontwarn javax.**
-dontwarn org.yaml.snakeyaml.**
-dontwarn okhttp3.**
have you tried writing a task for it in your build.gradle?
task obfuscate(type: proguard.gradle.ProGuardTask, dependsOn: jar) {
mustRunAfter ('javadoc')
inputs.file file("${jar.archivePath}")
outputs.file file("$buildDir/proguard/${project.name}-${project.version}.jar")
injars "${jar.archivePath}"
// JDK 8 and below use jars on the classpath
if (JavaVersion.current().java8Compatible && !JavaVersion.current().java9Compatible) {
println "Obfuscation inputs based on JDK 8 layout."
libraryjars "$javaHome/lib/rt.jar"
libraryjars "$javaHome/lib/jce.jar"
libraryjars "$javaHome/lib/ext/jfxrt.jar"
} else {
// JDK 9 and above use modules on the module-path
println "Obfuscation inputs based on JDK 9+ module layout."
def jdkModuleList = [
'java.base', 'java.datatransfer', 'java.desktop',
'java.instrument', 'java.logging',
'java.management', 'java.prefs', 'java.rmi',
'java.scripting', 'java.xml',
'jdk.attach'
]
jdkModuleList.forEach {
libraryjars "$javaHome/jmods/${it}.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
}
target '10' // JDK 9 is obsolete, would target 11, but Proguard can't deal with 11's class files yet
}
// dependencies
configurations.runtime.files.each {
libraryjars it, filter: '!META-INF/versions/**'
}
outjars "$buildDir/proguard/${project.name}-${project.version}.jar"
printseeds "$buildDir/proguard/proguard_seeds.txt"
printmapping "$buildDir/proguard/proguard_map.txt"
configuration 'src/main/proguard/configuration.pro'
}
this thread might be helpful with your situation:
https://discuss.gradle.org/t/obfuscated-jars-what-are-the-best-practices/18834/6

Why is PropertyState Necessary

Suppose I'm developing a Gradle plugin and the inputs that some of the tasks the plugin configures depend on how it is configured via an extension. For example:
class MyTask extends DefaultTask {
#InputFile
File toTrack
#TaskAction
def run() {
println("The file now contains ${toTrack.text}")
}
}
class MyConfig {
File toTrack = new File('bad-default.txt')
}
class MyPlugin implements Plugin<Project> {
#Override
def apply(Project project) {
project.with {
extensions.create('config', MyConfig)
task('printChanges', type: MyTask) {
toTrack = config.toTrack
}
}
}
}
Unfortunately, this doesn't work correctly. The problem is that if I have a build.gradle like:
apply plugin: my-plugin
config {
toTrack = file('file-to-track.txt')
}
where I've specified a file to track, Gradle will evaluate the #InputFile on my task before the config block is run so it'll decide if the task is up to date or not by looking at bad-default.txt rather than file-to-track.txt.
The recommended fix to this seems to be to use PropertyState like so:
class MyTask extends DefaultTask {
PropertyState<File> toTrack = project.property(File)
#InputFile
File getToTrack { return toTrack.get() }
#TaskAction
def run() {
println("The file now contains ${toTrack.get().text}")
}
}
class MyConfig {
private PropertyState<File> toTrack
MyConfig(Project project) {
toTrack = = project.property(File)
toTrack.set('bad-default.txt')
}
void setToTrack(File fileToTrack) { toTrack.set(fileToTrack) }
}
class MyPlugin implements Plugin<Project> {
#Override
def apply(Project project) {
project.with {
extensions.create('config', MyConfig)
task('printChanges', type: MyTask) {
toTrack = config.toTrack
}
}
}
}
That works but it seems very verbose and the PropertyState stuff seems entirely unnecessary. It seems like the real fix was simply to change the #InputFile annotation to be on a getter instead of having it be on the property. In other words, I believe the following has the same effect and is less code and easier to understand:
class MyTask extends DefaultTask {
File toTrack
// This is the only change: put the annotation on a getter
#InputFile
File getToTrack() { return toTrack }
#TaskAction
def run() {
println("The file now contains ${toTrack.text}")
}
}
class MyConfig {
File toTrack = new File('bad-default.txt')
}
class MyPlugin implements Plugin<Project> {
#Override
def apply(Project project) {
project.with {
extensions.create('config', MyConfig)
task('printChanges', type: MyTask) {
toTrack = config.toTrack
}
}
}
}
With some experiments this does seem to have the desired effect. What am I missing? Is there ever a time when PropertyState is necessary?
The problem is not the time #InputFile is evaluated. #InputFile is evaluated just before the task is executed, so after the configuration phase has finished and during gradles execution phase. The problem PropertyState is solving is the wiring between an extension and a task.
let's look again on your apply method:
def apply(Project project) {
project.with {
extensions.create('config', MyConfig)
task('printChanges', type: MyTask) {
toTrack = config.toTrack
}
}
}
here you:
1) Create your custom extension with the default value provided in the MyConfig class.
2) Link the value currently set in MyConfig to the MyTask toTrack property.
Now looking at the plugin usage:
apply plugin: my-plugin
config {
toTrack = file('file-to-track.txt')
}
here you:
1) Apply the plugin (and basically executing the apply method of your plugin).
2) Reconfigure the MyConfig#toTrack extension property.
But what isn't happening here, is updating the value in the printChanges task. That's what PropertyState is solving. It has nothing todo with Task inputs and outputs evaluation.

Gradle custom plugin task

Tried to understand how to create custom plugins. But I can't get my new task working. I get this error:
Failed to apply plugin [id 'code4reference']
No such property: printTask for class: libs.gradle.NewPlugin
But it's working when I run println from NewPlugin class.
There are two groovy files, one for plugin initialization and one for task. They are located:
/home/boilerplate/boilerPlate/src/main/groovy/libs/gradle/build.groovy
package libs.gradle;
import org.gradle.api.*;
apply plugin: NewPlugin
class NewPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('myTask', type: printTask)
}
}
/home/boilerplate/boilerPlate/src/main/groovy/libs/gradle/printTask.groovy
package libs.gradle;
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class printTask extends DefaultTask {
#TaskAction
def showMessage() {
println '------------showMessage-------------------'
}
}
Implementation:
/home/boilerplate/boilerPlate/src/main/resources/META-INF/gradle-plugins/code4reference.properties
implementation-class=libs.gradle.NewPlugin
Update:
Now I faced another issue when executing my new task that should sign all RPMs found on path. I know this works just fine when in build.gradle:
package libs.gradle;
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class Sign extends DefaultTask {
def workdir = project.projectDir.getPath()
#TaskAction
def showMessage() {
ext.signfiles = files{
fileTree(dir: "$workdir").matching{ include 'build/**/*.rpm' }
}
doLast {
signfiles.each{ File file->
def args = /echo -e "spawn rpm --resign ${file}\nexpect \"Enter pass phrase:\"\nsend \"\\r\"\nexpect" | expect/
exec {
commandLine 'bash', '-c', "$args"
}
}
}
}
}
Could not find method files() for arguments [libs.gradle.Sign$_showMessage_closure1#4bf8b77] on task ':myTask' of type libs.gradle.Sign.
ext.signfiles = project.fileTree(dir: "$workdir").matching{ include 'build/**/*.rpm' }
But not sure what workdir is.
The exec below should also be
project.exec {
commandLine 'bash', '-c', "$args"
}

gradle custom task execution phase

This question is for gradle (>= 2.4). I would like to write a custom task like the following:
https://docs.gradle.org/current/userguide/custom_tasks.html
class GreetingTask extends DefaultTask {
#TaskAction
def greet() {
println 'hello from GreetingTask'
}
}
task hello(type: GreetingTask)
how can I make this task run during execution phase? Is passing an empty closure with
<< {
}
the only solution?
Edit
the task is supposed to be used in a multiproject build with several tasks as dependencies.
I'd like that the command gradle build would build all the projects by saying something like
`build.dependsOn(hello)`
but seems that the task hello is called during configuration phase of the build.
Add the following to a build.gradle file:
class GreetingTask extends DefaultTask {
#TaskAction
def greet() {
println 'hello from GreetingTask'
}
}
task hello(type: GreetingTask) {
println "This is the configuration phase"
doFirst {
println "This is the execution phase"
}
}
Now execute gradle hello. The output you will see is
This is the configuration phase
:hello
This is the execution phase
hello from GreetingTask
BUILD SUCCESSFUL
As you can see, the output from the task occurs after the doFirst(), which definitely happens during the execution phase.

Resources