Ver3.5 ๐๏ธ C++ ๋น๋ ์์คํ ๊ณผ CMake ยท Make๋ฅผ ํ์ฉํ ํ๋ก์ ํธ ๋น๋ ๊ด๋ฆฌ ์์ ์ ๋ณต

๐๏ธ C++ ๋น๋ ์์คํ ๊ณผ CMake ยท Make๋ฅผ ํ์ฉํ ํ๋ก์ ํธ ๋น๋ ๊ด๋ฆฌ ์์ ์ ๋ณต
๋ณต์กํ ๋น๋ ์ธ๊ณ, ์ด์ ์ฝ๊ณ ์ฌ๋ฏธ์๊ฒ ๋ง์คํฐํด๋ณด์! ๐
๋ง์, ์ฒ์ C++ ๋ฐฐ์ธ ๋๋ ๊ทธ๋ฅ g++ main.cpp -o myapp ์ด๋ ๊ฒ ํ๋ฉด ๋์ด์์.
๊ทผ๋ฐ ์ค์ ํ๋ก์ ํธ๋ ๊ทธ๋ ๊ฒ ๋จ์ํ์ง ์์. ํ์ผ์ด ์์ญ ๊ฐ, ์๋ฐฑ ๊ฐ๊ฐ ๋๊ณ , ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ถ๊ณ , ํ๋ซํผ๋ ๋ฌ๋ผ์ง๊ณ ... ์ด๊ฑธ ์ผ์ผ์ด ์์ผ๋ก ๊ด๋ฆฌํ๋ฉด ์ง์ง ์ง์ฅ์ด ๋ฐ๋ก ์์ ๐ฑ
๋น๋ ์์คํ
(Build System)์ด๋ ์์ค ์ฝ๋๋ฅผ ์คํ ๊ฐ๋ฅํ ํ๋ก๊ทธ๋จ์ผ๋ก ๋ณํํ๋ ์ ์ฒด ๊ณผ์ ์ ์๋ํํ๊ณ ๊ด๋ฆฌํ๋ ๋๊ตฌ์ผ.
๋จ์ํ ์ปดํ์ผ๋ง ํ๋ ๊ฒ ์๋๋ผ, ์์กด์ฑ ์ถ์ , ์ฆ๋ถ ๋น๋(๋ณ๊ฒฝ๋ ํ์ผ๋ง ์ฌ์ปดํ์ผ), ๋งํน, ์ค์น ๋ฑ ๋ชจ๋ ๊ณผ์ ์ ์ฒด๊ณ์ ์ผ๋ก ์ฒ๋ฆฌํด์ค.
โ ์์กด์ฑ ๊ด๋ฆฌ : A.cpp๊ฐ B.h๋ฅผ includeํ๋ฉด, B.h๊ฐ ๋ฐ๋ ๋ A.cpp๋ ์ฌ์ปดํ์ผํด์ผ ํด
โก ์ฆ๋ถ ๋น๋ : 1000๊ฐ ํ์ผ ์ค 1๊ฐ๋ง ๋ฐ๊ฟจ๋๋ฐ ์ ์ฒด ๋ค์ ๋น๋ํ๋ฉด ์๊ฐ ๋ญ๋น ๊ทน์ฌ
โข ํฌ๋ก์ค ํ๋ซํผ : Windows, Linux, macOS์์ ๋์ผํ ์ฝ๋๋ฅผ ๋น๋ํด์ผ ํ ๋
โฃ ๋น๋ ์ค์ ๊ด๋ฆฌ : Debug/Release ๋ชจ๋, ์ปดํ์ผ ์ต์ , ๋งํฌ ์ต์ ๋ฑ ์ฒด๊ณ์ ๊ด๋ฆฌ
C++ ์ํ๊ณ์์ ๋ํ์ ์ธ ๋น๋ ๋๊ตฌ๋ค์ ๊ฐ๋จํ ์ดํด๋ณด๋ฉด:
| ๋๊ตฌ | ์ญํ | ํน์ง |
|---|---|---|
| Make | ๋น๋ ์๋ํ | ์ ๋์ค ์ ํต์ ๋น๋ ๋๊ตฌ, Makefile ๊ธฐ๋ฐ |
| CMake | ๋น๋ ์์คํ ์์ฑ๊ธฐ | ํฌ๋ก์ค ํ๋ซํผ, Makefile/Ninja ๋ฑ ์์ฑ |
| Ninja | ๋น๋ ์๋ํ | Make๋ณด๋ค ๋น ๋ฅธ ๋น๋ ์๋ |
| Bazel | ๋น๋ ์๋ํ | Google ๊ฐ๋ฐ, ๋๊ท๋ชจ ํ๋ก์ ํธ์ฉ |
| Meson | ๋น๋ ์์คํ ์์ฑ๊ธฐ | Python ๊ธฐ๋ฐ, ๋น ๋ฅด๊ณ ํ๋์ |
์ด ์ค์์ ์ค๋ ์ฐ๋ฆฌ๊ฐ ์ง์ค์ ์ผ๋ก ํ๋ณผ ๊ฑด Make์ CMake์ผ.
์ด ๋ ๊ฐ๊ฐ C++ ์ธ๊ณ์์ ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ ์กฐํฉ์ด๊ฑฐ๋ . ํนํ ์คํ์์ค ํ๋ก์ ํธ์ ๋๋ถ๋ถ์ด CMake๋ฅผ ์ฌ์ฉํ๊ณ ์์ด์ ์ด๊ฑธ ๋ชจ๋ฅด๋ฉด ๋จ์ ์ฝ๋ ๋น๋ํ๋ ๊ฒ๋ ํ๋ค์ด ใ
ใ
ใ
Make๋ 1976๋
์ Stuart Feldman์ด ๋ง๋ ์ ์ ๊น์ ๋น๋ ๋๊ตฌ์ผ. ๊ฑฐ์ 50๋
๋ ๋๊ตฌ์ธ๋ฐ ์์ง๋ ํ์ญ์ด๋ผ๋ ๊ฒ ๋ ์ ๋ ใทใท
GNU Make๋ ๊ทธ ์ค์์๋ ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ ๋ฒ์ ์ผ๋ก, Linux/macOS ํ๊ฒฝ์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋ผ.
Make๋ Makefile์ด๋ผ๋ ํ์ผ์ ์ฝ์ด์ ๋น๋๋ฅผ ์ํํด.
Makefile์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ ํ๊ฒ(target) : ์์กด์ฑ(dependencies) ํํ์ผ.
# Makefile ๊ธฐ๋ณธ ๋ฌธ๋ฒ ํ๊ฒ : ์์กด์ฑ1 ์์กด์ฑ2 ๋ช ๋ น์ด โ ๋ฐ๋์ ํญ(Tab)์ผ๋ก ๋ค์ฌ์ฐ๊ธฐ! # ์์ myapp : main.o utils.o g++ -o myapp main.o utils.o main.o : main.cpp utils.h g++ -c main.cpp utils.o : utils.cpp utils.h g++ -c utils.cpp
๋ช ๋ น์ด ์์ ๋ค์ฌ์ฐ๊ธฐ๋ ๋ฐ๋์ ํญ(Tab) ๋ฌธ์์ฌ์ผ ํด. ์คํ์ด์ค๋ก ํ๋ฉด ์๋ฌ๋จ. ์ด๊ฑฐ ๋๋ฌธ์ ์ด๋ณด์๋ค์ด ์ง์ง ๋ง์ด ์ฝ์งํจ ใ ใ ใ ์๋ํฐ ์ค์ ๊ผญ ํ์ธํด!
์ค์ ํ๋ก์ ํธ์์ ์ธ ์ ์๋ ์ข ๋ ์์ฑ๋ ์๋ Makefile์ ๋ง๋ค์ด๋ณด์.
๋ค์๊ณผ ๊ฐ์ ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํ ๊ฒ:
# ํ๋ก์ ํธ ๊ตฌ์กฐ myproject/ โโโ src/ โ โโโ main.cpp โ โโโ calculator.cpp โ โโโ utils.cpp โโโ include/ โ โโโ calculator.h โ โโโ utils.h โโโ build/ โโโ Makefile
# ์ค์ Makefile ์์ # ๋ณ์ ์ ์ CXX = g++ CXXFLAGS = -std=c++17 -Wall -Wextra -I./include LDFLAGS = TARGET = myapp BUILD_DIR = build SRC_DIR = src # ์์ค ํ์ผ ์๋ ํ์ SRCS = $(wildcard $(SRC_DIR)/*.cpp) # SRCS = src/main.cpp src/calculator.cpp src/utils.cpp # ์ค๋ธ์ ํธ ํ์ผ ๊ฒฝ๋ก ๋ณํ OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(SRCS)) # ๊ธฐ๋ณธ ํ๊ฒ (all) .PHONY: all clean debug release all: $(BUILD_DIR)/$(TARGET) # ๋งํน ๋จ๊ณ $(BUILD_DIR)/$(TARGET): $(OBJS) @mkdir -p $(BUILD_DIR) $(CXX) $(LDFLAGS) -o $@ $^ @echo "๋น๋ ์๋ฃ! โ $@" # ์ปดํ์ผ ๋จ๊ณ (ํจํด ๊ท์น) $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp @mkdir -p $(BUILD_DIR) $(CXX) $(CXXFLAGS) -c $< -o $@ # ๋๋ฒ๊ทธ ๋น๋ debug: CXXFLAGS += -g -DDEBUG debug: all # ๋ฆด๋ฆฌ์ฆ ๋น๋ release: CXXFLAGS += -O2 -DNDEBUG release: all # ํด๋ฆฐ clean: rm -rf $(BUILD_DIR) @echo "๋น๋ ํ์ผ ์ญ์ ์๋ฃ!" # ์์กด์ฑ ์๋ ์์ฑ (ํค๋ ๋ณ๊ฒฝ ๊ฐ์ง) -include $(OBJS:.o=.d) $(BUILD_DIR)/%.d: $(SRC_DIR)/%.cpp @mkdir -p $(BUILD_DIR) $(CXX) $(CXXFLAGS) -MM -MT $(@:.d=.o) $< > $@
| ๋ฌธ๋ฒ | ์๋ฏธ | ์์ |
|---|---|---|
$@ |
ํ์ฌ ํ๊ฒ ์ด๋ฆ | myapp |
$^ |
๋ชจ๋ ์์กด์ฑ ๋ชฉ๋ก | main.o utils.o |
$< |
์ฒซ ๋ฒ์งธ ์์กด์ฑ | main.cpp |
$(wildcard *.cpp) |
ํจํด ๋งค์นญ ํ์ผ ๋ชฉ๋ก | ๋ชจ๋ .cpp ํ์ผ |
$(patsubst ...) |
ํจํด ์นํ | .cpp โ .o ๋ณํ |
.PHONY |
๊ฐ์ง ํ๊ฒ ์ ์ธ | clean, all ๋ฑ |
@ ์ ๋์ฌ |
๋ช ๋ น์ด ์ถ๋ ฅ ์จ๊ธฐ๊ธฐ | @echo "์๋ฃ" |
๋ง์ฝ ํ๋ก์ ํธ ํด๋์
clean์ด๋ผ๋ ์ด๋ฆ์ ํ์ผ์ด ์ค์ ๋ก ์กด์ฌํ๋ฉด, Make๋ "์ด๋ฏธ ์ต์ ์ํ"๋ผ๊ณ ํ๋จํด์ clean ๋ช
๋ น์ ์คํ ์ ํด๋ฒ๋ ค..PHONY: clean์ผ๋ก ์ ์ธํ๋ฉด "์ด๊ฑด ํ์ผ ์ด๋ฆ์ด ์๋๋ผ ๋ช
๋ น์ด์ผ!"๋ผ๊ณ ์๋ ค์ฃผ๋ ๊ฑฐ์ผ.
Make์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ๋ณ๋ ฌ ๋น๋์ผ. -j ์ต์
์ผ๋ก ๋์์ ์ฌ๋ฌ ํ์ผ์ ์ปดํ์ผํ ์ ์์ด:
# CPU ์ฝ์ด ์๋งํผ ๋ณ๋ ฌ ๋น๋ make -j4 # 4๊ฐ ๋ณ๋ ฌ make -j$(nproc) # CPU ์ฝ์ด ์ ์๋ ๊ฐ์ง (Linux) make -j8 # 8๊ฐ ๋ณ๋ ฌ # ๋น๋ ์งํ ์ํฉ ํ์ธ make -j4 VERBOSE=1
ํ์ผ์ด ๋ง์์๋ก ๋ณ๋ ฌ ๋น๋์ ํจ๊ณผ๊ฐ ๊ทน์ ์ผ๋ก ๋ํ๋. 100๊ฐ ํ์ผ ๋น๋ํ ๋ -j8 ์ต์ ์ฐ๋ฉด ๊ฑฐ์ 8๋ฐฐ ๋น ๋ฅด๊ฒ ๋น๋๋๋ ๊ฑฐ ์ฒด๊ฐํ ์ ์์ด ใทใท
CMake๋ 2000๋
์ Kitware์์ ๊ฐ๋ฐํ ํฌ๋ก์ค ํ๋ซํผ ๋น๋ ์์คํ
์์ฑ๊ธฐ์ผ.
์ค์ํ ํฌ์ธํธ๋ CMake ์์ฒด๊ฐ ๋น๋๋ฅผ ํ๋ ๊ฒ ์๋๋ผ, ๋ค๋ฅธ ๋น๋ ์์คํ
(Make, Ninja, Visual Studio ๋ฑ)์ ์ํ ๋น๋ ํ์ผ์ ์์ฑํด์ค๋ค๋ ๊ฑฐ์ผ.
# Ubuntu/Debian sudo apt-get install cmake # macOS (Homebrew) brew install cmake # Windows (winget) winget install Kitware.CMake # ๋ฒ์ ํ์ธ cmake --version # cmake version 3.28.1 (์์) # ์ต์ ๋ฒ์ ์ 3.15 ์ด์ ๊ถ์ฅ (Modern CMake)
CMake ํ๋ก์ ํธ์ ํต์ฌ์ CMakeLists.txt ํ์ผ์ด์ผ.
ํ๋ก์ ํธ ๋ฃจํธ์ ๋ฐ๋์ ์์ด์ผ ํ๊ณ , ์๋ธ ๋๋ ํ ๋ฆฌ์๋ ์ถ๊ฐ๋ก ๋ง๋ค ์ ์์ด.
# CMakeLists.txt ๊ธฐ๋ณธ ์์
# CMake ์ต์ ๋ฒ์ ์๊ตฌ์ฌํญ (ํ์!)
cmake_minimum_required(VERSION 3.15)
# ํ๋ก์ ํธ ์ ์ธ
project(MyProject
VERSION 1.0.0
DESCRIPTION "๋์ ์ฒซ CMake ํ๋ก์ ํธ"
LANGUAGES CXX
)
# C++ ํ์ค ์ค์
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ์คํ ํ์ผ ํ๊ฒ ์์ฑ
add_executable(myapp
src/main.cpp
src/calculator.cpp
src/utils.cpp
)
# ํค๋ ํ์ผ ๊ฒฝ๋ก ์ถ๊ฐ
target_include_directories(myapp PRIVATE
${CMAKE_SOURCE_DIR}/include
)
# ์ปดํ์ผ ์ต์
์ถ๊ฐ
target_compile_options(myapp PRIVATE
-Wall
-Wextra
)
mkdir build && cd build์์ค ๋๋ ํ ๋ฆฌ๋ฅผ ๊นจ๋ํ๊ฒ ์ ์งํ๋ ๋ชจ๋ฒ ์ฌ๋ก์ผ. ๋น๋ ํ์ผ๋ค์ด src ํด๋์ ์์ด์ง ์์์ ๊น๋ํจ ๐
cmake .. ๋๋ cmake -S . -B buildCMakeLists.txt๋ฅผ ์ฝ์ด์ Makefile(๋๋ ๋ค๋ฅธ ๋น๋ ํ์ผ)์ ์์ฑํด
cmake --build . ๋๋ make์์ฑ๋ ๋น๋ ํ์ผ๋ก ์ค์ ์ปดํ์ผ ๋ฐ ๋งํน ์ํ
cmake --install . --prefix /usr/local๋น๋๋ ํ์ผ์ ์์คํ ์ ์ค์น
# ์ค์ ๋น๋ ๋ช ๋ น์ด ํ๋ฆ # ๋ฐฉ๋ฒ 1: ์ ํต์ ์ธ ๋ฐฉ์ mkdir build cd build cmake .. make -j4 # ๋ฐฉ๋ฒ 2: ํ๋์ ์ธ ๋ฐฉ์ (CMake 3.13+) cmake -S . -B build cmake --build build --parallel 4 # ๋ฐฉ๋ฒ 3: ๋น๋ ํ์ ์ง์ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --config Release # ๋ฐฉ๋ฒ 4: Ninja ์ฌ์ฉ cmake -S . -B build -G Ninja cmake --build build
CMake 3.0 ์ดํ๋ถํฐ Modern CMake๋ผ๋ ํจ๋ฌ๋ค์์ด ๋ฑ์ฅํ์ด.
ํต์ฌ ๊ฐ๋
์ "ํ๊ฒ(Target) ์ค์ฌ์ผ๋ก ์๊ฐํ๋ผ"๋ ๊ฑฐ์ผ.
์์ ๋ฐฉ์(Old CMake)๊ณผ ๋น๊ตํด๋ณด๋ฉด ์ฐจ์ด๊ฐ ํ ๋๊ปด์ ธ:
# โ Old CMake ๋ฐฉ์ (๋น์ถ์ฒ) include_directories(include) # ์ ์ญ์ผ๋ก ์ํฅ add_definitions(-DDEBUG) # ์ ์ญ์ผ๋ก ์ํฅ link_libraries(mylib) # ์ ์ญ์ผ๋ก ์ํฅ # โ Modern CMake ๋ฐฉ์ (๊ถ์ฅ) target_include_directories(myapp PRIVATE include) target_compile_definitions(myapp PRIVATE DEBUG) target_link_libraries(myapp PRIVATE mylib)
Modern CMake์์ ๊ฐ์ฅ ์ค์ํ ๊ฐ๋ ์ค ํ๋์ผ. ์ด๊ฑธ ์ดํดํ๋ฉด ์์กด์ฑ ์ ํ๋ฅผ ์ ๋๋ก ์ ์ดํ ์ ์์ด:
| ํค์๋ | ํ์ฌ ํ๊ฒ | ์์กดํ๋ ํ๊ฒ | ์ฌ์ฉ ์์ |
|---|---|---|---|
| PRIVATE | โ ์ ์ฉ | โ ๋ฏธ์ ํ | ๋ด๋ถ ๊ตฌํ์๋ง ํ์ํ ๋ |
| PUBLIC | โ ์ ์ฉ | โ ์ ํ | ํค๋์์๋ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ |
| INTERFACE | โ ๋ฏธ์ ์ฉ | โ ์ ํ | ํค๋ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ |
# PRIVATE / PUBLIC / INTERFACE ์ค์ ์์
# ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ฑ
add_library(mylib STATIC
src/mylib.cpp
)
# mylib์ include ๋๋ ํ ๋ฆฌ ์ค์
target_include_directories(mylib
PUBLIC ${CMAKE_SOURCE_DIR}/include # ์ธ๋ถ์๋ ๊ณต๊ฐ
PRIVATE ${CMAKE_SOURCE_DIR}/src # ๋ด๋ถ์์๋ง ์ฌ์ฉ
)
# ์คํ ํ์ผ์ด mylib์ ๋งํฌ
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# myapp์ mylib์ PUBLIC include ๊ฒฝ๋ก๋ฅผ ์๋์ผ๋ก ์์๋ฐ์!
# ์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (.a / .lib)
add_library(mystaticlib STATIC
src/lib1.cpp
src/lib2.cpp
)
# ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (.so / .dll)
add_library(mysharedlib SHARED
src/lib1.cpp
src/lib2.cpp
)
# ํค๋ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์ปดํ์ผ ์์)
add_library(myheaderlib INTERFACE)
target_include_directories(myheaderlib INTERFACE
${CMAKE_SOURCE_DIR}/include
)
# BUILD_SHARED_LIBS ๋ณ์๋ก ๊ธฐ๋ณธ ํ์
์ ์ด
# cmake -DBUILD_SHARED_LIBS=ON ..
add_library(mylib # ํ์
๋ฏธ์ง์ ์ BUILD_SHARED_LIBS ๋ฐ๋ฆ
src/lib.cpp
)
CMake์ ๋ณ์ ์์คํ
์ ์ฒ์์ ์ข ํท๊ฐ๋ฆด ์ ์์ด. ์ผ๋ฐ ๋ณ์์ ์บ์ ๋ณ์๊ฐ ๋ฐ๋ก ์๊ฑฐ๋ ใ
ใ
ใ
์ ๋๋ก ์ดํดํ๋ฉด ์์ฒญ ๊ฐ๋ ฅํ ์ค์ ์์คํ
์ ๊ตฌ์ถํ ์ ์์ด.
# ๋ณ์ ์ค์
set(MY_VAR "Hello CMake")
set(MY_LIST item1 item2 item3)
# ๋ณ์ ์ฐธ์กฐ
message(STATUS "๋ณ์๊ฐ: ${MY_VAR}")
message(STATUS "๋ฆฌ์คํธ: ${MY_LIST}")
# ํ๊ฒฝ ๋ณ์ ์ ๊ทผ
message(STATUS "HOME: $ENV{HOME}")
# ์ ์ฉํ ๋ด์ฅ ๋ณ์๋ค
message(STATUS "์์ค ๋๋ ํ ๋ฆฌ: ${CMAKE_SOURCE_DIR}")
message(STATUS "๋น๋ ๋๋ ํ ๋ฆฌ: ${CMAKE_BINARY_DIR}")
message(STATUS "ํ์ฌ ์์ค: ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "ํ์ฌ ๋น๋: ${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "ํ๋ก์ ํธ๋ช
: ${PROJECT_NAME}")
message(STATUS "ํ๋ก์ ํธ ๋ฒ์ : ${PROJECT_VERSION}")
์บ์ ๋ณ์๋ cmake-gui๋ ccmake์์ ์ค์ ํ ์ ์๊ณ , ์ปค๋งจ๋๋ผ์ธ์์ -D ์ต์
์ผ๋ก ์ ๋ฌํ ์ ์์ด:
# ์บ์ ๋ณ์ ์ ์ธ
option(ENABLE_TESTS "ํ
์คํธ ๋น๋ ํ์ฑํ" ON)
option(ENABLE_LOGGING "๋ก๊น
ํ์ฑํ" OFF)
set(MY_INSTALL_PREFIX "/usr/local" CACHE PATH "์ค์น ๊ฒฝ๋ก")
set(MAX_CONNECTIONS 100 CACHE STRING "์ต๋ ์ฐ๊ฒฐ ์")
# ์บ์ ๋ณ์ ํ์
: BOOL, PATH, FILEPATH, STRING, INTERNAL
# ์กฐ๊ฑด๋ถ ๋น๋
if(ENABLE_TESTS)
add_subdirectory(tests)
message(STATUS "ํ
์คํธ ๋น๋ ํ์ฑํ๋จ")
endif()
if(ENABLE_LOGGING)
target_compile_definitions(myapp PRIVATE ENABLE_LOGGING)
endif()
# ์ปค๋งจ๋๋ผ์ธ์์ ์ค์
# cmake -DENABLE_TESTS=ON -DENABLE_LOGGING=OFF ..
CMake์ ๊ณ ๊ธ ๊ธฐ๋ฅ ์ค ํ๋์ผ. ๋น๋ ํ์ ์ด๋ ํ๋ซํผ์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ฐ์ ์ค์ ํ ๋ ์ฌ์ฉํด:
# ์ ๋๋ ์ดํฐ ํํ์ ๊ธฐ๋ณธ ๋ฌธ๋ฒ: $<์กฐ๊ฑด:๊ฐ>
# ๋น๋ ํ์
์ ๋ฐ๋ฅธ ์ปดํ์ผ ์ต์
target_compile_options(myapp PRIVATE
$<$:-g -O0 -DDEBUG>
$<$:-O3 -DNDEBUG>
)
# ์ปดํ์ผ๋ฌ์ ๋ฐ๋ฅธ ์ต์
target_compile_options(myapp PRIVATE
$<$:-Wall -Wextra>
$<$:/W4>
$<$:-Wall -Wextra>
)
# ํ๋ซํผ์ ๋ฐ๋ฅธ ์ ์
target_compile_definitions(myapp PRIVATE
$<$:WINDOWS_BUILD>
$<$:LINUX_BUILD>
)
์ค์ ํ๋ก์ ํธ์์๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋๋ถ๋ถ์ด์ผ.
CMake์์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋ํ๋ ๋ฐฉ๋ฒ์ ํ๋์ฉ ์ดํด๋ณด์.
# OpenSSL ์ฐ๋ ์์
find_package(OpenSSL REQUIRED)
if(OpenSSL_FOUND)
message(STATUS "OpenSSL ๋ฒ์ : ${OPENSSL_VERSION}")
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
# Boost ์ฐ๋ ์์
find_package(Boost 1.70 REQUIRED COMPONENTS
filesystem
system
thread
)
target_link_libraries(myapp PRIVATE
Boost::filesystem
Boost::system
Boost::thread
)
# Qt6 ์ฐ๋ ์์
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
target_link_libraries(myapp PRIVATE Qt6::Core Qt6::Widgets)
# ์ปค์คํ
๊ฒฝ๋ก ์ง์
find_package(MyLib REQUIRED
PATHS /opt/mylib
NO_DEFAULT_PATH
)
CMake 3.11๋ถํฐ ๋ด์ฅ๋ FetchContent๋ ๋น๋ ์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์๋์ผ๋ก ๋ค์ด๋ก๋ํ๊ณ ๋น๋ํด์ค.
GitHub ํ๋ก์ ํธ๋ฅผ ์ง์ ๊ฐ์ ธ์ฌ ์ ์์ด์ ์ง์ง ํธํจ ใ
ใ
ใ
include(FetchContent)
# Google Test ๊ฐ์ ธ์ค๊ธฐ
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)
# spdlog (๋ก๊น
๋ผ์ด๋ธ๋ฌ๋ฆฌ) ๊ฐ์ ธ์ค๊ธฐ
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.12.0
)
FetchContent_MakeAvailable(spdlog)
# nlohmann/json ๊ฐ์ ธ์ค๊ธฐ
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
)
FetchContent_MakeAvailable(nlohmann_json)
# ์ด์ ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ!
target_link_libraries(myapp PRIVATE
GTest::gtest_main
spdlog::spdlog
nlohmann_json::nlohmann_json
)
FETCHCONTENT_BASE_DIR ๋ณ์๋ก ๋ค์ด๋ก๋ ์บ์ ์์น๋ฅผ ์ง์ ํ ์ ์์ด.cmake -DFETCHCONTENT_BASE_DIR=/path/to/cache ..ํ ๋ฒ ๋ค์ด๋ก๋ํ๋ฉด ๋ค์ ๋น๋์์๋ ์บ์๋ฅผ ์ฌ์ฉํด์ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌ๋ผ!
CMake์๋ CTest๋ผ๋ ํ
์คํธ ์คํ ๋๊ตฌ๊ฐ ๋ด์ฅ๋์ด ์์ด.
Google Test, Catch2 ๋ฑ ๋ค์ํ ํ
์คํธ ํ๋ ์์ํฌ์ ์ฝ๊ฒ ์ฐ๋ํ ์ ์์ด.
# CMakeLists.txt (๋ฃจํธ)
cmake_minimum_required(VERSION 3.15)
project(MyProject)
set(CMAKE_CXX_STANDARD 17)
# ํ
์คํธ ํ์ฑํ
enable_testing()
# ๋ฉ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
add_library(mylib STATIC
src/calculator.cpp
src/utils.cpp
)
target_include_directories(mylib PUBLIC include)
# ์คํ ํ์ผ
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# ํ
์คํธ ์๋ธ๋๋ ํ ๋ฆฌ
add_subdirectory(tests)
# tests/CMakeLists.txt
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# Windows์์ gtest์ ๋ฐํ์ ์ค์ ๊ณต์
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
# ํ
์คํธ ์คํ ํ์ผ
add_executable(mylib_tests
test_calculator.cpp
test_utils.cpp
)
target_link_libraries(mylib_tests PRIVATE
mylib
GTest::gtest_main
)
# CTest์ ํ
์คํธ ๋ฑ๋ก
include(GoogleTest)
gtest_discover_tests(mylib_tests)
# ํ ์คํธ ์คํ ๋ฐฉ๋ฒ # ๋น๋ ํ ํ ์คํธ ์คํ cmake --build build cd build ctest # ๋ชจ๋ ํ ์คํธ ์คํ ctest -V # ์์ธ ์ถ๋ ฅ ctest -R "calculator" # ํน์ ํ ์คํธ๋ง ์คํ ctest --parallel 4 # ๋ณ๋ ฌ ํ ์คํธ ctest --output-on-failure # ์คํจ ์ ์ถ๋ ฅ ํ์
ํ๋ก์ ํธ๊ฐ ์ปค์ง๋ฉด CMakeLists.txt๋ ์ฒด๊ณ์ ์ผ๋ก ๊ตฌ์กฐํํด์ผ ํด.
add_subdirectory()๋ฅผ ํ์ฉํ ๊ณ์ธต์ ๊ตฌ์กฐ๊ฐ ํต์ฌ์ด์ผ.
MyProject/
โโโ CMakeLists.txt โ ๋ฃจํธ (ํ๋ก์ ํธ ์ ์ฒด ์ค์ )
โโโ cmake/
โ โโโ FindMyLib.cmake โ ์ปค์คํ
Find ๋ชจ๋
โ โโโ CompilerOptions.cmake
โโโ src/
โ โโโ CMakeLists.txt โ ๋ฉ์ธ ์์ค
โ โโโ main.cpp
โ โโโ core/
โ โโโ CMakeLists.txt โ ์ฝ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โ โโโ core.cpp
โ โโโ core.h
โโโ libs/
โ โโโ CMakeLists.txt
โ โโโ mylib/
โ โโโ CMakeLists.txt โ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โ โโโ ...
โโโ tests/
โ โโโ CMakeLists.txt โ ํ
์คํธ
โ โโโ ...
โโโ docs/
โโโ include/
โโโ myproject/ โ ๊ณต๊ฐ ํค๋
cmake_minimum_required(VERSION 3.20)
project(MyProject
VERSION 2.1.0
DESCRIPTION "๋๊ท๋ชจ C++ ํ๋ก์ ํธ ์์"
HOMEPAGE_URL "https://example.com"
LANGUAGES CXX
)
# ===== ์ ์ญ ์ค์ =====
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ๋น๋ ํ์
๊ธฐ๋ณธ๊ฐ
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "๋น๋ ํ์
" FORCE)
endif()
# cmake/ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ชจ๋ ๊ฒฝ๋ก์ ์ถ๊ฐ
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# ===== ์ต์
=====
option(BUILD_TESTS "ํ
์คํธ ๋น๋" ON)
option(BUILD_DOCS "๋ฌธ์ ์์ฑ" OFF)
option(ENABLE_SANITIZERS "AddressSanitizer ํ์ฑํ" OFF)
# ===== ์ปดํ์ผ๋ฌ ์ต์
๋ชจ๋ ํฌํจ =====
include(CompilerOptions)
# ===== ์๋ธ๋๋ ํ ๋ฆฌ =====
add_subdirectory(libs)
add_subdirectory(src)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# ===== ์ค์น ์ค์ =====
include(GNUInstallDirs)
install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# cmake/CompilerOptions.cmake
# ์ธํฐํ์ด์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ปดํ์ผ ์ต์
๋ฌถ๊ธฐ
add_library(project_options INTERFACE)
add_library(project_warnings INTERFACE)
# C++ ํ์ค
target_compile_features(project_options INTERFACE cxx_std_20)
# ๊ฒฝ๊ณ ์ต์
if(MSVC)
target_compile_options(project_warnings INTERFACE
/W4 /WX /permissive-
)
else()
target_compile_options(project_warnings INTERFACE
-Wall -Wextra -Wpedantic -Werror
-Wshadow -Wnon-virtual-dtor
-Wold-style-cast -Wcast-align
)
endif()
# Sanitizer ์ต์
if(ENABLE_SANITIZERS)
target_compile_options(project_options INTERFACE
-fsanitize=address,undefined
-fno-omit-frame-pointer
)
target_link_options(project_options INTERFACE
-fsanitize=address,undefined
)
endif()
# ์ฌ์ฉ๋ฒ: target_link_libraries(myapp PRIVATE project_options project_warnings)
CMake 3.19๋ถํฐ ๋์
๋ CMakePresets.json์ ๋น๋ ์ค์ ์ ํ์ผ๋ก ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ค.
ํ ์ ์ฒด๊ฐ ๋์ผํ ๋น๋ ์ค์ ์ ๊ณต์ ํ ์ ์์ด์ "๋ด ์ปดํจํฐ์์๋ ๋๋๋ฐ?" ๊ฐ์ ์ํฉ์ ๋ฐฉ์งํ ์ ์์ด ใ
ใ
ใ
// CMakePresets.json
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "20"
}
},
{
"name": "debug",
"displayName": "Debug ๋น๋",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ENABLE_SANITIZERS": "ON"
}
},
{
"name": "release",
"displayName": "Release ๋น๋",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"BUILD_TESTS": "OFF"
}
},
{
"name": "ci",
"displayName": "CI ๋น๋",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"BUILD_TESTS": "ON"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"jobs": 8
},
{
"name": "release",
"configurePreset": "release",
"jobs": 8
}
],
"testPresets": [
{
"name": "debug",
"configurePreset": "debug",
"output": {"outputOnFailure": true},
"execution": {"jobs": 4}
}
]
}
# ํ๋ฆฌ์ ์ฌ์ฉ๋ฒ cmake --list-presets # ์ฌ์ฉ ๊ฐ๋ฅํ ํ๋ฆฌ์ ๋ชฉ๋ก cmake --preset debug # debug ํ๋ฆฌ์ ์ผ๋ก ๊ตฌ์ฑ cmake --build --preset debug # debug ํ๋ฆฌ์ ์ผ๋ก ๋น๋ ctest --preset debug # debug ํ๋ฆฌ์ ์ผ๋ก ํ ์คํธ
๋น๋๊ฐ ๋๋ฆฌ๋ฉด ๊ฐ๋ฐ ์์ฐ์ฑ์ด ํ ๋จ์ด์ง์์. ๋น๋ ์๋๋ฅผ ๋์ด๋ ์ค์ฉ์ ์ธ ํ๋ค์ ์์๋ณด์!
ccache๋ ์ปดํ์ผ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํด์ ๋์ผํ ์ฝ๋๋ฅผ ๋ค์ ์ปดํ์ผํ ๋ ์บ์์์ ๊ฐ์ ธ์.
ํด๋ฆฐ ๋น๋๋ฅผ ์์ฃผ ํด์ผ ํ๋ ์ํฉ์์ ์์ฒญ๋ ์๊ฐ ์ ์ฝ์ด ๋ผ:
# ccache ์ค์น
sudo apt-get install ccache # Ubuntu
brew install ccache # macOS
# CMakeLists.txt์์ ccache ํ์ฑํ
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
message(STATUS "ccache ํ์ฑํ: ${CCACHE_PROGRAM}")
endif()
# ๋๋ ์ปค๋งจ๋๋ผ์ธ์์
cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..
# ccache ํต๊ณ ํ์ธ
ccache --show-stats
์ฌ๋ฌ .cpp ํ์ผ์ ํ๋๋ก ํฉ์ณ์ ์ปดํ์ผํ๋ ๊ธฐ๋ฒ์ด์ผ. ํค๋ ํ์ฑ ์ค๋ฒํค๋๋ฅผ ์ค์ฌ์ ๋น๋๊ฐ ๋นจ๋ผ์ ธ:
# CMake 3.16+ Unity Build ์ง์
set_target_properties(myapp PROPERTIES
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE 10 # 10๊ฐ์ฉ ๋ฌถ์ด์ ์ปดํ์ผ
)
# ์ ์ญ์ผ๋ก ํ์ฑํ
set(CMAKE_UNITY_BUILD ON)
# ํน์ ํ์ผ ์ ์ธ (ODR ์๋ฐ ๊ฐ๋ฅ์ฑ ์๋ ํ์ผ)
set_source_files_properties(problematic.cpp PROPERTIES
SKIP_UNITY_BUILD_INCLUSION ON
)
# CMake 3.16+ PCH ์ง์
# pch.h ํ์ผ ์์ฑ
target_precompile_headers(myapp PRIVATE
pch.h
)
# ๋๋ ์ง์ ํค๋ ์ง์
target_precompile_headers(myapp PRIVATE
<vector>
<string>
<memory>
<algorithm>
<iostream>
"include/common.h"
)
# ์ฌ๋ฌ ํ๊ฒ ๊ฐ PCH ๊ณต์
target_precompile_headers(myapp REUSE_FROM mylib)
# Ninja ์ค์น sudo apt-get install ninja-build # Ubuntu brew install ninja # macOS # CMake์์ Ninja ์ฌ์ฉ cmake -G Ninja -S . -B build cmake --build build # Ninja๋ Make๋ณด๋ค ์ผ๋ฐ์ ์ผ๋ก 2~3๋ฐฐ ๋น ๋ฅธ ๋น๋ ์๋๋ฅผ ๋ณด์ฌ์ค!
| ์ต์ ํ ๊ธฐ๋ฒ | ํจ๊ณผ | ์ ์ฉ ๋์ด๋ |
|---|---|---|
| ๋ณ๋ ฌ ๋น๋ (-j) | โญโญโญโญโญ | ๋งค์ฐ ์ฌ์ |
| ccache | โญโญโญโญโญ | ์ฌ์ |
| Ninja ์ฌ์ฉ | โญโญโญโญ | ์ฌ์ |
| PCH (๋ฏธ๋ฆฌ ์ปดํ์ผ๋ ํค๋) | โญโญโญโญ | ๋ณดํต |
| Unity Build | โญโญโญ | ๋ณดํต |
| ๋ถ์ฐ ๋น๋ (distcc) | โญโญโญโญโญ | ์ด๋ ค์ |
CMake ์๋ฌ ๋ฉ์์ง๊ฐ ์ฒ์์ ์ง์ง ์ดํดํ๊ธฐ ์ด๋ ค์ ใ
ใ
ใ
์์ฃผ ๋ง๋๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ๋ฒ์ ์ ๋ฆฌํด๋ดค์ด.
# ์์ธ ์ถ๋ ฅ ๋ชจ๋
cmake -S . -B build --log-level=DEBUG
cmake -S . -B build --trace
cmake -S . -B build --trace-expand
# ๋ณ์ ๊ฐ ํ์ธ
cmake -S . -B build -L # ์บ์ ๋ณ์ ๋ชฉ๋ก
cmake -S . -B build -LA # ๊ณ ๊ธ ์บ์ ๋ณ์ ํฌํจ
cmake -S . -B build -LH # ๋์๋ง ํฌํจ
# CMakeLists.txt ์์์ ๋๋ฒ๊น
message(STATUS "๋ณ์ ํ์ธ: ${MY_VAR}")
message(WARNING "๊ฒฝ๊ณ ๋ฉ์์ง")
message(FATAL_ERROR "์๋ฌ ๋ฐ์ - ๋น๋ ์ค๋จ")
# ํ๊ฒ ์์ฑ ํ์ธ
get_target_property(INCLUDES myapp INCLUDE_DIRECTORIES)
message(STATUS "Include ๊ฒฝ๋ก: ${INCLUDES}")
# ๋น๋ ์์ธ ์ถ๋ ฅ
cmake --build build --verbose
make VERBOSE=1
โ
find_package(XXX)๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ชป ์ฐพ๋ ๊ฒฝ์ฐโ ํด๊ฒฐ:
cmake -DCMAKE_PREFIX_PATH=/path/to/lib .. ๋๋ ํจํค์ง ์ค์น ํ์ธโ
cmake --find-package -DNAME=XXX -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST
โ Modern CMake์์ PRIVATE/PUBLIC/INTERFACE ํค์๋ ๋๋ฝ
โ ํด๊ฒฐ:
target_link_libraries(myapp PRIVATE mylib) ํํ๋ก ์์
โ ๋งํน ๋จ๊ณ์์ ์ฌ๋ณผ์ ๋ชป ์ฐพ๋ ๊ฒฝ์ฐ
โ ํด๊ฒฐ:
target_link_libraries()์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐโ ๋งํฌ ์์ ํ์ธ (์์กดํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ค์ ์์ผ ํจ)
# CMake ์บ์ ์ด๊ธฐํ (์ค์ ๊ผฌ์์ ๋) rm -rf build/CMakeCache.txt build/CMakeFiles/ # ๋๋ rm -rf build/ cmake -S . -B build # ์ฒ์๋ถํฐ ๋ค์ # ํน์ ์บ์ ๋ณ์๋ง ์ญ์ cmake -U MY_VARIABLE -S . -B build
ํ๋ ๊ฐ๋ฐ์์ CI/CD๋ ํ์์์. CMake ํ๋ก์ ํธ๋ฅผ GitHub Actions์ ์ฐ๋ํ๋ ์์๋ฅผ ๋ณด์ฌ์ค๊ฒ:
# .github/workflows/build.yml
name: C++ CMake ๋น๋ ๋ฐ ํ
์คํธ
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
build_type: [Debug, Release]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: ccache ์ค์
uses: hendrikmuhs/ccache-action@v1.2
- name: CMake ๊ตฌ์ฑ
run: |
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DBUILD_TESTS=ON \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: ๋น๋
run: cmake --build build --parallel 4
- name: ํ
์คํธ
working-directory: build
run: ctest --output-on-failure --parallel 4
CMake ์ค์ ์ด ๋ณต์กํ๊ฑฐ๋ ํฌ๋ก์ค ํ๋ซํผ ๋น๋ ํ๊ฒฝ ๊ตฌ์ถ์ด ์ด๋ ค์ธ ๋, ์ฌ๋ฅ๋ท์์ C++ ๋น๋ ์์คํ ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์๋ณผ ์ ์์ด. ๋ค์ํ ๊ฐ๋ฐ ์ฌ๋ฅ์ ๊ฐ์ง ๋ถ๋ค์ด ํ๋ก์ ํธ ๋น๋ ํ๊ฒฝ ์ค์ ๋ถํฐ ์ต์ ํ๊น์ง ๋์์ ์ค ์ ์๊ฑฐ๋ !
์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ์ข
ํฉํด์ ์ค์ ๋ก ๋์ํ๋ ์์ฑ๋ CMake ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์!
๊ฐ๋จํ ๊ณ์ฐ๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ + ์คํ ํ์ผ + ํ
์คํธ ๊ตฌ์กฐ์ผ.
calculator_project/
โโโ CMakeLists.txt
โโโ CMakePresets.json
โโโ include/
โ โโโ calculator/
โ โโโ calculator.h
โโโ src/
โ โโโ CMakeLists.txt
โ โโโ main.cpp
โ โโโ calculator.cpp
โโโ tests/
โโโ CMakeLists.txt
โโโ test_calculator.cpp
#pragma once
#include <stdexcept>
namespace calc {
class Calculator {
public:
double add(double a, double b) const;
double subtract(double a, double b) const;
double multiply(double a, double b) const;
double divide(double a, double b) const;
// 0์ผ๋ก ๋๋๋ฉด std::invalid_argument ๋์ง
};
} // namespace calc
#include "calculator/calculator.h"
namespace calc {
double Calculator::add(double a, double b) const {
return a + b;
}
double Calculator::subtract(double a, double b) const {
return a - b;
}
double Calculator::multiply(double a, double b) const {
return a * b;
}
double Calculator::divide(double a, double b) const {
if (b == 0.0) {
throw std::invalid_argument("0์ผ๋ก ๋๋ ์ ์์ด์!");
}
return a / b;
}
} // namespace calc
cmake_minimum_required(VERSION 3.20)
project(CalculatorProject
VERSION 1.0.0
DESCRIPTION "CMake ์ค์ ์์ - ๊ณ์ฐ๊ธฐ"
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ccache ์๋ ๊ฐ์ง
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
option(BUILD_TESTS "ํ
์คํธ ๋น๋" ON)
# ๊ณ์ฐ๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
add_library(calculator STATIC
src/calculator.cpp
)
target_include_directories(calculator PUBLIC
${CMAKE_SOURCE_DIR}/include
)
target_compile_options(calculator PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# ์คํ ํ์ผ
add_executable(calc_app src/main.cpp)
target_link_libraries(calc_app PRIVATE calculator)
# ํ
์คํธ
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# ์ค์น
include(GNUInstallDirs)
install(TARGETS calc_app RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_executable(calculator_tests
test_calculator.cpp
)
target_link_libraries(calculator_tests PRIVATE
calculator
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(calculator_tests)
#include <gtest/gtest.h>
#include "calculator/calculator.h"
class CalculatorTest : public ::testing::Test {
protected:
calc::Calculator calc;
};
TEST_F(CalculatorTest, ๋ง์
_๊ธฐ๋ณธํ
์คํธ) {
EXPECT_DOUBLE_EQ(calc.add(2.0, 3.0), 5.0);
EXPECT_DOUBLE_EQ(calc.add(-1.0, 1.0), 0.0);
}
TEST_F(CalculatorTest, ๋บ์
_๊ธฐ๋ณธํ
์คํธ) {
EXPECT_DOUBLE_EQ(calc.subtract(5.0, 3.0), 2.0);
}
TEST_F(CalculatorTest, ๊ณฑ์
_๊ธฐ๋ณธํ
์คํธ) {
EXPECT_DOUBLE_EQ(calc.multiply(4.0, 3.0), 12.0);
}
TEST_F(CalculatorTest, ๋๋์
_๊ธฐ๋ณธํ
์คํธ) {
EXPECT_DOUBLE_EQ(calc.divide(10.0, 2.0), 5.0);
}
TEST_F(CalculatorTest, ๋๋์
_0์ผ๋ก๋๋๊ธฐ_์์ธ๋ฐ์) {
EXPECT_THROW(calc.divide(10.0, 0.0), std::invalid_argument);
}
# ๋น๋ cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug cmake --build build --parallel 4 # ์คํ ./build/calc_app # ํ ์คํธ cd build ctest --output-on-failure # ๋๋ ์ง์ ์คํ ./build/tests/calculator_tests
"๊ทธ๋์ CMake ์ฐ๋ ๊ฒ ๋์? Make ์ฐ๋ ๊ฒ ๋์?" ๋ผ๋ ์ง๋ฌธ์ ๋ํ ๋ช ํํ ๋ต์ ๋๋ฆด๊ฒ:
| ๋น๊ต ํญ๋ชฉ | Make (Makefile) | CMake |
|---|---|---|
| ํ์ต ๊ณก์ | ์๋์ ์ผ๋ก ๋ฎ์ | ์ด๊ธฐ ์ง์ ์ฅ๋ฒฝ ์์ |
| ํฌ๋ก์ค ํ๋ซํผ | โ Unix ์ค์ฌ | โ ์์ ์ง์ |
| IDE ํตํฉ | ์ ํ์ | โ VS, CLion, VSCode ๋ฑ |
| ์์กด์ฑ ๊ด๋ฆฌ | ์๋ ๊ด๋ฆฌ | โ ์๋ํ |
| ๋๊ท๋ชจ ํ๋ก์ ํธ | ๋ณต์กํด์ง | โ ๊ณ์ธต์ ๊ตฌ์กฐ ์ง์ |
| ์ํ๊ณ | ์ฑ์ํจ | โ ๋งค์ฐ ํ๋ฐ |
| ๋น๋ ์๋ | ๋ณดํต | Ninja ์ฐ๋ ์ ๋น ๋ฆ |
| ๋๋ฒ๊น | ์ง๊ด์ | ๋ณต์กํ ์ ์์ |
1๋จ๊ณ : cmake_minimum_required, project, add_executable ๊ธฐ๋ณธ ์ตํ๊ธฐ
2๋จ๊ณ : target_* ํจ์๋ค, PRIVATE/PUBLIC/INTERFACE ์ดํด
3๋จ๊ณ : find_package, FetchContent๋ก ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ๋
4๋จ๊ณ : ๋ฉํฐ ์๋ธ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ, ์ปค์คํ ๋ชจ๋ ์์ฑ
5๋จ๊ณ : CMakePresets, CI/CD ํตํฉ, ํจํค์ง ๋ฐฐํฌ
๋จ๊ณ๋ณ๋ก ์ฐจ๊ทผ์ฐจ๊ทผ ์ตํ๋ฉด ์ด๋ ต์ง ์์! ์ฌ๋ฅ๋ท(jaenung.net)์์ C++ ๋ฉํ ๋ฅผ ์ฐพ์ 1:1๋ก ๋ฐฐ์ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ ๐
์, ์ฌ๊ธฐ๊น์ง C++ ๋น๋ ์์คํ
๊ณผ CMake/Make์ ๋ํด ์์ฒญ ๋ง์ ๋ด์ฉ์ ๋ค๋ค์ด!
ํต์ฌ ํฌ์ธํธ๋ง ๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ:
๋น๋ ์์คํ
์ ์ฒ์์ ๋ณต์กํด ๋ณด์ด์ง๋ง, ํ ๋ฒ ์ ๋๋ก ์ค์ ํด๋๋ฉด ๊ฐ๋ฐ ์์ฐ์ฑ์ด ์์ฒญ๋๊ฒ ์ฌ๋ผ๊ฐ.
ํนํ ํ ํ๋ก์ ํธ์์ "๋ด ์ปดํจํฐ์์๋ ๋๋๋ฐ?" ๊ฐ์ ์ํฉ์ ๋ฐฉ์งํ๋ ๋ฐ CMake๊ฐ ์ ๋ง ํฐ ์ญํ ์ ํด ใ
ใ
ใ
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ผ๋ก ์ฌ๋ฌ๋ถ์ C++ ํ๋ก์ ํธ ๋น๋ ํ๊ฒฝ์ ํ ๋จ๊ณ ์
๊ทธ๋ ์ด๋ํด๋ด! ํ์ดํ
! ๐
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.