← Back to Blog

[CMake] CMakeLists.txt

computer science > programming

2026-07-064 min read

#computer-science #programming #cmake #cpp #build-system

CMakeLists.txt란?

CMakeLists.txt는 CMake가 project를 어떻게 build할지 읽는 설정 파일이다.

C++ project에서는 compiler에 직접 긴 명령어를 입력하는 대신, CMakeLists.txt에 executable, library, include path, link library, compile option 등을 작성한다.

예를 들어 다음 명령어를 직접 입력할 수도 있다.

g++ -std=c++17 -Iinclude src/main.cpp src/math.cpp -o app

하지만 project가 커지면 source file과 option을 직접 관리하기 어렵다. CMake를 쓰면 build 설정을 파일로 관리할 수 있다.

cmake -S . -B build
cmake --build build

최소 CMakeLists.txt

가장 단순한 C++ project는 다음처럼 작성할 수 있다.

cmake_minimum_required(VERSION 3.16)

project(hello_cpp)

add_executable(hello
    main.cpp
)

파일 구조는 다음과 같다.

hello_cpp/
  CMakeLists.txt
  main.cpp

main.cpp 예시는 다음과 같다.

#include <iostream>

int main() {
    std::cout << "Hello, CMake\n";
    return 0;
}

build는 다음처럼 한다.

cmake -S . -B build
cmake --build build

실행 파일은 보통 build directory 안에 생성된다.

./build/hello

기본 구조

일반적인 CMakeLists.txt는 다음 순서로 작성한다.

cmake_minimum_required(VERSION 3.16)

project(my_project
    VERSION 0.1.0
    LANGUAGES CXX
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(my_app
    src/main.cpp
)

각 줄의 의미는 다음과 같다.

명령어의미
cmake_minimum_required필요한 최소 CMake version
projectproject 이름, version, language 설정
set(CMAKE_CXX_STANDARD ...)C++ 표준 설정
add_executable실행 파일 target 생성

CMake에서는 executable이나 library를 target이라고 부른다. 현대 CMake는 전역 설정보다 target 단위 설정을 권장한다.


target 중심으로 작성하기

예전 CMake에서는 다음처럼 전역 include path를 설정하는 경우가 많았다.

include_directories(include)

하지만 현대 CMake에서는 target에 직접 설정하는 방식이 좋다.

add_executable(my_app
    src/main.cpp
)

target_include_directories(my_app
    PRIVATE
        include
)

target 중심으로 작성하면 어떤 executable이나 library가 어떤 include path, compile option, link library를 사용하는지 명확해진다.

target_include_directories
target_compile_features
target_compile_options
target_link_libraries
target_compile_definitions

source file 추가

source file이 여러 개라면 add_executable에 함께 적는다.

add_executable(my_app
    src/main.cpp
    src/calculator.cpp
    src/logger.cpp
)

파일 구조는 다음과 같다.

my_project/
  CMakeLists.txt
  include/
    calculator.hpp
    logger.hpp
  src/
    main.cpp
    calculator.cpp
    logger.cpp

include directory를 target에 연결한다.

target_include_directories(my_app
    PRIVATE
        include
)

PRIVATE는 이 include path가 my_app을 build할 때만 필요하다는 의미이다.


library 만들기

공통 코드는 library target으로 분리할 수 있다.

add_library(my_math
    src/math/add.cpp
    src/math/sub.cpp
)

target_include_directories(my_math
    PUBLIC
        include
)

실행 파일은 library를 link한다.

add_executable(my_app
    src/main.cpp
)

target_link_libraries(my_app
    PRIVATE
        my_math
)

전체 예시는 다음과 같다.

cmake_minimum_required(VERSION 3.16)

project(my_project LANGUAGES CXX)

add_library(my_math
    src/math/add.cpp
    src/math/sub.cpp
)

target_include_directories(my_math
    PUBLIC
        include
)

add_executable(my_app
    src/main.cpp
)

target_link_libraries(my_app
    PRIVATE
        my_math
)

PUBLIC, PRIVATE, INTERFACE

CMake target 설정에서 자주 나오는 keyword가 있다.

keyword의미
PRIVATE현재 target을 build할 때만 필요
PUBLIC현재 target과 이 target을 사용하는 target 모두 필요
INTERFACE현재 target 자체에는 필요 없고 사용하는 target에만 필요

예를 들어 library header가 include directory에 있고, 이 header를 사용하는 쪽에서도 include path가 필요하다면 PUBLIC을 사용한다.

target_include_directories(my_library
    PUBLIC
        include
)

반대로 library 내부 구현 파일에서만 필요한 include path라면 PRIVATE가 맞다.

target_include_directories(my_library
    PRIVATE
        src/internal
)

header-only library는 source file 없이 interface만 제공하므로 INTERFACE를 사용할 수 있다.

add_library(my_header_only INTERFACE)

target_include_directories(my_header_only
    INTERFACE
        include
)

C++ 표준 설정

C++17을 사용하려면 다음처럼 전역으로 설정할 수 있다.

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

하지만 target 단위로 설정하는 방식도 좋다.

target_compile_features(my_app
    PRIVATE
        cxx_std_17
)

library라면 이 표준 요구사항이 사용자에게도 전파되어야 하는지 생각해야 한다.

target_compile_features(my_library
    PUBLIC
        cxx_std_17
)

my_library의 public header가 C++17 문법을 사용한다면 PUBLIC이 맞다. 내부 구현만 C++17을 사용한다면 PRIVATE로 충분하다.


compile option

compiler warning option을 추가하려면 target_compile_options를 사용한다.

target_compile_options(my_app
    PRIVATE
        -Wall
        -Wextra
        -Wpedantic
)

하지만 compiler마다 option이 다르다. GCC/Clang 기준 option을 MSVC에 그대로 넣으면 문제가 될 수 있다.

간단히 compiler를 나눌 수 있다.

if(MSVC)
    target_compile_options(my_app PRIVATE /W4)
else()
    target_compile_options(my_app PRIVATE -Wall -Wextra -Wpedantic)
endif()

compile definition

macro를 정의하려면 target_compile_definitions를 사용한다.

target_compile_definitions(my_app
    PRIVATE
        APP_VERSION="0.1.0"
        ENABLE_LOGGING
)

C++ 코드에서는 다음처럼 사용할 수 있다.

#ifdef ENABLE_LOGGING
    // logging code
#endif

문자열 macro는 quote 처리가 헷갈릴 수 있으므로 필요한 경우 header file 생성 방식을 고려할 수도 있다.


option 만들기

사용자가 build option을 켜고 끌 수 있게 하려면 option()을 사용한다.

option(ENABLE_TESTS "Build tests" ON)

조건부로 test를 추가한다.

if(ENABLE_TESTS)
    add_subdirectory(tests)
endif()

configure할 때 option 값을 바꿀 수 있다.

cmake -S . -B build -DENABLE_TESTS=OFF

subdirectory 나누기

project가 커지면 directory별로 CMakeLists.txt를 나눌 수 있다.

my_project/
  CMakeLists.txt
  src/
    CMakeLists.txt
    main.cpp
  tests/
    CMakeLists.txt
    test_main.cpp

root CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

project(my_project LANGUAGES CXX)

add_subdirectory(src)
add_subdirectory(tests)

src/CMakeLists.txt:

add_executable(my_app
    main.cpp
)

이렇게 하면 directory별로 build 설정을 나눌 수 있다.


외부 library 찾기

system에 설치된 library를 찾을 때는 find_package()를 사용한다.

예를 들어 OpenCV를 사용하는 경우:

find_package(OpenCV REQUIRED)

add_executable(my_app
    src/main.cpp
)

target_link_libraries(my_app
    PRIVATE
        ${OpenCV_LIBS}
)

package가 modern CMake target을 제공한다면 target 이름으로 link하는 것이 더 좋다.

find_package(fmt REQUIRED)

target_link_libraries(my_app
    PRIVATE
        fmt::fmt
)

fmt::fmt처럼 namespace가 붙은 target은 include path와 link option을 함께 들고 있으므로 관리가 쉽다.


install 규칙

실행 파일이나 library를 설치하려면 install()을 사용한다.

install(TARGETS my_app
    RUNTIME DESTINATION bin
)

library와 header를 설치하는 예시는 다음과 같다.

install(TARGETS my_math
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION bin
)

install(DIRECTORY include/
    DESTINATION include
)

설치는 다음 명령어로 실행한다.

cmake --install build

설치 prefix를 바꾸려면 configure 단계에서 지정한다.

cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local

build type

single-config generator에서는 CMAKE_BUILD_TYPE을 지정할 수 있다.

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

자주 쓰는 값은 다음과 같다.

build type의미
Debugdebug symbol 포함, 최적화 낮음
Release최적화 활성화
RelWithDebInfo최적화 + debug symbol
MinSizeRel크기 최소화

Ninja나 Makefile generator에서는 이 방식을 자주 사용한다. Visual Studio 같은 multi-config generator에서는 build 단계에서 config를 지정한다.

cmake --build build --config Release

좋은 CMakeLists.txt 기준

좋은 CMake 설정은 target 중심으로 읽힌다.

피하는 것이 좋은 예시는 다음과 같다.

include_directories(include)
link_directories(lib)
add_definitions(-DDEBUG)

대신 target 단위로 작성한다.

target_include_directories(my_app PRIVATE include)
target_link_libraries(my_app PRIVATE my_library)
target_compile_definitions(my_app PRIVATE DEBUG)

기준은 다음과 같다.

전역 설정보다 target 설정을 사용한다.
source file은 명시적으로 적는다.
include path는 PUBLIC/PRIVATE를 구분한다.
외부 library는 가능하면 imported target으로 link한다.
build directory는 source directory 밖에 둔다.

전체 예시

다음은 작은 C++ project의 CMakeLists.txt 예시이다.

cmake_minimum_required(VERSION 3.16)

project(sample_app
    VERSION 0.1.0
    LANGUAGES CXX
)

option(ENABLE_WARNINGS "Enable compiler warnings" ON)

add_library(sample_core
    src/calculator.cpp
)

target_include_directories(sample_core
    PUBLIC
        include
)

target_compile_features(sample_core
    PUBLIC
        cxx_std_17
)

add_executable(sample_app
    src/main.cpp
)

target_link_libraries(sample_app
    PRIVATE
        sample_core
)

if(ENABLE_WARNINGS)
    if(MSVC)
        target_compile_options(sample_app PRIVATE /W4)
        target_compile_options(sample_core PRIVATE /W4)
    else()
        target_compile_options(sample_app PRIVATE -Wall -Wextra -Wpedantic)
        target_compile_options(sample_core PRIVATE -Wall -Wextra -Wpedantic)
    endif()
endif()

build:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build

정리

CMakeLists.txt를 작성할 때 핵심은 target 중심으로 생각하는 것이다.

  1. cmake_minimum_required()로 최소 CMake version을 정한다.
  2. project()로 project 이름과 language를 설정한다.
  3. add_executable() 또는 add_library()로 target을 만든다.
  4. target_include_directories()로 include path를 연결한다.
  5. target_link_libraries()로 library를 연결한다.
  6. target_compile_features()로 C++ 표준을 설정한다.
  7. PUBLIC, PRIVATE, INTERFACE를 구분한다.
  8. build는 cmake -S . -B build, cmake --build build 흐름을 사용한다.

작은 project에서는 최소 예제로 시작하고, source file, library, option, install 규칙을 필요한 만큼 추가하면 된다.