C++ CMake Complete Guide | Mastering Cross-Platform Build Systems
이 글의 핵심
CMake fundamentals: targets, generator expressions, install trees, and cross-platform project layout for C++ teams.
What is CMake and Why Do You Need It?
The Pain of Cross-Platform Builds
Scenario: You’ve developed a C++ project using Visual Studio on Windows and now need to deploy it to a Linux server. On Windows, you build using .vcxproj files, but on Linux, you need to write a Makefile. If you also want to test on macOS, you’ll need to create an Xcode project. Managing build configurations for each platform separately can quickly turn into a maintenance nightmare.
Solution: CMake allows you to write a platform-independent build configuration (CMakeLists.txt) that automatically generates platform-specific build systems (Makefile, Visual Studio projects, Ninja, etc.). Write it once, build it anywhere.
flowchart LR
subgraph input["Input"]
cmake["CMakeLists.txt"]
end
subgraph cmake_tool["CMake"]
gen["Generator"]
end
subgraph output["Output"]
make["Makefile (Linux)"]
vs["Visual Studio (Windows)"]
xcode["Xcode (macOS)"]
ninja["Ninja (All Platforms)"]
end
cmake --> gen
gen --> make
gen --> vs
gen --> xcode
gen --> ninja
Core Concepts of CMake
- Build System Generator: CMake doesn’t build your project directly. Instead, it generates platform-specific build systems (e.g., Makefile, .sln).
- Target-Based: Use
add_executableandadd_libraryto define build targets, andtarget_link_librariesandtarget_include_directoriesto specify dependencies. - Variables and Cache: Use
set()to define variables andoption()to provide user-configurable options.
Table of Contents
- Minimal CMakeLists.txt
- Targets: Executables and Libraries
- Finding External Libraries: find_package
- Build Types and Compiler Options
- Project Structure and Subdirectories
- Common Issues and Solutions
- Production Patterns
- Complete Example: Multi-Target Project
- Performance Optimization
- CMake Adoption Checklist
1. Minimal CMakeLists.txt
Building a Hello World
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyProject)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(myapp main.cpp)
# Create a build directory (out-of-source build)
mkdir build
cd build
# Run CMake (generate the build system)
cmake ..
# Build (compile using the generated build system)
cmake --build .
# Or directly use make (Linux/macOS)
make
# Run the executable
./myapp
Key Commands Explained
| Command | Description |
|---|---|
cmake_minimum_required(VERSION 3.20) | Specifies the minimum required version of CMake |
project(MyProject) | Sets the project name |
set(CMAKE_CXX_STANDARD 20) | Specifies the use of C++20 standard |
add_executable(myapp main.cpp) | Creates an executable target |
cmake .. | Generates the build system using the parent directory’s CMakeLists.txt |
cmake --build . | Platform-independent build command |
2. Targets: Executables and Libraries
Executables
# Single source file
add_executable(myapp main.cpp)
# Multiple source files
add_executable(myapp
src/main.cpp
src/utils.cpp
src/config.cpp
)
# Using variables
set(SOURCES
src/main.cpp
src/utils.cpp
)
add_executable(myapp ${SOURCES})
Libraries
# Static library (.a, .lib)
add_library(mylib STATIC
src/lib.cpp
src/helper.cpp
)
# Shared library (.so, .dll)
add_library(mylib SHARED
src/lib.cpp
src/helper.cpp
)
# Header-only library
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include)
# Link library to executable
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
PUBLIC vs PRIVATE vs INTERFACE
# PRIVATE: Used only within the target
target_include_directories(mylib PRIVATE src/internal)
# PUBLIC: Used by the target and any target linking to it
target_include_directories(mylib PUBLIC include)
# INTERFACE: Used only by targets linking to this target (for header-only libraries)
target_include_directories(mylib INTERFACE include)
Practical Example: If mylib internally uses src/internal/impl.h, it should be added as PRIVATE. Headers exposed to external projects, like include/mylib/api.h, should be added as PUBLIC. When myapp links to mylib, it will only have access to the PUBLIC headers.
3. Finding External Libraries: find_package
Boost Example
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Boost::filesystem Boost::system)
OpenSSL Example
find_package(OpenSSL REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
Handling Missing Packages
find_package(SomeLib)
if(SomeLib_FOUND)
target_link_libraries(myapp PRIVATE SomeLib::SomeLib)
else()
message(WARNING "SomeLib not found, using fallback")
endif()
Using pkg-config
find_package(PkgConfig REQUIRED)
pkg_check_modules(CURL REQUIRED libcurl)
target_link_libraries(myapp PRIVATE ${CURL_LIBRARIES})
target_include_directories(myapp PRIVATE ${CURL_INCLUDE_DIRS})
4. Build Types and Compiler Options
Build Types
# Set default build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Flags for each build type
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
# Debug build
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Release build
cmake -DCMAKE_BUILD_TYPE=Release ..
# Optimized build with debug info
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
Target-Specific Compiler Options
add_executable(myapp main.cpp)
# Enable warnings
target_compile_options(myapp PRIVATE
-Wall -Wextra -Wpedantic
$<$<CONFIG:Debug>:-Werror> # Treat warnings as errors in Debug mode
)
# Preprocessor definitions
target_compile_definitions(myapp PRIVATE
APP_VERSION="1.0"
$<$<CONFIG:Debug>:DEBUG_MODE>
)
# Include directories
target_include_directories(myapp PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/generated
)
Generator Expressions
# Conditional flags based on build type
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3>
)
# Conditional flags based on compiler
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-fno-exceptions>
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>
)
(Translation continues with the same structure for the remaining sections.)
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ CMake Targets 완벽 가이드 | 타겟 기반 빌드 시스템
- C++ CMake find_package 완벽 가이드 | 외부 라이브러리 통합
- C++ Conan 완벽 가이드 | 현대적인 C++ 패키지 관리
- C++ vcpkg 완벽 가이드 | Microsoft C++ 패키지 관리자
이 글에서 다루는 키워드 (관련 검색어)
C++, cmake, build, makefile, tools, cross-platform 등으로 검색하시면 이 글이 도움이 됩니다.