How do you access gradle.ext properties in Plugin Java source code? - gradle

I need a property to (one) be available when a plugin is applied and (two) allow for a calculated override value in the settings.gradle file. A project property would be ideal as it can have a default set in gradle.properties:
# gradle.properties
myProp=originalValue
This is great because it can be overrode with a command line argument like -PmyProp=newValue, but I was not able to find a good way to override the property in the settings.gradle file before the build.gradle executes (i.e. before the plugins are applied).
For instance all of these leave rootProject.myProp unaltered at plugin application:
// settings.gradle
rootProject.getProperties.put("myProp", "overrideValue")
settings.ext.myProp = "overrideValue"
settings.extensions.myProp = "overrideValue"
gradle.startParameters.projectProperties.myProp = "overrideValue"
We cannot do any magic in the build.gradle either because no logic can exist before the plugins block:
// build.gradle
plugins {
id 'com.myCompany.myPlugin' version 1.0.0 // 'myProp' must be set by now
}
One workaround I can think of would be to use:
// settings.gradle
gradle.ext.myProp = "overrideValue"
... but there doesn't seem to be a good way to access gradle.ext properties in Java source code (for a plugin), or is there?

This seems to work for the gradle.ext.myProp use case, but it is surprising to me that the only workable approach is to cast the Gradle object to an ExtensionAware object:
// MyPlugin.java
String myProp = (String) project.getRootProject().getProperties().getOrDefault("myProp", null);
Gradle gradle = project.getRootProject().getGradle();
if ((myProp == null) && (gradle instanceof ExtensionAware)) {
ExtensionAware gradleExtensions = (ExtensionAware) gradle;
myProp = (String) gradleExtensions.getExtensions().getExtraProperties().get("myProp");
}
It seems like what I'm trying to do should be commonplace, so is there a better way like solely using project properties?
If so, then how do you change the values in the settings.gradle file?

This is probably not what you’re looking for but maybe it still helps: have you considered an initialization script? In such a script it is possible to override a project property.
Example:
$ ./gradlew -PmyProp=originalValue properties | grep myProp
myProp: originalValue
$ ./gradlew -PmyProp=originalValue -I init.gradle properties | grep myProp
myProp: overrideValue
… where init.gradle is the following:
allprojects {
project.ext.myProp = 'overrideValue'
}
Note that there are also other ways of specifying the init script.

Related

How to configure Dokka for package documentation using Gradle?

I am unable to configure Dokka to include the documentation of my packages without using a full absolute path from the root of the file system. I have in my Gradle file (the includes line is the one in problem):
dokka {
outputFormat = 'html'
outputDirectory = "$projectDir/../doc"
configuration {
// Use to include or exclude non public members.
includeNonPublic = false
// Do not output deprecated members. Applies globally, can be overridden by packageOptions
skipDeprecated = false
// Emit warnings about not documented members. Applies globally, also can be overridden by packageOptions
reportUndocumented = true
// Do not create index pages for empty packages
skipEmptyPackages = true
includes = ["${projectDir}/app/src/main/kotlin/com/biexpertise/simplewebview/packages.md"]
}
}
When, I run ./gradlew dokka if I have this error:
> Task :app:dokka FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:dokka'.
> org.codehaus.groovy.runtime.GStringImpl cannot be cast to java.lang.String
If I remove $projectDir and use an absolute path, things are working. It is possible to use a relative path instead?
So that's in fact a problem with dokka's Gradle plugin, or to be more specific, with Groovy String implementation. Groovy uses it's own GStringImpl for strings, instead of the String class, which leads to a problem when casting a List<GStringImpl> to List<String> (this cast doesn't succeed).
The easiest solution is to call .toString() on your include, like this:
includes = ["${projectDir}/app/src/main/kotlin/com/biexpertise/simplewebview/packages.md".toString()]
This should be fixed on the dokka side though, you can file a bug report on GitHub

Using a default value between two different build gradle files

I have the following scenario in my Android project:
Project1 --> Build.gradle (1)
Project2--> Build.gradle (2)
Example: Define the following variable:
//first Gradle file
def getProductFlavor() {
//Logic here
Gradle gradle = getGradle()
String requestingTask = gradle.getStartParameter().getTaskRequests().toString()
return requestingTask
}
Instead of defining getCurrentTime() in the second Gradle file,
I call the getCurrentTime() from the first Gradle file.
Maybe my example is wrong and the default value needs to be implementing in another gradle file like the script gradle or somewhere else, but the intent of the example was to clarify what I'm trying to achieve.
The two projects are independents but both belong to the same android project. I want to use ONE def value in both of these gradle files.
I'm a gradle newbie by the way. Never mind if I'm asking this question the wrong way.
Feels like a scenario for external script:
main-project
....| sub-project1
........| src
........| build.gradle
....| sub-project2
........| src
........| build.gradle
....| common.gradle
build.gradle
apply from: '../common.gradle'
def flavor = getProductFlavor()
common.gradle
def getProductFlavor() {
Gradle gradle = getGradle()
String requestingTask = gradle.getStartParameter().getTaskRequests().toString()
return requestingTask
}
ext {
getProductFlavor = this.&getProductFlavor
}

get gradle to ignore version number in jar name

I have a project that has to build about 10 different dummy jars for unit testing. I have a gradle project setup like this
project(':CodeTools_dummydriver-true') {
ext.dummyDriver = true
archivesBaseName = "dummydriver-true"
}
But the problem is the jarfile is still named dummydriver-true-0.0.1.jar. Is there any way I can tell gradle to ignore the standard pattern and please name the output file what I want it to be named? Meaning, without the version number?
The Java plugin defines the jar task to follow the following template for archiveName:
${baseName}-${appendix}-${version}-${classifier}.${extension}
I don't think there's a way to apply a new "naming template", but what you can do is to explicitly set your jar task's archive name, like this. (Also, isn't it a good idea to use the dummyDriver property directly, instead of hardcoding "true" into the archive name?)
archivesBaseName = "dummydriver"
jar.archiveName = "${jar.baseName}-${dummyDriver}.${jar.extension}"
Alternately, set the version property to null or an empty string, like suggested in Circadian's answer, but if you ever want to use the version property for anything, you don't want to destroy it.
For newer gradle you should use:
build.gradle.kts:
tasks.jar {
archiveFileName.set("${project.name}-new-name.jar")
}
build.gradle:
jar.archiveFileName = "new-name.jar"
Because archiveName is deprecated. (You have to specify .jar as well).
Adding a version property and setting its value to an empty string worked for me.
Here is an example:
project(':CodeTools_dummydriver-true') {
ext.dummyDriver = true
archivesBaseName = "dummydriver-true"
version= ""
}
Hope that helps.

Getting Gradle 'rootProject' object to honor env vars

I ran across the following configuration in a Gradle project's buildfile (build.gradle):
codenarcMain {
configFile = rootProject.file("gradle/codenarc/CodeNarcMain.groovy")
}
When I Google "Gradle rootProject" I find this link which makes it look like rootProject is a ProjectDescriptor instance.
But looking at ProjectDescriptor, I don't see any property called file. I see a buildFile and projectDir, but no file property.
Ultimately, I am trying to get Gradle to load the CodeNarc config file from outside the build directory. On my system I have an env var called $CODENARC_HOME with the following directory structure:
CODENARC_HOME/ (say this is /home/myuser/tools/codenarc/)
CodeNarcMain.groovy
CodeNarcTest.groovy
README.md
Now I would like to change the CodeNarc config in Gradle to look something like this:
codenarcMain {
configFile = rootProject.file("CODENARC_HOME/CodeNarcMain.groovy")
}
And then, no matter where CODENARC_HOME is defined, the Gradle build will still be able to locate the config file.
So my questions:
What is the file property on rootProject, and why don't I see it in the API docs (linked above)?; and
How to get rootProject.file(...) to honor system/env vars inside its file path string argument?
rootProject in settings.gradle is-a ProjectDescriptor. rootProject in build.gradle is-a Project. Environment variables and system properties can be accessed in the standard Java way:
codenarcMain {
configFile = rootProject.file("${System.getenv("CODENARC_HOME")}/CodeNarcMain.groovy")
// or: System.getProperty("codenarc.home")
}

How to expand property references in jar resources?

I'm using Gradle to build a jar containing an xml file in META-INF. This file has a row like
<property name="databasePlatform" value="${sqlDialect}" />
to allow for different SQL databases for different environments. I want to tell gradle to expand ${sqlDialect} from the project properties.
I tried this:
jar {
expand project.properties
}
but it fails with a GroovyRuntimeException that seems to me like the Jar task attempts to expand properties in .class files as well. So then I tried
jar {
from(sourceSets.main.resources) {
expand project.properties
}
}
which does not throw the above exception, but instead results in all resources being copied twice - once with property expansion and once without. I managed to work around this with
jar {
eachFile {
if(it.relativePath.segments[0] in ['META-INF']) {
expand project.properties
}
}
}
which does what I want, since in my use case I only need to expand properties of files in the META-INF directory. But this feels like a pretty ugly hack, is there a better way to do this?
I stumbled across this post in a thread about a different but closely related issue. Turns out you want to configure the processResources task, not the jar task:
processResources {
expand project.properties
}
For some reason, though, I did have to clean once before Gradle noticed the change.
In addition to #emil-lundberg 's excellent solution, I'd limit the resource processing to just the desired target file:
build.gradle
processResources {
filesMatching("**/applicationContext.xml") {
expand(project: project)
}
}
An additional note: if the ${...} parentheses are causing "Could not resolve placeholder" errors, you can alternatively use <%=...%>. N.B. tested with a *.properties file, not sure how this would work for an XML file.
I've had similar problems migrating from maven to gradle build. And so far the simplest/easiest solution was to simply do the filtering yourself such as:
processResources {
def buildProps = new Properties()
buildProps.load(file('build.properties').newReader())
filter { String line ->
line.findAll(/\$\{([a-z,A-Z,0-9,\.]+)\}/).each {
def key = it.replace("\${", "").replace("}", "")
if (buildProps[key] != null)
{
line = line.replace(it, buildProps[key])
}
}
line
}
}
This will load all the properties from the specified properties file and filter all the "${some.property.here}" type placeholders. Fully supports dot-separated properties in the *.properties file.
As an added bonus, it doesn't clash with $someVar type placeholders like expand() does. Also, if the placeholder could not be matched with a property, it's left untouched, thus reducing the possibility of property clashes from different sources.
here is what worked for me (Gradle 4.0.1) in a multi-module project:
in /webshared/build.gradle:
import org.apache.tools.ant.filters.*
afterEvaluate {
configure(allProcessResourcesTasks()) {
filter(ReplaceTokens,
tokens: [myAppVersion: MY_APP_VERSION])
}
}
def allProcessResourcesTasks() {
sourceSets*.processResourcesTaskName.collect {
tasks[it]
}
}
and my MY_APP_VERSION variable is defined in top-level build.gradle file:
ext {
// application release version.
// it is used in the ZIP file name and is shown in "About" dialog.
MY_APP_VERSION = "1.0.0-SNAPSHOT"
}
and my resource file is in /webshared/src/main/resources/version.properties :
# Do NOT set application version here, set it in "build.gradle" file
# This file is transformed/populated during the Gradle build.
version=#myAppVersion#
I took your first attempt and created a test project. I put a pom file from a jenkins plugin in ./src/main/resources/META-INF/. I assume it is a good enough xml example. I replaced the artifactId line to look like the following:
<artifactId>${artifactId}</artifactId>
My build.gradle:
apply plugin: 'java'
jar {
expand project.properties
}
When I ran gradle jar for the first time it exploded because I forgot to define a value for the property. My second attempt succeeded with the following commandline:
gradle jar -PartifactId=WhoCares
For testing purposes I just defined the property using -P. I'm not sure how you are trying to define your property, but perhaps that is the missing piece. Without seeing the stacktrace of your exception it's hard to know for sure, but the above example worked perfectly for me and seems to solve your problem.

Resources