1

I'm looking for a way to execute shell code when starting and finishing the build of a target in cmake. The final goal is to send a message to a data tracking tool indicating when builds start and finish.

So for example, if "make" build targets alpha, beta and gamma, I'd like to call foo_begin() when alpha starts building and foo_end when target alpha is successfully built, and so on for all the targets.

Is this possible?

Spacemoose
  • 3,856
  • 1
  • 27
  • 48
  • 1
    Sounds like you want the secondary "build events" variant of `add_custom_commands` – Botje Jun 28 '22 at 12:11
  • I have answered your question directly, but if you gave us more detail about what you're actually trying to accomplish, we (I) could give you a better, more pointed answer. "Execute code" isn't a goal, it's a step to a goal. – Alex Reinking Jun 28 '22 at 14:34

2 Answers2

0

It is not possible to call arbitrary CMake functions at build time. Once the build system is generated, all the normal variable state and function definitions are discarded (i.e. they are not in cache). The COMMAND argument to add_custom_command takes a shell command, not a CMake function.

However, you can use CMake's script mode, so if what you want is to use CMake as a scripting language just to implement some build hooks, you can do this:

Let's create a file called proc_target.cmake with these contents:

cmake_minimum_required(VERSION 3.23)

if (PRE_BUILD)
  message(STATUS "PRE_BUILD: ${TARGET}")
else ()
  message(STATUS "POST_BUILD: ${TARGET}")
endif ()

Now in the CMakeLists.txt we can write:

cmake_minimum_required(VERSION 3.23)
project(example)

add_executable(foo main.cpp)

add_custom_command(
  TARGET foo PRE_BUILD
  COMMAND "${CMAKE_COMMAND}"
          -DTARGET=foo
          -DPRE_BUILD=1
          -P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
)

add_custom_command(
  TARGET foo POST_BUILD
  COMMAND "${CMAKE_COMMAND}"
          -DTARGET=foo
          -DPRE_BUILD=0
          -P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
)

Then when you run this build, you'll see the following commands:

$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
...
$ cmake --build build -- -nv
[1/2] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/foo.dir/main.cpp.o -MF CMakeFiles/foo.dir/main.cpp.o.d -o CMakeFiles/foo.dir/main.cpp.o -c /path/to/main.cpp
[2/2] cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=1 -P /path/to/proc_target.cmake && cd /path/to/build && /usr/bin/c++ -O3 -DNDEBUG  CMakeFiles/foo.dir/main.cpp.o -o foo   && cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=0 -P /path/to/proc_target.cmake

You can see how proc_target.cmake is called twice: once just before and once just after invoking the linker for foo. If you want proc_target to run before any of the sources are compiled, then you would want to write:

add_custom_command(
  OUTPUT pre-foo.stamp
  COMMAND "${CMAKE_COMMAND}"
          -DTARGET=foo
          -DPRE_BUILD=1
          -P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
  COMMAND "${CMAKE_COMMAND}" -E touch pre-foo.stamp
  DEPENDS "$<TARGET_PROPERTY:foo,SOURCES>"
)
add_custom_target(pre-foo DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pre-foo.stamp")
add_dependencies(foo pre-foo)

instead of the PRE_BUILD custom command above. Then you would get:

[1/3] cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=1 -P /path/to/proc_target.cmake
[2/3] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/foo.dir/main.cpp.o -MF CMakeFiles/foo.dir/main.cpp.o.d -o CMakeFiles/foo.dir/main.cpp.o -c /path/to/main.cpp
[3/3] : && /usr/bin/c++ -O3 -DNDEBUG  CMakeFiles/foo.dir/main.cpp.o -o foo   && cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=0 -P /path/to/proc_target.cmake

And now you can see that the custom target pre-foo is processed before foo.

This is most likely not what you want, however. It's over-engineered. If you want to generate a source file for a target, you should use add_custom_command(OUTPUT) directly and attach the output to the target as a source file (i.e. when you call add_executable or via target_sources).

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • Thanks for the detailed answer. In fact, running something from the shell is what I desire, I'll clarify the – Spacemoose Jul 01 '22 at 14:43
0

Per @botje's comment, it seems I need cmake's add_custom_command with build event specifies. I will however need PRE_BUILD, which the documentation informs me is only available for vs-studio, while I am building use cmake & gcc. So I guess I have a new question: how to duplicate the behavior of PRE_BUILD in a cmake/gcc build environment.

Spacemoose
  • 3,856
  • 1
  • 27
  • 48
  • "So I guess I have a new question: how to duplicate the behavior of PRE_BUILD in a cmake/gcc build environment." -- my answer shows this with the `add_custom_target` solution at the end. – Alex Reinking Jul 01 '22 at 16:46