This document explains how Makefiles work, their workflow, and the role of dynamic link libraries (DLLs) in a C++ project. It includes examples and clarifies their build-time and runtime usage.
A Makefile automates the build process by specifying rules and dependencies for building a project. When you run make, it:
- Reads the first target (default).
- Resolves dependencies for that target.
- Executes rules (commands) to build required files.
Example Makefile:
# Compiler
CXX = g++
# Flags
CXXFLAGS = -Wall -Wextra -std=c++11
# Directories
SRC_DIR = src
BUILD_DIR = build
INCLUDE_DIR = include
LIB_DIR = lib
# Source files
SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp)
# Object files
OBJ_FILES = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(SRC_FILES))
# Targets
all: $(LIB_DIR)/trig.lib $(LIB_DIR)/compound_interest.dll calculator
calculator: $(OBJ_FILES)
$(CXX) $(CXXFLAGS) -o $@ $^ -L$(LIB_DIR)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(CXXFLAGS) -I$(INCLUDE_DIR) -c -o $@ $<
# Rule to create the compound interest DLL
$(LIB_DIR)/compound_interest.dll: $(BUILD_DIR)/compound_interest.o
$(CXX) -shared -o $@ $^
# Rule to create the trigonometric static library
$(LIB_DIR)/trig.lib: $(BUILD_DIR)/trig_operations.o
lib /OUT:$@ $^
clean:
rm -rf $(BUILD_DIR)/*.o calculator $(LIB_DIR)/compound_interest.dll $(LIB_DIR)/trig.lib
.PHONY: all clean-
Default Target: The first target
allis invoked by default:all: $(LIB_DIR)/trig.lib $(LIB_DIR)/compound_interest.dll calculator
- Dependencies like
trig.lib,compound_interest.dll, andcalculatorare built.
- Dependencies like
-
Building Dependencies:
- If
trig.libdoesn’t exist or is outdated, its rule is invoked:$(LIB_DIR)/trig.lib: $(BUILD_DIR)/trig_operations.o lib /OUT:$@ $^
- If
-
Building Executable:
- The
calculatortarget links all.ofiles and libraries:calculator: $(OBJ_FILES) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(LIB_DIR)
- The
-
Output:
makeproduces:lib/trig.lib(static library)lib/compound_interest.dll(shared library)calculator(executable).
Makefiles require tabs for indentation. To ensure proper formatting in VS Code:
-
Set Tabs in VS Code:
- Go to the bottom-right corner and select "Convert Indentation to Tabs".
-
Force Tabs for Makefiles:
- Add this to your VS Code settings (
settings.json):"[makefile]": { "editor.insertSpaces": false, "editor.tabSize": 4 }
- Add this to your VS Code settings (
-
Check Indentation:
- Enable "Render Whitespace" in VS Code to see if tabs (
→) are used.
- Enable "Render Whitespace" in VS Code to see if tabs (
-
Static Libraries (
trig.lib):- Bundled into the executable at compile time.
- Increases executable size but makes it self-contained.
-
Dynamic Libraries (
compound_interest.dll):- Referenced at runtime, not bundled into the executable.
- Reduces size and promotes modularity.
DLLs must still be built during the build process, even though they are used at runtime.
-
Building the DLL:
$(LIB_DIR)/compound_interest.dll: $(BUILD_DIR)/compound_interest.o $(CXX) -shared -o $@ $^
- Compiles
compound_interest.ointocompound_interest.dllusing the-sharedflag.
- Compiles
-
Runtime Usage:
- The executable (
calculator) references the DLL. - At runtime, the OS loads the DLL.
- The executable (
-
Build-Time:
- DLLs are compiled and generated.
- The Makefile ensures they are created and placed in the correct location.
-
Runtime:
- DLLs are loaded by the OS when the executable runs.
- They must be in the same directory as the executable or a directory in the system's
PATH.
- Source files:
src/*.cpp→build/*.o→calculator. - Static library:
src/trig_operations.cpp→build/trig_operations.o→lib/trig.lib. - Shared library:
src/compound_interest.cpp→build/compound_interest.o→lib/compound_interest.dll.
The clean target removes generated files:
clean:
rm -rf $(BUILD_DIR)/*.o calculator $(LIB_DIR)/compound_interest.dll $(LIB_DIR)/trig.libRun it with:
make cleanThis Makefile demonstrates:
- Automated builds using static and dynamic libraries.
- Understanding the difference between DLL usage at runtime vs. build-time.
- Proper formatting and debugging of Makefiles in tools like VS Code.
By mastering this workflow, you can handle complex C++ projects efficiently!