Create task for dependency lock in Gradle >5.0 - gradle

I want to create a task which to execute
dependencies --update-locks ':'
I had a configuration:
dependencyLocking {
lockAllConfigurations()
}
I try with
task lockDependencies {
dependsOn = ['dependencies','--update-locks *:*']
}
But have:
What went wrong: Could not determine the dependencies of task ':lockDependencies'.
Task with path '--update-locks :' not found in root project

You cannot pass Gradle command line parameters as a task dependency, that's what your error above is about.
The state of writing locks, either with --write-locks or --update-locks, is something that happens really early in the build lifecycle.
You can somewhat control it from a task with the following:
* Create a placeholder task in your build script
* In the settings.gradle(.kts) query the requested tasks from the command line, and if it is there, mutate the start parameters:
if (startParameter.taskNames.contains('placeHolder')) {
startParameter.setWriteDependencyLocks(true)
}
Note that this is not an option if you are trying to lock the classpath of the build itself, which is one of the motivations behind using a command line flag.
Note also that this just allows replacing a flag, like --update-locks *:* with a task invocation like updateLocks but will not work if that task is wired as a dependency of other tasks, as it needs to be requested explicitly. And doing the start parameter mutation after the task graph is computed is too late in the lifecycle.

The best way to do this in my opinion is to add inside the build.gradle file the following code:
dependencyLocking {
lockAllConfigurations()
}
task commitLockDependencies {
'git add /gradle/dependency-locks '.execute()
}
init {
dependsOn('commitLockDependencies')
}
And then inside the settings.gradle the following line:
startParameter.setWriteDependencyLocks(true)
Working with gradle 7.1.

Related

Gradle not running copy task after jar

I am trying to do something like this:
jar {
doLast{
from "build/libs/TheJar.jar"
into "."
}
}
So far, I have tried various tutorials including all forms from this answer but non have worked. The only thing that works is calling a separate task but I'd like to know why my construction is wrong and why can't I run something after the jar or shadowJar tasks.
It looks like you took some parts of the answers in the linked post and somehow mixed them without knowing what your final code actually does.
Tasks in Gradle may have a type (e.g. Copy, Jar, ...). This type defines what the task will do once it gets executed (so called task actions). A task without a type won't do anything when its executed, unless you add task actions manually. Using doFirst will add the passed action (also called closure) to the start of the list of task actions, using doLast will add it to the end of the list.
Everything outside of doFirst and doLast closures is not part of the execution of the task, but can be used to configure the task:
task example {
doLast {
println "second action"
}
doFirst {
println "first action"
}
println "configuration"
}
Run the code above with gradle example and you will see the order of the log messages as configuration, first action, second action.
Task configuration will run, even if the task won't be executed later on. Even if you call gradle (without any task names as arguments), the console will still print configuration. This was the actual problem in the linked question.
Lets come to the real question: How to copy a file?
Well, Gradle offers two ways to copy a file. The first one is the task type Copy. You can create a task based on this type, apply your configuration and then either call it directly from the command line or define task dependencies using dependsOn or finalizedBy:
task copySomeFiles(type: Copy) {
from '...'
into '...'
}
However, you don't want to create an additional task. Gradle also has a method called copy that may be called anywhere in your script and will instantly copy the files:
copy {
from '...'
into '...'
}
The code above will copy your files, but it will copy your files every time Gradle executes (which may be often, e.g. when using an IDE). To solve this problem, you may move your code with copy to a task action, e.g. inside a doFirst or doLast closure:
jar {
doLast {
copy {
from "build/libs/TheJar.jar"
into "."
}
}
}
As you can see, your code was missing the copy statement. Now, whenever your task jar gets executed, its last task action will copy the files as intended.
Bonus question: Why is there no error?
The "problem" in your case is that your code is perfectly valid Gradle code. The task jar is of type Jar. Every task of type Jar may be configured using methods called from and into. The method from adds files to the JAR file and the method into sets the destination directory inside the JAR. So instead of copying, your code configures the underlying task jar. However, this has no negative consequences, as this configuration gets applied inside doLast, which only runs once the JAR file has already been created. Something that already happened cannot be configured.

Gradle task is broken - cannot execute

import org.apache.tools.ant.filters.ReplaceTokens
task genScript(type:Copy){
copy{
from "../../scripts/script.txt"
into projectDir
filter ReplaceTokens, tokens: [baseName: jar.baseName, version: jar.version, prefix: 'x']
}
}
jar.doLast{
tasks.genScript.execute()
}
genScript executes fine if I just click on it and run. But when I do ..\gradlew clean jar, it gives me the following error:
Could not find method execute() for arguments [] on task ':myModule:genScript' of type org.gradle.api.tasks.Copy.
How to fix it?
I am using Gradle 6.0.1.
You can't programatically execute tasks from other tasks in newer versions of Gradle. Instead, you are supposed to declare task dependencies and Gradle will ensure they get executed in the correct order.
The quick fix is just to make jar depend on your task like this:
jar.dependsOn('genScript')
Alternatively, you could move your logic into the doLast block in the jar task.

How to diagnose / troubleshoot gradle build not running dependency task?

I'm adding gradle to a multi-project build and hitting the learning curve.
I need to invoke a custom task of type:Exec before compilation of a subproject. The task is not invoked. Why?
build.gradle of parent project
task precompiletask(type:Exec) {
println "Executing pre-compile task"
// ...
}
task(":cppproj:build").dependsOn precompiletask
// Also tried this, same output
//project(":cppproj").task(":build").dependsOn precompiletask
// Also tried this -> error "Cannot add task 'build' as a task with that name already exists"; why on Earth would this syntax *add* the task "build"?
//project(":cppproj").task("build").dependsOn precompiletask // I also tried this
build.gradle of subproject 'cppproj'
apply plugin: "cpp"
model {
components {
api(NativeLibrarySpec) {
sources {
// ...
}
}
}
}
-
Related questions
Why do I find it so hard to debug this? I am running gradle with the verbose-est output (gradle build --debug --warning-mode all). The only mentions of my custom task precompiletask are these, clustered towards the beginning of the output:
07:24:41.151 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Build operation 'Realize task :precompiletask' started
07:24:41.243 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Completing Build operation 'Realize task :precompiletask'
07:24:41.243 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationExecutor] Build operation 'Realize task :precompiletask' completed
07:24:41.249 [QUIET] [system.out] Executing pre-compile task
Why no errors related to failing to link the two tasks using dependsOn? Since dependsOn is clearly invoked in my code I'd expect an error that either the subproject or tasks are not found, or dependsOn itself fails somehow. Does this have to do with gradle's flexibility of being able to refer to stuff that doesn't exist yet?
What does it mean to 'Realize task' anyway? Could not find this documented. Thanks.
You are not using the proper way to access the :cppproj:build task in your root project build: you are using project.task() methods , which is actually creating a new task.
You have several ways available to 'locate' the task you want to configure (adding the dependsOn constraint), as explained here : locating tasks
In your case you could write:
tasks.getByPath(':cppproj:build').dependsOn precompiletask
Note 1:
When using the syntax task(":cppproj:build").dependsOn precompiletask: you create a new task named ':cppproj:build' on the root project and make it depend on precompiletask: that's why precompiletask is not executed if you execute build tasks from parent or subproject.
Note 2:
// Also tried this -> error "Cannot add task 'build' as a task with that name already exists"; why on Earth would this syntax add the task "build"?
//project(":cppproj").task("build").dependsOn precompiletask
=> because the project.task(String) method creates a task, so you are trying to add new task named build to the subproject which already have one build task.
Just adding an answer to my sequel question: why does this seemingly equivalent syntax fail to achieve the same thing: project("cppproj").tasks["build"].dependsOn.
I found that the "configuration" step of gradle build is done by default in a top-down manner, meaning when the root project build.gradle is executed, the subprojects don't contain their tasks yet. This can be changed by doing:
evaluationDependsOnChildren()
... and then one can write:
subprojects.each {
it.tasks["build"].dependsOn myPreCompileTask
}

Generate a dependency build number using a task

I am attempting to generate a build number for an artifact using a task and then plug that into my dependency. My task looks something like this:
task getCurrentBuild() {
doFirst{
if(! file('/folder/dailyBuildNumber.txt').exists()) {
assert false,'Cannot find latest runtime build at /folder/dailyBuildNumber.txt'
}
else {
ext.set("myVersion", file('folder/dailyBuildNumber.txt').getText('UTF-8'))
}
}
}
while my dependencies look something like this:
latestArtifactCompile "com.example.1.0-${project.ext.get("myVersion")}"
I am getting errors that it cannot find the extra or get the property myVersion
doFirst closures are executed during execution phase, but the dependencies closure is executed before, during the configuration phase.
Why do you want to generate your dependency in a task? This seems to me like a regular and mandatory configuration required for each build, not depeding on the tasks you execute.

Dynamically created task of type Copy is always UP-TO-DATE

I've prepared a very simple script, that illustrates the problem I see using Gradle 1.7 (need to stick with it because of some plugins not yet supporting newer versions).
I'm trying to dynamically create tasks each of which corresponds to a file in the project directory. This works fine, but the tasks I create never get executed as soon as I assign them type 'Copy'.
Here is my problem build.gradle:
file('templates').listFiles().each { File f ->
// THIS LINE DOES NOT WORK
task "myDist-${f.name}" (type: Copy) {
// NEXT LINE WORKS
//task "myDist-${f.name}" {
doLast {
println "MYDIST-" + f.name
}
}
}
task distAll(dependsOn: tasks.matching { Task task -> task.name.startsWith("myDist")}) {
println "MYDISTALL"
}
defaultTasks 'distAll'
in this way my tasks do not get executed when I call default task calling simply gradle:
MYDISTALL
:myDist-template1 UP-TO-DATE
:myDist-template2 UP-TO-DATE
:distAll UP-TO-DATE
BUILD SUCCESSFUL
If I remove type Copy from my dynamic task (uncommenting the line above), my tasks get executed:
MYDISTALL
:myDist-template1
MYDIST-template1
:myDist-template2
MYDIST-template2
:distAll
BUILD SUCCESSFUL
(You'll need to create a folder name templates in the same directory where build.gradle is located and put couple of empty files into there in order to run the test)
According to the debug output:
Skipping task ':myDist-template1' as it has no source files.
Skipping task ':myDist-template2' as it has no source files.
So how can I specify source files and make my Copy tasks execute?
I've tried adding
from( '/absolute/path/to/existing/file' ) {
into 'myfolder'
}
to the task body, I've tried assigning task's inputs.source file('/my/existing/file') with no success.
Could you please advise on how to modify my simple script leaving dynamic task creation and keeping my dynamic tasks of type Copy?
Thank you!
Edit:
All right, this way the task gets called:
file('templates').listFiles().each { File f ->
task "myDist-${f.name}" (type: Copy) {
from f
into 'dist'
doLast {
println "MYDIST-" + f.name
}
}
}
but it looks I must always specify from/into. It doesn't suffice to do that in the doLast{} body.
A Copy task only gets executed if it has something to copy. Telling it what to copy is part of configuring the task, and therefore needs to be done in the configuration phase, rather than the execution phase. These are very important concepts to understand, and you can read up on them in the Gradle User Guide or on the Gradle Forums.
doFirst and doLast blocks get executed in the execution phase, as part of executing the task. Both are too late to tell the task what to copy: doFirst gets executed immediately before the main task action (which in this case is the copying), but (shortly) after the skipped and up-to-date checks (which are based on the task's configuration). doLast gets executed after the main task action, and is therefore clearly too late.
I think the following Gradle User Guide quote answers my question the best:
Secondly, the copy() method can not honor task dependencies when a task is used as a copy source (i.e. as an argument to from()) because it's a method and not a task. As such, if you are using the copy() method as part of a task action, you must explicitly declare all inputs and outputs in order to get the correct behavior.
Having read most of the answers to "UP-TO-DATE" Copy tasks in gradle, it appears that the missing part is 'include' keyword:
task copy3rdPartyLibs(type: Copy) {
from 'src/main/jni/libs/'
into 'src/main/libs/armeabi/'
include '**/*.so'
}
Putting from and into as part of the doLast section does not work. An example of a working task definitions is:
task copyMyFile(type: Copy) {
def dockerFile = 'src/main/docker/Dockerfile'
def copyTo = 'build/docker'
from dockerFile
into copyTo
doLast {
println "Copied Docker file [$dockerFile] to [$copyTo]"
}
}
Not the behavior I was expecting.
Using gradle 3.2.1

Resources