MantisBT - CMake
View Issue Details
0015858CMakeDocumentationpublic2015-11-24 13:212016-06-10 14:31
Sam Thursfield 
Kitware Robot 
normalminorN/A
closedmoved 
 
 
0015858: Rationalise documentation for DEPENDS fields of add_custom_command() and add_custom_target()
Hi!

I've been finding that the way dependencies between files and targets
work in CMake is totally confusing. I think that the documentation makes
it a lot more confusing than it needs to be. I have some suggestions
which would hopefully improve things ...

Currently the documentation for the DEPENDS field of
add_custom_command() says this:

              Specify files on which the command depends. If any dependency
              is an OUTPUT of another custom command in the same directory
              (CMakeLists.txt file) CMake automatically brings the other cus‐
              tom command into the target in which this command is built. If
              DEPENDS is not specified the command will run whenever the OUT‐
              PUT is missing; if the command does not actually create the OUT‐
              PUT then the rule will always run. If DEPENDS specifies any
              target (created by the add_custom_target(), add_executable(), or
              add_library() command) a target-level dependency is created to
              make sure the target is built before any target using this cus‐
              tom command. Additionally, if the target is an executable or
              library a file-level dependency is created to cause the custom
              command to re-run whenever the target is recompiled.

              Arguments to DEPENDS may use generator expressions.

Currently the documentation for the DEPENDS field of add_custom_target()
says this:

              Reference files and outputs of custom commands created with
              add_custom_command() command calls in the same directory (CMake‐
              Lists.txt file). They will be brought up to date when the tar‐
              get is built.

              Use the add_dependencies() command to add dependencies on other
              targets.

Based on my testing, I would suggest changing the documentation for the
DEPENDS field of add_custom_command() to say this:

    Specifies files and/or targets that this custom command depends on.

    ONLY files generated by add_custom_command() calls IN THIS DIRECTORY
    can be added as file dependencies. If you add a dependency on file
    generated somewhere else, it will be silently ignored.

    Targets from any directory can be listed here, which will cause
    those targets to be built before this one. If a target listed here
    is re-built, this rule will rerun as well, UNLESS the target is
    created by add_custom_target(). In that case you will need to
    manually list the file-level dependencies of that target here.

    If no dependencies are specified, the command will run whenever the
    OUTPUT is missing; if the command does not actually create the
    OUTPUT then the rule will always run.

    The add_dependencies() command cannot be used to make this custom
    command depend on other targets or files -- it is only for
    target->target dependencies.

    Arguments to DEPENDS may use generator expressions.

I would suggest changing the documentation for the DEPENDS field of
add_custom_target() to say this:

    Specifies files and/or targets that this custom target depends on.

    ONLY files generated by add_custom_command() calls IN THIS DIRECTORY
    can be added as file dependencies. If you add a dependency on file
    generated somewhere else, it will be silently ignored.

    Targets from any directory can be listed here, which will cause
    those targets to be built before this rule runs.

    The add_dependencies() command can also be used to make this target
    depend on other targets (but cannot be used to make it depend on
    other files).

    Arguments to DEPENDS may use generator expressions.

Finally, I would suggest adding this somewhere prominent in the main
add_custom_command documentation:

    Files generated by add_custom_command() are NOT targets, and do not
    behave in the same way as targets created by add_executable(),
    add_library() and add_custom_target(). They can only be referenced
    in the directory where the add_custom_command() call runs. If you
    need to depend on a generated file outside that directory, you must
    wrap it in a target using add_custom_target(), and depend on the
    target instead.

Another option would be to keep the text of the DEPENDS commands minimal,
and have a section in the `cmake-buildsystem` documentation describing
how files, targets and custom targets relate to each other.

I haven't done a patch yet, but if you like these improvements I can do it.
There appear to be a couple inaccuracies in the current documentation,
which I tried to correct above in addition to making things clearer.

I've written some test cases which demonstrate where the documentation
and the reality don't match up. These have all been tested with the
"Unix Makefiles" generator.

It may be that they don't work under other generators, but in that case
CMake should be raising an error here rather than silently accepting the
incorrect rules.


# 1. Generator expressions work in DEPENDS of custom targets.
#
# Currently the documentation of add_custom_command() DEPENDS says that
# generator expressions may be used, but the documentation of
# add_custom_target() DEPENDS does not say that. But they seem to work
# fine there -- if you build this project, 'foo' is generated as
# expected.

cmake_minimum_required(VERSION 3.3)

add_custom_command(
    OUTPUT foo
    COMMAND cmake -E echo foo > foo
)

add_custom_target(
    all-foo ALL
    DEPENDS $<1:foo>
)

---

# 2. The add_custom_target DEPENDS field is not limited to depending on files.
#
# The documentation for the add_custom_target DEPENDS field implies that only
# files and outputs of custom commands can be passed, and that the
# add_dependencies() command must be used to create dependencies on other
# targets.
#
# This is clearly false: CMake doesn't raise an error in the following
# CMakeLists.txt when I pass targets 'generate-bar' and 'generate-baz' as
# DEPENDS of the 'generate-all' custom target, and those targets get built
# when I run 'make'.
#

cmake_minimum_required(VERSION 3.2)

add_custom_command(
    OUTPUT foo
    COMMAND sleep 1
    COMMAND cmake -E echo 'foo' > foo
)

add_custom_target(generate-foo DEPENDS foo)

add_custom_command(
    OUTPUT bar
    COMMAND cmake -E copy foo bar
    DEPENDS generate-foo
    )
add_custom_target(generate-bar DEPENDS bar)

add_custom_command(
    OUTPUT baz
    COMMAND cmake -E copy foo baz
    DEPENDS generate-foo
    )
add_custom_target(generate-baz DEPENDS baz)

add_custom_target(
    generate-all ALL
    DEPENDS generate-bar generate-baz
    )

Related bugs:
  - https://cmake.org/Bug/view.php?id=12311 [^]

No tags attached.
Issue History
2015-11-24 13:21Sam ThursfieldNew Issue
2015-11-25 04:45BartoszNote Added: 0039909
2016-06-10 14:29Kitware RobotNote Added: 0042890
2016-06-10 14:29Kitware RobotStatusnew => resolved
2016-06-10 14:29Kitware RobotResolutionopen => moved
2016-06-10 14:29Kitware RobotAssigned To => Kitware Robot
2016-06-10 14:31Kitware RobotStatusresolved => closed

Notes
(0039909)
Bartosz   
2015-11-25 04:45   
+1 from me for that changes. It will help novice users to start adopting cmake in their projects.

I would also vote for adding some usage examples of these commands (eg. add_custom_target etc.), and explain what will be the output for common platforms (eg. Linux).
(0042890)
Kitware Robot   
2016-06-10 14:29   
Resolving issue as `moved`.

This issue tracker is no longer used. Further discussion of this issue may take place in the current CMake Issues page linked in the banner at the top of this page.