[CMake] Output of ADD_CUSTOM_COMMAND generated multiple times

Brad King brad.king at kitware.com
Tue Oct 7 16:09:10 EDT 2008


Alan W. Irwin wrote:
> On 2008-10-07 10:22+0200 Martin Apel wrote:
> 
>> Hi all,
>>
>> I'm experiencing the following problem with ADD_CUSTOM_COMMAND:
>> The commands defined for it are executed multiple times, if multiple
>> targets depend on it and I run a parallel make afterwards.
>> An example makes this easier to understand:
>>
>> PROJECT(Test)
>> cmake_minimum_required(VERSION 2.6)
>>
>> ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_BINARY_DIR}/a
>>                   COMMAND ${CMAKE_COMMAND} -E touch
>> ${CMAKE_BINARY_DIR}/a)
>>
>> ADD_CUSTOM_TARGET(b
>>                  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/b
>>                  DEPENDS ${CMAKE_BINARY_DIR}/a)
>> ADD_CUSTOM_TARGET(c
>>                  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/c
>>                  DEPENDS ${CMAKE_BINARY_DIR}/a)
>>
>> ADD_CUSTOM_TARGET(world)
>> ADD_DEPENDENCIES(world b)
>> ADD_DEPENDENCIES(world c)
> 
> You have run into a CMake limitation which I am told is an inevitable
> result
> of their two-tier dependency approach (file dependencies and target
> dependencies).  To avoid the issue you have found for parallel builds you
> must use the following constraint on your CMake logic.
> 
> If two targets file depend on the same custom_command output file you must
> serialize them so they don't interfere with each other in a parallel build.
> 
> So to bring the above example into compliance with that rule you must
> 
> ADD_DEPENDENCIES(c b)

This is not correct.  You don't have to serialize anything.  You just
need all dependencies to be in place:

-------------------------------------------------------------------
ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_BINARY_DIR}/a
                   COMMAND ${CMAKE_COMMAND} -E touch
                   ${CMAKE_BINARY_DIR}/a)
ADD_CUSTOM_TARGET(generate_a DEPENDS ${CMAKE_BINARY_DIR}/a)

ADD_CUSTOM_TARGET(b
  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/b
  DEPENDS ${CMAKE_BINARY_DIR}/a # file-level dependency
  )
ADD_DEPENDENCIES(b generate_a) # target-level dependency

ADD_CUSTOM_TARGET(c
  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/c
  DEPENDS ${CMAKE_BINARY_DIR}/a # file-level dependency
  )
ADD_DEPENDENCIES(c generate_a) # target-level dependency

ADD_CUSTOM_TARGET(world)
ADD_DEPENDENCIES(world b)
ADD_DEPENDENCIES(world c)
-------------------------------------------------------------------

Now targets b and c and build in parallel, but neither will build until
'a' is generated.

The reason the separate target-level dependency is needed is because
targets b and c might not even know that 'a' is a generated file when
they are created in a different directory.

-Brad


More information about the CMake mailing list