In the previous chapter, CI Workflows, we learned how Praktika schedules work based on events like Pull Requests. The first and most important task in almost any workflow is compiling the code.
But simply saying "Compile ClickHouse" is not enough. Are we building it for Linux or macOS? Do we want it to run fast (Release) or do we want to hunt for bugs (Debug)?
This brings us to the Build Configuration.
Imagine you are building a LEGO castle.
.cpp).If you just have a pile of bricks without instructions, you cannot build the castle. C++ is similar. You have thousands of files, and the compiler (the tool that turns code into software) needs to know exactly how to fit them together.
The Challenge: ClickHouse is massive. We cannot type a manual command to compile 5,000 files every time. We also need different "flavors" of the database:
Central Use Case: We want to define a "Recipe" that allows us to switch between a Release build and a Sanitizer build by changing just one setting, without rewriting the whole build script.
To manage this complexity, ClickHouse uses CMake.
This is the master file found at the root of the project. Think of it as the Table of Contents for the build. It tells the system: "Here is the project name, and here are the folders you need to look into."
cmake/ DirectoryThis folder contains specialized logic. It holds smaller configuration files that handle specific tasks, like "Find the Linux libraries" or "Setup the Clang compiler."
These are small switches passed to the compiler.
-O3: "Optimize this code to run as fast as possible."-g: "Add debug information so I can read the crash logs."Sanitizers are like X-ray glasses for code. When enabled (e.g., AddressSanitizer), they make the program slower, but if the program touches memory it shouldn't, it crashes immediately with a detailed report.
The configuration is primarily handled in CMakeLists.txt. We use CMake commands to define our logic.
At the very top of the file, we set up the basics.
# Root CMakeLists.txt
cmake_minimum_required (VERSION 3.20)
project (ClickHouse)
# Tell CMake where to look for extra modules
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
Explanation: We declare the project name "ClickHouse" and tell CMake to look inside the cmake/ folder for helper scripts.
Here is how we solve our Central Use Case: allowing a switch for Sanitizers.
# Define an option that users can toggle (ON or OFF)
option (SANITIZE "Enable address sanitizer" OFF)
if (SANITIZE)
# If the option is ON, add these special flags
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif ()
Explanation:
SANITIZE.if (SANITIZE) checks if the user turned it on.add_compile_options tells the compiler to inject the "X-ray" code into the binary.Finally, we tell CMake where the actual C++ code lives.
# Go into the 'src' folder and process its CMakeLists.txt
add_subdirectory (src)
# Go into the 'programs' folder (where the main server code is)
add_subdirectory (programs)
Explanation: add_subdirectory tells CMake to descend into those folders and look for more CMakeLists.txt files, recursively building the whole tree.
When you run the build command, CMake does not actually compile the code itself. It is a Generator.
CMakeLists.txt.Ninja or Makefiles). These are the actual step-by-step instructions for the computer.ninja reads those generated files and runs the compiler (clang++).Here is the flow:
cmake/ Folder
ClickHouse is very strict about which compiler it uses (usually specific versions of Clang). This logic is hidden in cmake/.
For example, looking inside cmake/find_ccache.cmake (simplified):
# Try to find the 'ccache' program (speeds up recompilation)
find_program (CCACHE_FOUND ccache)
if (CCACHE_FOUND)
# If found, wrap the compiler with it
set_property (GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
message (STATUS "Using ccache to speed up build")
endif ()
Explanation: This script checks if the developer has ccache installed. If yes, it configures CMake to use it, which makes rebuilding the project much faster by caching previous results.
We also handle OS differences here. In cmake/linux.cmake:
if (OS_LINUX)
# Linux needs the 'pthread' library for threading
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
# We might want static linking on Linux for portability
set (CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif ()
Explanation: If CMake detects we are on Linux (OS_LINUX), it automatically adds the -pthread flag. This ensures the developer doesn't need to know the specific requirements of the OSβthe Build Configuration handles it.
In this chapter, we learned that Build Configuration acts as the blueprint for our software.
cmake/ folder to hide complex logic for compilers and platforms.Now that we have the recipe (Build Configuration), we need a robot to actually cook it in our CI environment.
In the next chapter, we will look at the Build Job Script, which is the actual shell script that runs these CMake commands inside our CI runners.
Next Chapter: Build Job Script
Generated by Code IQ