CMake Custom Commands

Want to take your CMake-fu to the next level? Cusstom commands to the rescue!

Basic Tutorial

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.

Post-processing

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.

Explicit target

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.

Simpleasy

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!

Tags:  codingtutorial

Home