The introduction is great, as it explains in detail why you need cmake. You can immediately under the cat, if you already know.
Introduction
Compiling a project with your hands is a waste of time. This is actually an axiom and those who program know about it. But in order to compile everything you need to automatically set the rules, right? Often and in the old-fashioned way use
makefile for * nix or some nmake for windows.
Though I’m not the first year of programming, I’ve made up with my hands simple project designers on the basis of a makefile, but it’s worth a little forgetting and you have to re-learn how to create this clever scheme. Basically you have to do projects designed for any one system, be it linux or windows, and often not cross-linked with each other. For portability, the makefile uses
automake and
autogen , but their syntax is even more confused. Not to say that the choice is perfect, but for myself I decided to switch to cmake, since it is ported to everything that is available. It seemed to me more personable. I'll try to explain the basics. You write the rules in words, and from them the makefile is generated, which you already run in the standard way.
Likbez
Why is it needed? So that when transferring to another machine, with other paths, you two teams put together a project without correcting anything in the makefile file.
But is there a configure? This is an alternative. And configure is not cross-platform, for its generation you need autoconf / autogen, for which there is also a set of rules.
Only benefits? Compiling with an autogenerated makefile is a little slower than the old way. For example, in KDE-4 is the official
release tool .
Getting started
In the current tutorial, I will talk about the simplest C ++ project template that should be compiled on Linux. There will be no talk of portability, no different compilers will be assigned, etc. In one article does not fit, and the jumble of conventions too confuse the novice.
First, about the tacitly accepted structure of projects on cmake. Usually in the root of the project are
src folders (for closed source),
include (for public headers),
lib (for plug-in libraries, if they are not system), empty
build folder (
build takes place in it, if not, create), I still I add
bin (or out, for the resulting binaries). It also requires the presence of files
AUTHOTS, COPYING, INSTALL, NEWS, README, ChangeLo g in the root. They may be empty, but availability is required. The
config.h.in file can be
optional (more on this later). The cmake configuration files are in each folder with the name
CMakeLists.txt . This name was coined by the authors of cmake and does not change.
Documentation is initially hard to understand, although it is complete. This is another drawback. But compared to autotools ...
A list of the types of project files that cmake can generate:
Hidden textBorland makefiles
Msys makefiles
Mingw makefiles
NMake Makefiles
NMake makefiles JOM
Ninja
Unix makefiles
Visual studio 10
Visual Studio 10 IA64
Visual Studio 10 Win64
Visual studio 11
Visual Studio 11 ARM
Visual studio 11 win64
Visual studio 6
Visual studio 7
Visual Studio 7 .NET 2003
Visual Studio 8 2005
Visual Studio 8 2005 Win64
Visual Studio 9 2008
Visual Studio 9 2008 IA64
Visual Studio 9 2008 Win64
Watcom wake
Xcode
CodeBlocks - MinGW Makefiles
CodeBlocks - NMake Makefiles
CodeBlocks - Ninja
CodeBlocks - Unix Makefiles
Eclipse CDT4 - MinGW Makefiles
Eclipse CDT4 - NMake Makefiles
Eclipse CDT4 - Ninja
Eclipse CDT4 - Unix Makefiles
Suppose we have a certain
source.cpp from which we get software - put it in the src folder. But we have the same project, so there are several
core.cpp, core.hpp, common.hpp, types.hpp files , which we also put in src and we need some kind of library, for example
pthread . Understood the source code, proceed to the description of the project and prepare it for auto-compiling.
')
It all starts with the creation in the root of the project file CMakeLists.txt. The cmake rules are similar to a scripting language, midway between javascript and php. Only much easier. There are conditions, functions, variables, constants, plug-ins.
The CMakeLists.txt file I will break into several parts to explain them. Part 1:
# cmake_minimum_required (VERSION 2.6) cmake_policy(SET CMP0011 NEW) cmake_policy(SET CMP0003 OLD) OPTION(WITH_DEBUG_MODE "Build with debug mode" ON) if ( NOT UNIX ) message (FATAL_ERROR "Not Unix!") endif ()
Here is the cmake_minimum_required version check function.
Operators are written as
if () opening and endif () closing. Similar to
foreach () and endforeach ().
The
message function displays our message. I used the FATAL_ERROR flag to indicate the type of message.
Note also that a semicolon (;) is not put at the end of commands. Here one line - one command. The brackets are moved away from the operators just for readability.
Each team usually has several options for setting the parameters, so looking at the
manual is not enough.
Quick
reference and a simple
example is in the documentation, but in my opinion too simple.
Part 2:
message ("Starting cmake") # , , include (myproj.cmake) # set (PROJECT myproj) # set (LIBRARIES) # foreach (LIBRARY ${LIBRARIES}) find_library("${LIBRARY}_FOUND" ${LIBRARY}) message(STATUS "Check the ${LIBRARY} is installed: " ${${LIBRARY}_FOUND}) if ( "${${LIBRARY}_FOUND}" STREQUAL "${LIBRARY}_FOUND-NOTFOUND" ) message(STATUS "Adding library sources") add_subdirectory (../${LIBRARY} lib/${LIBRARY}) endif () endforeach () # set (TARGETS "") set (HEADERS "") message ( STATUS "SOURCES: ${SOURCES}") add_subdirectory (src)
The
set () function creates or overwrites variables. If there is no value, then the variable will be empty. The variables defined here are named for the meaning they carry in the generation.
include () connects an external file with a piece of configuration to the current location. Here without explanation.
And add_subdirectory (src) indicates where to continue building the project's makefile.
If only general rules were specified here, then options for combining specific source files will be set inside the src directory in CMakeLists.txt.
I have not mentioned cmake_policy () yet. I decided not to perplex now understanding this. Let it hang here © Just makes assembly easier.
About foreach () cycle and biblioteki will be discussed further. Skip for now.
What was it that was rendered into a separate cmake file? Let's consider:
set ("${PROJECT}_BINARY_DIR" bin) set ("${PROJECT}_SOURCE_DIR" src:include) set ("${PROJECT}_LIB_DIR" lib) set (CMAKE_INCLUDE_PATH ${${PROJECT}_SOURCE_DIR}) set (CMAKE_LIBRARY_PATH ${${PROJECT}_LIB_DIR}) set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/${${PROJECT}_BINARY_DIR}) set (CMAKE_VERBOSE_MAKEFILE ON) set (CMAKE_BUILD_TYPE Debug) set (ERR_NO_UNIX "Cannot build on non Unix systems") if ( WITH_DEBUG_MODE ) ADD_DEFINITIONS( -DMY_DEBUG_MODE=1) endif() if ( CMAKE_COMPILER_IS_GNUCXX ) set(MY_CXX_FLAGS "-Wall -std=c++0x -fmessage-length=0 -v -L/usr/local/lib -L/usr/lib") set(CMAKE_CXX_FLAGS "-O0 ${MY_CXX_FLAGS}") # . . #set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -fno-reorder-blocks -fno-schedule-insns -fno-inline") #set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") else () message (FATAL_ERROR ${ERR_NO_UNIX}) endif ()
We see that a strange (or already familiar) construction in the form of $ {name} is used in set () - it substitutes an earlier defined variable using the same set () or cmake itself (all variables defined by cmake are in the documentation). For example, $ {PROJECT} inserts the value of myproj from the previously defined PROJECT variable.
The src: include entry is simply a string that in Linux means enumeration of paths (they are separated by a colon, not a semicolon).
The first three lines are the variables I set. But then the necessary variables are set, required by cmake. They can also not be explicitly defined, they are already defined with the launch, but they will indicate the wrong place with the current folder structure.
Sometimes you need to make the name of a variable based on other variables, and this is possible: $ {$ {PROJECT} _SOURCE_DIR}. First, PROJECT is dereferenced to $ {myproj_SOURCE_DIR}, which is already defined at the beginning. The result will be its value. All these are necessary difficulties, so that if the name of the project changes from myproj to superpuper, I didn’t have to climb the entire code by changing the names of variables and other things.
In the if block (CMAKE_COMPILER_IS_GNUCXX), we determined which flags are used for compilation. So far not cross-platform, but using branching and various variable flags created at the start, you can pile up different dependent builds for different platforms and compilers. The entire list of variables is in the
Variables section and this section is quite large. In other matters, this block can be removed completely from the config or prepared with the help of extensive cmake functionality. But it would take too much space.
if (WITH_DEBUG_MODE) - there is a special kind of variables that begin with WITH_. They are created using the option () function, a function that defines user options (it was in the code at the very beginning). These variables take only two values ​​ON or OFF and can be used as boolean values, for example. And ADD_DEFINITION (-DMY_DEBUG_MODE = 1) will add to the compiler the option -DMY_DEBUG_MODE if debug mode is enabled. For the C ++ compiler (in this case) this will mean adding a define.
Work with source code
So we got to the src folder. CMakeLists.txt is also needed in it, let's take a closer look at it.
set ("${PROJECT}_VERSION_MAJ" 0) set ("${PROJECT}_VERSION_MIN" 1) set ("${PROJECT}_VERSION_A" 1) set ("${PROJECT}_VERSION_B" 1) set ("${PROJECT}_VERSION" ${${PROJECT}_VERSION_MAJ}0${${PROJECT}_VERSION_MIN}0${${PROJECT}_VERSION_A}0${${PROJECT}_VERSION_B}) message(STATUS ${${PROJECT}_VERSION}) # set (MAIN_SOURCES source.cpp ) # set (PRIVATE_CLASSES core ) # , - SET (HEADERS_ONLY types common ) # set (PUBLIC_CLASSES) # set (ADDITIONAL_LIBRARIES stdc++ pthread ) set (PUBLIC_HEADERS) set (SOURCES) foreach (class ${PRIVATE_CLASSES}) LIST (APPEND SOURCES ${class}.cpp) LIST (APPEND HEADERS ${class}.hpp) endforeach () foreach (class ${HEADERS_ONLY}) LIST (APPEND HEADERS ${class}.hpp) endforeach () foreach (class ${PUBLIC_CLASSES}) LIST (APPEND SOURCES ${class}.cpp) LIST (APPEND HEADERS ../include/${PROJECT}/${class}.hpp) LIST (APPEND PUBLIC_HEADERS ../include/${PROJECT}/${class}.hpp) endforeach () add_executable (${PROJECT} ${MAIN_SOURCES} ${SOURCES}) target_link_libraries (${PROJECT} ${ADDITIONAL_LIBRARIES}) set_target_properties(${PROJECT} PROPERTIES VERSION "${${PROJECT}_VERSION}" SOVERSION "0") INSTALL ( TARGETS ${PROJECT} DESTINATION lib${LIB_SUFFIX} ) INSTALL ( FILES ${PUBLIC_HEADERS} DESTINATION include/${PROJECT} )
Here specific files and their assembly are recorded. First determined the version of the program. Further we define groups (or as they are called here, lists) of source codes, all names are written through a space. MAIN_SOURCES - one main source, PRIVATE_CLASSES - list of source pairs (source.cpp-header.hpp with a common name), PUBLIC_CLASSES - empty for this project, HEADERS_ONLY - list of header files only, ADDITIONAL_LIBRARIES - included in the C ++ library project.
The following cycles combine the lists of headers and sources into a single plug-in list (I note that the previous division into public and hidden was purely conditional). And finally, the rule means “build the project into the executable file”
add_executable (). After building the binary, you need to add the “link” libraries to it using
target_link_libraries () and that's it. Project rules are defined.
For convenience, you can add install-install rules with
install ().
Go to the build directory, which is at the root of the project, and run
cmake ..
from the command line. If everything went well (and should go well), we get
- Configuring done
- Generating done
- Build files have been written to: / home / username / tmp / fooproj / build
and next to makefile
After that, the usual way
make
Instead of conclusion
I found this method more simple and convenient than configuring configure. If you suddenly want to argue about this, I will refrain from commenting. Because why?
If you have changed something and cmake gives errors, first delete the entire cache of files from the build directory (roughly speaking everything) or at least CMakeCache.txt from the same build directory.
What was not considered in the tutorial:
1) Connecting Qt or Boost libraries
2) Search for installed libraries
3) Multiplatform
4) Options for different compilers
Documentation:
1)
Main documentation documentation2)
Documentation for version 2.8.93) The source of this disgrace is
attached .