C++ 크로스 플랫폼 빌드 | Windows·Linux·macOS·모바일 완벽 가이드 [#55-4]
이 글의 핵심
C++ 멀티 플랫폼 빌드 실전: CMake 툴체인, 조건부 컴파일, 플랫폼별 최적화, 모바일 빌드. Windows에서 빌드한 게 Linux에서 안 돌아가요 문제부터 프로덕션 패턴까지.
들어가며: Windows에서 빌드한 게 Linux에서 안 돌아가요
”팀원 A는 Windows, B는 macOS, C는 Linux인데 빌드가 매번 깨져요”
크로스 플랫폼 C++ 프로젝트를 운영하면 플랫폼마다 다른 경로 구분자·라이브러리·API 때문에 빌드가 실패하거나, 한 OS에서만 동작하는 코드가 섞여 들어갑니다. 비유하면 “한 나라에서 쓰는 전기 플러그를 다른 나라로 가져가면 어댑터가 필요해요”처럼, OS별로 빌드·실행 환경이 다르기 때문에 툴체인·조건부 컴파일·플랫폼 추상화가 필요합니다.
문제의 핵심:
- Windows:
\경로,LoadLibrary,ws2_32.dll, MSVC - Linux:
/경로,dlopen,pthread, GCC/Clang - macOS:
/경로,dlopen,pthread, Clang, Xcode 특별 규칙 - 모바일: iOS/Android 각각 별도 툴체인·SDK
이 글에서 다루는 것:
- 문제 시나리오: 실무에서 겪는 크로스 플랫폼 빌드 고통
- CMake 툴체인: 플랫폼별 컴파일러·SDK 설정
- 조건부 컴파일:
#ifdef로 플랫폼별 코드 분리 - 완전한 크로스 플랫폼 예제: Windows·Linux·macOS·모바일 빌드
- 자주 발생하는 에러와 해결법
- 모범 사례와 프로덕션 패턴
요구 환경: C++17 이상, CMake 3.16+
이 글을 읽으면:
- 핵심 개념을 이해할 수 있습니다.
- 실전에서 활용할 수 있습니다.
- 성능을 최적화할 수 있습니다.
실무 적용 경험: 이 글은 대규모 C++ 프로젝트에서 실제로 겪은 문제와 해결 과정을 바탕으로 작성되었습니다. 책이나 문서에서 다루지 않는 실전 함정과 디버깅 팁을 포함합니다.
목차
1. 문제 시나리오: 크로스 플랫폼 빌드가 깨지는 순간
시나리오 1: 경로 구분자로 인한 파일 열기 실패
문제: Windows에서 path/to/file.txt 같은 경로로 파일을 열면 std::ifstream이 실패합니다. Windows는 \를 구분자로 쓰지만, Linux는 /를 씁니다. 하드코딩된 경로로는 한 플랫폼에서만 동작합니다.
해결: std::filesystem::path를 사용하면 OS가 자동으로 올바른 구분자를 사용합니다. /를 사용해도 Windows는 내부적으로 처리합니다.
시나리오 2: Windows에서만 “undefined reference to pthread”
문제: Linux에서 -lpthread로 링크한 코드가 Windows에서 빌드됩니다. Windows에는 pthread가 없고, MSVC는 pthread를 기본으로 지원하지 않습니다. find_package(Threads)를 쓰면 CMake가 플랫폼별로 올바른 스레드 라이브러리를 링크합니다.
해결: CMake에서 target_link_libraries(myapp PRIVATE Threads::Threads)로 플랫폼별 스레드 라이브러리를 자동 링크합니다.
시나리오 3: 동적 라이브러리 확장자 불일치
문제: 플러그인을 로드할 때 plugin.so로만 찾습니다. Windows에서는 plugin.dll, macOS에서는 plugin.dylib이 필요합니다. 확장자를 하드코딩하면 다른 OS에서 로드 실패합니다.
해결: CMAKE_SHARED_LIBRARY_SUFFIX를 사용하거나, 플랫폼별 매크로로 확장자를 분기합니다.
시나리오 4: 모바일 크로스 컴파일 실패
문제: 데스크톱용 CMake로 iOS·Android 앱을 빌드하려 합니다. arm64·armv7 아키텍처, iOS SDK 경로, Android NDK 경로가 설정되지 않아 빌드가 실패합니다.
해결: CMake 툴체인 파일(-DCMAKE_TOOLCHAIN_FILE=<path>)로 iOS/Android 전용 컴파일러·SDK를 지정합니다.
시나리오 5: 플랫폼별 API 혼용
문제: #ifdef _WIN32 블록 안에 CreateFile을 쓰고, Linux 블록에 open을 쓰는데, 공통 헤더에서 한쪽만 include되어 컴파일 에러가 납니다.
해결: 플랫폼 추상화 레이어를 두고, Platform::openFile() 같은 공통 인터페이스로 감싸서 플랫폼별 구현을 분리합니다.
시나리오 6: ABI/호환성 문제
문제: Windows에서 MSVC로 빌드한 DLL을 MinGW로 빌드한 앱에서 로드하려 합니다. C++ ABI·표준 라이브러리(libstdc++ vs MSVCRT)가 달라 크래시가 발생합니다.
해결: C ABI로 경계를 만들거나, 같은 툴체인으로 빌드합니다. 플러그인·DLL은 extern "C"로 내보내는 것이 안전합니다.
2. 기본 개념
플랫폼 감지 매크로
// 플랫폼별 매크로 — 컴파일러마다 정의됨
// Windows: _WIN32 (32비트·64비트 공통), _WIN64 (64비트만)
// Linux: __linux__
// macOS: __APPLE__ (macOS + iOS), __MACH__ (Mach 커널)
// Android: __ANDROID__
// iOS: __APPLE__ && TARGET_OS_IPHONE
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM_WINDOWS 1
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define PLATFORM_IOS 1
#else
#define PLATFORM_MACOS 1
#endif
#elif defined(__linux__)
#if defined(__ANDROID__)
#define PLATFORM_ANDROID 1
#else
#define PLATFORM_LINUX 1
#endif
#else
#define PLATFORM_UNKNOWN 1
#endif
주의: _WIN32는 64비트 Windows에서도 정의됩니다. _WIN64만으로 64비트를 구분하는 것은 권장하지 않습니다. _WIN32가 있으면 Windows로 간주합니다.
CMake 플랫폼 변수
# CMake에서 플랫폼 감지
if(WIN32)
message(STATUS "Building for Windows")
elseif(APPLE)
message(STATUS "Building for macOS or iOS")
elseif(UNIX AND NOT APPLE)
message(STATUS "Building for Linux")
endif()
# 아키텍처
message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
# x86_64, AMD64, ARM64, aarch64 등
크로스 플랫폼 빌드 흐름
flowchart TB
subgraph source[소스 코드]
S1[공통 C++ 코드]
S2[플랫폼별 #ifdef]
S3[추상화 레이어]
end
subgraph cmake[CMake]
C1[CMakeLists.txt]
C2[툴체인 파일]
C3[플랫폼별 옵션]
end
subgraph targets[타겟 플랫폼]
T1[Windows .exe/.dll]
T2[Linux .so]
T3[macOS .dylib]
T4[iOS/Android]
end
S1 --> C1
S2 --> C1
S3 --> C1
C1 --> C2
C1 --> C3
C2 --> T1
C2 --> T2
C2 --> T3
C2 --> T4
3. 핵심 구현
3.1 플랫폼별 경로 처리
// platform_path.hpp — std::filesystem으로 크로스 플랫폼 경로
#include <filesystem>
#include <string>
namespace platform {
inline std::string joinPath(const std::string& a, const std::string& b) {
return (std::filesystem::path(a) / b).string();
}
inline std::string getExecutableDir() {
// C++17 이상: std::filesystem::current_path() 또는
// 플랫폼별 API로 실행 파일 경로 획득
#if defined(_WIN32)
return std::filesystem::current_path().string();
#else
return std::filesystem::current_path().string();
#endif
}
} // namespace platform
실용 팁: std::filesystem::path는 /를 사용해도 Windows에서 \로 변환합니다. path("foo") / "bar"는 모든 플랫폼에서 동작합니다.
3.2 플랫폼별 동적 로딩
// platform_dlopen.hpp — 플러그인 로드 추상화
#include <string>
#include <functional>
#if defined(_WIN32)
#include <windows.h>
using ModuleHandle = HMODULE;
#define LOAD_LIB(name) LoadLibraryA(name)
#define GET_SYMBOL(handle, name) GetProcAddress(handle, name)
#define FREE_LIB(handle) FreeLibrary(handle)
#else
#include <dlfcn.h>
using ModuleHandle = void*;
#define LOAD_LIB(name) dlopen(name, RTLD_NOW | RTLD_LOCAL)
#define GET_SYMBOL(handle, name) dlsym(handle, name)
#define FREE_LIB(handle) dlclose(handle)
#endif
// 플랫폼별 라이브러리 확장자
inline std::string getSharedLibExtension() {
#if defined(_WIN32)
return ".dll";
#elif defined(__APPLE__)
return ".dylib";
#else
return ".so";
#endif
}
3.3 조건부 컴파일 패턴
// network_platform.hpp — 소켓 초기화 (Windows는 WSA 필요)
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
class NetworkPlatform {
public:
static void init() {
#if defined(_WIN32)
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
throw std::runtime_error("WSAStartup failed");
}
#endif
}
static void shutdown() {
#if defined(_WIN32)
WSACleanup();
#endif
}
};
4. 완전한 크로스 플랫폼 예제
4.1 최소 CMakeLists.txt (데스크톱 3대)
cmake_minimum_required(VERSION 3.16)
project(CrossPlatformApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
# 플랫폼별 라이브러리
if(WIN32)
# Windows: ws2_32 (소켓)
set(PLATFORM_LIBS ws2_32)
elseif(UNIX AND NOT APPLE)
# Linux: pthread
find_package(Threads REQUIRED)
set(PLATFORM_LIBS Threads::Threads)
elseif(APPLE)
find_package(Threads REQUIRED)
set(PLATFORM_LIBS Threads::Threads)
endif()
add_executable(app main.cpp)
target_link_libraries(app PRIVATE ${PLATFORM_LIBS})
# 플랫폼별 컴파일 정의
if(WIN32)
target_compile_definitions(app PRIVATE PLATFORM_WINDOWS=1)
elseif(APPLE)
target_compile_definitions(app PRIVATE PLATFORM_APPLE=1)
else()
target_compile_definitions(app PRIVATE PLATFORM_LINUX=1)
endif()
4.2 전체 동작 예제: main.cpp
// main.cpp — 실행 시 플랫폼 정보 출력
#include <iostream>
#include <filesystem>
#include <string>
#if defined(_WIN32)
#define PLATFORM_NAME "Windows"
#elif defined(__APPLE__)
#define PLATFORM_NAME "macOS"
#elif defined(__linux__)
#define PLATFORM_NAME "Linux"
#else
#define PLATFORM_NAME "Unknown"
#endif
int main() {
std::cout << "Platform: " << PLATFORM_NAME << "\n";
std::cout << "CWD: " << std::filesystem::current_path().string() << "\n";
// 경로 결합 — 모든 플랫폼에서 동작
auto path = std::filesystem::path("config") / "settings.json";
std::cout << "Config path: " << path.string() << "\n";
return 0;
}
4.3 iOS 툴체인 파일
# toolchains/ios.toolchain.cmake (예시)
set(CMAKE_SYSTEM_NAME iOS)
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
set(CMAKE_OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}" CACHE STRING "iOS architectures")
set(CMAKE_OSX_SYSROOT "iphoneos" CACHE STRING "iOS SDK")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
실제 사용: cmake -B build-ios -DCMAKE_TOOLCHAIN_FILE=toolchains/ios.toolchain.cmake
4.4 Android NDK 툴체인
# Android 빌드 예시
# cmake -B build-android \
# -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
# -DANDROID_ABI=arm64-v8a \
# -DANDROID_PLATFORM=android-21
4.5 플랫폼 추상화 레이어 예제
// file_platform.hpp — 파일 열기 추상화
#include <string>
#include <fstream>
#include <filesystem>
namespace platform {
inline bool fileExists(const std::string& path) {
return std::filesystem::exists(path);
}
inline std::string readFile(const std::string& path) {
std::ifstream f(path);
if (!f) return "";
return std::string((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
}
} // namespace platform
5. 자주 발생하는 에러와 해결법
에러 1: “fatal error: ‘unistd.h’ file not found” (Windows)
원인: unistd.h는 POSIX 전용 헤더입니다. Windows(MSVC)에는 없습니다.
해결:
// ❌ 잘못된 코드
#include <unistd.h> // Windows에서 없음
// ✅ 올바른 코드
#if defined(_WIN32)
#include <io.h>
#include <process.h>
#define close _close
#define read _read
#else
#include <unistd.h>
#endif
또는 std::filesystem·std::thread 등 표준 라이브러리로 대체합니다.
에러 2: “undefined reference to dlopen” (Linux)
원인: dlopen는 libdl에 있습니다. 링크하지 않으면 에러가 납니다.
해결:
# CMakeLists.txt
if(UNIX AND NOT APPLE)
target_link_libraries(app PRIVATE dl)
endif()
참고: find_package(Threads)처럼 find_library(DL_LIBRARY dl)로 찾을 수도 있습니다.
에러 3: “DLL not found” 또는 “dyld: Library not loaded” (런타임)
원인: 동적 라이브러리 경로가 PATH(Windows) 또는 LD_LIBRARY_PATH(Linux)·DYLD_LIBRARY_PATH(macOS)에 없습니다.
해결:
# Linux
export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH
./app
# macOS
export DYLD_LIBRARY_PATH=/path/to/libs:$DYLD_LIBRARY_PATH
./app
# Windows: 실행 파일와 같은 디렉터리에 DLL을 두거나 PATH에 추가
에러 4: “symbol not found” (macOS)
원인: macOS는 기본적으로 모든 심볼이 숨겨져 있습니다. DLL을 내보내려면 __attribute__((visibility("default")))가 필요합니다.
해결:
#if defined(__APPLE__)
#define EXPORT_API __attribute__((visibility("default")))
#elif defined(_WIN32)
#define EXPORT_API __declspec(dllexport)
#else
#define EXPORT_API __attribute__((visibility("default")))
#endif
extern "C" EXPORT_API void my_exported_function();
에러 5: Windows에서 “LNK2019: unresolved external symbol”
원인: 라이브러리를 링크하지 않았거나, __declspec(dllimport)/dllexport가 누락되었습니다.
해결:
// mylib.h
#if defined(_WIN32)
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API
#endif
MYLIB_API void my_function();
# DLL 빌드 시
add_library(mylib SHARED mylib.cpp)
target_compile_definitions(mylib PRIVATE MYLIB_EXPORTS)
에러 6: endianness·정렬 문제
원인: ARM과 x86의 바이트 순서가 다를 수 있습니다. 바이너리 포맷으로 파일·네트워크에 쓸 때 문제가 됩니다.
해결:
#include <cstdint>
inline uint32_t swapEndian(uint32_t x) {
return ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) |
((x << 8) & 0xff0000) | ((x << 24) & 0xff000000);
}
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define TO_NETWORK_ORDER(x) (x)
#else
#define TO_NETWORK_ORDER(x) swapEndian(x)
#endif
에러 7: MinGW에서 “undefined reference to WinMain”
원인: Windows GUI 앱이 아닌데 -mwindows로 링크했거나, 콘솔 앱인데 진입점이 잘못 설정되었습니다.
해결:
# 콘솔 앱인 경우
add_executable(app WIN32 main.cpp) # WIN32 제거
# 또는
add_executable(app main.cpp)
# -mwindows 사용하지 않음
에러 8: macOS에서 “code signing” 오류
원인: iOS·macOS 앱은 코드 서명이 필요합니다. 개발 빌드에서도 서명 없이 실행하려면 설정이 필요합니다.
해결:
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
6. 모범 사례
6.1 플랫폼별 코드 최소화
// ❌ 나쁜 예: 플랫폼별 코드가 산재
void doWork() {
#ifdef _WIN32
// Windows 전용 50줄
#else
// Unix 전용 50줄
#endif
}
// ✅ 좋은 예: 추상화 레이어로 분리
void doWork() {
platform::init();
platform::execute();
platform::cleanup();
}
6.2 std::filesystem 사용
// ❌ 나쁜 예
std::string path = "config" + std::string(1, '/') + "file.txt"; // Windows에서 문제
// ✅ 좋은 예
auto path = std::filesystem::path("config") / "file.txt";
6.3 CI에서 멀티 플랫폼 빌드
# GitHub Actions 예시
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Configure
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build
6.4 툴체인 파일 분리
project/
├── toolchains/
│ ├── linux.toolchain.cmake
│ ├── mingw.toolchain.cmake
│ ├── ios.toolchain.cmake
│ └── android.toolchain.cmake
├── CMakeLists.txt
└── src/
6.5 헤더 가드와 플랫폼 매크로
// ❌ 나쁜 예: 플랫폼별 헤더를 조건 없이 include
#include <windows.h> // Linux에서 컴파일 실패
// ✅ 좋은 예: 조건부 include
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#endif
6.6 정수 타입 크기
// 플랫폼마다 long 크기가 다를 수 있음 (Windows: 4바이트, Linux 64비트: 8바이트)
// ✅ 고정 크기 타입 사용
#include <cstdint>
int64_t value; // 항상 8바이트
uint32_t count; // 항상 4바이트
6.7 빌드 검증 시퀀스
sequenceDiagram
participant Dev as 개발자
participant CMake as CMake
participant CC as 컴파일러
participant CI as CI
Dev->>CMake: cmake -B build
CMake->>CMake: 플랫폼 감지
CMake->>CMake: 툴체인 설정
CMake-->>Dev: Makefile/솔루션 생성
Dev->>CC: cmake --build build
CC-->>Dev: 바이너리
Dev->>CI: push
CI->>CMake: 매트릭스 빌드 (Win/Linux/macOS)
CMake->>CC: 각 플랫폼 빌드
CI-->>Dev: 빌드 결과
7. 프로덕션 패턴
7.1 통합 빌드 스크립트
#!/bin/bash
# build-all.sh — 모든 플랫폼 빌드 (Linux/macOS에서 실행)
set -e
BUILD_DIR=build
PLATFORMS="linux windows macos"
for p in $PLATFORMS; do
echo "Building for $p..."
case $p in
linux)
cmake -B $BUILD_DIR/linux -DCMAKE_BUILD_TYPE=Release
cmake --build $BUILD_DIR/linux
;;
windows)
cmake -B $BUILD_DIR/windows -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw.cmake
cmake --build $BUILD_DIR/windows
;;
macos)
cmake -B $BUILD_DIR/macos -DCMAKE_BUILD_TYPE=Release
cmake --build $BUILD_DIR/macos
;;
esac
done
7.2 버전·플랫폼 정보 주입
# CMakeLists.txt
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
else()
set(GIT_HASH "unknown")
endif()
target_compile_definitions(app PRIVATE
GIT_HASH="${GIT_HASH}"
BUILD_PLATFORM="${CMAKE_SYSTEM_NAME}"
)
7.3 플랫폼별 패키징
# CMakeLists.txt
if(WIN32)
install(TARGETS app RUNTIME DESTINATION bin)
install(FILES $<TARGET_FILE:mylib> DESTINATION bin)
elseif(APPLE)
install(TARGETS app RUNTIME DESTINATION bin)
install(FILES $<TARGET_FILE:mylib> DESTINATION lib)
else()
install(TARGETS app RUNTIME DESTINATION bin)
install(FILES $<TARGET_FILE:mylib> DESTINATION lib)
endif()
7.4 테스트 매트릭스
# 테스트: 각 플랫폼에서 동일 테스트 실행
enable_testing()
add_test(NAME PlatformTest COMMAND app --test)
7.5 플랫폼별 설치 경로
# GNU 스타일 설치 레이아웃 (Linux/macOS)
if(UNIX)
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_INCLUDEDIR "include")
endif()
# Windows: Program Files 또는 사용자 지정
if(WIN32)
set(CMAKE_INSTALL_PREFIX "C:/Program Files/MyApp" CACHE PATH "")
endif()
7.6 CPack 패키징 (크로스 플랫폼)
# CMakeLists.txt 하단에 추가
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "MyCompany")
if(WIN32)
set(CPACK_GENERATOR "NSIS;ZIP")
elseif(APPLE)
set(CPACK_GENERATOR "DragNDrop;TGZ")
else()
set(CPACK_GENERATOR "DEB;RPM;TGZ")
endif()
include(CPack)
7.7 환경 변수 기반 툴체인 선택
#!/bin/bash
# build.sh — 환경 변수로 타겟 선택
TARGET=${1:-native}
case $TARGET in
native)
cmake -B build
;;
windows)
cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw64.cmake
;;
android)
cmake -B build -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a
;;
*)
echo "Unknown target: $TARGET"
exit 1
;;
esac
cmake --build build
8. 성능 최적화
8.1 플랫폼별 최적화 플래그
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
target_compile_options(app PRIVATE -march=native)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
target_compile_options(app PRIVATE -mcpu=native)
endif()
elseif(MSVC)
target_compile_options(app PRIVATE /arch:AVX2)
endif()
8.2 조건부 기능 활성화
# 플랫폼별로 불필요한 기능 비활성화
if(NOT WIN32)
add_definitions(-DHAVE_POSIX_SIGNALS=1)
endif()
8.3 크로스 컴파일 시 네이티브 타겟 빌드
# Android 빌드 시 호스트 도구(native)도 함께 빌드
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND ANDROID)
add_executable(host_tool host_main.cpp)
set_target_properties(host_tool PROPERTIES
EXCLUDE_FROM_ALL TRUE
)
endif()
8.4 LTO(Link Time Optimization) 플랫폼별 설정
# 릴리스 빌드에서 LTO 활성화
if(CMAKE_BUILD_TYPE STREQUAL "Release")
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported)
if(ipo_supported)
set_property(TARGET app PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
8.5 플랫폼별 디버그 심볼
if(MSVC)
target_compile_options(app PRIVATE /Zi)
target_link_options(app PRIVATE /DEBUG)
else()
target_compile_options(app PRIVATE -g)
endif()
9. 정리
| 항목 | 설명 |
|---|---|
| 플랫폼 감지 | _WIN32, __linux__, __APPLE__ 등 매크로 |
| 경로 | std::filesystem::path로 크로스 플랫폼 |
| 동적 로딩 | LoadLibrary/dlopen 추상화 |
| CMake | WIN32, APPLE, UNIX 변수 |
| 툴체인 | -DCMAKE_TOOLCHAIN_FILE로 크로스 컴파일 |
| 에러 | unistd.h·dlopen·DLL 경로·visibility |
핵심 원칙:
- 플랫폼별 코드 최소화: 추상화 레이어로 분리
- 표준 라이브러리 우선:
std::filesystem,std::thread - CMake 툴체인 활용: iOS·Android·MinGW 등
- CI에서 멀티 플랫폼 빌드: 매트릭스 빌드로 검증
구현 체크리스트
-
std::filesystem으로 경로 처리 - 플랫폼별 매크로로
#ifdef분기 -
target_link_libraries에 플랫폼별 라이브러리 추가 - 동적 로딩 시 확장자(
.dll/.so/.dylib) 분기 -
extern "C"로 ABI 안정성 확보 (플러그인·DLL) - iOS·Android 툴체인 파일 준비
- CI에서 Windows·Linux·macOS 빌드 검증
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. 크로스 플랫폼 라이브러리, 멀티 OS 지원 앱, 모바일 게임 등에 활용합니다. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
Q. vcpkg·Conan과 크로스 플랫폼 빌드를 함께 쓰려면?
A. vcpkg는 vcpkg install --triplet x64-windows 등으로 타겟을 지정합니다. Conan은 conan install . -s os=Linux -s arch=x86_64처럼 프로필로 지정합니다. CMake에서 find_package로 찾을 때 툴체인과 일치하는 triplet·프로필을 사용해야 합니다.
Q. 프로덕션에서 주의할 점은?
A. (1) CI에서 모든 지원 플랫폼을 매트릭스로 빌드해 검증하고, (2) 플랫폼별 바이너리를 별도 아티팩트로 배포하며, (3) ABI 호환성이 필요한 플러그인·DLL은 extern "C"로 경계를 만듭니다.
한 줄 요약: Windows·Linux·macOS·모바일를 마스터할 수 있습니다.
관련 글
- C++ 크로스 플랫폼 기초 완벽 가이드 | 플랫폼 감지·std::filesystem
- C++ 크로스 플랫폼 빌드 완벽 가이드 | CMake 툴체인·CPack·ABI 안정성·프로덕션 패턴
- C++ 크로스 플랫폼 테스트 완벽 가이드 | CI 매트릭스·Docker·엔디안·프로덕션 패턴 [실전]