How to always run command when building regardless of any dependency?
Asked Answered
O

4

35

I want to run a cmake command that parses the whole source tree, so I can't list all possible dependencies in cmake's add_custom_command/add_custom_target commands.

Is it possible to tell cmake just to run a command without any conditions? I tried all solutions found on the net (including SO) but they all assume that the command is dependent on few known files being up to date.

I found a solution but it does not work reliably:

cmake_minimum_required(VERSION 2.6)

project(main)

add_custom_command(
   OUTPUT file1
   COMMAND echo touching file1
   COMMAND touch file1
   DEPENDS file2)
add_custom_target(dep ALL DEPENDS file1 file2)

# this command re-touches file2 after dep target is "built"
# and thus forces its rebuild
ADD_CUSTOM_COMMAND(TARGET dep
          POST_BUILD
          COMMAND echo touching file2
          COMMAND touch file2
)

and this is output:

queen3@queen3-home:~/testlib$ make
[100%] Generating file1
touching file1
touching file2
[100%] Built target dep
queen3@queen3-home:~/testlib$ make
[100%] Generating file1
touching file1
touching file2
[100%] Built target dep
queen3@queen3-home:~/testlib$ make
touching file2
[100%] Built target dep
queen3@queen3-home:~/testlib$ 

As you can see, on third run it did not generate file1, even though file2 was touched previously. Sometimes it happens every 2nd run, sometimes every 3rd, sometimes every 4th. Is it a bug? Is there another way to run a command without any dependency in cmake?

Strange but if I add TWO commands to re-touch file2, i.e. just copy-paste the post-build command, it works reliably. Or maybe it will fail every 1000th run, I'm not sure yet ;-)

Octave answered 17/12, 2012 at 18:38 Comment(0)
D
31

While I'm not at all pleased with this solution, posting since I stumbled on this page and didn't see it mentioned.

You can add a custom target that references a missing file,

eg:

if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/__header.h)
    message(FATAL_ERROR "File \"${CMAKE_CURRENT_BINARY_DIR}/__header.h\" found, \
    this should never be created, remove!")
endif()

add_custom_target(
    my_custom_target_that_always_runs ALL
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/__header.h
)

add_custom_command(
    OUTPUT
        ${CMAKE_CURRENT_BINARY_DIR}/__header.h  # fake! ensure we run!
        ${CMAKE_CURRENT_BINARY_DIR}/header.h    # real header, we write.
    # this command must generate: ${CMAKE_CURRENT_BINARY_DIR}/header.h
    COMMAND some_command
)

This will keep running the custom command because __header.h is not found.

See a working example where this is used.

Drag answered 20/7, 2015 at 13:44 Comment(7)
what does create_ad refer to? The interwebs don't seem to know about thisFlannelette
@Flannelette The name isn't important, also linked to a working example where this is used.Drag
Cool. I was just confused :) The name seemed to be magic, like so many things in CMake syntax. I must admit I'm starting to like CMake more and more, but doing the simple things that are /just/ outside the ordinary is hard.Flannelette
One problem I found with this, is that for some God forsaken reason, cmake updates the LM timestamp on the generated file(s) (if some were generated). Which throws off the build if you didn't mean to modify it under some conditions.Jonijonie
Depending on exactly what you're doing - a way around this is to generate a file in a temp location and only copy it over if they differ. (not nice but better than slow build times!)Drag
@Drag Do you know ccache? It speeds up compilation considerably, for the source files which have not changed since last time it ran.Redintegration
Yes, I use ccache - but prefer not to depend on platform/tool spesific utilities which make developers not using those tools have a worse experience.Drag
M
22

A twist on ideasman42's answer is to create a dummy output with an empty echo statement. The advantage is that you can have several custom commands depend on this dummy output.

Also, the cmake build system will know what the output file of your custom command is so that any dependencies on that output can be properly resolved.

# Custom target will always cause its dependencies to be evaluated and is
# run by default
add_custom_target(dummy_target ALL
    DEPENDS
        custom_output
    )

# custom_output will always be rebuilt because it depends on always_rebuild
add_custom_command(
    OUTPUT custom_output
    COMMAND command_that_produces_custom_output
    DEPENDS
        always_rebuild
    )

# Dummy output which is never actually produced. Anything that depends on
# this will always be rebuilt.
add_custom_command(
    OUTPUT always_rebuild
    COMMAND cmake -E echo
    )

The cmake -E echo is as close to a no-op as cmake has.

Macadamia answered 18/8, 2015 at 3:8 Comment(2)
This solution will not work if I need to produce a specific file (autogenerated), which in a turn will be used in project.Nought
@Nought Why won't it? The purpose of this solution is to allow for specific files to be generated that other parts of the project can depend on. The name of the specific file in my solution is 'custom_output'. I may not have made that clear or are you encountering a different issue?Macadamia
A
15

I searched for exactly the same and I finally found a "notSoWorkaround" solution.

ADD_CUSTOM_TARGET(do_always ALL COMMAND yourCommandRegardlessOfAnyDependency)

This adds a target that will be run after ALL. And as custom targets are always considered out of date it will run always.

You may need DEPENDS yourA.out for running after the build

My sources :

Assuming answered 4/4, 2017 at 11:55 Comment(3)
Unfortunately this only works if you actually build the ALL target (i.e. you don't explicitly set a target). If you do something like make my_other_target it won't run. Although, to be fair this is true of all the other answers as far as I can see so this is definitely the simplest.Miun
Wonderfully helped me out with catkin build (which builds the CMake ALL target). Everything else failed.Kileykilgore
You an also do: "add_dependencies(mytarget do_always)" to all targets to avoid the problem mentioned here by @Miun It's still the cleanest solution of all answersDogtired
O
1

So here's my solution. I add a fake library:

add_subdirectory(fake)
add_dependencies(${PROJECT_NAME} fake)

and there I do this:

cmake_minimum_required (VERSION 2.6)
project(fake CXX)
add_library(${PROJECT_NAME} SHARED fake.cpp)
add_custom_command(TARGET fake
    POST_BUILD
    COMMAND ./mycommand.sh
    COMMAND rm ${ROOT_BIN_DIR}/libfake.so
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

So as you can see I just remove .so file after build, which causes fake lib to be rebuilt each time, with POST_BUILD executed, and all this before the main PROJECT_NAME because it is dependent on fake.

Octave answered 18/12, 2012 at 8:34 Comment(1)
This solution will only work on unix platforms. You can use cmake -E to make it platform independent, but then this involves creating a fake library which is unnecessary. See ideasman42's answer for a solution which does not involve creating any files.Macadamia

© 2022 - 2024 — McMap. All rights reserved.