[2026] CMake Targets in C++ | PUBLIC/PRIVATE, Propagation & Modern CMake

[2026] CMake Targets in C++ | PUBLIC/PRIVATE, Propagation & Modern CMake

이 글의 핵심

Modern CMake targets: add_executable/add_library, target_* commands, visibility, transitive deps, OBJECT libs, aliases—multi-library project walkthrough for English readers.

What are CMake Targets? Why Target Based?

Problem Scenario: Confusion in global settings

Problem: Older CMake styles used global commands like include_directories() and link_libraries(). As a project grows, it becomes difficult to keep track of which target uses which header, and dependencies become complicated. 아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# ❌ Old school: global settings
include_directories(/usr/local/include)
link_libraries(boost_system)
add_executable(app1 main1.cpp)
add_executable(app2 main2.cpp)
# app1, app2 are both linked to boost_system (maybe unintentional)

Solution: Using target-based commands (target_*) makes the dependencies of each target clear and eliminates unnecessary links. 아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# ✅ Modern: Target-specific settings
add_executable(app1 main1.cpp)
target_include_directories(app1 PRIVATE /usr/local/include)
target_link_libraries(app1 PRIVATE Boost::system)
add_executable(app2 main2.cpp)
# app2 is not linked to boost

What is a target?

Target refers to the target (executable file, library) that CMake will build. Create targets with add_executable and add_library, and set the properties (header path, link library, compilation options) of each target with the target_* command. 다음은 mermaid를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 실행 예제
flowchart TD
    subgraph targets[targets]
        exe[add_executable(myapp)]
        lib[add_library(mylib)]
    end
    subgraph properties[Target properties]
        inc[target_include_directories]
        link[target_link_libraries]
        opt[target_compile_options]
        def[target_compile_definitions]
    end
    exe --> inc
    exe --> link
    lib --> inc
    lib --> opt

index

  1. Create target: add_executable, add_library
  2. Target properties: target_* commands
  3. Visibility: PUBLIC, PRIVATE, INTERFACE
  4. Dependency propagation
  5. Frequently occurring problems and solutions
  6. Production Patterns
  7. Complete example: multi-library project

1. Create target

executable

다음은 cmake를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# single source
add_executable(myapp main.cpp)
# Multiple sources
add_executable(myapp
    src/main.cpp
    src/utils.cpp
    src/config.cpp
)
# Use variables
set(APP_SOURCES
    src/main.cpp
    src/utils.cpp
)
add_executable(myapp ${APP_SOURCES})

Static library

다음은 간단한 cmake 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_library(mylib STATIC
    src/lib.cpp
    src/helper.cpp
)

Dynamic library

다음은 간단한 cmake 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_library(mylib SHARED
    src/lib.cpp
    src/helper.cpp
)

Header-only library

add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include)

OBJECT library

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Create only object files (no linking)
add_library(myobj OBJECT
    src/common.cpp
)
# Reuse on multiple targets
add_executable(app1 main1.cpp $<TARGET_OBJECTS:myobj>)
add_executable(app2 main2.cpp $<TARGET_OBJECTS:myobj>)

2. Target properties: target_* commands

target_include_directories

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_library(mylib src/lib.cpp)
target_include_directories(mylib
PUBLIC include # exposed to the outside world
PRIVATE src/internal # Internal use only
)

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    mylib
    Boost::filesystem
    pthread
)

target_compile_options

아래 코드는 cmake를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

target_compile_options(myapp PRIVATE
    -Wall
    -Wextra
    -Werror
    $<$<CONFIG:Debug>:-O0 -g>
    $<$<CONFIG:Release>:-O3>
)

target_compile_definitions

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

target_compile_definitions(myapp PRIVATE
    APP_VERSION="1.0"
    $<$<CONFIG:Debug>:DEBUG_MODE>
    $<$<PLATFORM_ID:Windows>:WINDOWS_BUILD>
)

target_compile_features

# C++20 feature requirements
target_compile_features(myapp PRIVATE cxx_std_20)

3. Visibility: PUBLIC, PRIVATE, INTERFACE

Concept

아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

flowchart LR
    subgraph lib[mylib]
        priv["PRIVATE\nInternal use only"]
        pub["PUBLIC\nExternal exposure"]
        iface["INTERFACE\nPropagation only"]
    end
    subgraph app[myapp]
        use[use]
    end
    pub --> use
    iface --> use
Keywordtarget yourselfDependent Target
PRIVATEuseDisabled
PUBLICuseuse
INTERFACEDisableduse

Practical example

다음은 cmake를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# mylib: library
add_library(mylib src/lib.cpp)
target_include_directories(mylib
PUBLIC include # Targets linking mylib also use include/
PRIVATE src/internal # Only used inside mylib
)
target_compile_definitions(mylib
PUBLIC MYLIB_VERSION=1 # Target linking mylib is also defined
PRIVATE MYLIB_INTERNAL # defined only inside mylib
)
# myapp: executable file
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# myapp can include/ be used (PUBLIC)
# myapp cannot use src/internal (PRIVATE)
# MYLIB_VERSION defined in myapp (PUBLIC)

INTERFACE USE CASE

Used in header-only libraries. 아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_library(header_only INTERFACE)
target_include_directories(header_only INTERFACE include)
target_compile_definitions(header_only INTERFACE HEADER_ONLY_LIB)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE header_only)
# myapp can use include/
# HEADER_ONLY_LIB defined in myapp

4. Dependency propagation

Transitive dependencies

아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# liba: lowest level library
add_library(liba STATIC a.cpp)
target_include_directories(liba PUBLIC include/a)
# libb: Depends on liba
add_library(libb STATIC b.cpp)
target_link_libraries(libb PUBLIC liba)
target_include_directories(libb PUBLIC include/b)
# myapp: Depends on libb
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE libb)
# myapp can use both include/a and include/b (PUBLIC propagation)

아래 코드는 mermaid를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

flowchart TD
    liba["liba\nPUBLIC include/a"]
    libb["libb\nPUBLIC include/b"]
    myapp[myapp]
    
    libb -->|PUBLIC| liba
    myapp -->|PRIVATE| libb
    
    note1["myapp can use both include/a and include/b"]

Block transmission with PRIVATE

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# libb links liba as PRIVATE
target_link_libraries(libb PRIVATE liba)
# myapp
target_link_libraries(myapp PRIVATE libb)
# myapp can only use include/b (liba is not propagated)

5. Frequently occurring problems and solutions

Issue 1: Using global commands

Cause: Global commands such as include_directories() and link_libraries() affect all future targets. 아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# ❌ Incorrect use
include_directories(/usr/local/include)
add_executable(app1 main1.cpp)
add_executable(app2 main2.cpp)
# Both app1 and app2 use /usr/local/include
# ✅ Correct use: Target-specific settings
add_executable(app1 main1.cpp)
target_include_directories(app1 PRIVATE /usr/local/include)
add_executable(app2 main2.cpp)
# app2 is not affected

Issue 2: PUBLIC/PRIVATE confusion

Symptom: Header not found, or unnecessary header exposed. 아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# ❌ Misuse: Internal header as PUBLIC
add_library(mylib src/lib.cpp)
target_include_directories(mylib PUBLIC src/internal)
# src/internal is exposed to the outside world
# ✅ Correct use
target_include_directories(mylib
PUBLIC include #API header
PRIVATE src/internal # implementation header
)

Problem 3: Circular dependencies

Symptom: CMake Error: Circular dependency. Cause: A links B, and B links A. 아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# ❌ Incorrect use
add_library(liba a.cpp)
add_library(libb b.cpp)
target_link_libraries(liba PRIVATE libb)
target_link_libraries(libb PRIVATE liba) # Circulation!
# ✅ Correct use: Dependency redesign
# Common code that both liba and libb depend on is separated into libcommon
add_library(libcommon common.cpp)
add_library(liba a.cpp)
add_library(libb b.cpp)
target_link_libraries(liba PRIVATE libcommon)
target_link_libraries(libb PRIVATE libcommon)

Issue 4: OBJECT library linking

Cause: OBJECT libraries cannot be linked directly with target_link_libraries (prior to CMake 3.12). 아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# CMake 3.12+: OBJECT library linking possible
add_library(myobj OBJECT common.cpp)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE myobj)
# CMake 3.11 and below: Use $<TARGET_OBJECTS:>
add_executable(myapp main.cpp $<TARGET_OBJECTS:myobj>)

6. production pattern

Pattern 1: Common settings with interface libraries

다음은 cmake를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Project global compilation options to interface library
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)
target_compile_options(project_options INTERFACE
    $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
    $<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Apply to all targets
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE project_options)
add_library(mylib lib.cpp)
target_link_libraries(mylib PRIVATE project_options)

Pattern 2: Alias ​​Target

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_library(mylib src/lib.cpp)
add_library(MyProject::mylib ALIAS mylib)
# Reference to the namespace elsewhere
target_link_libraries(myapp PRIVATE MyProject::mylib)

Pattern 3: Conditional Target

아래 코드는 cmake를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

option(BUILD_TOOLS "Build command-line tools" ON)
if(BUILD_TOOLS)
    add_executable(tool1 tools/tool1.cpp)
    target_link_libraries(tool1 PRIVATE mylib)
endif()

Pattern 4: Target attribute lookup

아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Get target properties
get_target_property(MYLIB_INCLUDES mylib INCLUDE_DIRECTORIES)
message(STATUS "mylib includes: ${MYLIB_INCLUDES}")
# Set target properties
set_target_properties(mylib PROPERTIES
    VERSION 1.0.0
    SOVERSION 1
    OUTPUT_NAME "my_library"
)

7. Complete example: multi-library project

Project structure

다음은 code를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

project/
├── CMakeLists.txt
├── core/
│   ├── CMakeLists.txt
│   ├── core.cpp
│   └── core.h
├── utils/
│   ├── CMakeLists.txt
│   ├── utils.cpp
│   └── utils.h
├── app/
│   ├── CMakeLists.txt
│   └── main.cpp
└── include/
    ├── core/
    │   └── core.h
    └── utils/
        └── utils.h

root CMakeLists.txt

아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

cmake_minimum_required(VERSION 3.20)
project(MultiLib VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Common settings
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)
add_subdirectory(core)
add_subdirectory(utils)
add_subdirectory(app)

core/CMakeLists.txt

아래 코드는 cmake를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

add_library(core STATIC
    core.cpp
    core.h
)
target_include_directories(core
    PUBLIC ${CMAKE_SOURCE_DIR}/include/core
    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(core PRIVATE project_options)
add_library(MultiLib::core ALIAS core)

utils/CMakeLists.txt

다음은 cmake를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

add_library(utils STATIC
    utils.cpp
    utils.h
)
target_include_directories(utils
    PUBLIC ${CMAKE_SOURCE_DIR}/include/utils
)
# Utils depend on core
target_link_libraries(utils
    PUBLIC MultiLib::core
    PRIVATE project_options
)
add_library(MultiLib::utils ALIAS utils)

app/CMakeLists.txt

아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

add_executable(myapp main.cpp)
# If you link utils, core will also be linked automatically (PUBLIC propagation)
target_link_libraries(myapp PRIVATE
    MultiLib::utils
    project_options
)

Target command summary

commandDescription
add_executable(name sources...)Create executable target
add_library(name STATIC sources...)Create static library
add_library(name SHARED sources...)Create dynamic library
add_library(name INTERFACE)Header-only library
add_library(name OBJECT sources...)Create only object files
target_include_directories(target vis dirs...)Add header path
target_link_libraries(target vis libs...)Library Link
target_compile_options(target vis opts...)Add compilation options
target_compile_definitions(target vis defs...)Add preprocessing definition
target_compile_features(target vis features...)C++ Feature Requirements

organize

conceptDescription
TargetBuild target (executable, library)
target_*Setting properties for each target
PUBLICtarget + dependent target
PRIVATETarget only
INTERFACEDependent targets only (header only)
transitive dependencyAutomatic propagation to PUBLIC
CMake’s target-based approach clarifies dependencies and isolates build settings by target, making it easy to maintain even large projects.

FAQ

Q1: Global commands vs target commands?

A: Use target command (target_*). Global commands (include_directories, link_libraries) affect all targets, thereby tying up dependencies.

Q2: When should I use PUBLIC vs PRIVATE?

A: The header to be exposed externally is PUBLIC, and the internal implementation header is PRIVATE. When creating a library, set the API header to PUBLIC and the implementation header to PRIVATE.

Q3: When do I use INTERFACE?

A: Used by header-only library. If there are no sources to compile and only headers are provided, create them with add_library(name INTERFACE) and specify the header path with target_include_directories(name INTERFACE ...).

Q4: When do I use the OBJECT library?

A: Used when the same source is reused in multiple targets. You can avoid duplicate compilation by creating only the object file and including it as $<TARGET_OBJECTS:myobj> in multiple executable files/libraries.

Q5: Why use alias targets?

A: By adding a namespace with add_library(MyProject::mylib ALIAS mylib), you can reference external packages and internal targets in a consistent way. Like target_link_libraries(myapp PRIVATE MyProject::mylib Boost::filesystem), they are all unified in :: format.

Q6: What are the CMake Targets learning resources?

A:


Good article to read together (internal link)

Here’s another article related to this topic.

Practical tips

These are tips that can be applied right away in practice.

Debugging tips

  • If you run into a problem, check the compiler warnings first.
  • Reproduce the problem with a simple test case

Performance Tips

  • Don’t optimize without profiling
  • Set measurable indicators first

Code review tips

  • Check in advance for areas that are frequently pointed out in code reviews.
  • Follow your team’s coding conventions

Practical checklist

This is what you need to check when applying this concept in practice.

Before writing code

  • Is this technique the best way to solve the current problem?
  • Can team members understand and maintain this code?
  • Does it meet the performance requirements?

Writing code

  • Have you resolved all compiler warnings?
  • Have you considered edge cases?
  • Is error handling appropriate?

When reviewing code

  • Is the intent of the code clear?
  • Are there enough test cases?
  • Is it documented? Use this checklist to reduce mistakes and improve code quality.

Keywords covered in this article (related search terms)

This article will be helpful if you search for C++, cmake, targets, library, build, dependency, etc.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3