Build Tooling and Linking
7 pages · ~5 min✓ Reviewed
Start here. Next up: Basics, Pointers and References.
Build Pipeline Mental Model
1. Four Stages
- Preprocess.cpp → TU
- Compile→ .o object
- Assembleasm → machine code
- Linkobjects + libs → exe
- Each
.cppis compiled independently into its own translation unit (TU) - Only the linker sees across TUs
2. Inspect Each Stage
| Command | Stops after | You get |
|---|---|---|
g++ -E main.cpp | preprocess | expanded source (TU) |
g++ -S main.cpp | compile | assembly main.s |
g++ -c main.cpp | assemble | object main.o |
g++ main.cpp | link | full executable |
3. Declaration vs Definition
| Says | Needed by | |
|---|---|---|
| Declaration | a name exists: int f(int); | compile |
| Definition | body or storage: int f(int a){...} | link |
Which error is which
Compile error = syntax or type problem in one TU. Linker error = missing or duplicate symbol across TUs.
Preprocessor and Header Guards
1. Pure Text Substitution
- Runs before compilation; it only edits text
#includepastes the file's contents#definereplaces tokens#if/#ifdefselects which code is kept
2. Header Guards
cpp
#ifndef FOO_H #define FOO_H int add(int a, int b); #endif
stops the same header being pasted twice into one TU
| #ifndef guard | #pragma once | |
|---|---|---|
| Standard | yes | no, but universal |
| Copied or symlinked file | safe | can fail |
| Length | 4 lines | 1 line |
3. Macro Traps and Paths
Macros do not evaluate
#define SQ(x) x*x makes SQ(1+2) expand to 1+2*1+2 = 5. Write ((x)*(x)), or better use constexpr / inline.
ODR, inline and static
1. The One Definition Rule
- A non-inline function or variable is defined once in the whole program
- Break it: multiple definition error, or silent UB
- Never define a normal function or global in a header
- Each including TU gets its own copy, so the linker finds duplicates
2. Ways to Stay Safe
| Tool | Effect | Copies |
|---|---|---|
inline | identical definitions allowed | linker keeps one |
static / anonymous namespace | internal linkage, hidden from linker | one per TU |
extern int x; | declaration only | defined in one .cpp |
inlineis about linkage, not a speed hint- C++17 allows
inlinevariables too - Classes, templates,
constexprfunctions are ODR-safe in headers
3. In Practice
cpp
// config.h inline int limit = 10; // shared extern int count; // declares static int hits = 0; // per TU // config.cpp int count = 0; // the one definition
exactly one definition of count
Linker Errors and Libraries
1. Two Linker Errors
| Error | Cause | Check |
|---|---|---|
undefined reference to foo() | Declared, never defined; or .o/lib not passed | Signature and namespace match exactly |
multiple definition of foo | Header definition without inline; or .cpp linked twice | Add inline, or define once in a .cpp |
2. Link Order and Flags
- Libraries go after the objects that need them
-Ladds a library dir-lfoofindslibfoo.aorlibfoo.so
bash
g++ main.o -L. -lfoo
-lfoo comes after main.o
3. Static vs Shared
| Static .a | Shared .so/.dll | |
|---|---|---|
| Linked | Copied into the exe | Loaded at run time |
| Size | Larger | Smaller |
CMake and Compiler Flags
1. What CMake Does
- CMake generates Make or Ninja files
- It does not compile anything itself
- CMakeLists.txt
cmake -S . -B buildconfigure- Makefile / Ninja
cmake --build buildcompile + link
2. Targets
- Targets carry properties: sources, include dirs, links
-DCMAKE_BUILD_TYPE=Releasepicks Debug or Release
cmake
cmake_minimum_required(VERSION 3.20) project(app CXX) set(CMAKE_CXX_STANDARD 20) add_executable(app main.cpp util.cpp) add_library(foo STATIC foo.cpp) target_include_directories(foo PUBLIC include) target_link_libraries(app PRIVATE foo)
| Keyword | foo itself | Consumers of foo |
|---|---|---|
PUBLIC | yes | yes |
PRIVATE | yes | no |
INTERFACE | no |
Build Cheat Sheet
1. Stages and Headers
| Flag | Stops after |
|---|---|
g++ -E | preprocessed text |
g++ -S | assembly |
g++ -c | object file .o |
| no flag | full build and link |
2. Linker Triage
| Error | Cause |
|---|---|
undefined reference | missing definition or library |
multiple definition | header definition without inline, or duplicate source |
3. Libraries and CMake
- Static
.awithar; shared.soneeds -fPIC
Check yourself
With #define SQ(x) x*x, what does SQ(1+2) give?
- 5, not 9
- It expands to
1+2*1+2 - Fix:
((x)*(x))or an inline function
util.h has int count = 0;. Both a.cpp and b.cpp include it. Where does the build fail?
- Each file compiles fine
- The linker reports multiple definition of
count - Fix:
inline int count = 0;(C++17)
g++ -c main.cpp works, but g++ main.o -o app fails. main.cpp calls foo(), declared in a header. Why?
- Declaration satisfied the compiler; no definition was linked
- Error: undefined reference to
foo() - Add
foo.oor the library to the link