CMake Introduction
"By dint of building well, you get to be a good architect." – Aristotle
I have been using CMake since about 2008 for many of my C++ projects. I highly recommend it for cross-platform development. It can generate Makefiles, but for the last few years it has expanded to use the Ninja build system. This is all done in a compiler-independent way; Visual Studio, Xcode, and gcc/g++, builds can all be generated with ease. I find Ninja gives a performance advantage over Make, which is important as your project grows. This tutorial was made with Ubuntu 20.04.
Installing
In a bash, zsh, or some other terminal on Ubuntu enter:
sudo apt install cmake cmake-curses-gui ninja-build
This installs apt packages for cmake, ninja, and a curses-style GUI wrapper for CMake.
Basic Hello World
I have borrowed some things from this page, https://cmake.org/cmake/help/v3.22/guide/tutorial/A%20Basic%20Starting%20Point.html, though I have endeavored to massage this tutorial to make it easier to follow. Note that I skip over the configured header entirely because I don't find them useful.
Start by creating a cmakeFromScratch directory:
mkdir cmakeFromScratch
Then create a Step1 directory and a build directory inside the cmakeFromScratch folder:
cd cmakeFromScratch
mkdir Step1
mkdir build
cd Step1
Add source code for your hello world program with your favorite editor to the Step1 directory, e.g.,
gvim tutorial.cxx
int main(int argc, char **argv) {
cout << "Howdy\n";
if (argc > 1) {
cout << argv[1] << endl;
}
return 0;
}
and then create a CMakeLists.txt file as follows:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(Tutorial VERSION 0.1)
# add the executable
add_executable(Tutorial tutorial.cxx)
now run CMake as follows:
cmake -B ../build -GNinja
the -GNinja tells CMake to generate using Ninja. If you don't specify this CMake will probably still work as the default has it use Makefiles -- though you have to ensure make is installed on your system.
At any rate, if successful you should see something like this:
-- Configuring done
-- Generating done
-- Build files have been written to: cmakeFromScratch/build
-- Configuring done
-- Generating done
-- Build files have been written to: cmakeFromScratch/build
Now do the ninja build. I find it's easier if you open a separate terminal tab or window to do this, otherwise you'll find yourself changing directories a lot. Open a new terminal and enter:
cd <path_to>/cmakeFromScratch/build
ninja
If you are successful you should see something like:
[1/2] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[2/2] Linking CXX executable Tutorial
Run the final Tutorial executable:
./Tutorial
You should see the following output from your program:
Howdy
Now try running Tutorial with command line arguments: https://en.wikipedia.org/wiki/Command-line_interface#Arguments
./Tutorial 223
and you should see:
Howdy
223
If you are adventurous then add some C++ 11 — https://en.wikipedia.org/wiki/C%2B%2B11 — code to tutorial.cxx:
if (argc > 1) {
cout << argv[1] << endl;
const double inputValue = stod(argv[1]);
cout << inputValue << endl;
}
Note that after you do this you can no longer specify a non-numerical argument to the Tutorial executable! Failing to do so will cause a runtime error.
Next, specify that we want to use C++ 11 in CMakeLists.txt. You could also specify that we want to see all warnings:
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
#want to see all warnings:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
I also find the "message" CMake directive comes in quite handy for debugging. Just add it to your CMakeLists.txt file as follows and it works like a print statement for CMake. You could use it to output some standard CMake variables like so:
message("PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR})
message("CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR})
Add a Library
I couldn't find the source code for mysqrt.cpp on this page, so I found another CMake library tutorial here. What follows is a combination of the two tutorials for adding CMake libraries.
First, create subdirectories inside the Step1 directory and change into that directory as follows:
mkdir -p lib/MathFunctions
cd lib/MathFunctions
Next, create an Operation class as follows:
// Operation.hpp
#ifndef CMAKEHELLO_OPERATIONS_HPP
#define CMAKEHELLO_OPERATIONS_HPPclass Operation {
public:
int sum(const int &a, const int &b);
int mult(const int &a, const int &b);
int div(const int &a, const int &b);
int sub(const int &a, const int &b);
};#endif //CMAKEHELLO_OPERATIONS_HPP
Note the use of header guards in the Operation.hpp file.
Then create a .cxx file to define the operations:
//Operation.cxx
#include <exception>
#include <stdexcept>
#include <iostream>
#include "Operation.hpp"int Operation::sum(const int &a, const int &b{
return a + b;
}int Operation::mult(const int &a, const int &b){
return a * b;
}int Operation::div(const int &a, const int &b){
if(b == 0) {
throw std::overflow_error("Divide by zero exception");
}return a/b;
}int Operation::sub(const int &a, const int &b){
return a - b;
}
Then create another CMakeLists.txt inside lib/MathFunctions and populate it with:
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
add_library(MathFunctions STATIC Operation.cxx)
Then change directories up to the Step1 folder:
cd ../..
and add/modify the following to the CMakeLists.txt file that lives there:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
# add the MathFunctions library
add_subdirectory(lib/MathFunctions)# add the executable
add_executable(Tutorial tutorial.cxx)target_link_libraries(Tutorial MathFunctions)
Next add the following #include to your tutorial.cxx file:
#include "lib/MathFunctions/Operation.hpp"
Finally, add the following code immediately before the "return 0;" (again to tutorial.cxx):
Operation anOp;
int i = 3;
int j;
j = anOp.sum(i, i);
cout << j << endl;
Run cmake again:
cmake -B ../build -GNinja
Change into the build directory and rerun ninja and the Tutorial. As I indicated earlier, I like to use separate terminals for CMake builds and Ninja builds.
ninja
./Tutorial 88
If everything is successful you have just built a CMake library! The output should be as follows:
Howdy
88
88
6
You can make any CMake library optional as well, but that is outside the scope of this tutorial.
Find complete example code for this tutorial on the author’s github repo. Note that a github account is required to access this repository.
Credits
https://code.visualstudio.com/docs/cpp/cmake-linux
https://github.com/microsoft/vscode-cmake-tools/
https://cmake.org/cmake/help/v3.22/guide/tutorial/index.html
https://dmerej.info/blog/post/chuck-norris-part-1-cmake-ninja/
https://ninja-build.org/manual.html
https://www.kitware.com/