add_custom_command is not generating a target

Mark Lakata picture Mark Lakata · Jun 8, 2015 · Viewed 37k times · Source

Perhaps this is impossible and I'm misreading the cmake 3.2 documentation, but I though creating a custom command would create a custom "target" in the Makefile so that I could build the target by invoking the name of the output file. The CMake docs says:

In makefile terms this creates a new target in the following form:

 OUTPUT: MAIN_DEPENDENCY DEPENDS
    COMMAND

so I thought I could then run make OUTPUT. Perhaps the documentation is confusing CMake targets with Makefile targets?

For example,

 add_custom_command(OUTPUT foo_out
    COMMAND post_process foo_in > foo_out
    DEPENDS foo_in
 )

I would like to do

 make foo_out

and it will make foo_out. However, if I do this, I get

make: **** No rule to make target `foo_out`. Stop.

and sure enough, the word "foo_out" doesn't exist anywhere in any file in the cmake binary output directory. If I change it to this

add_custom_target(bar DEPENDS foo_out)
add_custom_command(OUTPUT foo_out COMMAND post_process foo_in > foo_out)

Then I can do

make bar

and I can do

make foo_in

but I still can't do

make foo_out

The problem with make bar is that it is unintuitive, as the actual file output is foo_out not bar.

How do I do this?

In my case, I need to run a special processing step to the standard executable target which inserts optional resources into the ELF file. I would like the ability to have both executables as Makefile targets, so I can build the naked ELF executable as well as the resource-injected ELF executable.

If I was writing a custom Makefile, this is trivial to do!

foo_in: foo.c
    $(CC) $< -o $@

foo_out: foo_in
    post_process $< > $@   

And I can do make foo_in and make foo_out.

Answer

qCring picture qCring · Jun 9, 2015

add_custom_command does not create a new target. You have to define targets explicitly by add_executable, add_library or add_custom_target in order to make them visible to make.

If you have to fix things up for deployment, you could

1. use the install command (somewhere in your CMakeLists.txt) like this:

install(SCRIPT <dir>/post_install.cmake)

to store commands which are executed only when you run make install in a separate .cmake file. Or if the install target is already reserved for other things or you have more complex stuff going on:

2. manually define a deploy target. Once you got that, you can create a custom post-build command which is only executed when you explicitly run make on your deploy target. This allows you to execute commands through a separate target.

In your CMakeLists.txt this could look like:

cmake_minimum_required(VERSION 3.0)

add_executable("App" <sources>)

# option 1: do deployment stuff only when installing
install(SCRIPT <dir>/post_install.cmake)

# option 2: define a deploy target and add post-build commands
add_custom_target("deploy")
add_custom_command(TARGET "deploy" POST_BUILD <some command>)

Both approaches allow you to separate dev builds from expensive ready-to-deploy builds (if I understand correctly, that's the goal here). I would recommend option 1 since it's just cleaner.

Hope this helps!