Ninja: Minimize dependencies of add_custom_command()
I'm filing this bug as a break-out from the discussion on #15555 (closed), starting here: #15555 (comment 292643)
Right now, add_custom_command()
-based targets seem to be very conservative about their generated dependencies, beyond the ones that are specified. For example, start with this simple file:
project ( libDependencyTest C )
add_library ( mylib STATIC mylib-a.c )
add_custom_command (
OUTPUT ${CMAKE_BINARY_DIR}/generated.c
# The actual command doesn't matter here, just doing a simple
# copy as a trivial "custom" command
COMMAND cp ${CMAKE_SOURCE_DIR}/source-dependency ${CMAKE_BINARY_DIR}/generated.c
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/source-dependency
COMMENT "building ${CMAKE_BINARY_DIR}/generated.c"
)
add_executable ( final-binary ${CMAKE_BINARY_DIR}/generated.c extra.c )
target_link_libraries ( final-binary mylib )
and then:
$ cat "int main(){return 0;}" >source-dependency
$ touch mylib-a.c extra.c
$ cmake -G Ninja
and cmake-3.9.0 creates a build.ninja
with the following:
build generated.c: CUSTOM_COMMAND source-dependency || libmylib.a
...so this generated file now depends on a add_library()
-generated target it had nothing to do with (in a larger cmake setup you could end up with dozens of libraries listed there) delaying how soon this step could start in a parallel build.
Furthermore there are these two lines:
build cmake_object_order_depends_target_final-binary: phony || cmake_object_order_depends_target_mylib generated.c
[...]
build CMakeFiles/final-binary.dir/extra.o: C_COMPILER__final-binary extra.c || cmake_object_order_depends_target_final-binary
...so the other *.o
files that make up final-binary
can't start compiling until after generated.c
is built. Obviously generated.o
has to depend on generated.c
, but there's no reason for extra.o
to depend on it as well.
The effect these dependencies have on build times are quite noticeable for me. I have a large codebase with dozens of libraries and hundreds of other source files that go into a final binary. However, when I do a parallel build it effectively becomes a 4-stage process:
(1) The libraries all get built, with hundreds of compiles happening in parallel (this is with a distributed build farm)
(2) The single add_custom_command()
-based target runs, completely by itself
(3) Then all of the non-library source files start building, also in parallel
(4) Final executable link
There is no reason the work from (1), (2), and (3) can't all be happening together, but right now cmake is forcing them to happen sequentially. As stage (1) starts to complete there are lots of CPUs idle, so it's very noticeable that the build is taking longer than it should.
I wish there was a way of saying to add_custom_command()
"I am telling you the whole truth about your dependencies and outputs, don't add any extra ones"