Ninja: Compilation of generated Windows Resource .rc file has incorrect dependencies
I'm using add_custom_command
with the Ninja generator on Windows to invoke mc.exe on an .mc file, to generate an .rc file, to create a resource DLL.
When making changes to the example.mc file, rebuilding only invokes mc.exe, but dependencies that use the generated example.rc file are not rebuilt until I invoke ninja a second time:
> ninja
[0.014s 1/1] Generating example.rc, example.hpp, example_MSG00001.bin
MC: Compiling D:/repro/example.mc
> ninja
[0.027s 1/2] Building RC object CMakeFiles\example.dir\example.rc.res
Microsoft (R) Windows (R) Resource Compiler Version 10.0.10011.16384
Copyright (C) Microsoft Corporation. All rights reserved.
[0.084s 2/2] Linking CXX shared module example.dll
I tried the latest releases of CMake 3.11 through 3.15, and this behavior occurred with all of them. Here are my example CMakeLists.txt and example.mc.
The resulting build.ninja contains this dependency chain:
build example.dll: CXX_MODULE_LIBRARY_LINKER__example CMakeFiles\example.dir\example.rc.res
build CMakeFiles\example.dir\example.rc.res: RC_COMPILER__example D$:\repro\build\example.rc || cmake_object_order_depends_target_example
build D$:\repro\build\example.rc: CUSTOM_COMMAND cmake_object_order_depends_target_example
COMMAND = cmd.exe /c
restat = 1
build cmake_object_order_depends_target_example: phony || example.hpp example.rc example_MSG00001.bin
build example.rc example.hpp example_MSG00001.bin: CUSTOM_COMMAND ..\example.mc
COMMAND = cmd.exe /C "cd /D D:\repro\build && "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64\mc.exe" -b -e hpp -h D:/repro/build -r D:/repro/build D:/repro/example.mc"
DESC = Generating example.rc, example.hpp, example_MSG00001.bin
restat = 1
I think that D$:\repro\build\example.rc depending on the actual example.rc file in order-only
is the problem, since that tells ninja not to rebuild D$:\repro\build\example.rc if example.rc changes.
IIRC ninja does not try to figure out that two distinct paths refer to the same file (otherwise,
this would produce an error about multiple build statements producing the same file),
so ninja does not discover that D$:\repro\build\example.rc has changed until the next run.
The restat = 1
also doesn't help in this case, since it only causes a restat if the command runs.
If CMake instead emitted a phony like this:
build D$:\repro\build\example.rc: phony example.rc
Then targets depending on D$:\repro\build\example.rc would have a real dependency on example.rc,
and would be rebuilt when it changes.
Also, phony build statements don't appear in the build log, unlike the empty CUSTOM_COMMAND which shows up as cmd /c
.