How do I set up simple file dependencies with Exec tasks?

Sorry, this is a total newbie question. I’m currently working with a flow with a lot of makefiles, but I want to convert them to a more advanced build system, and Gradle is one of the tools I’m looking. One issue is that none of the jobs in our flow are standard software build jobs, most are just command line calls. Some are jobs sent off to a sun grid farm, and part of the evaluation is writing a function or class or whatever to deal with those jobs more cleanly. In the meantime, I’m trying to convert a couple of basic flows over to show the boss and the rest of the team what the converted flows would look like. But I’m a little confused with some of the basic job setup. I’ve figured out that I can use Exec tasks for most jobs, but I’m not super sure how to set sources and targets for those jobs. It looks like Tasks in general have an ‘outputs’ and ‘inputs’ properties that look like they can be set to FileCollection objects, but then reading the ‘File Dependencies’ section in the user guide (50.4.4) there’s stuff about dependency and compile closures and other stuff. So is there an easy way to do this, just set up an Exec task with simple file dependencies, or do I need to do all the closures and things? Thanks for any help, sorry again about the dumb question.

Task inputs/outputs mainly work on Gradle’s file API: ‘FileCollection’ or ‘FileTree’. It’s fairly easy to convert a bunch of files into a ‘FileCollection’. You just have to use the method ‘Project#files’ and then set the inputs/outputs of the tasks. To me it sounds like you don’t need to deal with dependencies in that sense. Maybe you can provide some of your code so we can dig deeper.

OK, I was trying something like this:

def inp1 = /…

def inp2 = /… def log1 = /…

task(‘run1’, type: Exec) {

commandLine ‘./runsomething.csh’

workingdir = “/somepath”

inputs.files = [inp1, inp2]

outputs.file = log1 }

So am I anywhere close here? Do I need other sections, or can I stick to inputs and outputs?

Looks good to me. Is there anything failing for you?

It’s telling me this: > Cannot set readonly property: files for class: org.gradle.api.internal.tasks.DefaultTaskInputs

And checking the docs, it does say that inputs is read only. Checking back to section 15.9.1 in the docs, it looks like they have it there without the equals sign. That seems to help, but not for the multiple file case.

Oh, sorry. I didn’t look close enough. ‘files’ is a method call. Remove the ‘=’ characters.

So with the “inputs.file inp1” it will run, but while trying to run the last job of 3 in the flow, it only runs the last job, even though the declared input doesn’t exist. So I’m thinking that the ‘inputs.file’ isn’t really declaring a dependency.

Declaring inputs/outputs for tasks doesn’t necessarily mean that you automatically create dependencies between tasks.

OK, so I need to keep these in the tasks, and also have a dependency closure someplace? Alright, so going back to section 50.4.4 of the user guide, I guess I need a dependencies section? And that requires a configuration as well? OK, let me try posting a longer reply with more code, and let me see if we can flesh this out more.

I think it would be helpful if you read up on the basics first. Task dependencies != Artifact dependencies. I think you should get started reading up on tasks in general. For creating task dependencies you can simply use the method ‘dependsOn’.

Alright, so let me try some more code here. Here’s a more complete vision of the flow I’m trying to set up:

def inp1 = /… def inp2 = /… def int1 = /… def int2 = /… def out1 = /…

configurations {

myconfig }

dependencies {

myconfig files(inp1, inp2, int1, int2) }

task (‘t1’, type:Exec, dependsOn:configurations.myconfig) {

commandLine “ $inp1 $inp2 $int1”

workingDir “.”

inputs.file files([inp1, inp2])

outputs.file int1 }

task (“t2”, type:Exec, dependsOn:configurations.myconfig) {

commandLine “ $int1 $int2”

workingDir “.”

inputs.file int1

outputs.file int2 }

task (“t3”, type:Exec, dependsOn:configuration.myconfig) {

commandLine “tool3.csh $int2 $out1”

workingDir “.”

inputs.file int2

outputs.file out1 }

Alright, so there’s 3 steps, two intermediate files created, and a final output file. What I’d really like to happen here is that I could say:

gradle t3

on the command line, and it would find out if the input files and intermediate files exist, and then run t1 or t2 as necessary so that t3 will work. Currently, it just runs t3, and I get an error message from the t3 script that says “int2 does not exist, failing”. So is this something that Gradle can handle? Am I missing something big here?

You won’t need the ‘configurations’ and ‘dependencies’ block. All you need is this:

task t1
task t2(dependsOn: t1)
task t3(dependsOn: t2)

Now that’s the job dependency, right? But if I want the tool to pay attention to when inp1 and inp2 change, so that if I run “gradle t3”, and then run it again, it won’t do a rebuild, but if I change inp1 or inp2, it will, will it do that? I know that’s not the most coherent way of putting that. Essentially, I’m looking to find a tool to replace GNU Make in our flows, and it figures out when flow steps need to be redone based on file timestamps. So to convince my boss to switch, Gradle needs to either do what Make does, or do something better that I can explain to my boss. What can I tell him?

That’s a task dependency. Whether a task should be build or not is determined by its inputs and outputs. So even if you define a task dependency it would execute its actions only if inputs/outputs change.

And I had the input/output definition right the first time? OK, back to testing.

Nope, I’ve been running ‘gradle t1’, but changing the contents of the inp2 file doesn’t make it want to run a rebuild, even when I ask for that step. Any ideas?

Actually, no, I think it is working. Thanks for all the great help!