Android - Run a Java method in Gradle after an apk build - java

I'm currently building a Android application, and I would like to do some processing after the apk is built.
I've already succeeded to launch a gradle task after the build:
tasks.whenTaskAdded {
task ->
if(task.name == 'assembleRelease'){
task.finalizedBy postApkProcess
}
}
task postApkProcess{
doLast {
println 'OK'
}
}
But I struggle to launch a method inside that task. What I would like to do is to call something like new MyClass().postBuild() inside the class (or if it is not possible, run the main method of a Java class), but I don't find a way to do it.
I've tried to build a task task postProcess(type: JavaExec), but the line apply plugin: 'java'conflicts with the Android plugin.
Is there a way to do it ?

You could use commandLine to run the main method in MyClass which could call postBuild:
commandLine 'java' '<pathToSrc>/<mypackage>.MyClass'
Be careful to set the package name and source location properly. The path should point to the location (minus package directory tree) of the MyClass.class file, not MyClass.java. If your class is not compiled or it changes often, you could call javac to compile it each time before you run it (optionally using a shell script / batch file) to bundle both commands and simplify the gradle call and help you test the setup directly.
If your class uses code from libraries ensure you add them to the classpath:
'-classpath' 'mylib1.jar:mylib2.jar'
If you use java in the build process you might also have a look at scar

Related

How to include a custom .jar file in a java gradle build?

I have a gradle project to build a .jar file from Java sources. Some of the classes it needs are in a separate .jar file (let's called it helper.jar). To build helper.jar there is an external build system that I can invoke via a shell script.
If I manually run the shell script first to create helper.jar, and then add the file as a top-level dependency:
dependencies {
....
implementation files('/path/to/helper.jar')
Then everything compiles ok.
I am trying to convert this to automatically run the shell script to build helper.jar if necessary. I removed the file from the dependencies above and tried adding this task:
tasks.register('buildHelperJar', Exec) {
workingDir '/path/to/helper/root'
commandLine 'build_helper.sh'
outputs.file file('/path/to/helper.jar')
}
And then add it as a dependency:
project.afterEvaluate { proj ->
tasks.findByName('compileReleaseJavaWithJavac').dependsOn('buildHelperJar')
}
This seems to invoke the buildHelperJar task ok (and helper.jar gets built ok) but the subsequent compileReleaseJavaWithJavac task fails with missing symbol errors for everything that should be in helper.jar. So it's like I'm not actually adding the jar file itself into the compile step. What should I be doing to achieve this? Thanks a lot

Are there any ways to run an arbitrary main method via Gradle?

I realize when I use IntelliJ IDEA CE and make a Gradle-Java project.
When I run a Class.main(), say Hoge.main(),
the run window shows like
0:15:03 PM: Executing task 'Hoge.main()'...
and in gradle tasks list
:Hoge.main()
like this (the bottom line).
How can I do this in a terminal (not using IDEA)? If possible, it may be like this? (I know about https://docs.gradle.org/current/userguide/application_plugin.html, which is not flexible for my purpose)
gradle run Java Hoge
Is this only in IDEA?
I saw some ideas using the application plugin but I could not find very simple way like above...
That's what the gradle wrapper is for. If you have the wrapper (in [ProjectDir]/gradle/wrapper folder), then you can use Gradle without an IDE.
A) In Windows, execute the gradlew.bat script;
B) In Unix, execute the gradlew script.
In your case, you would type in the terminal following:
gradlew run or ./gradlew run.
P.S.
If you want to have several "run" tasks, you need to create them like so:
task runHoge(type: JavaExec,group: 'application'){
classpath(sourceSets.main.runtimeClasspath)
// set the main class name here
setMain('package.Hoge')
}

Including Local Jars In Groovy

I'm attempting to use "HTTPBuilder" within my simple Groovy script. When I use '#Grab' to import the dependency, everything works fine. Though, I'd like to keep the jar within a different directory and import it using the classLoader function. I've copied the 'http-builder-0.7.jar' that '#Grab' placed into my grapes directory and pasted it into the same directory my Groovy script is running (on Windows). I then comment out the '#Grab' statement and include the classLoader, but get this error:
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed: C:\Groovy Scripts\test.groovy: 9: unable to resolve
class HTTPBuilder
Any ideas why the classLoader wouldn't be working in the script? I printed out the path of the jar when importing with '#Grab' and it's definitely using the one within the grape directory. If I uncomment the '#Grab' statement, it works again. Here's the small script...
//#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7')
this.getClass().classLoader.rootLoader.addURL(new File("http-builder-0.7.jar").toURL())
//return new File(groovyx.net.http.HTTPBuilder.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
def http = new HTTPBuilder('http://httpbin.org/get')
As mentioned, you would be wise to use another method, such as Gradle's application plugin.
However, this is one way to do what you're asking.
First, to get the jar and all dependencies, consider the following Gradle build.gradle script:
apply plugin: 'java'
dependencies {
compile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
}
repositories {
jcenter()
}
clean {
doLast {
ant.delete(dir: 'runtime')
}
}
task getDeps(type: Copy) {
from sourceSets.main.runtimeClasspath
into 'runtime/'
doFirst {
ant.delete(dir: 'runtime')
ant.mkdir(dir: 'runtime')
}
}
If you run gradle getDeps, it will write all of the jars into runtime.
Then, in a Unix terminal (for example), you could set the classpath with this (using wildcard syntax from Java 6+, and assuming the path is the same runtime as above):
export CLASSPATH=.:"/user/foo/some/path/runtime/*"
In the same terminal, this will work:
import groovyx.net.http.*
def http = new HTTPBuilder('http://httpbin.org/get')
println "Ready."

`System.console()` returns `null` if executed from `gradle run`

I have this simple Java program:
package me.fornever.javaterminal;
public class Main {
public static void main(String[] args) {
System.out.println("Console: " + System.console());
}
}
And this simple build.gradle:
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'me.fornever.javaterminal.Main'
When I'm executing it using gradle --no-daemon run, I get the following output:
Console: null
If I execute it from the terminal through gradle jar; java -cp '.\build\libs\java-terminal.jar' me.fornever.javaterminal.Main, I get the following:
Console: java.io.Console#3d4eac69
I am aware that System.console() may return null when the parent process uses stdout redirection. Is there some Gradle option to disable the redirection and make the console fully available for my program?
I am developing a terminal library for Java, so I want to run my tests and executables without Gradle intervention in stdin/stdout/stderr.
Please note that System.console() being null is not the only issue but the most obvious one. In reality I want to access WinAPI WriteConsoleW function from the program executed by gradle run, and I'm unable to use this function due to the same reasons System.console() being null. So I really need to disable output redirection in Gradle if this option is available.
Also please note that the question is different from Gradle build null console object because that question asks how to use System.console() inside of a Gradle script and not in the Java program invoked by gradle run; I believe they're working differently in that matter, because neither of the answers are working or applicable to my case.
In order for java.io.Console to be available, the child processes' stdin and stdout have to point to a Linux/macOS/Unix terminal or a Windows console. The easiest way to arrange that is to inherit the stdin and stdout from a parent process already set up that way. However, there is a known limitation of Gradle (GRADLE-3292) that the JavaExec task type does not support making the child process inherit the input/output streams of the parent process.
If the Gradle JavaExec task type was enhanced to support stdin/stdout inheritance, then this could be made to work for gradle --no-daemon run.
Getting it to work with the Gradle daemon would be far more complex, and likely involve platform-specific code to manipulate pseudoterminals or call the Windows console API.

How to embed Java code into Gradle build using JavaExec task

I have a Gradle-driven project to which I want to add a simple Java task. This task is very specific to the project and if it can be helped I don't want to develop it as a separate plugin. So the question is can I define such custom task within the same build.gradle I'm using for my project? Or is it inevitable that I need to package it as a separate project (plugin) and install to the local repo?
Also it's probably important to note that the original project is not Java related (no other Java code needs to be build)
P.S. Based on comments below:
I would like to add src/main/java/SomeUsefulStuff.java to the existing project and have that file compiled and used as a custom task. I do understand that it needs to be compiled each time I run the build but again - the code will be small. However it will have some external dependencies such as Commons IO
Thanks to RaGe who pointed to JavaExec this turned out to be pretty simple. Here's what you do:
Put your Java code in /src/main/java just as you would in the regular Gradle-driven Java project. Make sure it has main method in the file you are going to call
Add apply plugin: 'java' to the build.gradle
If your Java code has any dependencies on 3rd party libs add these to dependencies section
Add new task section to build.gradle like so:
task usefulStuff(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.me.gradle.UsefulStuff'
// arguments to pass to the application
args 'OhmyGod!'
}
Now you can refer to that task as any task in your build. For example imporantTask.dependsOn usefulStuff

Categories

Resources