NOTE: You can also take a look at this cmake c++ started project.
The bare minimum cmake project contains one CMakeLists.txt
file at the root of
the project with the following code:
cmake_minimum_required(VERSION 3.15)
project(MyAwesomeProject CXX)
This states that this project requires at least cmake 3.15
to compile, it is
called MyAwesomeProject
and the language of the project is c++. At this point
you could specify multiple languages or project version if you want, though it
is not required.
cmake_minimum_required(VERSION 3.15)
project(MyAwesomeProject VERSION 0.1.0 LANGUAGES C CXX)
Follow these steps to compile a binary:
- Add the binary.
- You could think of this step as creating an object within cmake to represent the binary.
- These objects are called "target"s.
- You will refer to the target by the name you gave it.
- Set various properties for the target.
add_executable("MyAwesomeExe" "src/main.cpp" "src/lib.cpp" "src/lib.hpp")
target_link_libraries("MyAwesomeExe" PRIVATE pthread)
add_executable
creates a target called "MyAwesomeExe". This target is compiled
using the sources listed after target name. Notice I am listing the header file
too. It will be ignored and can be omitted.
target_link_libraries
adds libraries to the target. The word PRIVATE
means
that if this target was added as a library (e.g. if it was an add_library
instead) to another target, the libraries linked to this target will not be
linked to the parent target. (Use PUBLIC
to make these libraries available to
the parent).
The old way of doing this was to have a different command on each system. So on unix you would run:
mkdir BUILD_DIR
cd BUILD_DIR
cmake ..
make
More recent versions of cmake (early version 3 I think) allow you to have a cross-platform command for building.
cmake -B BUILD_DIR # equivalent to `mkdir BUILD_DIR && cd BUILD_DIR && cmake ..; cd ..`
cmake --build BUILD_DIR # equivalent to `cd BUILD_DIR && make; cd ..`
The most common name of BUILD_DIR
is build
That's it. Your executable should be in ./BUILD_DIR/MyAwesomeExe
Data types:
In short, there is just 1: String. In certain contexts strings that look like numbers will be interpreted as numbers but by and large everything is a string.
Quotes are only required if you want to preserve spaces. Any text passed as a function argument is a string.
Functions:
In cmake a function is called with round brackets after function name, with arguments separated by spaces. Function names are case insensitive. Old cmake required all functions to be in caps, so you will see some people still doing that.
SomeFunction(1 2 3 4)
Variables:
A variable in cmake is set with the set
command
set(myVar someValue) # someValue is the literal string "someValue" (quotes are not required)
# message is `print`. STATUS means prepend "-- " to the printout to make it look uniform (it is not required)
message(STATUS "The value of myVar is ${myVar}") # variables are accessed by enclosing it in `${}`
#-- The value of myVar is someValue
You can create a list of items and pass it to a function that takes multiple arguments like so:
set(SOURCE_FILES src/lib.cpp src/main.cpp)
add_executable("MyExe" ${SOURCE_FILES})
Any undefined variable is set to empty string
message(STATUS ${UNSET_VARIABLE})
# --
set(UNSET_VARIABLE 42)
message(STATUS ${UNSET_VARIABLE})
# -- 42
set(UNSET_VARIABLE)
message(STATUS ${UNSET_VARIABLE})
# --
If statements:
Try to avoid them, it's where all the complexity starts. The one you will probably use the most is to check whether you are compiling with MSVC. This is done by:
if (MSVC)
else()
endif()
In old cmake it was required to put the condition in the argument of else
and
endif
, so sometimes you will see:
if (MSVC)
else(MSVC)
endif(MSVC)
Else if is called elseif
.
Boolean literals are
TRUE, ON, YES # true
FALSE, OFF, NO # false
They all mean the same thing and are interchangeable
As mentioned above, the root of any cmake project is the CMakeLists.txt
file,
but a project can (and often does) contain subprojects denoted by more
CMakeLists.txt
s further down in the hierarchy.
├── CMakeLists.txt # Root project
├── src
│ └── CMakeLists.txt # Subporject for managing application source
└── tests
└── CMakeLists.txt # Subporject for managing tests
These subprojects are included like so:
# ./CMakeLists.txt
add_subdirectory(src)
In ./src/CMakeLists.txt
if project(...)
or cmake_minimum_required(...)
are
not specified they will be inherited.
A complete example can look like this:
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyAwesomeProject CXX)
add_subdirectory(src)
# ./src/CMakeLists.txt
set(SOURCE_FILES src/lib.cpp src/main.cpp)
add_executable("MyAwesomeExe" ${SOURCE_FILES})
target_compile_features("MyAwesomeExe" PRIVATE cxx_std_17) # Set language standard to 17
target_link_libraries("MyAwesomeExe" PRIVATE pthread)
Cmake has too many flags. Probably the most used one is CMAKE_BUILD_TYPE
which
sets the build type. You can specify this when you run cmake -B
like so:
cmake -B -DCMAKE_BUILD_TYPE=Debug # replace Debug with Release for release mode
Generally when setting cmake variables from the command line use syntax
-DVARNAME=value
where VARNAME
is the variable you want to set and value
is
it's value.