How to use ant.properties in a `vendor.gradle` vendor settings file? - gradle

I have a multi-project gradle build with an ant build script that contains a task such as this (simplified):
<target name="get-version">
<!-- Goes out and fetches the versioning for each project and sets prop -->
<property name="version" value="${myDesiredVersion}" />
</target>
and inside my build.gradle script I have an ant script loader and then I depend on this target; my issue is that this isn't being evaluated in a way that allows me to use my ant.properties['version'] in vendor.gradle like
// version = '1.0.2' -- works
version = ant.properties['version'] // doesn't work
I'm a gradle noobie and to top it off I'm migrating an ant project to start using gradle so I could be way off course. It's important to note that I can't just outright include the version in the vendor.gradle file since it's being generated by the ant-script.
I'm not familiar with how I could insert this ahead of the gradle lifecycle... I tried something like this
gradle.beforeProject { p ->
tasks['get-version'].execute() // appears to execute successfully?
configure(p) {
def vendorSettings = file("${myRoot}/vendor.gradle")
if(vendorSettings.exists()){
println "Loading vendor settings for project " + p
println ant.properties['version'] // but outputs null here
// ant.properties is still a valid observable map
apply from: vendorSettings
}
}
}
and the ant version property was still null - note that it works outside of this, I think the scoping is a little different then I think it is in this specific situation

You might have something like
task antTask {
//...
doLast {
file("vendor.properties").withInputStream { is ->
Properties props = new Properties()
props.load(is)
props.each { prop -> ext.set(prop.key, prop.value) }
}
}
}
And after the task is executed you could access properties like ext.version, etc.
But, this won't work if you want to use the properties in task configurations, because all the tasks are configured first, only after that are executed if necessary. So your antTask is running only after all other tasks are already configured. So you probably need to rethink how you generate these ant properties and either precompute them before running gradle, or port the logic to gradle build.

Related

A code generator task in a multi-project gradle build

I have studied thousand similar questions on SO and I am still lost. I have a simple multiproject build:
rootProject.name = 'mwe'
include ":Generator"
include ":projectB"
include ":projectC"
with a top level build.gradle as follows (settings.gradle):
plugins { id "java" }
allprojects { repositories { jcenter() } }
and with two kinds of project build.gradle files. The first one (Generator) exposes a run command that runs the generator taking the command line argument:
plugins {
id "application"
id "scala"
}
dependencies { compile "org.scala-lang:scala-library:2.12.3" }
mainClassName = "Main"
ext { cmdlineargs = "" }
run { args cmdlineargs }
The code generator is to be called from projectB (and an analogous projectC, and many others). I am trying to do this as follows (projectB/build.gradle):
task TEST {
project (":Generator").ext.cmdlineargs = "Hurray!"
println ("Value set:" + project(":Generator").ext.cmdlineargs )
dependsOn (":Generator:run")
}
Whatever I try to do (a gradle newbie here) I am not getting what I need. I have two problems:
The property cmdlineargs is not set at the point that task :projectB:TEST is run. The println sees the right value but the argument passed to the executed main method is the one configured in Generator/build.gradle, not the one in projectB/build.gradle. As pointed out in responses this can be work around using lazy property evaluation, but this does not solve the second problem.
The generator is only run once, even if I build both projectB and projectC. I need to run Generator:run for each of projectB and projectC separately (to generate different sources for each dependent project).
How can I get this to work? I suppose a completely different strategy is needed. I don't have to use command line and run; I can also try to run the main class of the generator more directly and pass arguments to it, but I do find the run task quite convenient (the complex classpath is set up automatically, etc.). The generator is a Java/Scala project itself that is compiled within the same multi-project build.
Note: tasks aren't like methods in java. A task will execute either 0 or 1 times per gradle invocation. A task will never execute twice (or more) in a single Gradle invocation
I think you want two or more tasks. Eg:
task run1(type:xxx) {
args 'foo'
}
task run2(type:xxx) {
args 'bar'
}
Then you can depend on run1 or run2 in your other projects.

Wiring two Gradle tasks together via a property

The question:
In Gradle, how do I make the output of one task be a property and the input of another task be that same property? Especially in the context of that property being needed at configuration time.
What I'm trying to accomplish:
I'm attempting to write a Tar task that depends on user input. I'm having trouble with the need for lazy configuration given that the "baseName" is not known at configuration time.
Code
Here's what I would like to work but it doesn't.
task saveDb(type: Tar, dependsOn: getTarBaseName) {
// Next line doesn't work but does if I surround 2nd param with '{}'
inputs.property("baseName", getTarBaseName.baseName) // Doesn't work
from file("$dbsDir/data")
destinationDir = file(project.dbsBackupDir)
baseName = getTarBaseName.baseName // Doesn't work
extension = 'tar'
compression = Compression.NONE
}
task getTarBaseName() {
doFirst {
def result = BuildUtil.promptForName() // Uses Swing to prompt for a name
getTarBaseName.ext.baseName = result
}
}
As you can see I'm using ext to try to pass information between tasks, but that is just incidental not a requirement. Also I'm using 2 tasks, I'm completely willing to use only 1, however, that wouldn't really answer the general question which is one I hit against fairly often when attempting to use Gradle as a cross platform bash replacement for project related tasks.
To solve your specific problem (if I did not miss something), you don't need a second task. Just add a doFirst closure to your Tar task and set the baseName property there to whatever you want:
task saveDb(type: Tar) {
// static configuration
doFirst {
baseName = BuildUtil.promptForName()
// or for another task (don't forget to depend on that task)
baseName = otherTask.myProperty
}
}
task otherTask {
doFirst {
ext.myProperty = BuildUtil.promptForName()
}
}
However, your question boils down a general difficulty in Gradle: when to apply a specific piece of configuration.
Gradle just introduced a rather new feature for lazy configuration: Provider and Property
Gradle provides lazy properties, which delay the calculation of a property’s value until it’s absolutely required.
Before Gradle 4.0, only files could be lazy evaluated (via ConfigurableFileCollection), as an example:
task myZip(type: Zip) {
// zip some files
}
task copyMyZip(type: Copy) {
from myZip
}
myZip.baseName = 'myZip'
Even if the name of the zip file is defined after the Zip task is added to the Copy task configuration, its correct path will be used for the copy operation.
Now, with Gradle 4.0 and up, all configuration parameters that implement Property or Provider can be bound easily (check out the link above) and you can also lazily read a configuration parameter by wrapping it into a Provider, but it's difficult to put a provider value into an old configuration parameter. You still need to specify the moment when to evaluate (inside the task action, in a doFirst closure or an afterEvaluate handler). This problem is the topic of this discussion on GitHub.

How can I make Gradle extensions lazily evaluate properties that are set dynamically by tasks?

I'm pretty new to working with Gradle and I'm trying to develop a plugin that helps manage version numbering. This plugin defines a task that sets the project.version property of the project it's applied to.
What I'm trying to do is make it so that this property is set at the start of every Gradle build. Using Peter's answer to another Gradle question, I've managed to get my task to execute before any other by adding gradle.startParameter.taskNames = [":setProjectVersionNumber"] + gradle.startParameter.taskNames within my plugin's apply method.
However, other plugins (notably 'Maven-publish') rely on the version being specified during the configuration phase:
publishing {
publications {
somePublication(MavenPublication) {
version = project.version
}
}
}
What I'd like to know is if there's a way that I can make the evaluation of properties like version within these extensions as lazy as possible - such that they're not evaluated until a task that depends upon them is called, which in this case might be :publishToMavenLocal.
Below is an SSCCE that demonstrates what I'm hoping to achieve:
// This would be included within the plugin
class SetProjectVersionNumber extends DefaultTask {
#TaskAction
void start() {
// This will set project.version during execution phase
project.version = "1.2.3"
logger.info "Set project version number: $project.version"
}
}
task setProjectVersionNumber(type: SetProjectVersionNumber)
// Imagine this block being replaced by a maven 'publishing' block (or something similar)
ext {
version = project.version
// This will print 'unspecified', as it's evaluated during configuration phase
println "In extension, setting version=$project.version"
}
If you can provide a way to make ext.version equal 1.2.3 in the example above, I believe you've resolved my issue.
If this is asking too much, it may be possible for me to make my plugin generate the version string at configuration-time rather than execution-time. It would be nice to know if I could do it this way, though.
EDIT
In an experimental branch, I tried moving all the version string assignment logic to the configuration-phase (by making it all happen during plugin application rather than during task execution), but I don't believe this will work as the plugin extension has not yet been processed and trying to refer to properties defined in it fail.
EDIT 2
Wrapping the version string assignment logic in a project.afterEvaluate closure seems to have worked:
#Override
public void apply(Project project) {
logger = project.logger
project.extensions.create(EXTENSION_NAME, SemVerPluginExtension)
project.afterEvaluate {
setVersionProjectNumber(project)
addTasks(project)
}
}
In a mock project, I implement build.gradle as follows:
apply plugin: 'semver'
apply plugin: 'maven-publish'
group = 'temp'
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath 'com.github.tagc:semver-plugin:0.2.2'
}
}
semver {
versionFilePath = 'version.properties'
}
publishing {
publications {
testPublication(MavenPublication) {
version = project.version
assert version
println "Set publication version to $version"
}
}
}
For some reason, this seems to work. Although the version string assignment logic is wrapped in an 'afterEvaluate' closure and the test publication version assignment isn't, the former still occurs before the latter:
Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using StatementExtractingScriptTransformer.
Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using BuildScriptTransformer.
VERSION FILE PATH=version.properties
Current Git branch: develop
Set project version to 0.2.1-SNAPSHOT
Set publication version to 0.2.1-SNAPSHOT
All projects evaluated.
I'm leaving this question open and unresolved since I'd still like to know if it's possible to do it the way I originally intended. Additionally, I'd appreciate any explanation about why the publication version is assigned after the project version is set, and whether I can depend on that always being the case or whether that's just happening now by accident.
You can use lazy instantiation of GStrings to evaluate properties at run time:
project.tasks.create("example_task", Exec.class, {
commandLine 'echo', "${-> project.someproperty}"
})
Note that you have to use quotation marks and not apostrophes - "${...}" works, but '${...}' does not.

Reading Gradle build parameters

I have a Gradle project with subprojects that I can issue separate build commands if I don't want to build all the subprojects at once. For example,
parent
subprojectA
subprojectB
subprojectC
I can then issue commands like ./gradlew :subprojectA:assemble :subprojectC:assemble. What I like to do is construct a meaning git tag from each subproject group and version values for those subprojects that are being built, i.e., group and version for only subprojectA and subprojectC in this case. I am thinking of writing a standalone plugin to do this but unsure where / how to get this information at build time. Any suggestions would be much appreciated.
Both group and version (as well as any other project property) is available globally in your build script.
task myTask << {
println group + "-" + version
}
If you are writing a binary plugin you can also access properties off the Project object itself via the property() method.
def group = project.property('group')
Edit
If you want to determine if a particular project is being built you can inspect the TaskExecutionGraph.
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(':subprojectA:assemble')) {
println 'Will build subprojectA'
}
}

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