how to write variables to gradle property file when you execute task? - java

I have a such case. I need to save curent date after every release build with gradle. Is there any possibility to save date to gradle.properties file that I can read it in the next build job?
My files:
gradle.properties:
version=0.0.1
date=
build.gradle:
task changeDate() {
file = new File("changelogs/CHANGELOG_RELEASE_FROM_"+getDate()+".md");
project.setProperty("date",getDate());
}
It dosent work and it doesn't save date variable into gradle.properties.
So I wish that I can have a date from release in my gradle.properties file:
gradle.properties:
version=0.0.1
date=12.04.2019

The methods getProperty, findProperty and setProperty are not directly related to the gradle.properties file. Instead, they provide access to properties in the scope of the Project instance against which the build.gradle gets evaluated. This scope includes a lot of different properties, among them so-called extra properties (coming from the gradle.properties files).
However, Gradle provides a task type for this functionality called WriteProperties. Just specify the target file and define some properties:
task changeDate(type: WriteProperties) {
outputFile = file('gradle.properties')
property 'date', getDate()
}

You can try to do something like:
import java.time.*;
task rel {
doLast {
ant.propertyfile(file: "gradle.properties") {
entry( key: "date", value: LocalDateTime.now())
}
}
}
Suppose, rel is your release task or any other task, which execution means, that you release was made. You have to add to it's configuration a doLast closure to run some code after task is executed. In this closure you are modifying some property in properties file.
LocalDateTime and it's import are added just for example, you can use another method to get current date fo sure.
In your case it could look like:
task changeDate() {
doLast {
ant.propertyfile(file: "gradle.properties") {
entry( key: "date", value: LocalDateTime.now())
}
}
}
But you have to make your changeDate executed somehow, if it's not.

Related

gradle javaexec error "'apiElements' directly is not allowed"- Gradle 5.4.1

I am new to Gradle and trying to migrate an existing system build from ant to Gradle.
As part of this I need to run a java program on every file in a directory. Directory contains xml files and the java code will parse and convert .xml to .java files (and these Java files would be build to generate class and package in final jar) after performing some business specific transformation.
below is a function I wrote in Gradle
private runJavaFile(String dirPath) {
FileTree tree = fileTree(dir: dirPath, include: '**/*.xml')
tree.each {
def xmlfile = it.path
def javaFile = it.path.replaceFirst(".xml", ".java")
javaexec { //// getting error on this line
classpath configurations.all
main = 'XmlToJavaParser'
args = ["$xmlfile", "$javaFile", 'Java']
}
}
}
I am calling this function from a Gradle task by passing the dir path which contains the xml files to be parsed.
While running the task, I am getting below error:
> Resolving configuration 'apiElements' directly is not allowed
Any help would be appreciated.
Let me know if any more information is needed.
In Gradle, a configuration represents a group of artifacts and their dependencies. You typically have several configurations depending on what you want to do. For instance, you could have one where you declare which dependencies are needed for compilation, which are only needed at runtime, or which are needed for running a particular Java application.
In your case, you are saying that the classpath to the XmlToJavaParser class is "all configurations combined" and that doesn't really make sense. You are also not allowed to do that as some configurations from the Java plugin are not resolvable like this, which is why you get an error.
So to fix it, you should declare your own configuration for XmlToJavaParser. You can then declare dependencies for it like you normally do. Example (using the Groovy DSL):
configurations {
xmlJavaParser {
canBeResolved = true
canBeConsumed = false
}
}
dependencies {
xmlJavaParser "org.example:xml-java-parser:1.0" // or whatever you need
}
private runJavaFile(String dirPath) {
// ...
javaexec {
classpath = configurations.xmlJavaParser // The configuration is referenced here
main = 'XmlToJavaParser'
args = ["$xmlfile", "$javaFile", 'Java']
}
}
There are also other ways to go about it. But the main point is to not use configurations.all as a classpath.

How to use BuildConfig.DEBUG as task variable in build.gradle on Android Studio 4.0?

I have a custom task that required to check if the current build is debug or release, and then use the corresponding classpath.
The normal task definition:
task custom_java_task(type: JavaExec) {
classpath "build/intermediates/javac/debug/classes/"
main = "com.testapp.JavaTaskTest"
args "test", "${projectDir}"
}
The task definition needs to check BuildConfig.DEBUG and set the different value for classpath:
task custom_java_task(type: JavaExec) {
classpath BuildConfig.DEBUG ? "build/intermediates/javac/debug/classes/" : "build/intermediates/javac/release/classes/"
main = "com.testapp.JavaTaskTest"
args "test", "${projectDir}"
}
The build will fail with the following error:
Could not get unknown property 'BuildConfig' for task ':app:custom_java_task' of type org.gradle.api.tasks.JavaExec.
Thanks.
BuildConfig is generated set of properties that cannot be used from within gradle scripts. It is actually generated based on your build.gradle
But there are some ways to achieve what you want for example like this:
First in project level build.gradle under buildscript you can define the function / method and name ti however you want (eg. doStuff)
buildscript {
...
ext.doStuff = { buildType ->
// do stuff based on buildType param
}
...
}
Than in your module level build.gradle, simply do
android {
...
buildTypes {
debug {
...
doStuff("debug")
}
release {
...
doStuff("release")
}
}
...
}
Now this is just to give you a hint on the abilities of build.gradle files, you can build further from this.
Cheers

Get value from `build.gradle` to code

We have a build.gradle where the version is defined in it. I need to implement a version endpoint ( like /version) to get the version of the project. This version property in build.gradle has been there for a long time, I can't move it to the project.properties file. How can I access this version's values from my Java code?
There are many ways with which you can "plant" information into your code
First you can use the manifest file, and read from it
jar {
manifest {
attributes(
"lib-version": version
}
}
With conjunction to Reading my own Jar's Manifest
The other option is to add that info the your property files before the jar task
task('addVersion') {
doLast {
//append the version here, see example
file("src/main/resources/props.properties").append("version=$version")
}
}
jar.dependsOn(addVersion)
One of our project needs not only the version information but also build time etc. We use a copy task with template substitution.
task updateVersions(type: Copy) {
Properties props = new Properties()
props.load(new FileInputStream(file('build.properties')))
props.put("buildtime", new Date().format("dd MMM yyyy hh:mm aa"))
props.put("version", "${version}")
from project(':main').file('Version.tmpl')
into file('src/main/java').path
expand(props)
rename('Version.tmpl', 'Version.java')
}

Create makefile-like wildcard targets in Gradle

Use case: I have a bunch of images that have to be processed by a script before I build my app. In makefile I can simply define:
processed/%.png: original/%.png
script/process.sh $< $#
How do I implement this in Gradle? Specifically, I want it to work like in Makefile, that is only the modified original images will be processed again.
You can implement this behaviour as an incremental task, using IncrementalTaskInputs as its input parameter. This API docs contain an example how to use it and here is an example in another the documentation. Both of them do almost exactly what you need.
An incremental task action is one that accepts a single
IncrementalTaskInputs parameter. The task can then provide an action
to execute for all input files that are out of date with respect to
the previous execution of the task, and a separate action for all
input files that have been removed since the previous execution.
In the case where Gradle is unable to determine which input files need
to be reprocessed, then all of the input files will be reported as
IncrementalTaskInputs.outOfDate(org.gradle.api.Action).
Inside your task, call the script using an exec task. Your Gradle script could then look like this:
task processRawFiles(type: ProcessRawFiles)
class ProcessRawFiles extends DefaultTask {
#InputDirectory
File inputDir = project.file('src/raw')
#OutputDirectory
File outputDir = project.file('build/processed')
#TaskAction
void execute(IncrementalTaskInputs inputs) {
if (!inputs.incremental)
project.delete(outputDir.listFiles())
inputs.outOfDate { InputFileDetails change ->
File saveTo = new File(outputDir, change.file.name)
project.exec {
commandLine 'script/process.sh', change.file.absolutePath, saveTo.absolutePath
}
}
inputs.removed { InputFileDetails change ->
File toDelete = new File(outputDir, change.file.name)
if (toDelete.exists())
toDelete.delete()
}
}
}
This task looks for the images in src/raw. It will removed files from build directory and call your script on any files that are out of date or newly added.
Your specific case might be more complicated if you have the images scattered across multiple directories. In that case you will have to use #InputFiles instead of #InputDirectory. But the incremental task should still work.

Is it possible to specify multiple main classes using gradle 'application' plugin

I would like to use the Gradle "application" plugin to create startScripts for a second mainClass. Is this possible? Even if the application plugin doesn't have this functionality built in, is it possible to leverage the startScripts task to create a second pair of scripts for a different mainClass?
Add something like this to your root build.gradle:
// Creates scripts for entry points
// Subproject must apply application plugin to be able to call this method.
def createScript(project, mainClass, name) {
project.tasks.create(name: name, type: CreateStartScripts) {
outputDir = new File(project.buildDir, 'scripts')
mainClassName = mainClass
applicationName = name
classpath = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtimeClasspath
}
project.tasks[name].dependsOn(project.jar)
project.applicationDistribution.with {
into("bin") {
from(project.tasks[name])
fileMode = 0755
}
}
}
Then call it as follows either from the root or from subprojects:
// The next two lines disable the tasks for the primary main which by default
// generates a script with a name matching the project name.
// You can leave them enabled but if so you'll need to define mainClassName
// And you'll be creating your application scripts two different ways which
// could lead to confusion
startScripts.enabled = false
run.enabled = false
// Call this for each Main class you want to expose with an app script
createScript(project, 'com.foo.MyDriver', 'driver')
I combined parts of both of these answers to arrive at the relatively simple solution:
task otherStartScripts(type: CreateStartScripts) {
description "Creates OS specific scripts to call the 'other' entry point"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
mainClassName = 'some.package.app.Other'
applicationName = 'other'
}
distZip {
baseName = archivesBaseName
classifier = 'app'
//include our extra start script
//this is a bit weird, I'm open to suggestions on how to do this better
into("${baseName}-${version}-${classifier}/bin") {
from otherStartScripts
fileMode = 0755
}
}
startScripts is created when the application plugin is applied.
You can create multiple tasks of type CreateStartScripts and in each task you configure a different mainClassName. for convenience, you can do this in a loop.

Categories