CMake: Local Build

make: Problems

  • Writing a Makefile is tedious

  • “Assembly language of build systems”

  • Way too much boilerplate

  • ⟶ Targets all, clean, install

  • Error prone

  • Unmaintainable!!

Missing features …

  • Out-of-source build

  • Toolchain-independent build descriptions

  • Packaging

  • Dependency management

CMake To The Rescue

  • CMake: higher level build system

  • Generates Makefile (and files for other low level build tools, like Ninja)

  • CMakeLists.txt instead of Makefile

  • Written in a dedicated “language”

Listing of the project directory

../jfasch-home-linux-toolchain/cmake/
total 32
-rw-r--r--. 1 jfasch jfasch 303 Jun  2  2022 CMakeLists.txt
-rw-r--r--. 1 jfasch jfasch  89 Jun 26  2022 hello.c
-rw-r--r--. 1 jfasch jfasch  81 Jun 26  2022 hello-first.c
-rw-r--r--. 1 jfasch jfasch 122 Jun 26  2022 hello-flexible.c
-rw-r--r--. 1 jfasch jfasch  98 Jun 26  2022 hello-flexible.h
-rw-r--r--. 1 jfasch jfasch  59 Jun 26  2022 hello.h
-rw-r--r--. 1 jfasch jfasch 269 Jun 26  2022 hello-second.c
-rw-r--r--. 1 jfasch jfasch 746 Apr 26  2022 Toolchain-RaspberryPi.cmake
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
PROJECT(Toolchain-CMake-Demo LANGUAGES C)

ADD_LIBRARY(greet hello.h hello.c hello-flexible.c)

ADD_EXECUTABLE(hello-first hello-first.c)
TARGET_LINK_LIBRARIES(hello-first greet)

ADD_EXECUTABLE(hello-second hello-second.c)
TARGET_LINK_LIBRARIES(hello-second greet)

Out-Of-Source Build

  • Build artifacts (object files, libraries, executables) pollute source directory

  • Hiccups with version control systems ⟶ e.g. .gitignore

  • ⟶ CMake can build into a separate directory, the build directory

For the remainder, lets define these directories …

Source directory

/home/jfasch/source

Build directory

/home/jfasch/build

Step 1: Generate Makefile In Build Directory

$ cd /home/jfasch/build              # <--- CWD is the *build* directory
$ cmake /home/jfasch/source          # <--- parameter is the *source* directory
-- The C compiler identification is GNU 11.2.1
-- The CXX compiler identification is GNU 11.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jfasch/build

Note

Please be careful to pass the source directory to cmake, not the CMakeLists.txt file in that directory! (CMake builds in the source if you pass the file)

This creates a Makefile in the build directory

  • Used as usual ⟶ make

  • The file is generated ⟶ basically unreadable

Step 2: Build Using make

$ pwd                               # <--- CWD is the *build* directory when you call make
/home/jfasch/build
$ make
[ 14%] Building C object CMakeFiles/greet.dir/hello.c.o
[ 28%] Building C object CMakeFiles/greet.dir/hello-flexible.c.o
[ 42%] Linking C static library libgreet.a
[ 42%] Built target greet
[ 57%] Building C object CMakeFiles/hello-second.dir/hello-second.c.o
[ 71%] Linking C executable hello-second
[ 71%] Built target hello-second
[ 85%] Building C object CMakeFiles/hello-first.dir/hello-first.c.o
[100%] Linking C executable hello-first
[100%] Built target hello-first

Et voila:

$ ls -l
total 96
-rw-rw-r--. 1 jfasch jfasch 13921 Apr 22 10:58 CMakeCache.txt
drwxrwxr-x. 7 jfasch jfasch   280 Apr 22 11:02 CMakeFiles
-rw-rw-r--. 1 jfasch jfasch  1688 Apr 22 10:58 cmake_install.cmake
-rwxrwxr-x. 1 jfasch jfasch 26192 Apr 22 11:02 hello-first
-rwxrwxr-x. 1 jfasch jfasch 27920 Apr 22 11:02 hello-second
-rw-rw-r--. 1 jfasch jfasch  8102 Apr 22 11:02 libgreet.a
-rw-rw-r--. 1 jfasch jfasch  8503 Apr 22 10:58 Makefile

Goodie: Dependency Management

  • Executables depend on libraries

    ADD_LIBRARY(greet hello.h hello.c hello-flexible.c)
    
    ADD_EXECUTABLE(hello-first hello-first.c)
    TARGET_LINK_LIBRARIES(hello-first greet)
    
    ADD_EXECUTABLE(hello-second hello-second.c)
    TARGET_LINK_LIBRARIES(hello-second greet)
    
  • These relationships are not always so simple

  • Directed acyclic graph

  • ⟶ want to be visualized

  • Graphviz package

    Debian/Ubuntu
    # apt install graphviz
    
    Fedora
    # dnf install graphviz
    
  • During Makefile generation, pass the --graphviz option ⟶ .dot file

    $ pwd
    /home/jfasch/build
    $ cmake --graphviz=dependencies.dot /home/jfasch/source
    ... roedel ...
    
  • Massage the .dot file, e.g. turning it into a .png (or a .pdf, …)

    $ dot -Tpng dependencies.dot > dependencies.png
    
    digraph "Toolchain-CMake-Demo" {
node [
  fontsize = "12"
];
subgraph clusterLegend {
  label = "Legend";
  color = black;
  edge [ style = invis ];
  legendNode0 [ label = "Executable", shape = egg ];
  legendNode1 [ label = "Static Library", shape = octagon ];
  legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
  legendNode3 [ label = "Module Library", shape = tripleoctagon ];
  legendNode4 [ label = "Interface Library", shape = pentagon ];
  legendNode5 [ label = "Object Library", shape = hexagon ];
  legendNode6 [ label = "Unknown Library", shape = septagon ];
  legendNode7 [ label = "Custom Target", shape = box ];
  legendNode0 -> legendNode1 [ style = solid ];
  legendNode0 -> legendNode2 [ style = solid ];
  legendNode0 -> legendNode3;
  legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
  legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
  legendNode3 -> legendNode6 [ style = solid ];
  legendNode0 -> legendNode7;
}
    "node0" [ label = "greet", shape = octagon ];
    "node1" [ label = "hello-first", shape = egg ];
    "node1" -> "node0" [ style = dotted ] // hello-first -> greet
    "node2" [ label = "hello-second", shape = egg ];
    "node2" -> "node0" [ style = dotted ] // hello-second -> greet
}
cluster_linux Linux cluster_linux_toolchain Toolchain, And Cross Development cluster_linux_basics Linux Basics cluster_linux_basics_shell The Shell (Bash - “Bourne Again Shell”) cluster_linux_basics_intro Introduction: Concepts and Terminology linux_toolchain_static_library Object Code Archives/Static Libraries linux_toolchain_separate_compilation Zooming In: Separate Compilation, and Linking Statically linux_toolchain_static_library->linux_toolchain_separate_compilation linux_toolchain_basics Toolchain: Basics linux_toolchain_separate_compilation->linux_toolchain_basics linux_basics_shell_file_dir_create_rm Creating And Removing Files and Directories linux_toolchain_basics->linux_basics_shell_file_dir_create_rm linux_toolchain_cmake_local CMake: Local Build linux_toolchain_cmake_local->linux_toolchain_static_library linux_basics_shell_commandline Commandline linux_basics_intro_overview Overview linux_basics_shell_commandline->linux_basics_intro_overview linux_basics_shell_cwd Current Working Directory linux_basics_shell_cwd->linux_basics_shell_commandline linux_basics_shell_paths Absolute and Relative Paths linux_basics_shell_cwd->linux_basics_shell_paths linux_basics_intro_process Processes, Scheduling, Address Spaces linux_basics_shell_cwd->linux_basics_intro_process linux_basics_shell_paths->linux_basics_shell_commandline linux_basics_shell_file_dir_create_rm->linux_basics_shell_cwd linux_basics_shell_file_dir_create_rm->linux_basics_shell_paths linux_basics_intro_process->linux_basics_intro_overview