How can I add a dependent task to a task created by a C plugin?

I have successfully created a build script to produce a native executable using the C plugin. I am stuck, though, in my attempt to create a dependency on a task that produces a flex output file from a flex input file.

Here is what I believe to be the essential part of the build script. I’ve commented out my attempts to declare the dependency.

apply plugin: 'c'

model {
    components {
        test(NativeExecutableSpec) {
            sources {
                c {
                    source {
                        srcDir "."
                        include "*.c"
                    }
                }
            }
        }
    }
}

task compileLang (type: Exec) {
    inputs.file(project.file('test.l'))
    outputs.file(project.file('lex.test.c'))
    commandLine 'flex', '-f', '-L', '-8', '-i', '-P', 'test', 'test.l'
}

//buildDependentsTestExecutable.dependsOn compileLang
//project.task('buildDependentsTestExecutable').dependsOn compileLang
//project.tasks.getByName('buildDependentsTestExecutable').dependsOn compileLang

Here are what I believe to be the relevant tasks configured when I execute ‘gradle tasks’:

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
clean - Deletes the build directory.
installTestExecutable - Installs a development image of executable 'test:executable'
testExecutable - Assembles executable 'test:executable'.

Build Dependents tasks
----------------------
assembleDependentsTest - Assemble dependents of native executable 'test'.
assembleDependentsTestExecutable - Assemble dependents of executable 'test:executable'.
buildDependentsTest - Build dependents of native executable 'test'.
buildDependentsTestExecutable - Build dependents of executable 'test:executable'.

Can you describe how and what is not working?
Do you get an error when you try any of those attempts to add the dependent task?

When I uncomment the the first commented line

//buildDependentsTestExecutable.dependsOn compileLang

then I see this:

$ gradle build

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/jholt/gradle/build.gradle' line: 24

* What went wrong:
A problem occurred evaluating root project 'gradle'.
> Could not get unknown property 'buildDependentsTestExecutable' for root project 'gradle' of type org.gradle.api.Project.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s

When I uncomment the 2nd commented line

//project.task(‘buildDependentsTestExecutable’).dependsOn compileLang

then I see this (indicating that it never executed the dependent task because there’s no lex.test.c that was created and included in the compiling and linking)

$ gradle build

> Task :linkTestExecutable FAILED
Undefined symbols for architecture x86_64:
  "_testwrap", referenced from:
      _main in test.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':linkTestExecutable'.
> A build operation failed.
      Linker failed while linking test.
  See the complete log at: file:///Users/jholt/gradle/build/tmp/linkTestExecutable/output.txt
   > Linker failed while linking test.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
2 actionable tasks: 1 executed, 1 up-to-date

When I uncommented the 3rd commented line

//project.tasks.getByName(‘buildDependentsTestExecutable’).dependsOn compileLang

then I get the same error that I got when I uncommented the 1st commented line.

Attached are the files in the directory. All you need to reproduce the critical part of my environment is gradle, a toolchain, and flex.files.zip (913 Bytes)

Let me rephrase what I want.

I have a project with a main c program that calls a lexical analyzer that is stored in a .l file. That .l file has to be converted into a .c file before the compilation takes place. That means the model has to be evaluated as if its source file is the .l file but that it has to run a filter to produce the lex.test.c file before compilation starts.

And what I truly desire is something simple. I don’t want to have more than one build script as that would presumably increase the brittleness of the solution. I, in fact, already have a working solution that uses two build scripts and I hate it.

Thanks for the example! The task seems to be created at a later phase of the Gradle’s configuration phase. I suggest adding a listener on the tasks container and it worked for me on your example:

project.tasks.whenObjectAdded { Task t -> 
    println 'Task added: ' + t.name
    if ('buildDependentsTestExecutable'.equals(t.name)) {
        t.dependsOn compileLang
    }
}

I’m not familiar with the ‘c’ plugin or with the model{} configuration, so my suggestion is based on the general familiarity I’ve with Gradle.

1 Like

“I’m not familiar”

Me too and I’m betting we are members of a high cardinality set.

Your suggestion works but I had to change the task name from ‘buildDependentsTestExecutable’ to ‘compileTestExecutableTestC’ before it worked fully.

I tested the incremental build nature (i.e., changing test.c or test.l) and subsequent builds do the expected things and skips tasks when I expect them to be up-to-date and it executes them when I expect them not to be up-to-date.

I missed methods returned by Project.getTasks (that returns a TaskContainer), that are inherited by org.gradle.api.DomainObjectCollection.

I got frustrated by an apparently lack of attention at this site and asked an SO question. If you post your answer at https://stackoverflow.com/q/55732834/1707353, then I’ll accept it and upvote it and you’ll get the bounty.