-
Notifications
You must be signed in to change notification settings - Fork 139
CMake
This page is maintained by Konstantin Soshin
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
-
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.
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.
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
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
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(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).
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 asCMAKE_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 theLIBRARY
target files when built -
CMAKE_ARCHIVE_OUTPUT_DIRECTORY
- Where to put all the ARCHIVE target files when built.
Watch MOAR variables.
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")
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 library:
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
.
For example, in ./
we have main.cpp
, in ./mymath
we have mymath.h
where our math functions are declared and mymath1.cpp
, mymath2.cpp
, mymath3.cpp
where they are realized. So, how should we build that project? The first solution seems obvious: put all sources to add_executable
and that's all.
./CMakeLists.txt:
# First variant
cmake_minimum_required (VERSION 2.6)
project (calc)
set(SRC main.cpp mymath/mymath1.cpp mymath/mymath2.cpp mymath/mymath3.cpp)
set(HEADERS mymath/mymath.h)
add_executable(cartcalc ${SRC})
include_directories(${HEADERS})
But it is more convenient to organize our math files to one library using add_library
and then just link it using target_link_library
:
./CMakeLists.txt
# Second variant
cmake_minimum_required (VERSION 2.6)
project (calc)
set(SRC mymath/mymath1.cpp mymath/mymath2.cpp mymath/mymath3.cpp)
set(HEADERS mymath/mymath.h)
add_library(math STATIC ${SRC})
add_executable(calc main.cpp)
target_link_library(calc math)
include_directories(${HEADERS})
Another and the most well-styled variant is to have two CMakeLists.txt
: the first is for our math
library, and the second is the main CMakeLists.txt
for the whole project, which will call the first one using add_subdirectory
:
./CMakeLists.txt
#Third variant
cmake_minimum_required (VERSION 2.6)
project (calc)
set(SRC main.cpp)
set(HEADERS mymath/mymath.h)
add_executable(calc ${SRC})
add_subdirectory(mymath)
target_link_library(calc math)
include_directories(${HEADERS})
./mymath/CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (math)
set(SRC mymath1.cpp mymath2.cpp mymath3.cpp)
set(HEADERS mymath/mymath.h)
add_library(math STATIC ${SRC})
add_definitions(-DSOME_IMPORTANT_DEFINITION)
this command adds -D define flags to the compilation of source files. It is intended to add preprocessor definitions.
Conditionally execute a group of commands. Typical usage:
if(expression1)
COMMAND1(...)
COMMAND2(...)
elseif(expression2)
COMMAND3(...)
COMMAND4(...)
else(expression3)
COMMAND5(...)
COMMAND6(...)
endif()
Possible expressions are:
if(<constant>)
if(<variable>)
if(NOT <expr>)
if(<expr1> OR/AND <expr2>)
Whatch more in documentation
It is quite easy to handle a lot of objects (for example source files) organizing them into lists. list
provide operations with them.
It is a good way to process a lot objects (for example source files). foreach()
evaluates a group of commands for each value in a list.
Sometimes you need to do some operations with strings. For example you want to transform source file's name to object file's, our to replace some part(s) of filename. string()
may help you.
CMakeLists.txt
in our project can serve as an example of usage foreach
and string
.
# Here TESTS contains all source directories with unit tests
foreach(ITER IN LISTS TESTS) # cycle begins
# each name from TESTS is concatinates with string "/t/*.*"
string(CONCAT GLOBBING_EXPR ${ITER} "/t/*.*")
# watch below about file manupulations. Here in INPFILES we get all files from the current directory from
# TESTS
file(GLOB INPFILES LIST_DIRECTORIES false ${GLOBBING_EXPR})
foreach(ITER2 IN LISTS INPFILES)
# copy each name from INPFILES to directory where building is happening now
file(COPY ${ITER2} DESTINATION ./.)
endforeach()
# each name from TESTS concatinates with the string "/t/unit_test.cpp"
string(CONCAT SRC_UNIT_TEST ${ITER} "/t/unit_test.cpp")
# replace all "/" to "_" in each filename
string(REGEX REPLACE "/" "_" EXEC_NAME_NOTFULL ${ITER})
# another concatination
string(CONCAT EXEC_NAME ${EXEC_NAME_NOTFULL} "_test")
add_executable(${EXEC_NAME} ${SRC_UNIT_TEST})
target_link_libraries(${EXEC_NAME} gtest mipt-mips-src ${Boost_LIBRARIES} ${LIBELF_LIBRARIES})
add_test(NAME ${EXEC_NAME} COMMAND ${EXEC_NAME})
endforeach()
Someties it really necessary to work with files (searching, copying, creating, reading and writing them).
Copying, creating, etc are provided by function file
(watch example above).
For searching files there are some specialized functions:find_file
, find_library
, find_path
, find_program
WIP on that part
MIPT-V / MIPT-MIPS — Cycle-accurate pre-silicon simulation.