CMake의 플랫폼 식별 체계 이해
크로스 플랫폼 소프트웨어 개발에서 가장 중요한 단계 중 하나는 빌드 시점에 대상 운영체제(OS)를 정확히 인식하고 그에 맞는 설정을 적용하는 것입니다. CMake는 다양한 환경 변수와 내장 변수를 통해 실행 환경 및 타겟 환경에 대한 정보를 제공합니다. 이를 효율적으로 활용하면 단일 CMakeLists.txt 파일로 윈도우, 리눅스, macOS를 모두 지원하는 견고한 빌드 시스템을 구축할 수 있습니다.
1. 기본 내장 불리언 변수 활용
CMake는 특정 플랫폼에서 TRUE로 설정되는 간결한 예약 변수들을 제공합니다. 복잡한 문자열 비교 없이 직관적으로 시스템을 구분할 때 가장 많이 사용되는 방식입니다.
- WIN32: 윈도우 기반 시스템(32비트 및 64비트 포함)에서 TRUE
- UNIX: 리눅스, macOS, BSD 등 유닉스 계열 시스템에서 TRUE
- APPLE: macOS, iOS, tvOS 등 애플 플랫폼에서 TRUE
- ANDROID: 안드로이드 타겟 빌드 시 TRUE
다음은 이러한 변수를 사용하여 플랫폼별로 다른 라이브러리를 링크하는 예시입니다.
# 라이브러리 타겟 정의
add_executable(CoreEngine main.cpp)
if(WIN32)
# 윈도우 환경 전용 소켓 라이브러리 링크
target_link_libraries(CoreEngine PRIVATE wsock32)
target_compile_definitions(CoreEngine PRIVATE PLATFORM_WINDOWS)
elseif(APPLE)
# macOS 전용 프레임워크 검색 및 링크
find_library(CORE_GRAPHICS CoreGraphics)
target_link_libraries(CoreEngine PRIVATE ${CORE_GRAPHICS})
elseif(UNIX)
# 리눅스 등 유닉스 계열 환경 설정
target_link_libraries(CoreEngine PRIVATE pthread dl)
target_compile_definitions(CoreEngine PRIVATE PLATFORM_LINUX)
endif()
주의사항: UNIX 변수는 macOS에서도 TRUE를 반환합니다. 따라서 리눅스와 macOS를 구분해야 한다면 if(UNIX AND NOT APPLE)와 같은 조합 논리를 사용해야 합니다.
2. CMAKE_SYSTEM_NAME을 이용한 정밀 제어
더 구체적인 운영체제 이름이 필요하거나 FreeBSD, OpenBSD와 같은 세부 변종을 식별해야 할 때는 CMAKE_SYSTEM_NAME 변수를 사용합니다. 이 변수는 운영체제의 이름을 문자열 형태로 담고 있습니다.
| 플랫폼 | CMAKE_SYSTEM_NAME 값 |
|---|---|
| Windows | "Windows" |
| Linux | "Linux" |
| macOS | "Darwin" |
| Android | "Android" |
| FreeBSD | "FreeBSD" |
문자열 비교 방식을 사용하면 정규 표현식을 통한 패턴 매칭이 가능해져 유연성이 높아집니다.
# 문자열 일치 여부 확인
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(OS_TYPE "LINUX_DISTRO")
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(OS_TYPE "MACOS")
endif()
# 정규식을 이용한 다양한 BSD 계열 판별
if(CMAKE_SYSTEM_NAME MATCHES ".*BSD")
message(STATUS "BSD 계열 시스템 감지됨: ${CMAKE_SYSTEM_NAME}")
add_definitions(-DBSD_COMPATIBLE)
endif()
3. 실무 적용: 플랫폼별 경로 및 매크로 최적화
실제 프로젝트에서는 운영체제에 따라 디렉토리 구분자나 컴파일러 플래그를 다르게 설정해야 하는 경우가 빈번합니다. 아래 코드는 시스템 환경에 따라 경로 구분자와 전용 매크로를 정의하는 실무적인 구조를 보여줍니다.
# 시스템별 경로 구분자 및 환경 설정
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(DIR_DELIMITER "\\")
# 윈도우 최소 매크로 정의로 빌드 속도 개선 및 충돌 방지
add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
else()
set(DIR_DELIMITER "/")
# 유닉스 계열 공통 보안 플래그 추가
add_compile_options(-fPIC)
endif()
# 설정된 구분자 로그 출력
message(STATUS "현재 시스템의 경로 구분자: ${DIR_DELIMITER}")
# 플랫폼 독립적인 헤더 위치 지정
target_include_directories(CoreEngine PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include/common"
"${CMAKE_CURRENT_SOURCE_DIR}/include/platforms/${CMAKE_SYSTEM_NAME}"
)
4. 아키텍처 식별과 조합하기
운영체제 판별만으로 부족할 때는 CMAKE_SYSTEM_PROCESSOR를 함께 사용하여 CPU 아키텍처(x86_64, ARM64 등)에 따른 최적화를 수행할 수 있습니다.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|aarch64")
message(STATUS "리눅스 ARM 환경 빌드 - NEON 최적화 활성화")
add_compile_options(-mfpu=neon)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
message(STATUS "리눅스 x64 환경 빌드 - SSE4.2 활성화")
add_compile_options(-msse4.2)
endif()
endif()
이와 같이 CMake의 내장 변수들을 조합하면 플랫폼에 종속적인 코드를 깔끔하게 분리하고, 유지보수가 쉬운 크로스 플랫폼 빌드 스크립트를 완성할 수 있습니다.