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
).