Run CMake executable targets via CMake

Every time I use bazel for a while, and come back to CMake, I notice that I miss a cmake --run command. Bazel has a bazel run //target command.

Emulating the missing CMake --run functionality

I can’t add a cmake --run …​ command, but I thought I could add something similar.

My use case

I often have small C++ files that I want to build and run while exploring an idea.

One file, one executable. But there are plenty in a project. And I have different compilers and compiler settings.

That is easy enough in CMake. But it is nicer when the build also gives me a target that runs the executable.

So, for hello.cpp, after the required CMake configuration step:

cmake --preset default

I want to directly build the target hello from hello.cpp and also run the binary.

cmake --build --preset default-debug --target run-hello
[0/1] Re-running CMake...
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /where/ever/blog/cmake-run-target-example/build/default
[0/2] Running hello
hello

As you see, a cmake --run might not be possible, but having a run target does.

For a CMake project with plenty of executables and presets, to test different compilers and configurations, I find that super handy.

Show me the code

You can explore the example on GitHub, but here is a summary

Source

With this file:

#include <iostream>

int main()
{
    std::cout << "hello\n";
}

CMake files

And this CMakeLists.txt:

cmake_minimum_required(VERSION 3.30)

project(cmake_run_target_example LANGUAGES CXX)

include(cmake/simpletarget.cmake)

bin_from_file(hello.cpp)

The helper

The small helper below takes a source file, creates an executable named after the file base name, and adds a run-* target.

function(bin_from_file fname)
    get_filename_component(bname ${fname} NAME_WE)

    add_executable(${bname} ${fname})

    add_custom_target(run-${bname}
        COMMAND $<TARGET_FILE:${bname}>
        DEPENDS ${bname}
        COMMENT "Running ${bname}"
        USES_TERMINAL
    )
endfunction()

The important part is this:

DEPENDS ${bname}

Because of that dependency, the executable is brought up to date before it is run.

The run target itself always runs. The compile and link steps only happen when needed.

Presets

The example project uses Ninja Multi-Config through CMake presets.

{
  "version": 6,
  "configurePresets": [
    {
      "name": "default",
      "generator": "Ninja Multi-Config",
      "binaryDir": "${sourceDir}/build/default"
    }
  ],
  "buildPresets": [
    {
      "name": "default-debug",
      "configurePreset": "default",
      "configuration": "Debug"
    },
    {
      "name": "default-release",
      "configurePreset": "default",
      "configuration": "Release"
    }
  ]
}

Use it as shown above, in the intro.

Summary

When you test something with 2 or 3 compiler, in debug, release and sanitizer configurations, the direct link from preset to run is handy. This implementation works for my actual use-case, but there might be other ways of solving it. If you know one, please let me know in the comments below.

Disclaimer

This post was written by a human. AI was used for language polishing and for creating the GitHub example project.