Skip to content
Konstantin Soshin edited this page Mar 12, 2018 · 11 revisions

This page is maintained by Konstantin Soshin


About CMake

CMake is a cross-platform free and open-source software for managing the build process of software. It is used in conjunction with native build environments such as make, Apple's Xcode, and Microsoft Visual Studio. It has minimal dependencies, requiring only a C++ compiler on its own build system

Features

  • CMake can handle in-place and out-of-place builds, enabling several builds from the same source tree, and cross-compilation. The ability to build a directory tree outside the source tree is a key feature, ensuring that if a build directory is removed, the source files remain unaffected.

  • CMake can locate executables, files, and libraries. These locations are stored in a cache, which can then be tailored before generating the target build files.

  • CMake can generate project files for several prominent IDEs, such as Microsoft Visual Studio, Xcode, and Eclipse CDT. It can also produce build scripts for MSBuild or NMake on Windows; Unix Make on Unix-like platforms such as Linux, macOS, and Cygwin; and Ninja on both Windows and Unix-like platforms.

Basics of CMake

First iteration

For example you have src.cpp source file. First, you need to create a file for cmake, which is usually called CMakeLists.txt, and write this here:

   add_executable(myproject src.cpp)

add_executable adds an executable target to be built from the source files listed in the command invocation. That's enough to build simple projects.

Building the project

Simple variant

Type cmake CMakeLists.txt (you can also pass directory path to CMakeLists.txt by cmake argument cmake ./. or cmake ./path/to/my/cmake/ - watch below). CMake will generate all necessary files and create Makefile in the current directory:

   default_user@default-user:~/myproject$ cmake CMakeLists.txt
   -- The C compiler identification is GNU 7.2.0
   -- The CXX compiler identification is GNU 7.2.0 
   -- 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/myproject

Then just type make and everything is done!

   default_user@default-user:~/myproject$ make
   [ 50%] Building CXX object CMakeFiles/myproject.dir/src.cpp.o
   [100%] Linking CXX executable myproject
   [100%] Built target myproject

Briefly: cmake CMakeLists.txt, then make

Improved and more convenient variant

As it stated above, the main feature is that CMake can handle out-of-place builds. So it is quite convenient to have all build files and binaries away from current work directory in their own directory. We can make new build directory and launch CMake from there:

   default_user@default-user:~/myproject$ mkdir build && cd build
   default_user@default-user:~/myproject/build$ cmake ../.

Now binary myproject and all build files(CMakeCache.txt, cmake_install.cmake, etc) are in /build and aren't mixed with other source files.

Briefly: mkdir ./build && cd ./build, then cmake <path_to_CMakeLists>.txt, then make

Second iteration

Specifying the required version of cmake

   cmake_minimum_required(VERSION 2.6)

If the version of cmake is less than 2.6, it will not work. To write this command always is a good style

Project name

   project(Myproject)

Indicates that this cmake file is the root of some project. Stores the name in the PROJECT_NAME variable. Additionally this sets variables PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR, PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR(watch next paragraph about variables).

Variables

In CMake you can create text variables. Command

   set(MYVAR The variable's value)

will write value The variable's value to variable MYVAR. To use the value somewhere, you need to write ${MYVAR}. To add a certain text to a variable, you can do this:

   set(MYVAR "${MYVAR} is 42")

Variables are actively used by various libraries - to set flags, build / link parameters (watch paragraph below about libraries).

Quite beautiful variant of CMakeLists.txt with separate list of sources in vatiable:

   cmake_minimum_required(VERSION 2.6)
   set(SOURCES test.cpp lib1.cpp lib2.cpp)
   add_executable(test ${SOURCES})

Moreover, there are a lot of built-in variables, that provide information, change behavior, describe the system and control the build. There are some of them:

  • PROJECT_NAME - project name
  • CMAKE_CXX_FLAGS - compiler flags
  • CMAKE_LD_FLAGS - linker flags
  • CMAKE_BINARY_DIR - this is the full path to the top level of the current CMake build tree. For an in-source build, this would be the same as CMAKE_SOURCE_DIR.
  • CMAKE_SOURCE_DIR - the path to the top level of the source tree
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY - where to put all the executable target files when built.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY - Where to put all the LIBRARY target files when built
  • CMAKE_ARCHIVE_OUTPUT_DIRECTORY - Where to put all the ARCHIVE target files when built.

Watch MOAR variables.

Including directories with headers

If you want your header-files (*.h) to be searched in special directories, you can list it in include_directories:

   include_directories("headers/myheaders" "/usr/include" "blahblah")

Very important: including libraries

Let's learn how to search and connect libraries with cmake using Boost as an example

   set(Boost_USE_STATIC_LIBS OFF)
   set(Boost_USE_MULTITHREADED ON)

Firstly, we want this library be linked staticly. Second flag allows Boost use multithreading. Suppose we need the components of boost called chrono (a library for working with time) and filesystem (a library for working with the file system):

   find_package(Boost COMPONENTS chrono filesystem REQUIRED)

REQUIRED indicates that the library is necessary for the project. Then we will have automatically defined such variables: Boost_INCLUDE_DIRS - path to headers connected with necessary components, Boost_LIBRARIES - path to binary library. Then we include directories with headers:

   include_directories(${Boost_INCLUDE_DIRS})

and use special comand target_link_libraries to link libary:

   target_link_libraries(Myproject ${Boost_LIBRARIES})

Notice: not all libraries can be linked in that way. Only those which have Find%libraryname%.cmake could be found using find_package. For example, libelf doesn't have. But in that case plan is the same: find path to headers and path to binary library and then include_directories and target_link_libraries. How to find these paths watch: find_path, find_library.

How make an library in subdirectory and link it with the main program

Further Reading

CMake latest documentation CMake Tutorials CMake Tutorial (RUS) Введение в CMake / Хабрахабр (RUS)

Clone this wiki locally