What's CMake? CMake is a build system designed to build C++ projects.
What's a build system? Well, you know how a C++ compiler creates an application from source(.cpp) files? Those source files also `#include` header files too. Question is, how does the compiler find those files in the first place? In fact, how does the compiler know to build the application using settings like "build in debug mode" or "Use this library"? Visual Studio users don't think about it too much as Visual Studio takes care of that for you. Underneath the hood, Visual Studio uses a tool called MSBuild that configures the compiler to build your application.
Before build systems were used, programmers would interface with the compiler directly by running commands like:
Code: Select all
compiler build-app "app.exe" -debug_mode -header_files "header.hpp" -source_files "main.cpp"
Unlike Visual Studio, CMake is more universal as it could work with multiple compilers and editors from Windows to even Linux. Plus, a lot of libraries use it meaning that if your project uses CMake, it can more easily intergrate those libraries.
CMake works with CMake projects defined by a CMakeLists.txt. These projects contain "targets" that can then be "linked" together to build a library, an application or an "interface library"(a target that builds no library, no application and is just a raw collection of settings to use). There are various commands you can put in a CMakeLists.txt but the ones in this project contain all the commands you need for most projects.
Let's start with the main CMakeLists.txt in the main directory.
Code: Select all
cmake_minimum_required(VERSION 3.11)
Code: Select all
project(first-cmake VERSION 1.0.0 DESCRIPTION "A test to see if I can make a CMake Project" LANGUAGES CXX)
Code: Select all
# Build the math project.
Code: Select all
# Build the math project.
add_subdirectory("${PROJECT_SOURCE_DIR}/math")
This is a common pattern where you have one main CMake project include all the other CMake projects. Other than that, the main CMake project doesn't have much else.
Let's take a look at the parser project. This project has a library called parser.
Code: Select all
# Declare the library.
add_library(parser)
Code: Select all
# Declare that the library uses C++17
target_compile_features(parser PUBLIC cxx_std_17)
Code: Select all
# Declare our includes.
target_include_directories(parser
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
PRIVATE
"${PROJECT_SOURCE_DIR}/internal"
)
Code: Select all
# Declare the library's files.
target_sources(parser
PUBLIC
"${PROJECT_SOURCE_DIR}/include/parser/parser.hpp"
PRIVATE
"${PROJECT_SOURCE_DIR}/source/parser.cpp"
"${PROJECT_SOURCE_DIR}/internal/internal/internal.hpp"
"${PROJECT_SOURCE_DIR}/source/internal.cpp"
)
Now let's look at the math project. This project has a header only library. Since the library is header only(no source files), it needs to be declared slightly differently from regular libraries.
Code: Select all
# The library is a header only library. It won't be compiled.
add_library(math INTERFACE)
Header only libraries have to be interface libraries because header files don't actually get compiled by the compiler. They're just `#include`d into source files which are then compiled into an application. If you try to compile an application or library with no source files, the compiler will complain. As an interface library, other targets can link to it to inherit its headers.
The rest of the file is similar to the parser project but with the `INTERFACE` keyword.
Code: Select all
# Declare that the library uses C++17.
target_compile_features(math INTERFACE cxx_std_17)
# Declare the library's includes.
target_include_directories(math INTERFACE "${PROJECT_SOURCE_DIR}/include")
# Declare the library's files.
target_sources(math INTERFACE "${PROJECT_SOURCE_DIR}/include/math/math.hpp")
Finally, let's look at the calculator project which will build an actual application using these libraries.
Code: Select all
# Declare the executable.
add_executable(calculator)
The rest of the commands are ones you've seen before except for this line here:
Code: Select all
# Declare the libraries this executable uses.
target_link_libraries(calculator
PRIVATE
math
parser
)
That's pretty much it. This is all you need to create CMake projects and use other CMake projects people make. CMake is incredibly simple to use when used right and with additional commands, you can do incredible things like building different applications for different platforms(Windows, Linux, Android) with one project or automate thousands of tests that debug your application the moment you build it. CMake gives you much more flexibility than what the Visual Studio settings allow.
To learn more about CMake, check out the documentation here:
https://cmake.org/cmake/help/latest/gui ... index.html