# Implicit task dependency on directory

**URL:** <https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363>\
**Category:** Help/Discuss\
**Created:** [November 29, 2022, 9:42am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363 "2022-11-29T09:42:38Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![schaarda](https://avatars.discourse-cdn.com/v4/letter/s/6bbea6/32.png) [@schaarda](https://discuss.gradle.org/u/schaarda)\
**Post date:** [November 29, 2022, 9:42am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/1 "2022-11-29T09:42:38Z")

</div>

Hello,

I’m trying to understand implicit dependencies between tasks. Basically, I want a producer task to write something into a directory and a consumer task that reads something from there. As far as I understood implicit dependencies it should be possible that the consumer has an implicit dependency on the producer using the shared directory.  
I don’t want a dependency on the task itself, because I will have more than one task writing into said directory and the consumer should be dependent on all of them.

What I’ve tried is:

```gradle
abstract class ProducerTask: DefaultTask() {
    @get:OutputDirectory
    abstract val outDir : DirectoryProperty
    @TaskAction
    fun action() {
        println("Producer: ${outDir.get()}")
        outDir.get().file("foo.bar").asFile.writeText("foobar")
    }
}

abstract class ConsumerTask : DefaultTask() {
    @get:InputDirectory
    abstract val dir : DirectoryProperty
    @TaskAction
    fun test() {
        println("Consumer: ${dir.get()}, Content: ${dir.file("foo.bar").get().asFile.readText()}")
    }
}

```

and in the build.gradle.kts:

```gradle
val myDir = layout.buildDirectory.dir("mytest")

task<ProducerTask>("producer") {
    outDir.set(myDir)
}

task<ConsumerTask>("consumer") {
    dir.set(myDir)
}

```

I expected that when I call the ConsumerTask the ProducerTask would be automatically triggered. But this does not work, instead the Consumer throws an error because the directory does not exist.

Can you please shed some light on how this should work?

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [November 29, 2022, 7:23pm UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/2 "2022-11-29T19:23:57Z")

</div>

You got it exactly the wrong way around.  
You should not configure paths manually like that.  
You should wire task output properties to task input properties.  
By wiring the dependencies, you get the intended implicit task dependency that you actually should use. Practically any use of `dependsOn` without a lifecycle task on the left-hand side is a code smell.

Regarding depending on all tasks that write to that directory,  
the point is that it is extremely bad if multiple tasks have overlapping outputs.  
So having multiple tasks with the same output directory is a very bad practice anyway and should be avoided whereever possible (and it should always be possible).  
With overlapping task outputs, the task up-to-date checks cannot work properly, task output caching cannot work properly, wrong path usage cannot be calculated properly, …

---

<div class="post-metadata">

**Author:** ![schaarda](https://avatars.discourse-cdn.com/v4/letter/s/6bbea6/32.png) [@schaarda](https://discuss.gradle.org/u/schaarda)\
**Post date:** [December 2, 2022, 9:36am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/3 "2022-12-02T09:36:07Z")

</div>

Thank you for your answer, Björn.

I know that using the same output directory is considered a code smell.  
In our use case we have a custom task (TransformXml) which is executed multiple times with different parameters. After all of them are executed I want a task to pack them all into 1 zip file. My hope was, that the zip-File task could trigger all of these tasks at once (like mentioned here: [Gradle 7.0 seems to take an overzealous approach to inter-task dependencies](https://discuss.gradle.org/t/gradle-7-0-seems-to-take-an-overzealous-approach-to-inter-task-dependencies/39656))

Can you help me with another similar problem:  
I have a Task to extract an archive. Now I want another task to work with the extracted files and have an implicit dependency between them. How do I specify the output-input-relation correctly? I tried:

```gradle
val unzip = task<Copy>("unzip") {
    from(zipTree(myConfiguration.singleFile).matching {
        include("de/foobar/**")
    }.files)
    into("build/foobar")
}

task<ZipConsumer>("zipConsumer")
{
    inDir.set(unzip.destinationDir)
}

abstract class ZipConsumer: DefaultTask() {
    @get:InputDirectory
    abstract val inDir : DirectoryProperty

    @TaskAction
    fun action() {
        println("ZipConsumer: ${inDir.asFile.get().absolutePath}")
    }
}

```

But again, the unzip-Task is not executed automatically when I call zipConsumer. I really don’t want to use dependsOn because I can totally understand that this is not the intended way.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 4, 2022, 2:49am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/4 "2022-12-04T02:49:13Z")

</div>

> know that using the same output directory is considered a code smell.

It’s not only a code smell, it is a serious problem for build correctness.  
Things like that are the reason you always have to use `clean` for Maven builds. 😃

> In our use case we have a custom task (TransformXml) which is executed multiple times with different parameters. After all of them are executed I want a task to pack them all into 1 zip file. My hope was, that the zip-File task could trigger all of these tasks at once

Shouldn’t be a problem here, just use all of them as input to your zip task.  
Actually if your `TransformXml` task has the single files it outputs as outputfiles, you can also generate them to the same output directory without problem, as then there is no overlapping outputs.  
Just if you for example define the same `@OutputDirectory` for the tasks, then you have a problem.

But even then, just configure the `TransformXml` tasks to generate into separate directories. And then use the `TransformXml` tasks as input to your zip task and all works properly and cleanly.

If you want all existing `TransformXml` tasks, you can do something like

```kts
val zipit by tasks.registering(Zip::class) {
    from(tasks.withType<TransformXml>())
}

```

If you only want specific ones, well, configure them.

```kts
val foo by tasks.registering(TransformXml::class) { ... }
val bar by tasks.registering(TransformXml::class) { ... }
val zipit by tasks.registering(Zip::class) {
    from(foo)
    from(bar)
}

```

* * *

> ```gradle
> val unzip = task<Copy>("unzip") {
> 
> ```

Not your question, but don’t use this method, it is the old API that is not doing task configuration avoidance, so wastes your precious time when executing the build. See [Task Configuration Avoidance](https://docs.gradle.org/current/userguide/task_configuration_avoidance.html) for more information.

> But again, the unzip-Task is not executed automatically when I call zipConsumer

`unzip.destinationDir` is a `File`.  
The automatic task dependencies only work if you wire tasks, providers, file collections, or similar Gradle types to the inputs, as those can actually carry the information about the task dependency. The standard Java `File` class can not.  
In this case it indeed gets a bit ugly to get the proper implicit dependency:

```kts
val unzip by tasks.registering(Copy::class) {
    from(zipTree(myConfiguration.singleFile).matching {
        include("de/foobar/**")
    }.files)
    into("build/foobar")
}

val zipConsumer by tasks.registering(ZipConsumer::class) {
    inDir.set(unzip.map { layout.dir(provider { it.destinationDir }).get() })
}

```

Unless there is an easier way I forgot right now as it is almost 4 AM. 😃

---

<div class="post-metadata">

**Author:** ![schaarda](https://avatars.discourse-cdn.com/v4/letter/s/6bbea6/32.png) [@schaarda](https://discuss.gradle.org/u/schaarda)\
**Post date:** [December 5, 2022, 8:10am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/5 "2022-12-05T08:10:45Z")

</div>

Thank you again for taking the time to explain this to me (especially in the middle of the night).

I guess one of my main problems getting started with all of this are the many different ways to do something - and lot’s of deprecated ways, too. Depending on the time an example you look at was created, each one doing the same thing looks completely different.

For example the code to implicitly depend on the unzip: I would have never found this out on my own. My thinking was: this should be really easy, because doing something with the result of an unzip should be so common, there has to be an easy way. It will be difficult to convince my team to use something like “unzip.map { layout.dir(provider { it.destinationDir }).get() }” if a simple dependsOn just works.

Again, thank you!

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 5, 2022, 2:03pm UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/6 "2022-12-05T14:03:28Z")

</div>

Well, to work with the unzipped files themselves would be pretty easy.

The problem is, that you need the directory and that the `Copy` class do not provide that as `Directory` or a provider thereof so have to transform the `File` to a `Provider<Directory>` with the task dependency.

Maybe you like this version better:

```nohighlight
val unzipOutput = layout.buildDirectory.dir("foobar")

val unzip by tasks.registering(Copy::class) {
    from(zipTree(myConfiguration.singleFile).matching {
        include("de/foobar/**")
    }.files)
    into(unzipOutput)
}

val zipConsumer by tasks.registering(ZipConsumer::class) {
    inDir.set(unzip.map { unzipOutput.get() })
}

```

Another option would be to not use a `DirectoryProperty` but a `ConfigurableFileTree`:

```nohighlight
val unzip by tasks.registering(Copy::class) {
    from(zipTree(myConfiguration.singleFile).matching {
        include("de/foobar/**")
    }.files)
    into(layout.buildDirectory.dir("foobar"))
}

val zipConsumer by tasks.registering(ZipConsumer::class) {
    inFiles.from(unzip.map { it.destinationDir })
}

abstract class ZipConsumer : DefaultTask() {
    @get:InputFiles
    abstract val inFiles: ConfigurableFileTree

    @TaskAction
    fun action() {
        println("ZipConsumer: ${inFiles.dir.absolutePath}")
        println("ZipConsumer files: ${inFiles.files}")
    }
}

```

---

<div class="post-metadata">

**Author:** ![schaarda](https://avatars.discourse-cdn.com/v4/letter/s/6bbea6/32.png) [@schaarda](https://discuss.gradle.org/u/schaarda)\
**Post date:** [December 12, 2022, 7:41am UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/7 "2022-12-12T07:41:52Z")

</div>

Sorry, I’ve been away a week.

YES, your first option looks pretty close to what I wanted. Now I just have to comment why I use unzip.map […] instead of just the directory, but it should be a lot more understandable now.

Thank you again for your help!

---

<div class="post-metadata">

**Author:** ![Joffrey](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/joffrey/32/4246_2.png) [@Joffrey](https://discuss.gradle.org/u/Joffrey)\
**Post date:** [August 15, 2023, 5:06pm UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/8 "2023-08-15T17:06:14Z")

</div>

> [@Vampire](#):
>
> the point is that it is extremely bad if multiple tasks have overlapping outputs.  
> So having multiple tasks with the same output directory is a very bad practice anyway and should be avoided whereever possible (and it should always be possible)

Why does Gradle assume that task outputs in the same directory == overlapping task outputs? I came to realize that 2 tasks outputting to the same dir is indeed a problem for Gradle, but I don’t understand why it should be. If a task has a very well defined output file path `/a/b/output-1.txt`, and another task has a very well defined output file path `/a/b/output-2.txt`, how is the `/a/b` prefix relevant? These are not overlapping file paths, and only the files are the outputs, not the dir. We can check those outputs creation time, hash them, delete them, etc. independently.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [August 15, 2023, 6:22pm UTC](https://discuss.gradle.org/t/implicit-task-dependency-on-directory/44363/9 "2023-08-15T18:22:14Z")

</div>

The prefix should not be relevant.  
But as soon as one of the two uses `/a/b` as `@OutputDir`, you have overlapping outputs.  
If those two tasks have only the concrete files as single output files, there is no problem.  
If you see a problem with such a setup, please provide an [MCVE](https://stackoverflow.com/help/mcve) 🙂
