variable is read-only when it shouldn't be - gradle

When i execute this in my script
artifacts = ['abc123-com', 'abc123-ejb', 'abc123-spec', 'abc123-war', 'abc123-war2']
task clone_workspace() << {
for (item in artifacts) {
println item
}
}
i get
> Cannot set the value of read-only property 'artifacts' on root project 'abc123'.
I have tried scoping to project with project.artifacts, and to ext with project.ext.artifacts.
what am I doing wrong here?

The following minimalist Gradle file illustrates that project already has a property for artifacts (documented here):
println "TRACER : " + project.artifacts.class
output:
bash$ gradle
TRACER : class org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated
By contrast, this version of the original is happier:
def myArtifacts = ['abc123-com', 'abc123-ejb', 'abc123-spec', 'abc123-war', 'abc123-war2']
task clone_workspace() << {
for (item in myArtifacts) {
println item
}
}

Related

How to reference a subproject dir from a root gradle task when calling task from subproject

I have a root project gradle task defined below. I am
task createVersionTxtResourceFile {
doLast {
def webAppVersionFile = new File("$projectDir/src/main/resources/VERSION.txt")
def appVersion = project.ext.$full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
In a few subprojects, I want to run this task and create the VERSION.txt file in the subproject's src/main/resources/VERSION.txt. My problem is that the root level task's $projectDir is the root project.
Is it possible to define a root level task that uses the sub-project directory when invoking it? Or perhaps there's a better approach all together.
You could do it slightly more controlled when you register an action to wait for the java plugin to be applied on the subproject. With this you can create the task only in subprojects who contain the desired compileJava task and configure everything from the root project.
subprojects { sub ->
//register an action which gets executed when the java plugins gets applied.
//if the project is already configured with the java plugin
//then this action gets executed right away.
sub.plugins.withId("java") {
//create the task and save it.
def createVersionTxtResourceFile = sub.tasks.create("createVersionTxtResourceFile") {
doLast {
def webAppVersionFile = new File("${sub.projectDir}/src/main/resources/VERSION.txt")
def appVersion = rootProject.full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
// set the task dependency
sub.tasks.compileJava.dependsOn createVersionTxtResourceFile
}
}
I ended up just defining the task in each subproject, and setting a dependency on it in the appropriate subprojects:
subprojects {
task createVersionTxtResourceFile {
doLast {
def webAppVersionFile = new File("$projectDir/src/main/resources/VERSION.txt")
def appVersion = rootProject.full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
}
then in a subproject build.gradle:
compileJava.dependsOn createVersionTxtResourceFile

Can gradle script property extensions be shared between different scripts

If there is a build.gradle file as follows:
...
apply from: 'Other.gradle'
task hello {
project.ext.hello = "hello"
}
And Other.gradle has:
task getHello {
println project.ext.hello
}
I get an error saying:
Cannot get property 'hello' on extra properties extension as it does not exist
Is there a way to share property extensions between the scripts?
Try setting ext.hello then have the tasks update it
== build.gradle
ext {
hello = null
}
apply from: 'Other.gradle'
task hello {
doLast {
hello = "hello"
}
}
== Other.gradle
task getHello {
doLast {
println hello
}
}
If you really want to be able to set info on a task, you can also use the ext on a task and scope it to a task. If you were implementing a larger plugin you could create an extension and set it on the task.

How to list the configured repositories?

How can I list all the repositories configured for a project?
Background: I have a pretty complex gradle build script and cannot get my NetBeans to download the sources for maven dependencies. In that issue-report I was suggested to double check the order in which mavenCentral is being imported.
For anyone interested, here is the code to list the loaded repositories (thanks #kelemen):
task listrepos {
doLast {
println "Repositories:"
project.repositories.each { println "Name: " + it.name + "; url: " + it.url }
}
}
After adding this code to the build script, execute gradle listrepos and voilĂ ...
If someone comes to this page looking for the Kotlin (build.gradle.kts) equivalent of #Alberto's answer, it would be done as follows:
tasks.register("listrepos") {
doLast {
println("Repositories:")
project.repositories.map{it as MavenArtifactRepository}
.forEach{
println("Name: ${it.name}; url: ${it.url}")
}
}
}
Just as a heads up, the cast to a MavenArtifactRepository is required in the Kotlin version to get the url property. This could be different for you if you are not adding Maven Repositories.
Trying to use Alberto's task from his answer I was getting the following error as in my case I had a Plugin repository defined:
No such property: url for class: org.gradle.plugin.use.internal.PluginDependencyResolutionServices$PluginArtifactRepository
To avoid this error I have changed the task logic a bit:
task listrepos {
doLast {
println "Repositories:"
project.repositories.each {
if (it.name == '__plugin_repository__Gradle Central Plugin Repository') {
println "Name: " + it.name + "; url: " + it.url
} else {
println "Name: " + it.displayName
}
}
}
}

How to use a parameter in gradle copy task in the destination folder?

Given the following task in gradle, the dev would start a new module by:
gradle initModule -PmoduleName=derp
task initModule(type: Copy) {
description "Initialize an empty module based on the template. Usage: gradle initModule -P moduleName=derp"
onlyIf { project.hasProperty("moduleName") }
from "$rootDir/modules/template"
into "$rootDir/modules/$moduleName"
}
I am unable to run gradle since moduleName is not defined during evaluation, although I was hoping that "onlyIf" would do so.
To solve this I assign it to a locally defined variable in a guard block:
def modName = ""
if (!project.hasProperty("moduleName")) {
logger.error("Invalid moduleName : It can't be")
} else {
modName = moduleName
}
and finally use the new variable to survive the configuration phase.
Is this the right way to do this? It just doesn't feel right.
Additionally if I was using a rule to make this a tad more elegant:
tasks.addRule("Pattern: initModule_<mod name>") { String taskName ->
if (taskName.startsWith("initModule_")) {
def params = taskName.split('_');
task(taskName) {
doLast {
project.ext.moduleName = params.tail().head()
tasks.initModule.execute()
}
}
}
}
The moduleName is not passed around (even if I change it to finalizedBy).
Any help is appreciated.
As you already figured out the property is not available during the configuration phase.
But can postpone the look-up to the execution phase by using
into "$rootDir/modules/" + project.property("moduleName")
instead of
into "$rootDir/modules/$moduleName"

Can't add String variable to my manifest

I have a build.gradle file and I want to insert some variables into my manifest. This is what I have
def dateformat = new java.text.SimpleDateFormat("yyyy.MM.dd")
version = dateformat.format(new Date());
int userlimit = System.properties['user.limit'] == null ? 1 : System.properties['user.limit'] as int
def owner = System.properties['owner'] == null ? 'Demo version' : System.properties['owner']
task release(dependsOn: [jar, libs, obfuscate]) {
doLast {
def classesDir = new File('expanded-libs')
classesDir.mkdir()
configurations.embed.each {
println "Unzipping $it to expanded-libs.."
ant.unzip(src: it, dest: 'expanded-libs')
}
task combinedJar(type: Jar) {
from zipTree('build/foo-bar-pg-' + version + '.jar')
from configurations.embed.collect { println "Adding $it to the fat jar"; zipTree(it) }
archiveName "../foo-bar-${version}${outputFileNameSuffix}.jar";
exclude 'META-INF/*'
duplicatesStrategy 'EXCLUDE'
manifest {
attributes 'Implementation-Title': 'Foo Bar Module', 'Implementation-Version': version, 'User-Limit': userlimit, 'Licensed-To': owner
}
}
combinedJar.execute()
}
}
Now the interesting thing is that it works without the 'Licensed-To': owner piece, but somehow I can't use that variable in the manifest. For integers, it works, but owner is a String.
This is the error message I get if I run gradle release -Downer="foo" -Duserlimit=20
* What went wrong:
Execution failed for task ':release'.
> Could not find method task() for arguments [{type=class org.gradle.api.tasks.bundling.Jar}, combinedJar, build_36jmpdf
2mspiolilq5o7ipbd86$_run_closure14_closure27_closure29#547a8dfd] on task ':release'.
Can anyone help me how could I put String typed JVM arguments into my manifest?
You missed a closing } brace after the manifest attributes.

Resources