Menu Share

CMake targets

by | Last Updated:
ANSI C, C++

When using any kind of programming languages you would usually have the option to create some sort of executable or a library. The executables are the end products – something that you run and then it executes logic and returns a result – creates a windows, runs a game, outputs calculations, displays things, etc. Libraries are compiled code that is reusable for compilation into other applications.

Table of Contents

Before you start

This article requires that you have some basic knowledge of:

Library

There are two main types of targets for your C/C++ project. Lets start with library as it is the building block for executables. Libraries contain code that you write and you would like to share between multiple projects. In the internet you can find many libraries for your projects that are maintained by other people. Using libraries saves you a great deal of time and effort to learn how to make certain things.

You can also write libraries yourself. In C/C++ there are other benefits to write your code in a library.

One of these benefits is the ability to test your code. It is way easier to configure unit/integration testing on a separate library than on an executable.

Another main benefit is that you split your code into meaningful chunks. Later if you work on another project you could be able to separate those chunks and take the ones that you would need and are reusable.

Static vs Dynamic

When creating a library you have two choices – to make it static or dynamic. I am aware that the choice of dynamic linking is only available on Windows but it is useful for everyone to know this.

Static libraries produce a single file that you can later link into an executable (read more on compile and link) . Static linking means that the contents of the lib file will be compiled into the end executable. This takes more time during the build of the executable and produces a larger executable but in the long run the application will run faster.

Dynamic libraries produce two files – a static library that is hollow and only has references and a dynamic library that should be copied along the executable. The static library will be used in the linking process. It will produce a smaller executable file and will have a faster build time. The dynamic library will be evaluated at run time and usually needs to be in the same directory as the executable.

Usual extension for these libraries would be: ( .lib, .bc, .a ) for a static library and ( .dll ) for a dynamic library.

My recomendation for game development is to use dynamic linking during your debug process (debug builds) as you would want quicker build times and static linking when creating a release build.

How to create a library target in CMake

It is really simple actually. Lets start with my usual project strcuture for a library:

libfolder
├── include/
│  └── A.h
├── src/
│  └── A.cpp
└── CMakeLists.txt

You will notice that the headers and source files are split. This is to make it easier to later copy the header directory because to use a library you would usually need to specify the same function signatures in another project. It is best that the definitions of functions are written in the include files and that they are easily used in another project.

project(libfolder LANGUAGES C CXX)

# Creating a static library
# libfolder_static is called a target name and can be used later
add_library(libfolder_static STATIC include/A.h src/A.cpp)

# Creating a dynamic lybrary
# add_library(libfolder_dynamic SHARED include/A.h src/A.cpp)

# This will set include directories for this target
# This means that A.cpp can include A.h by writing
# #include "A.h"
# instead of using relative path like
# #include "../include/A.h"
# 
# Also because we set that to public any other target that links to
# libfolder_static would also have those includes
target_include_directories(libfolder_static
  PUBLIC
  $<INSTALL_INTERFACE:include>
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}/src
)

It is pretty easy to create the library right? You can also link against other libraries that were created. If the other library is part of your whole CMake project structure you can do so using the target name (for example if you want to create a new library called libfolder2 you would link libfolder_static into it. The other way would be to link the libfolder.lib file directly:

project(libfolder2 LANGUAGES C CXX)

add_library(libfolder2_static STATIC include/B.h src/B.cpp)

# Linking against our first target
target_link_libraries(libfolder2_static PUBLIC libfolder_static )

# Or
# target_link_libraries(libfolder2_static PUBLIC libfolder_static.lib)
# if we consider that the lib file is in the current directory. 
# You would still need the include files also...

Executables

Executable targets serve only the purpose of creating a file that could be run by the operating system and execute a set of commands then finish. They require that there is a main function that can be run or else the build will fail. You can link libraries into the executables the same way you can link libraries to other libraries.

How to create a executable target in CMake

Executables are even easier to create than libraries. You don’t need to think about your code being used by other targets so you may freely put your code only in once source directory (or keep the structure with the include directory for consistency). The code to create an executable is almost the same as for a library:

project(exefolder LANGUAGES C CXX)

add_executable(exefolder src/C.cpp src/C.h)

# The libfolder include directories would automatically be included here also
target_link_libraries(exefolder PRIVATE libfolder)

target_include_directories(exefolder PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

Empty/Custom targets

CMake also provides the option of creating other types of targets that are empty. They will not provide an executable of library file at the end but you can use them for running command line scripts. We call them custom targets and they can usually directly run a command within them. You can also set them to depend on other targets.

add_custom_target(mytarget ALL
  COMMENT "Just something to print out when this is run"
  COMMAND <... command line to be run ...>
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

add_dependencies(mytarget exefolder)

To run commands actually you could also just use add_custom_command that must be attached to an existing target. You can do this to add commands to your custom target also.

Read also: How to build and run custom tools with CMake using add_custom_command

Conclusion

This article covers one of the core concepts of project management in CMake. Having this knowledge you can now create your own projects correctly and using a more modern approach.

Leave a comment