Skip to content

Using CMake add_executable To Create C++ Programs

The first step everyone takes when learning C++ is to write a “hello world” program. Since CMake is the most popular build system generator for C++, you must also learn how to create C++ programs with add_executable(...).

In this post, we look at how to use add_executable(...) to create a simple C++ program with CMake.

Contents of this post:

  1. Video demonstrating how to create executables with CMake.
  2. Explaining the simple version of add_executable(...).
  3. Explaining advanced parameters and modes of add_executable(...).
    1. Extra parameters to creating executables with source files.
    2. Importing outside programs / executables with CMake.
    3. Aliasing to refer to other executables with different names.
  4. Further reading.

Video – Creating Simple Programs With CMake

For probably 95% of the use cases, creating simple executables from a couple of different cpp files is enough. For this reason, the video below demonstrates how you can use CMake to create a simple “hello world” program with add_executable(...).

Video: Using add_executable(...) in CMake to create your first C++ program.

Moreover, if you’re interested in learning about the different modes and parameters for add_executable(...), check out the following sections!

Simple Usage Of CMake add_executable For Creating Programs

Ideally, your first program will look something like the example in the video above. For example, if you’re creating a “hello world” program, then your main CMakeLists.txt will probably look something like the following.

cmake_minimum_required(VERSION 3.16)
project(MyProgram LANGUAGES CXX)


add_executable(MyProgram my_program.cpp)

In the snippet above, lines 1-6 are boilerplate CMake code. Interestingly, these lines just tell CMake what version it supports, what to look for in a compiler, and the C++ standard to use.

Specifically, the add_executable(MyProgram my_program.cpp) call at line 8 will create an executable called MyProgram (or MyProgram.exe on Windows).

In addition, the executable is created from compiling the files given after the name. For example, the MyProgram above is created from the file my_program.cpp, but you could also have add_executable(MyComplexProgram my_source1.cpp my_source2.cpp), that creates MyComplexProgram from my_source1.cpp and my_soure2.cpp files.

Generally, it’s not necessary to add the header files to the add_executable(...) call. Due to C++ compilers copying and pasting the header file contents when you write #include "...", you don’t need to specify which headers will be used in your executable! However, if you’re including other libraries into your executables with target_link_libraries(...), you may want to see how to add include directories to you libraries in C++.

Advanced Modes And Options For CMake add_executable

If you’re planning to become an advance user, then you should probably learn all the modes and parameters you can pass to add_executable(...). Specifically, the following block outlines most (if not all) useful options to add_executable(...).

// Compile executable target mode
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [source1] [source2 ...])

// Importing an EXE mode
add_executable(<name> IMPORTED [GLOBAL])

// Aliasing another target mode
add_executable(<name> ALIAS <target>)

Luckily for you, the rest of this section provides explanations, tips and demos for each mode and parameter.

Extra Options To The Compiling Mode Of add_executable

As we’ve seen, the most common form of add_executable(...) is the one used to create programs out of source files. Specifically, the parameters you can pass to line 2 above are summarised in the list below.

  • WIN32 is used to create Windows applications. This means that your application will be created with the WinMain() entry point, and it will be registered as a graphical application. Unsurprisingly, this option is ignored in non-Windows platforms, and you will need to make sure your application is compliant with the msvc compiler (entry point matches the expected params, etc). I’ve been developing on Windows and this is rarely used, unless you want to create a Windows-specific app.
  • MACOSX_BUNDLE will basically do something similar to WIN32, but in iOS/OSX. I’m not very experienced with this option, but if you’re intending on developing apps for iOS/OSX, I’d recommend reading the documentation on MACOSX_BUNDLE.
  • EXCLUDE_FROM_ALL stops your program from building by default when calling cmake --build ....

Interestingly, you will hardly ever use the options in the list above. Perhaps you may want to use EXCLUDE_FROM_ALL on a few programs that you don’t want to build by default. As an example, I recently worked on a project that had a compression tool. Since this tool was more of a developer tool, we used EXCLUDE_FROM_ALL so that it would only build when cmake --target compression --build ... was called.

Importing Programs Built Outside With CMake

Although it’s kind of a rare situation, you may want to bring executables / programs built outside your project into the CMake project. Why would you want to do this? Well, by importing an executable into CMake, it will have a CMake target name, and you can simply refer to it in your build with this CMake name.

For example, say you want to check the formatting of your code as part of your build with clang-format. Let’s also assume that you have this tool installed somewhere that is not in your PATH, and you’re not using an IDE with clang-format support (very unusual, I know). What you would need to do is something like the following block.

add_executable(ClangFormat IMPORTED)
set_property(TARGET ClangFormat PROPERTY IMPORTED_LOCATION "/path/to/clang-format")

In addition, you can use the likes of add_custom_command(...) to tell CMake to use the ClangFormat target (executable) to inspect your files during your build. CMake would essentially replace the ClangFormat target with the path declared in set_property(... IMPORTED_LOCATION ...), hence you should always define that property, or CMake won’t know where your tool is!

I understand that there are much better ways to run clang-format in your build, but this is just a simple example. Essentially, IMPORTED executables are very useful when you need to use a tool in your CMake, such as generating files or inspecting them with third-party tools.

Lastly, you can add GLOBAL to the add_executable(... IMPORTED ...) if you want the imported program to be visible outside the current CMakeLists.txt file (and its children). By default, this doesn’t happen, unlike executables you compile with add_executable(...).

Aliasing Executable Targets With add_executable(… ALIAS …)

Perhaps the simplest form of add_executable(...) is the alias one. Briefly speaking, it allows you to refer to different programs using different names. For example, the code block below shows how to refer to the MyProgram target we created in the first section with a different name.

add_executable(MyAliasedProgram ALIAS MyProgram)

Unsurprisingly, whenever you then use MyAliasedProgram, this will actually refer to MyProgram.

However, note that aliased programs / executables are read-only. This means that you cannot modify properties of the aliased (original) target.

I’ve found this to be quite useful for referring to a program that can have two different implementations. For example, for debugging a third-party tool, you may want to use the executable built with more information (verbose) if it’s provided to you. On the other hand, you may want to use the production build of your third-party tool if verbose information isn’t needed.

To be honest, I’ve only ever used the ALIAS option for executables a couple of times, you will rarely need it. There are also many different rules as to how you can use aliases, so I’d recommend checking the documentation for add_executable(... ALIAS ...).

Where Do I Go Next? What Other CMake Can I Learn?

Now that you at least know all the options for add_executable(...), you should probably read more on creating libraries with CMake’s add_library(...) command. Moreover, you can also improve your CMake arsenal by knowing how to expose headers with CMake’s target_include_directories(...) command.

Published inCMakeCPP