Want to take your CMake-fu to the next level? Cusstom commands to the rescue!
OK, here is a minimal CMakeLists.txt
file, based on the CMake tutorial.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
# set the project name
project(Tutorial)
# add the executable
add_executable(Tutorial tutorial.cxx)
And this is our tutorial program, just to see something in action.
#include <iostream>
int main(int, const char* argv[]) {
std::cout << "hello from " << argv[0];
return 0;
}
To build and run, we simply run these commands on a Windows command prompt.
mkdir build && cd build
cmake -S ..
cmake --build .
build\Debug\Tutorial.exe
This will print out the greeting to the console. Hooray! Now, let's do something a bit more interesting.
Often we'll need to do some post-processing of files. For example, we can copy over data files or media assets that should be loose on disk. Or perhaps we need to run some tool that CMake isn't aware of to do signing, submission, source indexing, etc.
Let's append this to our CMakeLists.txt
file.
# ok, now let's do something interesting after tutorial is built every time
add_custom_command(TARGET Tutorial
POST_BUILD
COMMAND pwsh -NonInteractive -Command Write-Host hello
)
Now re-run cmake --build .
, and you'll see that after the build, hello
will be printed to the console output.
Let's break this down.
Here we are using add_custom_command to run a command as part of the build event. After the target is built (and only if it's actually built!) we'll execute a bit of PowerShell to say hello.
POST_BUILD
is a common choice, but you can also use PRE_BUILD
(before any other rules) and PRE_LINK
(after compilation but before linking).
The argument to COMMAND
can be an executable created by add_executable
, in which case it's replace by the executable location. Otherwise, it's assumed to be a program found on PATH
.
Note that you can also use custom commands to create files, but that's a topic for another post.
Our solution so far triggers every time we build our Tutorial
target. What is we want more control over that?
Let's append this to our CMakeLists.txt
file.
add_custom_target(UsingTutorial
DEPENDS Tutorial
COMMAND pwsh -NonInteractive -Command Write-Host there handsome
)
Now re-run cmake --build .
. Again, hello
is printed, but nothing new happens.
Run instead with cmake --build . --target UsingTutorial
. You'll see hello
printed, and then there handsome
.
OK, so this time we're using add_custom_target. These targets have no output and always execute (if you want to generate a file with dependencies, use the other form of add_custom_command
).
By default, custom targets aren't included in the ALL
target, which is what we've been building so far. There's an ALL
switch you can use to change that.
Because it's a target, you can use things like DEPENDS
to do dependency management, which is quite convenient. The reference as always has more bells and whistles you might find useful.
The ability to add custom targets into your CMake-based build processes easily is quite powerful. Best of all, doing it this way lets you keep the simple, well-known instructions on how to build something with CMake, allows you to leverage the framework around options and configurations, but not being constrained to what's available in-box.
Happy commanding!