[CMake] CMake 명령어 순서에 대한 고찰
2026. 5. 11. 01:12CMake는 imperative(명령형) 언어로 스크립트를 위에서 아래로 순차 실행하게 된다. 따라서, target이 존재하기 전에 그 target을 참조하면 오류가 나게 되는데, CMake 명령어 순서에 대해 정리해본다.
순서 규칙
1. add_library / add_executable → 반드시 target_* 보다 먼저 선언한다.
target을 생성해야 그 target에 속성을 붙일 수 있으므로 add_* 는 반드시 target_* 명령어보다 먼저 선언한다.
# 잘못된 예: 타겟이 아직 없음
target_link_libraries(mylib PRIVATE somelib)
add_library(mylib src.cpp)
# 올바른 예
add_library(mylib src.cpp)
target_link_libraries(mylib PRIVATE somelib)
2. target_* 명령어들 간의 순서 → 대체로 무관하다.
target_link_libraries, target_include_directories, target_compile_options 등은 타겟 생성 이후라면 상호 간 순서는 결과에 영향 없다.
add_library(mylib src.cpp)
# 아래 셋의 순서는 바꿔도 동일한 결과
target_include_directories(mylib PUBLIC include/)
target_compile_options(mylib PRIVATE -Wall)
target_link_libraries(mylib PRIVATE pthread)
3. find_package / pkg_check_modules → 사용 전에 먼저
이 명령어들은 import target이나 변수를 생성하므로, target_link_libraries에서 참조하기 전에 호출해야 합니다. import target에 대한 개념은 아래의 부록에서 설명한다.
find_package(sdbus-c++ REQUIRED) # 먼저
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++) # 이후
4. 서브디렉토리 간 의존성 → add_subdirectory 순서가 중요
add_subdirectory(libfoo) # foo 타겟 생성
add_subdirectory(app) # app이 foo에 의존 → foo가 먼저여야 함
단, target_link_libraries로 연결된 타겟은 같은 CMake 빌드 트리 내라면 순서가 바뀌어도 CMake가 해결해 주는 경우도 있다. 하지만 명시적 순서를 유지하는 것이 안전하다.
관련 공식 문서
target_link_libraries 문서에 "target이 먼저 생성되어 있어야 한다" 는 제약이 명시되어 있다.
| 문서 | 내용 |
| add_library | 타겟 생성 |
| target_link_libraries | "The named <target> must have been created by a command such as add_executable() or add_library()" 명시 |
| cmake-buildsystem(7) | 타겟/의존성 전체 개념 설명 |
| CMake Tutorial | 실제 순서를 보여주는 예제 |
권장 관용적 순서
# 1. 의존성 탐색
find_package(...)
pkg_check_modules(...)
# 2. 타겟 생성
add_library(mylib ...)
add_executable(myapp ...)
# 3. 타겟 속성 설정 (순서 무관)
target_include_directories(...)
target_compile_definitions(...)
target_compile_options(...)
target_link_libraries(...)
이 순서가 CMake 공식 튜토리얼과 대부분의 오픈소스 프로젝트에서 따르는 관례이다.
부록
1. 임포트 타겟 (Imported Target) 과 변수 (Variable)
■ 임포트 타겟 (Imported target)
find_package가 성공하면, CMake가 가상의 타겟을 생성하게 된다. 이 때, 실제 .a 혹은 .so 파일이 아니라, CMake 내부에만 존재하는 타겟이다.
find_package(sdbus-c++ REQUIRED)
# → SDBusCpp::sdbus-c++ 라는 임포트 타겟이 생성됨
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
# ^^^^^^^^^^^^^^^^^^
# 이 타겟을 참조
이 임포트 타겟 안에는 아래 정보가 모두 내장되어 있다:
- 링크할 .so 경로
- 필요한 include 경로
- 필요한 컴파일 옵션
- 추이적 의존성 (transitive dependencies)
그래서 target_link_libraries 한 줄만 써도 include/link가 전부 해결된다.
■ 변수 (Variable)
pkg_check_modules 또는 구버전 find_package는 임포트 타겟 대신 변수를 설정한다:
pkg_check_modules(DBUS REQUIRED dbus-1)
# → 아래 변수들이 생성됨:
# DBUS_INCLUDE_DIRS = /usr/include/dbus-1.0 ...
# DBUS_LIBRARIES = dbus-1
# DBUS_CFLAGS = ...
# DBUS_FOUND = TRUE
target_include_directories(myapp PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${DBUS_LIBRARIES})
# ^^^^^^^^^^^^^^^^
# 변수를 직접 참조
변수는 단순 문자열이므로, 직접 꺼내서 써야 한다.
■ 비교 요약
| 임포트 타겟 | 변수 | |
| 생성 방식 | find_package (modern) | pkg_check_modules, 구버전 find_package |
| 사용 방법 | Foo::Bar 형태로 직접 전달 | ${FOO_LIBRARIES} 등 변수로 분리 전달 |
| include/link | 타겟 안에 통합 | 변수를 각각 따로 지정 |
| 추이적 의존성 | 자동 전파 | 수동 관리 필요 |
| 권장 여부 | Modern CMake 권장 | 레거시 방식 |
■ 순서 측면
# ❌ find_package 전에 참조
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
find_package(sdbus-c++ REQUIRED)
# → SDBusCpp::sdbus-c++ 타겟이 아직 존재하지 않으므로 오류
# ✅ find_package 후에 참조
find_package(sdbus-c++ REQUIRED)
# → SDBusCpp::sdbus-c++ 타겟 생성 완료
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
# → 정상 참조 가능
add_library로 직접 만드는 타겟과 동일한 이유이다. 즉, 존재하기 전에 참조할 수 없다.
추가 궁금증
Q. install()은 명령어 순서는 중요하지 않습니까?
A. 타겟 생성(add_library/add_executable) 이후라면 어디서든 괜찮다.
install()은 빌드 시점이 아니라 cmake --install 단계에서만 실행되는 명령어이기 때문에, 빌드 의존성과 무관하다.
'Build(빌드) > CMake' 카테고리의 다른 글
| [CMake] 플러그인 빌드 구조 만들기 (--export-dynamic 활용) (0) | 2026.05.14 |
|---|---|
| [CMake] target_include_directories와 target_link_libraries 개념 (0) | 2026.05.11 |
| [CMake] find_package와 pkg_check_modules(PkgConfig)의 차이에 대한 고찰 (0) | 2026.04.16 |
| [CMake] Module(모듈) (0) | 2025.12.03 |
| [CMake] CMake 기본 예제 - 02 실행파일 생성 (헤더 파일 디렉토리 분리) (0) | 2025.11.30 |