How do we properly create an execution phase tasks? - java

this is what the ideal build script I have:
I do want to execute tasks "unzip_natives_os" manually. But it seems it only works at config phase. And when I take a test run with this set-up it gives me an error: "java.lang.UnsatisfiedLinkError" but if I change the configuration from "nativesOS" into "runtimeOnly" inside of the dependencies block, it works fine. Do I have to explicitly create this "applicationDefaultJvmArgs" and insert the libraryPath of the natives. Is there any other way? And when I need to unzip the "nativesOS" config it needs an explicit version, it seems it did not see the platform/BOM?
// build.gradle.kts
val nativesOS : Configuration by configurations.creating {
this.isTransitive = false
this.extendsFrom(configurations.runtimeOnly.get())
}
dependencies {
implementation(platform("org.lwjgl:lwjgl-bom:3.2.3"))
listOf(
"", "-assimp", "-openal",
"-opengl", "-glfw"
).map { lib ->
implementation("org.lwjgl:lwjgl$lib")
// I give it an explicit version, because it would not work if I unzip this.
nativeOS("org.lwjgl","lwjgl$lib", "3.2.3", classifier = LWJGL.lwjglNatives)
}
...
}
// unzip_native_os tasks, here is the problem.
tasks.register<Copy>("unzip_native_os") {
this.group = "zip"
doLast {
nativesOS.asFileTree.filter { it.name.contains("natives") }.forEach {
unzipTo(File("$buildDir/libs/natives-os"), it)
}
}
}
Edited: Why this is not working? I config it first then execute it.
tasks.register<Copy>("unzip_native_os") {
this.group = "zip"
val nativesJar = nativesOS.asFileTree.filter { it.name.contains("natives") }.files
doFirst {
nativesJar.forEach {
println(">>>> $it")
unzipTo(File("$buildDir/libs/natives-os/v2"), it)
}
}
}

Edited: I found a possible answer and it looks promising but I did not implement it yet, because I need some learning to do on building this kind of script plugin/inline plugin. Here's the link: gradle custom task execution phase
Edited: found an alternative/another quick solution here: Fix custom tasks in gradle. Want to run it manually via at Execution Phase

Related

Gradle Kotlin DSL - Cannot access 'verbose': it is private in 'CompileOptions'

I am trying to set the verbose flag/option to true in the gradle build script (Kotlin DSL).
Gradle throws error that this property is private and not accessible in Kotlin DSL. The same thing works in Groovy DSL though.
Groovy DSL (working)
plugins {
id("java")
}
tasks.named("compileJava") {
options.verbose = true
}
Kotlin DSL (not-working)
plugins {
id("java")
}
tasks.named<JavaCompile>("compileJava") {
options.verbose = true
}
Error
Script compilation error:
Line 32: options.verbose = true;
^ Cannot access 'verbose': it is private in 'CompileOptions'
1 error
I am sure I am missing something. Any help is appreciated. Thanks.
CompileOptions has a setter for verbose, so this will work
tasks.named<JavaCompile>("compileJava") {
options.setVerbose(true)
}
It is also possible to set the flag via property:
tasks.named<JavaCompile>("compileJava") {
options.isVerbose = true
}
Not sure why it is private for Kotlin DSL and not for Groovy DSL, but found an alternative using compilerArgs from this stackoverflow post
tasks.named<JavaCompile>("compileJava") {
val compilerArgs = options.compilerArgs
compilerArgs.add("-verbose")
}
This, to me, feels a bit low-level since we are directly updating the compiler arguments instead of using objects/maps to set them. Will wait for someone to post a better solution (if it exists).

How to run lint for all product flavours in one command - Android?

I have implemented the product flavors for my project. I am building three new application in the same codebase.
After gradle sync, three different flavors have been generated, say flavor1Debug, flavor1Release, flavor2Debug, flavor2Release.
I have moved all flavor specific resources inside the flavor specific res folder. When I tried to run ./gradlew lintRelease(which is supposed to run lint for flavor1Release and flavor2Release), it's not detecting any of the errors
For testing the lint, I have introduced an unused resource inside the res folder of flavor1 and flavor2. I tried to run ./gradlew lintFlavor1Release or ./gradlew lintFlavor2Release, its detecting the error and throwing respectively.
But ./gradlew lintRelease is not throwing any errors. Where am I going wrong?
Try something like this:
make a custom gradle task that will run all those lint tasks separately.
tasks.register("runAllLinters")
afterEvaluate {
if ("lintFlavor1Release" in tasks.names) {
runAllLinters.dependsOn tasks.named("lintFlavor1Release")
}
}
of course, this will trigger only for Flavor1 - so you will need to expand both the if() condition, and the runAllLinters.dependsOn block to depend on ALL flavour-dependant lint tasks.
and finally, you should be able to run it like ./gradlew runAllLinters
Try this:
afterEvaluate {
def LINT_TASK_NAME = "lint"
def lintTask = tasks.findByName(LINT_TASK_NAME)
tasks.findAll { task ->
task.name.startsWith(LINT_TASK_NAME) &&
task.name != LINT_TASK_NAME &&
// remove some standard lint subtasks
!task.name.startsWith("lintFix") &&
!task.name.startsWith("lintAnalyze")
}.each { task -> lintTask.dependsOn.add(task) }
}
This makes task 'lint' to depend on all the other "lintXxxx" tasks except some standard.

Include specific classes in jar archive in gradle build

I have a newbie question on a gradle java project. I wish to configure the output artifact jar of this to only include certain classes from my project. I have the desired classes to be included in a file say classes.txt that is generated as a part of a separate task. How should one configure the jar task of the gradle build so that does this. This is what I have tried so far:
jar {
// reset actions
actions = []
copy {
def dependentClasses = file("classes.txt")
if (dependentClasses.exists()) {
dependentClasses.eachLine { include it }
}
from sourceSets.main.output
includeEmptyDirs = false
into "build/tmp/jar" //Some temporary location
}
// How to zip the contents?
}
I am sorry for the possibly naive question, but I haven't had luck with the other solutions seen.
Thanks!
It can be done in the following way:
apply plugin: 'java'
jar {
def excluded = project.file('classes.txt').readLines()
exclude { f ->
excluded.any { e -> f.path.replaceAll(File.separator, '.').contains(e) }
}
}
Demo can be found here, Lol1.class will be excluded.
Using Opal's answer, here is the answer for the include of files
apply plugin: 'java'
jar {
def included = project.file('classes.txt').readLines()
include { f ->
included.any { i -> f.isDirectory() ? true : f.path.replaceAll(File.separator, '.').contains(i) }
}
}
The slight trick was that the FileTreeElement f being passed to the closure also has the package directory passed and if that happens to return false, the subtree below is not traversed and hence the check on it being a directory to return true to enable processing of the subtree.
Demo for solution is here and Lol1.class will be included. Thanks for the help, Opal!

gradle - copy file after its generation

I try to build jar and after that copy it to another folder.
task createJar(type: Jar) {
archiveName = "GradleJarProject.jar"
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'me.test.Test'
}
baseName = project.name
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
task buildApp (dependsOn: [clean, createJar, copyJarToBin])
But I can't figure out one problem.
copyJarToBin task try to copy old jar. If I delete /build folder in the project and run buildApp() task, task createJar() will generate .jar file, but copyJarToBin() won't find that .jar file.
Could you help me?
Thanks.
The culprit is your copyJarToBin task. when doing
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
you copy the jar during the configuration time by using the copy method. (see the gradle user guide at https://docs.gradle.org/current/userguide/userguide_single.html#sec:build_phases to understand the build lifecycle)
You want to run the actual copy operation during the execution phase (the execution of the task).
One way to solve that is to move the call of the copy method into a doLast block:
task copyJarToBin {
doLast {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
}
The problem with this approach is that you won't benefit of gradles incremental build feature and copy that file every single time you execute the task even though the file hasn't changed.
A better and more idiomatic way of writing your copyJarToBin task is to change your task implementation to use the Copy task type:
task copyJarToBin(type: Copy) {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
We can even improve this snippet by taking advantage of gradle's autowiring feature. You can declare the output of one task as input to another. So instead of writing `build/libs/GradleJarProject.jar' you can simply do:
task copyJarToBin(type: Copy) {
from createJar // shortcut for createJar.outputs.files
into "d:/tmp"
}
Now you don't need to bother about task ordering as gradle know that the createJar task must be executed before the copyJarToBin task can be executed.
I think the above answer is somehow old. Here is an answer using gradle 3.3
jar {
baseName = 'my-app-name'
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // here it automatically reads jar file produced from jar task
into 'destination-folder'
}
build.dependsOn copyJar
Just made few corrections to above Answers:
jar {
baseName = "$artifactId"
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // copies output of file produced from jar task
into 'destination-folder'
}
build.finalizedBy copyJar
You probably need to ensure they are run in the right order,
task copyJarToBin(type:Copy,dependsOn:[createJar]) {
copy {
from "${buildDir}/GradleJarProject.jar" // needs to be gstring
into "d:/tmp"
}
}
In my use case I needed projectA to consume (unzip) contents from projectB's jar output. In this case the embedded doLast { copy { .. }} was required.
configurations {
consumeJarContents
}
dependencies {
consumeJarContents project(':projectB')
}
task copyFromJar() {
dependsOn configurations.consumeJarContents
doLast {
copy {
configurations.consumeJarContents.asFileTree.each {
from( zipTree(it) )
}
into "$buildDir/files"
}
}
}
The problem with task copyFromJar(type: Copy) in this scenario is that the Copy configuration phase checks for the jar file, which it does not find because it has not yet been created, and so declares NO-INPUT for the task at execution time.

How to give System property to my test via Gradle and -D

I have a a Java program which reads a System property
System.getProperty("cassandra.ip");
and I have a Gradle build file that I start with
gradle test -Pcassandra.ip=192.168.33.13
or
gradle test -Dcassandra.ip=192.168.33.13
however System.getProperty will always return null.
The only way I found was to add that in my Gradle build file via
test {
systemProperty "cassandra.ip", "192.168.33.13"
}
How Do I do it via -D
The -P flag is for gradle properties, and the -D flag is for JVM properties. Because the test may be forked in a new JVM, the -D argument passed to gradle will not be propagated to the test - it sounds like that is the behavior you are seeing.
You can use the systemProperty in your test block as you have done but base it on the incoming gradle property by passing it with it -P:
test {
systemProperty "cassandra.ip", project.getProperty("cassandra.ip")
}
or alternatively, if you are passing it in via -D
test {
systemProperty "cassandra.ip", System.getProperty("cassandra.ip")
}
Came across this very much problem, except i don't want to list all properties given on the commandline in the gradle script again. Therefore i send all system properties to my test
task integrationTest(type: Test) {
useTestNG()
options {
systemProperties(System.getProperties())
}
}
I had a case where I needed to pass multiple system properties into the test JVM but not all (didn't want to pass in irrelevant ones). Based on the above answers, and by using subMap to filter the ones I needed, this worked for me:
task integrationTest(type: Test) {
// ... Do stuff here ...
systemProperties System.getProperties().subMap(['PROP1', 'PROP2'])
}
In this example, only PROP1 and PROP2 will be passed in, if they exist in gradle's JVM.
Here's a variant that passes numerous project properties to the test JVM as system properties. I prefer project properties over system properties to increase flexibility.
task intTest(type: Test) {
systemProperties project.properties.subMap(["foo", "bar"])
}
Which may be passed on the command-line:
$ gradle intTest -Pfoo=1 -Pbar=2
And retrieved in your test:
String foo = System.getProperty("foo");
Here is something that worked for me
//in build.gradle file
tasks.withType(Test) {
systemProperties = [
ip: System.getProperty('ip', '192.168.33.13'),
]
}
task integrationTests(type: Test){
useTestNG()
}
Suppose if you are using TestNG, you can add the annotation #Parameters as shown below
public class IpAddress {
#Test
#Parameters("ip")
public void printIpAddress(String ip) {
System.out.println(ip);
}
}
Now you are good to execute a gradlew command
./gradlew clean -Dip="xx.xx.xx.xx" integrationTests --tests "IpAddress"
If you want to use #DataProvider to pass the test data, you can pass it like below and execute the same above gradle command to run the test
public class IpAddress {
#DataProvider(name = "GetIP")
private static Object[][] getIp() {
return new Object[][]{
//if -Dip is not provided in command, then by default it gets the value assigned in build.gradle file i.e.'192.168.33.13'
{System.getProperty("ip")},
};
}
#Test(dataProvider = "GetIP")
public void printIpAddress(String ip) {
System.out.println(ip);
}
}
So I've stumbled on that issue today as well, and what worked for me was the following:
ext.env='prod'
test {
systemProperty 'env', System.properties['env'] ?: "${env}"
println "# test environment: " + systemProperties['env']
...
}
I'm calling my test task using -Penv=dev and I get my 'dev' value in my print, or 'prod' if I do not send any value, which is the expected behavior for me.
Value is also accessible on java side, using System.getProperty("env").
My conclusion on the matter is that input value (parameter) is actually stored under System, making it accessible through either System.properties['env'] or System.getProperty("env"), whereas output (system property) is stored in a systemProperties array, making it readable through systemProperties['env'].

Categories

Resources