
CMake is a cross-platform utility for automatically building a program from source code. At the same time, CMake itself does not directly build, but is a front-end. Different versions of make and Ninja can be used as a back-end. CMake also allows you to create projects for CodeBlocks, Eclipse, KDevelop3, MS VC ++ and Xcode. It should be noted that most of the projects are not native, but with the same back-end.
In order to build a project using CMake, you need to place the CMakeLists.txt file in the root of the source tree, which stores the rules and goals of the assembly, and perform several simple steps.
Let's look at examples.
Example 1. Hello, World:
To begin with, let's write the simplest heloworld and create a project structure:
main.cpp#include <iostream> int main(int argc, char** argv) { std::cout << "Hello, World!" << std::endl; return 0; }
CMakeLists.txt cmake_minimum_required(VERSION 2.8)
The CMake syntax is similar to the bash syntax, everything after the "#" character is a comment and will not be processed by the program. CMake allows you not to litter the source tree with temporary files - it is very simple and without extra gestures, the assembly is done “Out-of-Source”.
Create an empty directory for temporary files and go there.
fshp @ panica-desktop: ~ $ mkdir tmp
fshp @ panica-desktop: ~ $ cd tmp /
fshp @ panica-desktop: ~ / tmp $
Now run the cmake command, passing the path to the source folder as a parameter to it:
fshp @ panica-desktop: ~ / tmp $ cmake ~ / cmake / example_1 /
...
- Build files have been written to: / home / fshp / tmp
fshp @ panica-desktop: ~ / tmp $
fshp @ panica-desktop: ~ / tmp $ ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
fshp @ panica-desktop: ~ / tmp $
We see that in the folder there are several temporary files needed to build the project.
Now you can run make directly:
fshp @ panica-desktop: ~ / tmp $ make
Scanning dependencies of target main
[100%] Building CXX object CMakeFiles / main.dir / main.cpp.o
Linking CXX executable main
[100%] Built target main
fshp @ panica-desktop: ~ / tmp $ ./main
Hello, World!
fshp @ panica-desktop: ~ / tmp $
So, our program has gathered.
The tmp folder can be cleaned \ deleted without the risk of breaking the source. If CMakeLists.txt has been changed, the make call will automatically start cmake. If the source has been moved, then you need to clear the temporary directory and run cmake manually.
')
Example 2. Libraries:
If your project contains a library, then CMake will build it without problems.
To do this, let's complicate an example.
foo.cpp #include <iostream> void hello_world() { std::cout << "Hello, World!" << std::endl; }
main.cpp #include "foo.h" int main(int argc, char** argv) { hello_world(); return 0; }
CMakeLists.txt cmake_minimum_required(VERSION 2.8)
Variables can store lists of values ​​separated by spaces \ tabs \ hyphens:
set(SOURCE main.cpp foo.cpp) set(HEADER main.h foo.h)
Both options are correct.
To get the value of a variable, we use the construct:
${var_name}
So, this version of our project includes one static library collected from source codes. If we replace “STATIC” with “SHARED”, then we will get a dynamic library. If the library type is not specified, by default it will be collected as static.
When linking all necessary libraries are indicated:
target_link_libraries(main foo ogg vorbis)
As with manual compilation, library names are specified without the standard “lib” prefix.
So, building libraries with CMake does not cause problems, and the type of library static / dynamic changes only with one parameter.
Example 3. Subprojects:
Subprojects are very convenient if your program is divided into several libraries or a project consists of several programs.
Each subproject is essentially a complete project and can be used independently.
Now we have “foo” in the subdirectory and CMakeLists.txt of the subproject is also there.
CMakeLists.txt cmake_minimum_required(VERSION 2.8)
main.cpp #include "foo.h" int main(int argc, char** argv) { hello_world(); return 0; }
foo / CMakeLists.txt cmake_minimum_required(VERSION 2.8)
foo / foo.cpp #include <iostream> void hello_world() { std::cout << "Hello, World!" << std::endl; }
There is nothing new for you in the subproject file. But in the main file new commands:
include_directories(foo)
we did not change main.cpp, but foo.h was moved. The command tells the compiler where to look for header files. May be called several times. Headers will be searched in all specified directories.
add_subdirectory(foo)
Specify the directory with the subproject, which will be assembled as an independent.
Conclusion: CMake projects can be combined into fairly complex hierarchical structures, with each subproject in reality being an independent project, which in turn can consist of subprojects itself. This makes it easy to split your program into the required number of individual modules. An example of this approach is KDE.
Example 4. Library search:
CMake has quite advanced search facilities for installed libraries, although they are not built-in, but implemented as separate modules. In the standard package there are quite a few modules, but some projects (for example, Ogre) supply their own. They allow the system to automatically determine the presence of libraries required for linking a project.
On debian, the modules are located in /usr/share/cmake-2.8/Modules/ (your version may differ). The search for libraries is answered by modules called FindNAME.cmake, where NAME is the name of the library.
find_package(SDL REQUIRED) if(NOT SDL_FOUND) message(SEND_ERROR "Failed to find SDL") return() else() include_directories(${SDL_INCLUDE_DIR}) endif()
I think the meaning should be clear. The first and second block is a library search. If it is not in the system, an error message is displayed and cmake completes. The third block is similar, only it is not looking for a whole library package, but only a necessary component. Each such automated search determines after performing at least 3 variables:
SDL_FOUND, LIBXML2_FOUND, Boost_FOUND - a sign of the presence of the library;
SDL_LIBRARY, LIBXML2_LIBRARIES, Boost_LIBRARIES - names of libraries for linking;
SDL_INCLUDE_DIR, LIBXML2_INCLUDE_DIR, Boost_INCLUDE_DIRS - paths to header files.
If the first is more or less clear, then the second and third brought me a lot of trouble - half have names in the singular, half - in the plural. But it turned out, it is easy to track. In each module, there are comments at the beginning, the defined variables are described there. See, for example, /usr/share/cmake-2.8/Modules/FindLibXml2.cmake
As you can see, CMake is able to determine the presence and location of the necessary libraries and header files. In principle, this should be able to any automatic assembly system, otherwise the meaning of it?
Example 5. External libraries and object files:
If you write for “uncle”, and the evil “uncle” loves samopisnye libraries and does not want to share the source code, so sends the finished library, then you are at the address.
The object files in CMake are along with the source code - it is enough to include the object file in the list of files to compile.
With libraries tighter. As you know, a static library is nothing more than an ar-archive, inside of which there are ordinary object workers who are not related to each other. You probably already guessed how I acted first. Yes, just gutting the library. But then a more elegant way was found:
add_library(netutil STATIC IMPORTED) set_property(TARGET netutil PROPERTY IMPORTED_LOCATION Binary/game_client/libnetutil.a)
The word "IMPORTED" indicates that the library is being taken from the outside.
In CMake, each target has parameters, and set_property allows you to change them.
This library is standardly linked:
target_link_libraries(${TARGET} netutil)
For dynamic libraries, everything is the same, only the “SHARED” type, the extension is “.so”.
Unfortunately, support for non-system libraries is a bit of a crutch. Maybe I just don’t know the right option, so I’ll be happy if you “poke a muzzle.” On the other hand, this is not a heaped up exoskeleton with a life support system, but a simple two-line crutch.
Generators:
As stated at the beginning, CMake can generate many different types of projects. This is convenient and allows you to use CMake for almost any popular IDE.
If you run cmake without parameters, the available generators will be described at the end. Use this:
fshp @ panica-desktop: ~ / tmp $ cmake ~ / cmake / example_3 / -G "KDevelop3 - Unix Makefiles"
Conclusion:
This is not a translation of the manual, but the result of using CMake in a single commercial project. I would be glad if the article will help at least one person - in Russian there is not enough such documentation.
What I personally liked CMake with:
- one project - one file. No need to store a bunch of customization scripts, builds and other junk;
- Work speed in comparison with autotools;
- simple and understandable syntax, of course, with the elegance of python, do not contend, but also not Brainfack, after all .;
- is a front-end for a variety of IDEs;
- displaying progress is quite convenient;
- color output - in everyday life a little paint will not hurt;
For Sublime Text there is a plugin that adds CMake syntax highlighting, it is called “CMake”.
Examples