Learning CMake 3: creating custom targets
Tags: cmake, cpp, buildsystems, crossplatform
Published: 2019-10-25 10:58:53

---
title: Learning CMake 3: creating custom targets
published: true
description: A different approach to learn CMake.
tags: cmake, cpp, buildsystems, crossplatform
cover_image: https://cdn.dribbble.com/users/2939235/screenshots/6575723/how-to-build-effective-web-pages.jpg
series: "Learning CMake"
---
Learning CMake 3: understanding add_custom_command and add_custom_target

During the previous chapters we learn how to create targets. These targets will build one library or one application. Sometimes it is needed to generate code, copy files or run some commands that are not just compile or link one binary. How can we do that?

## The `add_custom_target` CMake macro
As [the documentation says](https://cmake.org/cmake/help/latest/command/add_custom_target.html), this macro *"Adds a target with the given name that executes the given commands*". So, you can create a CMake target that will execute a command. Imagine we have the following CMakeLists.txt file:

```cmake
cmake_minimum_required(VERSION 3.12)
add_custom_target(my_custom_target
	COMMAND
		${CMAKE_COMMAND} -E echo Hi this is a custom target
	VERBATIM
)
```

There is something really interesting here. The CMake variable `CMAKE_COMMAND` contains the path to the CMake executable we are using right now. That allow us to use all the commands provided by CMake when the argument `-E` is provided. In this case, we will print "`Hi this is a custom target`". But you can use this mechanism to do different things like copying a file, creating a folder or calculate the SHA256 checksum of a file.

Also, we provided the `VERBATIM` argument to the CMake macro. Why? Because this command can be interpret diferently depending on the OS where it is executed. The `VERBATIM` will make the command to be the same in all the platforms. [You can find the full explanation in the official CMake documentation](https://cmake.org/cmake/help/latest/command/add_custom_target.html).

If we run `cmake` and we build the target `my_custom_target`, the output will be:

```bash
➜  ~/  mkdir build
➜  ~/  cd build
➜  ~/build  cmake ..
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 8.3.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/israel/build
➜  ~/build  cmake --build . --target my_custom_target
Scanning dependencies of target my_custom_target
Hi this is a custom target
Built target my_custom_target
```

**Important 1**: take into account that this custom target will not be part of the target `all`. So, if you try to build the target `all`, nothing will happen.

```sh
➜  ~/build  cmake --build .
➜  ~/build
```
 You need to build explicitly your target. Why? You need to make your custom target depend on the target `all` or in another target depending on the target `all`. You can do this with the [add_dependencies CMake macro](https://cmake.org/cmake/help/latest/command/add_dependencies.html).

**Important 2**: what will happen if we try to build the `my_custom_target` target again?
```sh
➜  ~/build  cmake --build . --target my_custom_target
Hi this is a custom target
Built target my_custom_target
```

As you can see, the command was executed again. Let's imagine we want to run the `my_custom_target` target only if is needed. How can we do this?
![Efficiency](https://media.giphy.com/media/SpB3dVjmHZsu4/giphy.gif)

## The `add_custom_command` CMake macro
The documentation of [the add_custom_command CMake macro says](https://cmake.org/cmake/help/latest/command/add_custom_command.html): *"This defines a command to generate specified OUTPUT file(s)*. What does it mean? The command will generate some output files only if needed.

Ok, let's create a custom command to generate one file:

```cmake
cmake_minimum_required(VERSION 3.12)
add_custom_command(
	OUTPUT
		"${CMAKE_CURRENT_BINARY_DIR}/generated_file"
	COMMAND
		${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generated_file
)
```

Take into account that we are generating the new file in the binary directory (the path is relative to the `CMAKE_CURRENT_BINARY_DIR` CMake variable).

If we call CMake, we will not have a way to call this custom command. Why? Because in CMake we build targets. Since there was not a defined target, the custom command will not be executed. So, we have to create a custom target and create a dependency between our custom target and our custom command:

```cmake
cmake_minimum_required(VERSION 3.12)

add_custom_target(my_custom_target
	DEPENDS
		"${CMAKE_CURRENT_BINARY_DIR}/generated_file"
)

add_custom_command(
	OUTPUT
		"${CMAKE_CURRENT_BINARY_DIR}/generated_file"
	COMMAND
		${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generated_file
)
```

So, learned lesson: to run a custom command, a custom target must depend on it.

![Dependency](https://media.giphy.com/media/Ydm10JaC6aXYY/giphy.gif)

If we are generating more than one file, we have to list all of them in the `OUTPUT` argument for `add_custom_command` and in the `DEPENDS` argument for `add_custom_target`. Doing this, we will ensure that all the files will be generated. If try to build `my_custom_target`:

```cmake
➜  ~/  mkdir build
➜  ~/build  cmake ..
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 8.3.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/israel/build
➜  ~/build  cmake --build . --target my_custom_target
Scanning dependencies of target my_custom_target
[100%] Generating generated_file
[100%] Built target my_custom_target
➜  ~/build
cmake_install.cmake  CMakeFiles      Makefile
CMakeCache.txt       generated_file
```

We can see that the file `generated_file` was generated properly. If we run the build again:

```cmake
cmake --build . --target my_custom_target
[100%] Built target my_custom_target
```

The target was built but the command was not executed again.

We can improve the process. Imagine that we need to call this command only if something changed in the source code. We need to add this file to the `DEPENDS` argument of `add_custom_command`:

```cmake
cmake_minimum_required(VERSION 3.12)

add_custom_target(my_custom_target
	DEPENDS
		"${CMAKE_CURRENT_BINARY_DIR}/generated_file"
)

add_custom_command(
	OUTPUT
		"${CMAKE_CURRENT_BINARY_DIR}/generated_file"
	COMMENT
		"This is generating my custom command"
	COMMAND
		${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generated_file
	DEPENDS
		${CMAKE_CURRENT_SOURCE_DIR}/source_file
)
```

In this case, the custom command will be run:
* The first time
* Each time the `source_file` file placed in the source directory is modified

Why is this useful? Imagine that you have to call a program to generate code. Hundreds of files. Hundreds of calls to that program. If nothing changes... why do you need to regenerate all that code. You can save a lot of time!

**Important**: if a custom target depends on a custom command, the custom target will run the custom command. If the custom command contains also a command, it will be executed always and after running the custom command.

# Recap
**When is needed to use `add_custom_target`?**
Each time we need to run a command to do something in our build system different to build a library or an executable.

**When is a good idea to run a command in `add_custom_target`?**
When the command must be executed always the target is built.

**When is a good idea to use `add_custom_command`?**
Always we want to run the command when is needed: if we need to generate a file (or more) or regenerate it if something changed in the source folder.

## Extra info
CMake has its own template engine that you can use [with the `configure_file` CMake macro](https://cmake.org/cmake/help/latest/command/configure_file.html). When is a good idea to use it to generate files? And how about [using `execute_process`](https://cmake.org/cmake/help/latest/command/execute_process.html) to run a command in CMake?

It is fine to use these ways to generate files and execute commands but these actions will take place only during the build system generation. So, if you need to run these processes each time you change something in the source directory (because you are developing, for instance), you will need to run CMake and the native tool instead of just calling the native tool and let it do its magic (solving all the dependencies automatically).