Introduction
Have you ever encountered the following issues:
- • Need to run programs on embedded devices, but the development environment is x86_64
- • Different path separators for different platforms (Windows uses
<span>\</span>, Unix uses<span>/</span>) - • Platform-specific API calls require conditional compilation
- • Resource files (images, configuration files) are difficult to manage
- • Need to support multiple languages but unsure how to integrate internationalization
These issues are common challenges in cross-platform development.CMake provides a comprehensive solution for cross-platform development, from toolchain configuration to resource management, from platform adaptation to internationalization support.
This article will introduce best practices for cross-platform development with CMake:
- • Writing toolchain files to support cross-compilation
- • Handling platform-specific code and API differences
- • Managing resource files and platform-specific resources
- • Integrating internationalization (i18n) support
- • Building truly cross-platform projects
Why Cross-Platform Development is Necessary
Challenges of Cross-Platform Development
Differences in Hardware Architecture
# Different CPU architectures require different binaries
x86_64 # Desktop and server
ARM64 # Mobile devices and servers
ARM32 # Embedded devices
MIPS # Specific embedded scenarios
Differences in Operating Systems
- • Windows: path separator
<span>\</span>, dynamic library<span>.dll</span>, static library<span>.lib</span> - • Linux: path separator
<span>/</span>, dynamic library<span>.so</span>, static library<span>.a</span> - • macOS: path separator
<span>/</span>, dynamic library<span>.dylib</span>, static library<span>.a</span>
API Differences
// Windows specific API
#ifdef _WIN32
#include <windows.h>
Sleep(1000); // milliseconds
#endif
// Unix/Linux specific API
#ifdef __unix__
#include <unistd.h>
sleep(1); // seconds
#endif
Resource File Paths
// Windows path
std::string path = "C:\Program Files\MyApp\config.ini";
// Unix path
std::string path = "/usr/local/share/myapp/config.ini";
The Value of CMake for Cross-Platform Development
Unified Build Configuration
# A single CMakeLists.txt supports multiple platforms
cmake_minimum_required(VERSION 3.15)
project(MyApp)
# Automatically detect platform
if(WIN32)
# Windows specific configuration
elseif(APPLE)
# macOS specific configuration
elseif(UNIX)
# Linux specific configuration
endif()
Toolchain Abstraction
# Switch compilation target via toolchain file
cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux-gnueabihf.cmake ..
Platform-Independent Resource Management
# CMake automatically handles path differences
install(FILES resources/config.ini
DESTINATION ${CMAKE_INSTALL_DATADIR}/myapp
)
Core Advantages:
- • ✅ Write Once: Unified CMake configuration
- • ✅ Build in Multiple Places: Supports various platforms and architectures
- • ✅ Automatic Adaptation: CMake handles platform differences
- • ✅ Easy Maintenance: Centralized management reduces duplication
Writing Toolchain Files
Basics of Toolchain Files
A toolchain file is a key file used by CMake to configure cross-compilation, defining the compiler, linker, system root directory, and other information.
Structure of Toolchain Files
# arm-linux-gnueabihf.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Set cross-compiler
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Set toolchain root directory
set(CMAKE_FIND_ROOT_PATH /opt/arm-linux-gnueabihf)
# Only search for libraries in the toolchain
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
Usage
# Configure project using toolchain file
cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux-gnueabihf.cmake \
-S . -B build-arm
# Build
cmake --build build-arm
Cross-Compiling from Windows to Linux
# linux-cross.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Use compiler in WSL or Docker
set(CMAKE_C_COMPILER wsl gcc)
set(CMAKE_CXX_COMPILER wsl g++)
# Or use Docker
set(CMAKE_C_COMPILER docker run --rm gcc:latest gcc)
set(CMAKE_CXX_COMPILER docker run --rm gcc:latest g++)
Best Practices for Toolchain Files
1. Parameterized Configuration
# arm-linux-gnueabihf.cmake
# Allow overriding default values via command line parameters
if(NOTDEFINED ARM_TOOLCHAIN_ROOT)
set(ARM_TOOLCHAIN_ROOT /opt/arm-linux-gnueabihf)
endif()
if(NOTDEFINED ARM_COMPILER_PREFIX)
set(ARM_COMPILER_PREFIX arm-linux-gnueabihf)
endif()
set(CMAKE_C_COMPILER ${ARM_TOOLCHAIN_ROOT}/bin/${ARM_COMPILER_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${ARM_TOOLCHAIN_ROOT}/bin/${ARM_COMPILER_PREFIX}-g++)
# Usage
cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux-gnueabihf.cmake \
-DARM_TOOLCHAIN_ROOT=/custom/path \
..
2. Compiler Detection
# Check if the compiler exists
find_program(ARM_CC ${CMAKE_C_COMPILER})
if(NOT ARM_CC)
message(FATAL_ERROR "Cross-compiler not found: ${CMAKE_C_COMPILER}")
endif()
3. System Information Settings
# Set system version information
set(CMAKE_SYSTEM_VERSION "5.10")
set(CMAKE_SYSTEM_PROCESSOR "armv7l")
# Set C standard library
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
Cross-Compilation Configuration
Basics of Cross-Compilation
Cross-compilation is compiling a program for another platform on one platform. CMake achieves cross-compilation through toolchain files.
Basic Process
# 1. Prepare toolchain file
# arm-linux-gnueabihf.cmake
# 2. Configure project
cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux-gnueabihf.cmake \
-S . -B build-arm
# 3. Build
cmake --build build-arm
# 4. Verify
file build-arm/myapp
Cross-Compilation Adaptation in CMakeLists.txt
Detecting Cross-Compilation
# Check if cross-compiling
if(CMAKE_CROSSCOMPILING)
# Cross-compilation mode configuration
# Target system: ${CMAKE_SYSTEM_NAME}
# Target processor: ${CMAKE_SYSTEM_PROCESSOR}
endif()
Conditional Compilation
# Set compilation options based on target platform
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
# ARM specific optimizations
add_compile_options(-march=armv7-a -mfpu=neon)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
# x86_64 specific optimizations
add_compile_options(-march=native)
endif()
find_package Adaptation
# find_package needs special handling during cross-compilation
if(CMAKE_CROSSCOMPILING)
# Set search path
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# Manually specify library path
set(OPENSSL_ROOT_DIR ${CMAKE_FIND_ROOT_PATH}/usr)
find_package(OpenSSL REQUIRED)
else()
# Local compilation, normal search
find_package(OpenSSL REQUIRED)
endif()
Handling Platform-Specific Code
Platform Detection
CMake provides rich platform detection variables that can be used in CMakeLists.txt and C++ code.
Platform Detection in CMake
# Operating system detection
if(WIN32)
# Windows platform configuration
elseif(APPLE)
# macOS platform configuration
elseif(UNIX)
# Unix/Linux platform configuration
endif()
# Compiler detection
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# GCC compiler configuration
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Clang compiler configuration
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# MSVC compiler configuration
endif()
# Architecture detection
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
# x86_64 architecture configuration
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
# ARM architecture configuration
endif()
Platform Detection in C++ Code
// platform_detection.h
#ifndef PLATFORM_DETECTION_H
#define PLATFORM_DETECTION_H
// Windows platform
#ifdef _WIN32
#ifdef _WIN64
#define PLATFORM_WINDOWS_64
#else
#define PLATFORM_WINDOWS_32
#endif
#define PLATFORM_WINDOWS
#endif
// macOS platform
#ifdef __APPLE__
#include <TargetConditionals.h>
#if TARGET_OS_MAC
#define PLATFORM_MACOS
#endif
#endif
// Linux platform
#ifdef __linux__
#define PLATFORM_LINUX
#endif
// Unix platform (including Linux and macOS)
#ifdef __unix__
#define PLATFORM_UNIX
#endif
#endif // PLATFORM_DETECTION_H
Platform-Specific Compilation Options
Set Compilation Options Based on Platform
# Windows
if(WIN32)
# MSVC
if(MSVC)
add_compile_options(/W4 /WX- /EHsc)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
# MinGW
if(MINGW)
add_compile_options(-Wall -Wextra)
endif()
endif()
# Unix/Linux
if(UNIX AND NOT APPLE)
add_compile_options(-Wall -Wextra -Wpedantic)
add_compile_definitions(_GNU_SOURCE)
endif()
# macOS
if(APPLE)
add_compile_options(-Wall -Wextra)
add_compile_definitions(_DARWIN_C_SOURCE)
endif()
Platform-Specific Link Libraries
# Add platform-specific libraries
if(WIN32)
target_link_libraries(myapp PRIVATE
ws2_32 # Windows Socket API
winmm # Windows Multimedia API
)
elif(APPLE)
target_link_libraries(myapp PRIVATE
"-framework Foundation"
"-framework CoreFoundation"
)
elif(UNIX)
target_link_libraries(myapp PRIVATE
pthread
rt
dl
)
endif()
Implementation of Platform-Specific Code
Using CMake to Generate Platform Configuration Header Files
# Generate platform configuration header file
configure_file(
"${CMAKE_SOURCE_DIR}/cmake/platform_config.h.in"
"${CMAKE_BINARY_DIR}/platform_config.h"
)
# Include directories
target_include_directories(myapp PRIVATE
${CMAKE_BINARY_DIR}
)
platform_config.h.in
#ifndef PLATFORM_CONFIG_H
#define PLATFORM_CONFIG_H
// Platform definitions
#cmakedefine PLATFORM_WINDOWS
#cmakedefine PLATFORM_LINUX
#cmakedefine PLATFORM_MACOS
// Compiler definitions
#cmakedefine COMPILER_GCC
#cmakedefine COMPILER_CLANG
#cmakedefine COMPILER_MSVC
// Architecture definitions
#cmakedefine ARCH_X86_64
#cmakedefine ARCH_ARM64
#endif // PLATFORM_CONFIG_H
Setting Definitions in CMakeLists.txt
# Set platform definitions
if(WIN32)
set(PLATFORM_WINDOWS ON)
elif(APPLE)
set(PLATFORM_MACOS ON)
elif(UNIX)
set(PLATFORM_LINUX ON)
endif()
# Set compiler definitions
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(COMPILER_GCC ON)
elif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(COMPILER_CLANG ON)
elif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(COMPILER_MSVC ON)
endif()
# Set architecture definitions
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
set(ARCH_X86_64 ON)
elif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
set(ARCH_ARM64 ON)
endif()
# Generate header file
configure_file(
"${CMAKE_SOURCE_DIR}/cmake/platform_config.h.in"
"${CMAKE_BINARY_DIR}/platform_config.h"
)
Usage in C++ Code
// platform.cpp
#include "platform_config.h"
#ifdef PLATFORM_WINDOWS
#include <windows.h>
void sleep_ms(int ms) {
Sleep(ms);
}
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
#include <unistd.h>
void sleep_ms(int ms) {
usleep(ms * 1000);
}
#endif
Internationalization Support
Basics of Internationalization
Internationalization (i18n) refers to making software capable of supporting multiple languages and regions.
CMake Configuration for Internationalization
# Set translation source files
set(TRANSLATION_SOURCES
src/main.cpp
src/ui.cpp
)
# Extract translation strings
gettext_create_translations(
PO_FILES
${TRANSLATION_SOURCES}
POT_FILE myapp.pot
UPDATE_PO_FILES
)
# Install translation files
install(FILES ${PO_FILES}
DESTINATION ${CMAKE_INSTALL_DATADIR}/locale
)
Common Questions and Solutions
Q
Cannot find libraries during cross-compilation?

# Set search path in toolchain file
set(CMAKE_FIND_ROOT_PATH /opt/arm-linux-gnueabihf)
# Manually specify in CMakeLists.txt
if(CMAKE_CROSSCOMPILING)
set(OPENSSL_ROOT_DIR ${CMAKE_FIND_ROOT_PATH}/usr)
find_package(OpenSSL REQUIRED)
endif()
A

Q
Compilation errors for platform-specific APIs?

// Use conditional compilation
#ifdef _WIN32
#include<windows.h>
void sleep_ms(int ms) { Sleep(ms); }
#else
#include<unistd.h>
void sleep_ms(int ms) { usleep(ms * 1000); }
#endif

Quick Reference Manual
Platform Detection Variables
| Variable | Description |
<span>WIN32</span> |
Windows platform |
<span>APPLE</span> |
macOS/iOS platform |
<span>UNIX</span> |
Unix/Linux platform |
<span>CMAKE_SYSTEM_NAME</span> |
System name (Linux, Windows, Darwin) |
<span>CMAKE_SYSTEM_PROCESSOR</span> |
Processor architecture (x86_64, arm, aarch64) |
<span>CMAKE_CROSSCOMPILING</span> |
Whether cross-compiling |
Compiler Detection Variables
| Variable | Description |
<span>CMAKE_CXX_COMPILER_ID</span> |
Compiler ID (GNU, Clang, MSVC) |
<span>CMAKE_CXX_COMPILER_VERSION</span> |
Compiler version |
<span>MSVC</span> |
MSVC compiler |
<span>CMAKE_COMPILER_IS_GNUCXX</span> |
GCC compiler |
Tags: #CMake #Cross-Platform Development #Cross-Compilation #Toolchain #Internationalization #Resource Management
Conclusion
This article comprehensively covers best practices for cross-platform development with CMake, including:
Core Content:
- • ✅ Writing Toolchain Files: Support cross-compilation to different platforms
- • ✅ Cross-Compilation Configuration: Adaptation for ARM, Android, iOS, etc.
- • ✅ Handling Platform-Specific Code: Conditional compilation and platform adaptation
- • ✅ Resource File Management: Cross-platform resource installation and access
- • ✅ Internationalization Support: Gettext integration and multi-language support
Key Points:
- 1. Toolchain Files are fundamental for cross-compilation and need to be correctly configured for compilers and search paths
- 2. Platform Detection uses CMake variables and C++ macros to achieve conditional compilation
- 3. Resource Management needs to consider different paths for development and production environments
- 4. Internationalization is implemented through the Gettext toolchain, requiring POT, PO, and MO files
Related Resources:
- • CMake Cross-Compilation Documentation
- • Gettext Documentation
- • CMake Platform Detection Variables
- If you like it, follow us!
Give us a thumbs up!
Click on “See First” for the best content!