# add\_custom\_command with target dependency doesn't rebuild

**URL:** https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029
**Category:** Usage
**Created:** [December 7, 2022, 4:38pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029 "2022-12-07T16:38:58Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![Barry\_Revzin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/barry_revzin/32/1283_2.png) [@Barry\_Revzin](https://discourse.cmake.org/u/Barry_Revzin)
#### Post date: [December 7, 2022, 4:38pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/1 "2022-12-07T16:38:58Z")

</div>

Here’s a small reproduction:

CMakeLists.txt:

```cmake
cmake_minimum_required(VERSION 3.21)
project(cc)

add_custom_command(OUTPUT inter.dat
    COMMAND cp ${CMAKE_CURRENT_LIST_DIR}/num inter.dat
    DEPENDS ${CMAKE_CURRENT_LIST_DIR}/num
    )

add_custom_target(inter-target DEPENDS inter.dat)

add_custom_command(OUTPUT meow.cxx
    COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/gen.py --src inter.dat > meow.cxx
    DEPENDS inter-target
    )

add_executable(meow meow.cxx)

```

gen.py:

```py
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--src', type=argparse.FileType('r'))
args = parser.parse_args()

num = args.src.read().strip()
print(f"""#include <iostream>

int main() {{
std::cout << {num} << std::endl;
}}""")

```

And num just contains `1`.

With that:

```auto
$ cmake ..
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/lib64/ccache/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/brevzin/sandbox/custom_command/build

$ make && ./meow
[25%] Generating inter.dat
[25%] Built target inter-target
[50%] Generating meow.cxx
[75%] Building CXX object CMakeFiles/meow.dir/meow.cxx.o
[100%] Linking CXX executable meow
[100%] Built target meow
1
$ echo 2 > ../num
$ make && ./meow
[25%] Generating inter.dat
[25%] Built target inter-target
Consolidate compiler generated dependencies of target meow
[100%] Built target meow
1
$ cat inter.dat
2

```

The rule for `meow.cxx` `DEPENDS` on `inter-target` which `DEPENDS` on `inter.dat`. Here, `inter.dat` did get rebuilt, but `meow.cxx` did not, it still prints `1`.

The docs say that:

> If the argument is the name of a target […] a target-level dependency is created to make sure the target is built before any target using this custom command. Additionally, if the target is an executable or library, a file-level dependency is created to cause the custom command to re-run whenever the target is recompiled.

But then the docs _also say_:

> Do not list the output in more than one independent target that may build in parallel or the two instances of the rule may conflict (instead use the [`add_custom_target()`](https://cmake.org/cmake/help/latest/command/add_custom_target.html#command:add_custom_target) command to drive the command and make the other targets depend on that one)

So if I want things to rebuild properly, I need to use the filename, because a target doesn’t trigger a rebuild. But I want things to build in parallel _at all_, I need to use a custom target, because otherwise multiple rules conflict.

How… am I supposed to get this to work?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 7, 2022, 5:27pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/2 "2022-12-07T17:27:28Z")

</div>

Is the `inter-target` actually used for something else? Typically I try to keep a chain of custom commands and only “cap” it at the end with a custom target. The docs are consistent with this; I suspect that an “order only” dependency is used in Make and Ninja generators (not sure what IDEs use) which means that rerunning the intermediate doesn’t necessarily trigger downstream bits as needing to rerun. I don’t know that there’s a mechanism for custom targets to work in this way right now.

Cc: @brad.king

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [December 7, 2022, 7:18pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/3 "2022-12-07T19:18:46Z")

</div>

From the docs quoted above:

> if the target is an executable or library, a file-level dependency is created

`DEPENDS inter-target` will not add a file-level dependency because `inter-target` is a custom target, not an executable or library. Only the target-level ordering dependency is added, but that just means that if the custom command does run, `inter-target` will be up-to-date.

Try making the second custom command depend on the target and the file: `DEPENDS inter-target inter.dat`. That will cause the `meow` target to get an ordering dependency on `inter-target`, so first custom command producing `inter.dat` will only be attached to `inter-target` rather than duplicated. The file-level dependency will cause the latter custom command to rebuild when neeed.

---

<div class="post-metadata">

### Author: ![Barry\_Revzin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/barry_revzin/32/1283_2.png) [@Barry\_Revzin](https://discourse.cmake.org/u/Barry_Revzin)
#### Post date: [December 7, 2022, 7:49pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/4 "2022-12-07T19:49:08Z")

</div>

> [@brad.king](#):
>
> Try making the second custom command depend on the target and the file: `DEPENDS inter-target inter.dat`.

Confirmed that this does work - it both only runs the initial `add_custom_command` command one time (unlike with `DEPENDS inter.dat` which runs the command for every dependency) and actually reruns downstream targets when the upstream changes (unlike with `DEPENDS inter-target` which does not actually rebuild anything).

However, this really seems like a cmake bug. When I write something like:

```make
add_custom_command(OUTPUT x ...)
add_custom_command(OUTPUT y DEPENDS x ...)
add_custom_command(OUTPUT z DEPENDS x ...)

```

That really looks like I’m expressing the idea correctly. I need to generate `x`, and use the output of `x` to generate `y` and `z`. But that doesn’t work. Instead I have to do this extra dance:

```make
add_custom_command(OUTPUT x ...)
add_custom_target(x-target DEPENDS x)
add_custom_command(OUTPUT y DEPENDS x x-target ...)
add_custom_command(OUTPUT z DEPENDS x x-target ...)

```

which seems like the thing I would _always_ want to do in this scenario, since anything else is simply wrong (it either is broken for parallel builds or doesn’t actually rebuild everything that needs to be rebuilt).

> [@brad.king](#):
>
> From the docs quoted above:

I would really like to stress that (a) the docs explicitly state to use a `custom_target` for this, without pointing out that this does not work and (b) the docs do not actually say how to solve this problem.

Moreover, the docs state that

> In makefile terms this creates a new target in the following form:
> 
> ```auto
> OUTPUT: MAIN_DEPENDENCY DEPENDS
> COMMAND
> 
> ```

Which sure makes it look like my example above with the three `add_custom_command`s and no `add_custom_target` would generate:

```make
x : 
    x-rule

y : x
    y-rule

z : x
    z-rule

```

That’s precisely the set of rules I’m trying to generate. It’s just that something about the makefile generator that causes `x-rule` to be run twice. The Ninja generator actually works with _just_ `DEPENDS x`, it does not need `DEPENDS x x-target`.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [December 7, 2022, 11:20pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/5 "2022-12-07T23:20:22Z")

</div>

> [@Barry\_Revzin](#):
>
> ```auto
> add_custom_command(OUTPUT x ...)
> add_custom_command(OUTPUT y DEPENDS x ...)
> add_custom_command(OUTPUT z DEPENDS x ...)
> 
> ```
> 
> … But that doesn’t work.

That _is_ correct and _does_ work, if all the outputs go in the same consuming target, and does build them in parallel.

Once you want to start assigning different outputs to different targets, it becomes your responsibility to add them to targets whose ordering dependencies match the build dependencies.

---

<div class="post-metadata">

### Author: ![Barry\_Revzin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/barry_revzin/32/1283_2.png) [@Barry\_Revzin](https://discourse.cmake.org/u/Barry_Revzin)
#### Post date: [December 7, 2022, 11:58pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/6 "2022-12-07T23:58:30Z")

</div>

> [@brad.king](#):
>
> > [@Barry\_Revzin](#):
> >
> > ```auto
> > add_custom_command(OUTPUT x ...)
> > add_custom_command(OUTPUT y DEPENDS x ...)
> > add_custom_command(OUTPUT z DEPENDS x ...)
> > 
> > ```
> > 
> > … But that doesn’t work.
> 
> That _is_ correct and _does_ work, if all the outputs go in the same consuming target, and does build them in parallel.
> 
> Once you want to start assigning different outputs to different targets, it becomes your responsibility to add them to targets whose ordering dependencies match the build dependencies.

It does not work, at least not for the Makefile generator. Let me demonstrate by extending the above example slightly:

```cmake
cmake_minimum_required(VERSION 3.21)
project(cc)

add_custom_command(OUTPUT inter.dat
    COMMAND python3 cp.py > ${CMAKE_BINARY_DIR}/inter.dat
    DEPENDS ${CMAKE_CURRENT_LIST_DIR}/num
    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
    )

#add_custom_target(inter-target DEPENDS inter.dat)

foreach(i RANGE 1 10)
    add_custom_command(OUTPUT meow-${i}.cxx
        COMMAND
            python3 ${CMAKE_CURRENT_LIST_DIR}/gen.py --src inter.dat > meow-${i}.cxx
        DEPENDS inter.dat
        )

    add_executable(meow-${i} meow-${i}.cxx)
endforeach()

```

Here, I have 10 programs `meow-N` which are built from a corresponding generated `meow-N.cxx` which is itself built from a single global generated `inter.dat` which is generated from `num`. Similar to what I pasted originally, except now I’m depending on `inter.dat` (the `OUTPUT`) instead of `inter-target` (the `custom_target`). Also, instead of `cp` I’m running a script `cp.py` which just also prints that it’s running, no surprises.

When I build this, this happens:

```auto
$ cmake ..
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/lib64/ccache/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/brevzin/sandbox/custom_command/build

$ make -j30
[2%] Generating inter.dat
[5%] Generating inter.dat
[7%] Generating inter.dat
[12%] Generating inter.dat
[12%] Generating inter.dat
[25%] Generating inter.dat
[25%] Generating inter.dat
[25%] Generating inter.dat
[25%] Generating inter.dat
[25%] Generating inter.dat
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
Running cp.py
[32%] Generating meow-9.cxx
[32%] Generating meow-6.cxx
[32%] Generating meow-10.cxx
[35%] Generating meow-1.cxx
[37%] Generating meow-2.cxx
[45%] Generating meow-8.cxx
[50%] Generating meow-5.cxx
[50%] Generating meow-3.cxx
[50%] Generating meow-4.cxx
[50%] Generating meow-7.cxx
[57%] Building CXX object CMakeFiles/meow-9.dir/meow-9.cxx.o
[57%] Building CXX object CMakeFiles/meow-10.dir/meow-10.cxx.o
[60%] Building CXX object CMakeFiles/meow-6.dir/meow-6.cxx.o
[60%] Building CXX object CMakeFiles/meow-2.dir/meow-2.cxx.o
[62%] Building CXX object CMakeFiles/meow-1.dir/meow-1.cxx.o
[70%] Building CXX object CMakeFiles/meow-4.dir/meow-4.cxx.o
[75%] Building CXX object CMakeFiles/meow-5.dir/meow-5.cxx.o
[75%] Building CXX object CMakeFiles/meow-7.dir/meow-7.cxx.o
[75%] Building CXX object CMakeFiles/meow-8.dir/meow-8.cxx.o
[75%] Building CXX object CMakeFiles/meow-3.dir/meow-3.cxx.o
[87%] Linking CXX executable meow-10
[87%] Linking CXX executable meow-6
[87%] Linking CXX executable meow-2
[87%] Linking CXX executable meow-1
[87%] Linking CXX executable meow-9
[100%] Linking CXX executable meow-7
[100%] Linking CXX executable meow-4
[100%] Linking CXX executable meow-5
[100%] Linking CXX executable meow-8
[100%] Linking CXX executable meow-3
[100%] Built target meow-9
[100%] Built target meow-1
[100%] Built target meow-2
[100%] Built target meow-6
[100%] Built target meow-10
[100%] Built target meow-8
[100%] Built target meow-7
[100%] Built target meow-4
[100%] Built target meow-5
[100%] Built target meow-3

```

My file here, `inter.dat`, is generated 10 times, in parallel. In this particular example, because the file contains a single byte, this works out fine. But if the file was much longer and the generation step took longer, then I would likely have different parts of my builds reading incomplete files at different times.

That is very broken.

With Ninja, however, this _does_ work. The file is only generated one time, not ten:

```auto
$ cmake .. -G Ninja
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/lib64/ccache/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/brevzin/sandbox/custom_command/build

$ ninja
[1/31] Generating inter.dat
Running cp.py
[31/31] Linking CXX executable meow-1

```

> [@brad.king](#):
>
> Once you want to start assigning different outputs to different targets, it becomes your responsibility to add them to targets whose ordering dependencies match the build dependencies.

I am though. `meow-N` depends on `meow-N.cxx` depends on `inter.dat` depends on `num`. Those dependencies are very clearly spelled out in the CMake above, in a way that’s a fairly direct translation of this Makefile:

```make
inter.dat : num
    python3 cp.py > inter.dat

meow-1.cxx : inter.dat
    python3 gen.py --src inter.dat > $@

meow-1 : meow-1.cxx
    g++ $^ -o $@

meow-2.cxx : inter.dat
    python3 gen.py --src inter.dat > $@

meow-2 : meow-2.cxx
    g++ $^ -o $@

# repeated 8 more times

```

With Make, the dependencies track right and the rule for generting `inter.dat` is run exactly once.

The problem is that, with the CMake version, the rule for generating `inter.at` is run lots of times with the Makefile generator.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [December 8, 2022, 12:42am UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/7 "2022-12-08T00:42:30Z")

</div>

From my previous post:

> That _is_ correct and _does_ work, **if all the outputs go in the same consuming target**

Your example has multiple separate consuming targets.

> With Ninja, however, this _does_ work

Ninja has a single monolithic build graph. The other generators don’t, and so custom commands need to each be assigned to the build systems for specific targets. No single `make` invocation sees the whole graph. Custom commands are assigned to targets based on what targets consume their output. Custom commands for transitive dependencies go into each least-dependent target that needs them. Without a common target dependency to contain the custom command for the common dependency, it gets duplicated. That’s why you need to manually create an extra target and explicitly add dependencies on it.

Everything is working as designed. [CMake MR 8002](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/8002) updates the documentation to add an example for this use case.

---

<div class="post-metadata">

### Author: ![Barry\_Revzin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/barry_revzin/32/1283_2.png) [@Barry\_Revzin](https://discourse.cmake.org/u/Barry_Revzin)
#### Post date: [December 8, 2022, 12:54am UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/8 "2022-12-08T00:54:15Z")

</div>

> [@brad.king](#):
>
> Everything is working as designed.

Can you just… fix the design?

CMake has enough information to simply do this the right thing. I could even implement my own version of `add_custom_command()` that:

1. Creates a new custom target that `DEPENDS` on all the outputs.
2. Attaches the name of that target as a property on all those outputs.
3. Goes through all the `DEPENDS` and, if any of them have this property attached to them, add that property as a dependency to the whole thing as well.

I see no reason why CMake couldn’t just do the right thing for users if the generator doesn’t support this natively.

> [@brad.king](#):
>
> [CMake MR 8002](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/8002) updates the documentation to add an example for this use case.

As the linked request states:

> This is a common use case that requires care to express correctly.

I am very much requesting that it simply _not_ require care to express correctly. While I appreciate that this is at least documented now, it would be significantly better for users if the expected thing just worked.

---

<div class="post-metadata">

### Author: ![Barry\_Revzin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/barry_revzin/32/1283_2.png) [@Barry\_Revzin](https://discourse.cmake.org/u/Barry_Revzin)
#### Post date: [December 8, 2022, 1:03am UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/9 "2022-12-08T01:03:32Z")

</div>

Having now seen the example, I don’t even understand why that’s the solution to the problem. It just looks wrong (I’ll expand on this in a bit).

Extending my example:

```diff
  add_custom_command(OUTPUT inter.dat
      COMMAND python3 cp.py > ${CMAKE_BINARY_DIR}/inter.dat
      DEPENDS ${CMAKE_CURRENT_LIST_DIR}/num
      WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
      )

+ add_custom_target(inter-target DEPENDS inter.dat)

  foreach(i RANGE 1 10)
      add_custom_command(OUTPUT meow-${i}.cxx
          COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/gen.py --src inter.dat > meow-${i}.cxx
          DEPENDS inter.dat
          )

      add_executable(meow-${i} meow-${i}.cxx)
+ add_dependencies(meow-${i} inter-target)
  endforeach()

```

This now works. It (1) builds `inter.dat` exactly once and (2) if I update `num`, all the `meow-N.cxx`s get rebuilt and all the `meow-N`s get rebuilt as desired. Perfect.

But what is the mental model for why this works? It looks like the graph here is (sorry, I don’t know how else to express this):

```diff
  meow-1 -> meow-1.cxx
  meow-1.cxx --> inter.dat
  inter.dat --> num
  inter-target --> inter.dat
+ meow-1 --> inter-target

```

The `meow-1 --> inter.dat` dependency is already expressed in the code (`meow-1 --> meow-1.cxx --> inter.dat`), so it’s unclear to me what this added extra dependency buys or how it solves the problem.

It also seems like the wrong layer of graph to add a dependency. Since really it’s `meow-1.cxx` that depends on that target, not `meow-1`. The target needs to have been built in order to generate this source file. This is a common mistake people make with Makefiles, where they express the rules `a : b` and `a : c` when they really also needed `b : c`.

I guess from your description it unifies the underlying `make` graphs somehow, but I wouldn’t know that this is necessary without having a deep understanding of cmake internals.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [December 8, 2022, 1:19pm UTC](https://discourse.cmake.org/t/add-custom-command-with-target-dependency-doesnt-rebuild/7029/10 "2022-12-08T13:19:19Z")

</div>

> [@Barry\_Revzin](#):
>
> I guess from your description it unifies the underlying `make` graphs somehow, but I wouldn’t know that this is necessary without having a deep understanding of cmake internals.

It really just gives it a single home. Without these explicit links, CMake prefers to “add the command where needed” so that `make meow-N` works. The Makefiles generator needs to be reworked to have a single unified view of the build graph at some point. Until then, this kind of difference is just a legacy of old CMake design decisions (here, the ability to `make` in any arbitrary directory preferred over a single build graph) made without full knowledge of the future.
