81

In CMake semantics there is some sort of distinction between "targets" and commands" that is baffling me. In Makefiles, there is no such distinction:

targetname:dependency
    command

i.e. Targets correspond to a generated file of the same name.

In CMake you have commands like "add_custom_command" and "add_custom_target" that have overlapping functionality, and even in the official documentation the semantics are confused, i.e. in "Mastering CMake, 5th edition", page 110 under "Adding a custom target":

The DEPENDS argument sets up a dependency between the custom target and the custom commands.

My understanding is that targets (generated files) have dependencies (other files, generated or no), and a command to actually do the generation. It is nonsensical to say a target depends on a command. To make matters worse, there are two flavors of "add_custom_command" that either attach an additional command to an existing target, or spit the command out into the ether.

Can someone please explain why this distinction even exists?

2 Answers 2

67

Targets

In general, targets comprise executables or libraries which are defined by calling add_executable or add_library and which can have many properties set.

They can have dependencies on one another, which for targets such as these just means that dependent ones will be built after their dependencies.

However, you can also define "custom targets" via add_custom_target. From the docs:

Adds a target with the given name that executes the given commands. The target has no output file and is ALWAYS CONSIDERED OUT OF DATE even if the commands try to create a file with the name of the target. Use ADD_CUSTOM_COMMAND to generate a file with dependencies. By default nothing depends on the custom target. Use ADD_DEPENDENCIES to add dependencies to or from other targets.

So these are different from "normal" targets in that they don't represent things which will produce an exe or lib, but they still benefit from all the properties that targets can have, including having or being dependencies. They appear as a target which can be built (e.g. make MyCustomTarget or msbuild MyCustomTarget.vcxproj). When you build them, you're simply invoking the commands that have been set for them. If they have dependencies on other targets (normal or custom), then these will be built first.


Custom Commands

A custom command defined via add_custom_command is quite different in that it's not a "buildable" object, and doesn't have settable properties in the way that a target does - it's not a named object which can be explicitly referred to again after it's added in the CMakeLists.txt.

It is basically a command (or set of commands) which will be invoked before building a dependent target. That's all that "depends" really means here (at least that's how I view it) - it's just saying that if A depends on B, then B will be built/executed before A is built.

The dependees of a custom command can be either set explicitly using the add_custom_command(TARGET target ... form, or implicitly by creating targets which include the files generated via the add_custom_command(OUTPUT output1 ... form.

In the first case, every time target is built, the custom command is executed first.

In the second case, it's a little more complex. If the custom command has targets which depend on its output file (and the output file doesn't already exist), it is invoked before these dependent objects are built. The dependencies are implicitly created when you do e.g. add_library(MyLib output1.h ... ) where output1.h is a file generated via add_custom_command(OUTPUT output1.h ... ).

8
  • 10
    Thanks for trying to explain the mess Fraser, but I'm trying to understand why on earth the CMake devs created this mess in the first place. I want there to be a good reason, but I'm suspecting this is just another bad design decision like the all-caps fiasco. Commented Aug 16, 2012 at 11:46
  • 4
    @DrewWagner Well, I agree about the all-caps issue, but I can't say I have a problem with the command vs target thing. Maybe I'm just used to it :-) I do know that the Kitware folks are very careful to retain backwards compatibility, and as a result they have to support things that they'd probably rather not. It creates grey areas as well I guess where new commands overlap on the responsibilities of existing ones. Regarding the original rationale for the command/target setup, you'd maybe be best to ask on the mailing list?
    – Fraser
    Commented Aug 16, 2012 at 12:12
  • 3
    Custom target is used to hang custom operations on a wholly fictional artifact, while custom command is used to hang a bit of custom processing on a an artifact which actually exists. some examples: use a custom target to create a target in your project which processes some resource or i18n files. use a custom command to apply code signing to an executable artifact. It's harder and less flexible to do the second with a custom target because it is a separate target, and doesn't inherently know anything about the properties of the executable target, so you will end up hardcoding paths, etc...
    – bmac6502
    Commented Feb 4, 2015 at 5:23
  • 2
    No - there's a second signature to add_custom_command (cmake.org/cmake/help/v3.5/command/…) which takes TARGET as a parameter.
    – Fraser
    Commented Apr 4, 2016 at 17:05
  • 1
    I might say, all that mess has ended up with total unavailability to construct a fair portable cmake list. And problems like circular rebuild problem (or miss to build dependency at all) still exists without solution to fix it once and forever. I've tried to fix it on mine own without luck, either it works with problems in Linux, or in Windows. Both together it never works without problems.
    – Andry
    Commented Apr 15, 2018 at 17:15
9

add_custom_command adds a callable function that can have defined outputs (using the OUTPUT and BYPRODUCTS arguments). It can also have dependencies that will be run before the function is called.

Notice that it does NOT do things that you may think it does due to strange documentation (the makefile examples are very misleading). In particular, it does not have any guarantees about numbers of times it executes. For example, imagine this:

add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)

How many times will "touch" be printed? You don't know, since it's not specified anywhere; make -j2 will print it twice, probably, but it's timing-dependent:

Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one

But Ninja will only print it once, probably:

# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch

Usually, you'll do an add_custom_command to do some work and that defines an OUTPUT, and then you'll have an add_custom_target that depends on the output of the custom command. Anyone who wants the output depends on the target, and that does give you the guarantees you want.

Caveat: see this bug for an great example of why building cross-platform metabuild tools is REALLY HARD.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.