Chapter 1 From a Simple Executable to Libraries
1.1 Compiling a single source file into an executable
The CMake language (e.g. the function name) is case insensitive while the arguments of the functions are case sensitive. For example, the
VERSIONbelow must be in upper case:cmake1
cmake_minimum_required(VERSION 3.5)
Typically
cmake -H . -B buildhas a equivalent effect with the below commands:cmake1
2
3mkdir -p build
cd build
cmake ..CMake is just a build system generator, which is to say it’s used to generate a build system and then use it to get the source code compiled. On Linux, the default build system is Makefile while on Windows, it’s Visual Studio.
Generically, we can use
cmake --build .to run instructions of the chosen build system.Alongside the target we specified with
add_excutable, CMake also generates other ones such asclean,depend, etc. which can be listed usingcmake --build . --target help
1.2 Switching generators
As mentioned above, CMake is just a collection of build system generators. We can switch to another generator to create different build systems such as Ninja. Use this:
cmake1
2cmake -G Ninja ..
cmake --build .Remember to install Ninja before this.
1.3 Building and linking static and shared libraries
add_libraryaccepts following arguments:- the first one is the name of the target (and also the library)
- the second one is the way to create the library, which includes
STATIC,SHARED,OBJECT,MODULE,IMPORTED,INTERFACEandALIAS. (They will be introduced later) - the following ones are files to be compiled into the library
One thing worth noting is that the library generated under
builddirectory has a name oflibplus target name, with suffix of.afor static one and.sofor dynamic one.OBJECTas the second argument ofadd_librarycan be useful when creating both static and shared libraries:cmake1
2
3add_library(message-objs OBJECT Message.hpp Message.cpp)
add_library(message-shared SHARED $<TARGET_OBJECTS:message-objs>)
add_library(message-static STATIC $<TARGET_OBJECTS:message-objs>)Note that
$<TARGET_OBJECTS:message-objs>cannot be simplymessage-objsbecause arguments in this place should otherwise be a filename if not generator expressions.
1.4 Controlling compilation with conditionals
- We can use
if,elseif,elseandendifto construct a branch control flow in CMake. - In CMake,
BUILD_SHARED_LIBSis a built-in flag, which indicates whetheradd_librarywill build a static or shared library if the second argument of it is omitted.
1.5 Presenting options to the user
- Use
optionto expose a flag to users, letting them decide whether to set the flag, usingcmake -D FLAG=ON .. cmake_dependent_optionin moduleCMakeDependentOptionprovides a way to expose a flag only when another flag is set or unset.
1.6 Specifying the compiler
- At configure time, CMake chooses a suitable compiler for us according to the platform and the chosen build system generator. There are two ways to override the default compiler:
- use
cmake -D CMAKE_CXX_COMPILER= - export an environment variable
CXX
- use
- Also,
cmake --system-informationcan be used to list some info about CMake system.
1.7 Switching the build type
CMake build type can be specified with
CMAKE_BUILD_TYPEinCMakeLists.txtor-Doption from command lineWe can set the default value of
CMAKE_BUILD_TYPElike below:cmake1
2
3
4
5if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
1.8 Controlling compiler flags
CMake provides two ways to specify the compiler flags. The first is on a per-target basis:
cmake1
2
3list(APPEND flags "-fPIC" "-Wall")
target_compile_options(geometry PRIVATE ${flags})With
target_compile_options, we can specify compiler flags for each target, which is a relatively fine granularity.Note here we specify a
PRIVATE, actually here can appear three kinds of values:PRIVATE: the compiler options will only be applied to the target itself, not to other ones consuming it.INTERFACE: the options will only be applied to targets consuming it.PUBLIC: the options will be applied to both.
We can use
make -- VERBOSE=1to verify the options are correctly applied to the corresponding targetsAnd the second approach is on a global basis, using
CMAKE_CXX_FLAGSin the CMake file or-Doption from the command line.
1.9 Setting the standard for the language
CMake provides two ways to specify the C++ standard you want to use. The first is still on a per-target basis:
cmake1
2
3
4
5
6set_target_properties(animal-farm
PROPERTIES
CXX_STANDARD 14
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
)If
CXX_STANDARD_REQUIREDflag is set, the version specified inCXX_STANDARDis required, which is to say, C++14 has to be available. If it’s unset, a latest version will be used as an alternative if C++14 isn’t available.And naturally, the second approach is on a global basis, using
CMAKE_CXX_STANDARDandCMAKE_CXX_STANDARD_REQUIRED.Interestingly, CMake even allows finer control over the language features (such as variadic templates, automatic return type deduction, etc.) with
target_compile_features.
1.10 Using control flow constructs
- We can use
foreach-endforeachandwhile-endwhileto construct a loop control flow in CMake.foreachcan be used in four ways:foreach(loop_var arg1 arg2), orforeach(loop_var ${list})foreach(loop_var RANGE total)foreach(loop_var IN LISTS list)(listhere will be expanded automatically)foreach(loop_var IN ITEMS item)(itemhere won’t be expanded automatically)
- Just like target, files also have properties in CMake, we can use
set_source_file_propertiesandget_source_file_propertyto set and get them.