Get value from `build.gradle` to code - java

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')
}

Related

Package dependencies into jar

I'm trying to develop a game plugin (oldschool runescape). I'm trying to add in org.json so that it's easier to read/write game states and stuff, but can't seem to figure out how to get it to package org.json with my plugin. It compiles fine, but doesn't run with that package. Any help?
This is what my plugin.gradle.kts looks like
version = "4.0.0"
project.extra["PluginName"] = "Plugin Name"
project.extra["PluginDescription"] = "Misc QOL fixes I wanted"
repositories{
mavenCentral()
}
dependencies{
// https://mavenlibs.com/maven/dependency/org.json/json
compileOnly(group = "org.json", name = "json", version = "20220320")
}
tasks {
jar {
manifest {
attributes(
mapOf(
"Plugin-Version" to project.version,
"Plugin-Id" to nameToId(project.extra["PluginName"] as String),
"Plugin-Provider" to project.extra["PluginProvider"],
"Plugin-Description" to project.extra["PluginDescription"],
"Plugin-License" to project.extra["PluginLicense"]
)
)
}
}
}
edit: I tried compileOnly, implementation, testImplementation, all with the same error "ClassnotFoundException: org.json.JSONObject"
You are using the wrong configuration, you should use "implementation" instead of "compileOnly" as per this documentation.
The gist of it is that "compileOnly" means these libraries are only needed at compiletime, and not at runtime, so they are not included in the jar, as the jar is used at runtime. The "implementation" configuration means these libraries are needed both at compile time and at runtime. Alternatively, you could also use "runtimeOnly" to indicate the package is only needed at runtime, but I don't know if that would work with your project.

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 write variables to gradle property file when you execute task?

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.

Crashlytics not finding API Key in crashlytics.properties at runtime

I'm currently implementing the API Key switching script suggested here, except with build types instead of flavors. My build.gradle looks like this:
...
buildTypes {
debug {
...
set("crashlyticsApiKey", "API_KEY_1")
set("crashlyticsApiSecret", "API_SECRET_1")
}
release {
...
set("crashlyticsApiKey", "API_KEY_2")
set("crashlyticsApiSecret", "API_SECRET_2")
}
}
...
productFlavors{...}
...
File crashlyticsProperties = new File("${project.projectDir.absolutePath}/crashlytics.properties")
applicationVariants.all { variant ->
variant.productFlavors.each { flavor ->
def variantSuffix = variant.name.capitalize()
def generateResourcesTask = project.tasks.getByName("crashlyticsGenerateResources${variantSuffix}")
def generatePropertiesTask = task("crashlyticsGenerateProperties${variantSuffix}") << {
Properties properties = new Properties()
println "...copying apiKey for ${variant.name}"
properties.put("apiKey", variant.buildType.crashlyticsApiKey)
println "...copying apiSecret for ${variant.name}"
properties.put("apiSecret", variant.buildType.crashlyticsApiSecret)
properties.store(new FileWriter(crashlyticsProperties), "")
}
generateResourcesTask.dependsOn generatePropertiesTask
def cleanResourcesTask = project.tasks.getByName("crashlyticsCleanupResourcesAfterUpload${variantSuffix}")
cleanResourcesTask.doLast {
println "...removing crashlytics.properties"
crashlyticsProperties.delete()
}
}
}
...
The gradle file builds successfully, and crashlytics.properties updates with the correct information according to the build type. This method of using crashlytics.properties was suggested here, and appears to work without any other updates other than the inclusion of dependencies in the gradle file. However, when Crashlytics.start(this) is called from the main activity, I get a runtime exception:
java.lang.RuntimeException: Unable to create application com.lookout.LookoutApplication: java.lang.IllegalArgumentException: Crashlytics could not be initialized, API key missing from AndroidManifest.xml. Add the following tag to your Application element
<meta-data android:name="com.crashlytics.ApiKey" android:value="YOUR_API_KEY"/>
Stripping it down to a static crashlytics.properties file (i.e. removing the dynamic script in the gradle file and just having one apiKey and apiSecret in crashlytics.properties) produces the same error, even though it builds successfully.
Is there some change to the AndroidManifest or the build.gradle file I should be making to point it towards crashlytics.properties?
Works fine with:
# Fabric properties file: app/fabric.properties
apiSecret=xx68f6074dxxxxxc11dxxx97c172e8ebf0
apiKey=xxxe76c4xxxx97e8cxxxx0135e9d46f5a2xxx
Add on .gitignore (for open source projects)
REMOVE entry on AndroidManifest.xml:
<meta-data
android:name="io.fabric.ApiKey"
android:value="xxx6c41xxx6ec601xxxd4xxxa2" />
Oficial documentation: https://docs.fabric.io/android/fabric/settings/working-in-teams.html#android-projects
While this is not the answer to the original question (since Instant Run didn't exist in 2014), you may find that Instant Run can cause problems. My process was:
Install Fabric plugin
Generate Crashlytics code (including API key in the manifest)
Switch to fabric.properties file
Spend an hour trying to figure out why it wasn't working
Disable Instant Run -> Rebuild -> Install -> Success
I'm on Android Studio 2.0.0-beta6. This will likely be resolved in future, but this was the only resource I could find online with the same problem so hopefully I can save someone else that hour.

Gradle java project replace single line in file during build

I have a simple Gradle build script to compile and package (similar to the application plugin) my Java application. The only thing I do not accomplish is to replace the current version number in a simple .properties file.
I have created a file 'src/main/resources/app-info.properties' with a single line 'application.version = #version#'. No I want to replace this version string whenever the file is copied to the build folder (think this happens during the build task).
I already tried a simple solution with ants ReplaceTokens. This one replaced the version but also broke my .png files in the resources..
So is there a simple solution to just replace tokens in one single file during the build task (or whatever task handles the copy to the build folder)?
Thank you for any help!
Ben
====== Edit based on the comment from Opal =====
Based on the hint I have added the following:
import org.apache.tools.ant.filters.ReplaceTokens
// ...
build {
from('src/main/resources') {
include '*.properties'
filter(ReplaceTokens, tokens: [version : project.version])
}
}
Which throws this error:
Could not find method from() for arguments [src/main/resources, build_vbjud9ah7v3pj5e7c5bkm490b$_run_closure6_closure12#43ead1a8] on root project
Seems like I am on the wrong task?
====== Edit for completeness adding the solution based on Opals suggest =====
Thanks man, the following is the working solution!
processResources {
from('src/main/resources') {
include '*.properties'
filter(ReplaceTokens, tokens: [version : project.version])
}
}
Books and blogs alike, including the answer from Opal all recommend using a vivid mixture of exclude/include, from() and filter(). And of course, so did I on my first attempt to replace the text {{app javascript library}} in a index.html file to the path of a JavaScript library which depended on a simple project property setting.
The problem that hit me was that my 'war' task produced duplicated index.html files in the war archive and getting rid of the problem, using the pattern described previously, resulted in one huge unreadable hack.
Then I found a really straight forward solution. The following example is from my own build script and you have to customize it a bit to suite your needs:
war {
eachFile { copyDetails ->
if (copyDetails.path == 'index.html') {
filter { line ->
line.replace('{{app javascript library}}', "lib/someLib.js")
}
}
}
}
Paste sample code. What You need to do is to include file for replacement and exclude other files from replacement. Here is sample usage. Search for ReplaceTokens and You'll see what am I talking about.
You need to add filtering to processResources task. Sample code:
processResources {
def profile = project.properties['profile']
def replace_tokens = profile ? filter_tokens[profile] : filter_tokens['default']
exclude '**/log4j-test.xml'
from('src/main/resources') {
exclude '**/*.ttf'
filter(ReplaceTokens, tokens: replace_tokens)
}
from('src/main/resources') {
include '**/*.ttf'
}
}
Above ttf (binary) files are excluded from filtering but copied. replace_tokens is a filter taken from map defined in other part of the script.

Categories