View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0013057CMakeCMakepublic2012-03-21 07:482012-09-03 16:00
ReporterRemko 
Assigned ToBrad King 
PrioritynormalSeveritymajorReproducibilityalways
StatusclosedResolutionwon't fix 
PlatformMacBook ProOSMac OS XOS Version10.7.2
Product VersionCMake 2.8.7 
Target VersionFixed in Version 
Summary0013057: Dependencies do not work (well) across directories
DescriptionWhen 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.
Steps To ReproduceConsider 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.
Additional InformationThis 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
TagsNo tags attached.
Attached Files

 Relationships

  Notes
(0028950)
Brad King (manager)
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 (manager)
2012-09-03 16:00

Closing resolved issues that have not been updated in more than 4 months.

 Issue History
Date Modified Username Field Change
2012-03-21 07:48 Remko New Issue
2012-03-21 08:40 Brad King Note Added: 0028950
2012-03-21 08:40 Brad King Status new => resolved
2012-03-21 08:40 Brad King Resolution open => won't fix
2012-03-21 08:40 Brad King Assigned To => Brad King
2012-09-03 16:00 David Cole Note Added: 0030839
2012-09-03 16:00 David Cole Status resolved => closed


Copyright © 2000 - 2018 MantisBT Team