MantisBT - CMake
View Issue Details
0013057CMakeCMakepublic2012-03-21 07:482012-09-03 16:00
Remko 
Brad King 
normalmajoralways
closedwon't fix 
MacBook ProMac OS X10.7.2
CMake 2.8.7 
 
0013057: Dependencies do not work (well) across directories
When a project is spread over a main directory and a subdirectory it becomes virtually impossible to properly set dependencies in the main directory on targets built in a
subdirectory. Although the target in the subdirectory is made, there is no "knowledge" in the main directory whether that target was REmade so that dependent targets in
the main directory should be remade as well.
Consider the following example which only needs a CMakeLists.txt file and some dummy c.txt file:
# CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
add_custom_target (a_txt DEPENDS a.txt)
add_custom_command (OUTPUT a.txt COMMAND cat {b,b}.txt > a.txt DEPENDS b.txt)
add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)

After running cmake, "make a_txt" will properly make "b.txt", then "c.txt". And if I later change "c.txt", both "b.txt" and "a.txt" are updated as well when running "make
a_txt" again.

Now split this over one file CMakeLists.txt and one in subdirectory sub:
# CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
add_subdirectory (sub)
add_custom_target (a_txt DEPENDS a.txt)
add_custom_command (OUTPUT a.txt COMMAND cat sub/{b,b}.txt > a.txt DEPENDS sub/b.txt)

# sub/CMakeLists.txt
add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)

One would expect this to work exactly the same. However, this doesn't work. You well get an error "do not know how to build sub/b.txt".
Instead, one now needs to create a target b_txt. That's not a problem. So we create this:

# CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
add_custom_target (a_txt DEPENDS a.txt)
add_custom_command (OUTPUT a.txt COMMAND cat sub/{b,b}.txt > a.txt DEPENDS b_txt)
add_subdirectory (sub)

# sub/CMakeLists.txt
add_custom_target (b_txt DEPENDS b.txt)
add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)

It it true that when running "make a_txt", we do get a "b.txt" and "c.txt". However, when I update c.txt, then run "make a_txt", then b.txt is updated, but NOT a.txt. This
is DISASTROUS if you build, say, some library in a subdirectory, but there no longer is a WORKING dependency on that library in the main directory.

The ONLY solution to this is that dependency in the main directory should INCLUDE NOT ONLY the target but ALSO the files in the subdirectory. EITHER one is not sufficient.
The solution is this:
# CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
add_subdirectory (sub)
add_custom_target (a_txt DEPENDS a.txt)
add_custom_command (OUTPUT a.txt COMMAND cat sub/{b,b}.txt > a.txt DEPENDS b_txt sub/b.txt)

# sub/CMakeLists.txt
add_custom_target (b_txt DEPENDS b.txt)
add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)

In this example this is an annoyance, though still tractable. However, when depending on a lot of targets, this becomes much more complex. Besides, in the SUBdirectory it
already says the b_txt depends on b.txt, why would I need to repeat that in the main directory AGAIN. Isn't that exactly one of those things CMake was intended to avoid.
This problem is mainly caused by the way CMake creates makefiles. What CMake basically does in the case of the split directories is to create the following Makefile:

a_txt: b_txt
    make a.txt

a.txt:
    cat sub/{b,b}.txt > $@

b_txt:
    make sub/b.txt

sub/b.txt: sub/c.txt
    paste {c,c}.txt > $@

Here you clearly see the problem. The b_txt target spawns its own "make" command, but without dependencies, b_txt really doesn't know whether an update took place or now.
Thus I can update c.txt without updating a.txt
No tags attached.
Issue History
2012-03-21 07:48RemkoNew Issue
2012-03-21 08:40Brad KingNote Added: 0028950
2012-03-21 08:40Brad KingStatusnew => resolved
2012-03-21 08:40Brad KingResolutionopen => won't fix
2012-03-21 08:40Brad KingAssigned To => Brad King
2012-09-03 16:00David ColeNote Added: 0030839
2012-09-03 16:00David ColeStatusresolved => closed

Notes
(0028950)
Brad King   
2012-03-21 08:40   
This is part of the way CMake works. CMake has to be able to generate build rules for VS and Xcode project files where a single monolithic Makefile is not possible. There are many projects that involve complex custom commands and dependencies spanning directories. Things just work a bit differently than a pure Make system might.

add_custom_command creates file-level rules and dependencies that turn into files on disk with timestamps but need to be attached to logical targets to actually appear in the build system. add_custom_target creates logical targets that contain file-level rules and order-only (phony) dependencies on other targets. The file-level rules do not propagate across directories. The knowledge about each source file is specific to the CMakeLists.txt (directory) referencing it. Target-level dependencies are visible across directories to order logical targets.

Use the add_dependencies command to enforce ordering among logical targets across directories. Use the DEPENDS option of add_custom_command and add_custom_target to add file-level dependencies within the directory defining the logical target containing them. Both are necessary. Your final example is almost correct, but should be:

---------------------------------------------------------------------------
# CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
add_subdirectory (sub)
add_custom_command (OUTPUT a.txt COMMAND cat sub/{b,b}.txt > a.txt DEPENDS sub/b.txt) # depend on file from subdirectory
add_custom_target (a_txt DEPENDS a.txt)
add_dependencies (a_txt b_txt) # evaluate rules in a_txt after b_txt is done

# sub/CMakeLists.txt
add_custom_command (OUTPUT b.txt COMMAND paste {c,c}.txt > b.txt DEPENDS c.txt)
add_custom_target (b_txt DEPENDS b.txt)
---------------------------------------------------------------------------

The add_dependencies in the above ensures that b.txt is up to date before the dependency in a_txt is evaluated, whether b.txt was rebuilt or not.

When designing CMake build rules think in terms of groups of file-level timestamped rules. Each group is contained in one directory and goes in one logical target. Logical targets form a DAG defining the order in which each group is evaluated.

Please join the mailing list for further help/discussion:

 http://www.cmake.org/mailman/listinfo/cmake [^]

See also this FAQ entry on the Makefile layout:

 http://www.cmake.org/Wiki/CMake_FAQ#Why_does_CMake_generate_recursive_Makefiles.3F [^]
(0030839)
David Cole   
2012-09-03 16:00   
Closing resolved issues that have not been updated in more than 4 months.