LATEST POSTS

Robot Operating System (ROS) - Command Line Tools

Coverage Problem in Mobile Robotics

Motion Planning in Robotics

Simulataneous Localization and Mapping - An Introduction

Visual SLAM




CMAKE META BUILD SYSTEM


CMake is a cross-platform generator system designed to create projects. It is not a direct build system, but a meta build system that uses a script with defined rules, called CMakeLists.txt to generate input files for build systems called project files (could be Unix Makefiles or MinGW Makefiles or others, discussed later) that support different kinds of compilation, configuration, testing and packaging options.

This article explains the reasoning behind using C++, related terminology used in C++ code compilation and general commands, options and tags to be used to write CMakeLists.txt as per desired requirements.

CMake Pipeline Overview


Introduction


C/C++ is an extremely useful language used for innumerable number of applications. However, with several compilers, operating systems and hardware platforms, it becomes essential to package an optimized C++ project comptabile accross various such platforms and for different desired uses.

CMake makes it possible for developers to generate the project makefiles to be used by the build system to build the software. CMake does not compile or link any source (C/C++ code files here) itself but only creates configuration files for a build system. The build system thereafter uses the configurations to compile and link the source files as desired.

Build systems are a collection of tools that compile the source code, execute all linking and other secondary tasks. They could be configured to not only compile source codes but also synchronise builds in multi-stage build systems. CMake uses the specified generators like Unix Makefiles, Ninja, MinGW Makefiles, Visual Studio and Codeblocks among others and writes the input files for the native build system. Exactly one build system is used at once.

Note:
gcc and g++ are the compiler-drivers of the GNU Compiler Collection. While gcc can compile .c and .cpp files as C and C++ language files respectively, g++ considers both of them to be C++ files. Also, unlike gcc, g++ compilation automatically links all STL C++ libraries.

For ease of understanding, here we can consider using them interchangeably.

Is CMake really needed?!


C++ is a widely used programming language owing to its powerful capabilities and fast operation. Among several other things, being a compiled language makes C++ really really fast.

While compilation is a topic for later, it can be understood naively as breaking down a code to its almost machine-level version and preprocessing parts as much as possible such that when executed, considerable sections of the code already have prepared answers or execute immediately on the processor and no time is spent in conversion of human-readable code into machine-compatible code at runtime.

The most common and rudimentary Hello World C++ program has one main.cpp file and it can be compiled using the following terminal command to obtain the

~$ g++ main.cpp -o main // Compile
~$ ./main // Execute


If there are 3 files, namely si_int.cpp, com_int.cpp and the main.cpp where the two former files have functions to compute simple interest and compound interest, used(by importing) in the last main.cpp file to calculate results; they could be compiled using terminal command (for C++11)

~$ g++ si_int.cpp com_int.cpp main.cpp -o main -std=c++11 // Compile
~$ ./main // Execute


The two examples are indicative of the cumbersome process that could shape if there are more files, more dependencies, more libraries to link and other such unique requirements while code compilation; command-line compilation is certainly not the way to go.

For reassurance, here are some tags needed for g++ to achieve more features.  
-I<include path> - Specify a directory (to include files from)  
-L<library path> - Specify a library directory  
-0 - Turn ON optimizations by the compiler  
-l<library> - Link with library lib<library>.a  
-Wall - Turn on Warnings during compilations  
-g - Generate flags for debugging during compilation (Used by GDB)  

Considering the challenges in compilation, CMake provides options to the user to configure the complete project and package it as per need be. The conditions, compilation tags, machine dependencies, language versions and many such parameters.

CMake is a high-level build-system customized for C++. CMake tracks and stores all flags and conditions that should be followed when the code is finally compiled and run. The liberty of allowing extensibility in code compilation conditions and external libraries/resources used makes CMake a very useful tool.

Nuances of CMake


The CMakeLists.txt has instructions that help developers design different build scenarios and conditional usage of generators, versions and more. Certain general CMake commands are used for some common operations.
The common parameters, description and command to configure the same in a CMakeLists.txt are summarized here.

NOTE:
Most of the command signatures are only short-hand signatures and do not show all the options and configurable parameters that CMake actually suppports.
More detailed explanation(generally needed only if a very complex setup is employed) can be found on the official website of CMake.

Set string variables using actual string values
  set(<variable_identifier> <string_value>)

Set string variables using another variable value
  set(<variable_identifier> <${another_variable_identifier}>)

Append string variables using another variable value
  string(APPEND <variable_identifier> <string_value>)

Set list variables using actual string values
  set(<list_identifier> <string_value1> <string_value2>)

Set list variables using another list value
  set(<list_identifier> <${another_list_identifier}>)

Append list variables using another list value
  list(APPEND <list_identifier> <string_value>)

The <string_value> variable can be a file name as well. The identifiers can either be user defined for later use the script or from the common list of CMake supported variables like CMAKE_BUILD_TYPE and PROJECT_LINK_LIBS among others.

CMake Package version to be used. Versions > 3.0.0 are generally used though.
  cmake_minimum_required(VERSION 2.8.9)

Operations dependent on OS type. CMake can generate different configurations depending on Linux/Apple/Windows OS.
 if(APPLE)
 // do something for APPLE OS
  endif()

  if(UNIX AND LINUX)
  // do something for LINUX
  endif())

  if(WIN32)
  // do something for WINDOWS OS
  endif()


Software build type
  set(CMAKE_BUILD_TYPE Release/Debug)

C++ Version. For instance, C++11 in this case
  set(CMAKE_CXX_STANDARD 11)

Give a name to the project
  project(<string_value>)

An executable is built from the source file and assign the string_value as the name of the executable
  add_executable(<string_value> <code_file_name>)

Add the executable library to the compilation sequence
  add_library(<string_value> <library_type> <file_location>)
  For example,
  add_library_(<libName> SHARED/STATIC src/lib_executable.cpp)
If STATIC, then the name of the library is libName.a and it is libName.so if SHARED

Include the header files from the source code into the build environment
  include_directories(<include_header_locations>)

While to set source code files to a string value, all files need to be mentioned manually, GLOB or GLOB_RECURSE allows for searching and selecting multiple source code files
  file(GLOB SOURCES "src/*.cpp")

Several properties need to define for the executable binary
An example would be setting the language as CXX(for C++).   set_target_properties(cmake_usage_example PROPERTIES LINKER_LANGUAGE CXX)

Defining the location in the system for installation of the library.
  install(TARGET <libray_name> DESTINATION <destination_location>)

Find the location of a library and store its reference in a temporary VARIABLE cache.
  find_package(<VARIABLE> <library_name> <path_to_library>)

Link an already extant library (either native library or third-party installed library or custom-built one ) to the current executable or library being created
  target_link_libraries(<to_be_linked> options <to_link_ref>)
  For example
  target_link_libraries(libApp LINK_PUBLIC ${VARIABLE})

<lib_to_link_ref> can be found from something like find_package

Example C++ Application & CMakeLists.txt


Install CMake and check version


  :~/$sudo apt-get install cmake // Install CMake
  :~/$cmake --version // Check CMake version

The following example is a simple C++ application. It has a class with functions for a few mathematical operations. The main script to be executed uses this class as need be. Finally, the Unix Makefiles CMake generator will be used to create the build system Makefile.

cpp-cmake-example
├── CMakeLists.txt
├── include
│    └── computations.h
└── src
   └── computations.cpp
   └── main,cpp

Example C++ Application - Source Code computations.cpp file


Example C++ Application - Header File computations.h file


Example C++ Application - Main file main.h file


Example C++ Application - CMake file CMakeLists.txt file


DOWNLOAD EXAMPLE SOURCE CODE


Finally, the project is executed using the steps shown in the image below.



IF YOU LIKED THE ARTICLE, DON'T FORGET TO LEAVE A REACTION OR A COMMENT!


Copyright @Akshay Kumar | Last Updated on 05/25/2019

counter