Understanding Cmake

In this article, we’ll learn to install cmake on Linux. CMakeis a cross-platform open-source meta-build system that can build, test, and package software. It can be used to support multiple native build environments including make in Linux/Unix, Apple’s Xcode, and Microsoft Visual Studio.

How to Install CMake on Linux?

If you are to work with CMake in Linux, you’ll probably need a C/C++ compiler and the make build system since CMake generates ‘Makefiles’ on Linux. A Makefile contains the steps for compiling the program and generating an executable. The installation steps for these tools depend on the distribution.

Understanding CMake phases. Ask Question Asked 7 years, 8 months ago. Active 7 years, 8 months ago. Viewed 558 times 1. I'm writing CMake script that should do the following: Make a static library. Process the above library file using a python script.

  • CMAKEBUILDTYPE with CMake you can have several build types. The most important thing to know is that if you use an IDE project generator (like Visual Studio or XCode) you specify this in the build step, but for Makefiles you specify it in the config step.
  • CMake is a build system generator, not a build system. It evaluates the GLOB expression to a list of files when generating the build system. The build system then operates on this list of files. Therefore, the build system cannot detect that something changed in the file system.

We will be looking at two ways to install CMake.

1. Using Package Managers like apt/dnf

Note: Installing this way, the version of CMake installed will generally be an older one.

For Ubuntu/Debian (and their derivatives)

We can obtain information of a package and its dependencies using the apt command. Doing that for cmake:

Cmake

As is highlighted in the screenshot above, cmake recommends installing gcc (GNU Compiler Collection) and make. It also suggests some packages which are optional. You’ll need to install g++ if you’re working on a C++ project.

To install cmake , g++ and make using the apt command, type:

Note that while installing these packages, the gcc package is also installed.

For Fedora/CentOS (RedHat based distros and their derivatives)

To install cmake , g++ and make using the dnf command, type:

2. Using CMake’s Official Website

Understanding Cmake

CMake’s official website has two options to install CMake on Linux as of now:

  • A shell script (.sh file)
  • A .tar.gz archive

Installing through either of them will get you the latest version of CMake. You’ll still need a compiler (gcc/g++) and make. You can install them using the package manager. For Ubuntu/Debian based distros, type:

For RedHat Based distros, type:

For CentOS 8 based systems, we’ll be using the shell script. You can either download the script by clicking on the .sh file link on the website or by using the wget command :

Note: The link is for the latest version of CMake as of writing this article and may change in the future.

It is in general a good idea to inspect scripts before running them. To do that you can open it in the editor of your choice. Using the nano editor to open it:

Going through the script, it also contains the .tar.gz archive for the CMake binary. Hence, it’s size (~40MB). The script acts like a ‘self extracting archive’. Now that we’ve inspected the script, we can run it from the current directory using:

The above command will install cmake globally for all users to /usr/local/bin and the exclude-subdir option is to get rid of the extra directory that is produced while extracting the .tar.gz archive.

(You need the tar command as the script uses it. If prompted tar: command not found , install it by typing $ sudo dnf install tar)

Also, cmake will not be managed by the system package manager when installed this way. To update cmake you’ll need to repeat this process for any new versions.

A Sample CMake project

We’ll create a simple C++ Hello World program which uses CMake. Let’s start by creating a different directory for our project. Using the mkdir and cd commands:

Currently, the directory is empty. We’ll now create a C++ source file named hello_world.cpp which, as the name suggests, will print just Hello World!

You can use the editor of your choice. I’ll be using nano:

Now we’ll create a CMakeLists.txt file(with this exact capitalization) which is required by CMake:

The root directory of the project ( ~/projectzero in this case) must contain a CMakeLists.txt file. Each line in a CMakeLists.txt file has a command.

The CMakeLists.txt file for this project is quite trivial and will only contain these three lines:

It’s a good practice to have a separate build directory for executables. So, let’s do that:

The project structure looks like this now:

Understanding

To run cmake we need to change into the build directory:

The . is an alias for the parent directory and tells cmake to find the CMakeLists.txt file in the parent directory. If you see CXX anywhere while working with CMake, it refers to C++ as CXX.

Running cmake . generated the configuration files for CMake in projectzero/build . We can list the contents of build directory using the ls command:

Now we can generate the executable simply by typing:

Run the executable by typing:

Congratulations on building your first CMake project. You can find more such beginner-friendly CMake projects on this GitHub profile. Clion cmake error.

Conclusion

In this article we learnt how to install CMake on Linux and created a simple Hello World program using CMake. CMake is a very useful tool for C/C++ development. You can find more information about CMake in its documentation.

Introduction

CMake is one of the most convenient building tools for C/C++ projects. When it comes to target_include_directories and target_link_libraries, there are several keywords, PUBLIC, PRIVATE, and INTERFACE, that I got confused about from time to time even if I have read the related official documentations. So when I was building my C/C++ projects using CMake, I often just use PUBLIC everywhere or leave the keyword blank (CMake will then use PUBLIC by default), the libraries and executables built from the projects would work in most of the scenarios. However, it is certainly not best practice.


Today, I read Kuba Sejdak’s blog post “Modern CMake is Like Inheritance” and I found his interpretation on the CMake keywords PUBLIC, PRIVATE, and INTERFACE inspiring. So in this blog post, I would like to discuss some of my thoughts on these CMake keywords from the perspective of “inheritance”.

C++ Inheritance

Access Specifiers

In C++ object oriented programming, there are three types of access specifiers for classes.

Access SpecifierDescription
publicMembers are accessible from outside the class.
protectedMembers cannot be accessed from outside the class. However, they can be accessed in inherited classes.
privateMembers cannot be accessed (or viewed) from outside the class.

Alternatively, this could be described using the following simplified table.

Access SpecifierSame ClassDerived ClassOutside Class
publicYesYesYes
protectedYesYesNo
privateYesNoNo

Inheritance Types

When it comes to class inheritance, there are also three types of inheritances.

Inheritance TypeDescription
publicPublic members of the base class become public members of the derived class and protected members of the base class become protected members of the derived class. A base class's private members are never accessible directly from a derived class, but can be accessed through calls to the public and protected members of the base class.
protectedPublic and protected members of the base class become protected members of the derived class.
privatePublic and protected members of the base class become private members of the derived class.

Alternatively, this could be described using the following simplified table.

Inheritance Typebase: public memberbase: protected memberbase: private member
publicderived: public memberderived: protected member-
protectedderived: protected memberderived: protected member-
privatederived: private memberderived: private member-

CMake Inheritance

CMake uses somewhat similar inheritance concepts to C++, especially for the C++ public and private access specifiers and inheritance types. The CMake keywords PUBLIC, PRIVATE, and INTERFACE used in target_include_directories and target_link_libraries, in my opinion, are mixtures of access specifier and inheritance type from C++.

Include Inheritance

Understanding Cmake

In CMake, for any target, in the preprocessing stage, it comes with a INCLUDE_DIRECTORIES and a INTERFACE_INCLUDE_DIRECTORIES for searching the header files building. target_include_directories will populate all the directories to INCLUDE_DIRECTORIES and/or INTERFACE_INCLUDE_DIRECTORIES depending on the keyword <PRIVATE PUBLIC INTERFACE> we specified. The INCLUDE_DIRECTORIES will be used for the current target only and the INTERFACE_INCLUDE_DIRECTORIES will be appended to the INCLUDE_DIRECTORIES of any other target which has dependencies on the current target. With such settings, the configurations of INCLUDE_DIRECTORIES and INTERFACE_INCLUDE_DIRECTORIES for all building targets are easy to compute and scale up even for multiple hierarchical layers of building dependencies and many building targets.

Include InheritanceDescription
PUBLICAll the directories following PUBLIC will be used for the current target and the other targets that have dependencies on the current target, i.e., appending the directories to INCLUDE_DIRECTORIES and INTERFACE_INCLUDE_DIRECTORIES.
PRIVATEAll the include directories following PRIVATE will be used for the current target only, i.e., appending the directories to INCLUDE_DIRECTORIES.
INTERFACEAll the include directories following INTERFACE will NOT be used for the current target but will be accessible for the other targets that have dependencies on the current target, i.e., appending the directories to INTERFACE_INCLUDE_DIRECTORIES.

Note that when we do target_link_libraries(<target> <PRIVATE PUBLIC INTERFACE> <item>), the dependent <item>, if built in the same CMake project, would append the INTERFACE_INCLUDE_DIRECTORIES of <item> to the INCLUDE_DIRECTORIES of <target>. By controlling the INTERFACE_INCLUDE_DIRECTORIES, we could eliminate some unwanted or conflicting declarations from <item> to the <target>.


For example, the fruit library has INCLUDE_DIRECTORIES of fruit_h, tree_h, and INTERFACE_INCLUDE_DIRECTORIES of fruit_h. If there is a apple library that is linked with the fruit library, the apple library would also have the fruit_h in its INCLUDE_DIRECTORIES as well. We could equivalently say, the apple library’s include directory inherited the fruit_h of the fruit library.

Link Inheritance

Similarly, for any target, in the linking stage, we would need to decide, given the item to be linked, whether we have to put the item in the link dependencies, or the link interface, or both, in the compiled target. Here the link dependencies means the item has some implementations that the target would use, and it is linked to the item, so that whenever we call the functions or methods corresponding to those implementations it will always be mapped correctly to the implementations in item via the link, whereas the link interface means the target becomes an interface for linking the item for other targets which have dependencies on the target, and the target does not have to use item at all.

Link TypeDescription
PUBLICAll the objects following PUBLIC will be used for linking to the current target and providing the interface to the other targets that have dependencies on the current target.
PRIVATEAll the objects following PRIVATE will only be used for linking to the current target.
INTERFACEAll the objects following INTERFACE will only be used for providing the interface to the other targets that have dependencies on the current target.

For example, if the fruit library has the implementation of functions, such as size and color, and the apple library has a function apple_size which called the size from the fruit library and was PRIVATE linked with the fruit library. We could create an executable eat_apple that calls apple_size by PUBLIC or PRIVATE linking with the apple library. However, if we want to create an executable eat_apple that calls the size and color from the fruit library, only linking with the apple library will cause building error, since the fruit library was not part of the interface in the apple library, and is thus inaccessible to eat_apple. To make the apple library to inherit the size and color from the fruit library, we have to make the linking of the apple library to the the fruit library PUBLIC instead of PRIVATE.

Conclusions

The CMake builds a hierarchical project via the include interface or link interface. The “inheritance” mechanism in C++ is built upon the include interface or link interface.

FAQ

How to Understand CMake Interface?

In my understanding, CMake interface is just like a telephone switch station in old times.

If A wants to call B and there is no direct telephone cable connection between A and B, A has to call a telephone switch station that has connection to B and the personal in the telephone switch station will connect A and B by jointing the cable of A and the cable of B together. If the telephone switch station does not know there is a B, it is impossible to get A and B connected. So CMake interface is simply a registration in the telephone switch station. When there is a dependency in CMake targets, targets from different levels of hierarchy are connected via interfaces, for both include and link.

Understanding Cmake

What are the Key Points for This Blog Post?

Understanding Cmake Model

PRIVATE only cares about himself and does not allow inheritance. INTERFACE only cares about others and allows inheritance. PUBLIC cares about everyone and allows inheritance.

Understanding Cmake Results

Is PUBLIC, PRIVATE, INTERFACE Part of the GCC/G++ Compiler?

Understanding Cmake And Make

No. Compilers, such as gcc and g++, do not have such mechanism. CMake invented those keywords for user to create a building graph that has very clear and explicit dependencies. The building graph translates to normal building commands using gcc and g++.

Understanding Cmake Levels

References