Makefile

CMake

CMake is an open-source, cross-platform family of tools designed to build, test and package software.

Why CMake?

Theoretically, any C++ program can be compiled with g++. However, when the program gets bigger and bigger, a project may have many folders and source files.

In these cases, using CMake will make your life a lot easier that compiling all fines together by running a long tedious command every time.

Resources

All CMake does is create a Makefile. ahh, so after you write your CMakeLists.txt, you would call

cmake .

This creates a Makefile. You then run

make 

Which creates the executable!

The cmake-make process

The cmake process handles the relationship between the project files, while the make process actually calls g++ to compile the program.

Compiling without Intermediate Files

The only thing that is dissatisfied with this process is that the intermediate files generated by CMake are still in our code files. When we want to release the code, we don’t want to publish these intermediate files together.

Therefore, the more common practice of compiling CMake projects is as follows:

mkdir build
cd build
cmake ..
make

Using static libraries (.a) and shared libraries (.so)

Usually, you have files (ex: main.cpp) calling functions from other files, which we call libraries.

We divide into 2 types of libraries

  1. static libraries (“.a” extension)
  2. shared libraries (”.so” extension)

What is the difference between the 2 kinds of libraries?

The difference is that a static library will generate a copy each time it is called, and the shared library has only one copy, which saves space.

// Static Library
add_library( hello libHelloSLAM.cpp )
 
// Shared library
add_library( hello_shared SHARED libHelloSLAM.cpp )
 
...
// Link the files at the end
target_link_libraries(helloExecutable hello)

When do I need add_library?

Whenever you are including other .h files, and these interface files have implementation files (.cpp format), you need to add those so they are linked properly. For Eigen, add_library is not needed because it doesn’t have .cpp files, only .h files.

it searches for the makefile and executes it. A makefile can look like the following:

default:
	g++ main.cpp -o out

Commands you should become super familiar with:

  • cmake_minimum_required
  • project
  • include_directories
  • add_library
  • add_executable
  • target_link_libraries

Using OpenCV with CMake

cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( DisplayImage DisplayImage.cpp )
target_link_libraries( DisplayImage ${OpenCV_LIBS} )
 

2023-09-17: Lessons on Cmake from this new personal project: https://chat.openai.com/share/1d3b9a2f-1667-4943-8809-50cab080a00b

In CMake, when you specify that a target (like an executable or a library) needs to link against another library, the relationship is often transitive.

Linkage is transitive in CMake

Yes, CMake handles transitive dependencies out-of-the-box. When you use target_link_libraries, dependencies are automatically propagated to dependent targets. Source

target_link_libraries(<target> <item>...)
  • Library dependencies are transitive by default with this signature

For example: In main.cpp

#include <opencv2/opencv.hpp>
#include "camera.hpp"
 
int main() {
    cv::Mat image;
    Camera camera{};
}

You can get away with this:

add_library(camera camera.cpp)
add_executable(main main.cpp)
 
target_link_libraries(camera ${OpenCV_LIBS})
target_link_libraries(main camera)
  • You didn’t link main to openCV, but this still builds, because because linking is transitive. camera is linked to openCV

What if you didn’t want this behavior? There are the PRIVATE, PUBLIC, and INTERFACE keywords.

Use of PRIVATE vs. PUBLIC vs. INTERFACE

  • PRIVATE: Used when the library is only needed for the target’s implementation.
  • PUBLIC: Used when both the target and consumers of the target require the library.
  • INTERFACE: Used when only the consumers of the target need the library.

Learn more here

You can add PRIVATE, but main.cpp still builds…

add_library(camera camera.cpp)
add_executable(main main.cpp)
...
target_link_libraries(camera PRIVATE ${OpenCV_LIBS})
target_link_libraries(main camera)

This is because inside main.cpp, you are including camera.hpp, which has already included the OPENCV header files. However, if you remove the include "camera.hpp", you’ll see that this won’t compile anymore

  • umm there’s some iffy behavior where this compiles
#include <opencv2/opencv.hpp>
 
int main() {
}

But not this?

#include <opencv2/opencv.hpp>
 
int main() {
	cv::Mat image;
}

Important

Simply including a header file does not require linking against the library.

When it comes to using the code, it will break unless you link with it in one way or the other.

Modern Cmake

Shown to me by Kajanan. See Modern CMake