目录
1.指定gcc版本编译
1.1.通过CMake参数来实现
1.2.使用 RPATH/RUNPATH 直接指定库路径
1.3.使用符号链接和 LD_LIBRARY_PATH
1.4.使用 wrapper 脚本封装 LD_LIBRARY_PATH
2.交叉编译
2.1.基本用法
2.2.工具链文件关键配置
2.3.多平台工具链示例
2.4.注意事项
2.5.相关资源
相关链接
1.指定gcc版本编译
背景
现在项目上碰到一种情况,在麒麟V4系统上正在维护的程序是gcc5.4.0编译出来的,现在增加一个需求需要用到了一个第三方库,第三方库编译需要C++20支持,那么就必须升级gcc到gcc9.3.0,安装完gcc9.3.0,因为程序一直是在gcc5.4.0环境下编译的,代码太多,用gcc9.3.0编译怕出问题,又不想改它。于是在相当一段时间,系统中共存gcc5.4.0和gcc9.3.0两个版本,在CMake编译的时候就需要指定gcc版本,下面就来说说实现方案。
1.1.通过CMake参数来实现
1.通过环境变量指定编译器
临时设置 CC
和 CXX
环境变量,仅对当前终端会话有效:
# 设置环境变量指向 GCC 9.3.0
export CC=/usr/bin/gcc-9.3.0
export CXX=/usr/bin/g++-9.3.0# 运行 CMake
cmake -S . -B build
cmake --build build
如果 GCC 9.3.0 的路径不是 /usr/bin/gcc-9.3.0
,可以通过以下命令查找:
find /usr -name "gcc-9.3.0" 2>/dev/null # 查找 GCC 9.3.0
find /usr -name "g++-9.3.0" 2>/dev/null # 查找 G++ 9.3.0或whichis gcc
whichis g++
2.在 CMake 命令中直接指定
cmake -S . -B build \-DCMAKE_C_COMPILER=/usr/bin/gcc-9.3.0 \-DCMAKE_CXX_COMPILER=/usr/bin/g++-9.3.0cmake --build build
3.使用工具链文件(推荐)
创建一个工具链文件(如 gcc93.toolchain.cmake
),集中管理编译器配置:
# gcc93.toolchain.cmake
set(CMAKE_C_COMPILER /usr/bin/gcc-9.3.0)
set(CMAKE_CXX_COMPILER /usr/bin/g++-9.3.0)# 可选:指定 C/C++ 标准
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
然后在运行 CMake 时指定该工具链文件:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=/path/to/gcc93.toolchain.cmake
cmake --build build
验证编译器版本
在 CMake 配置过程中,可以通过打印编译器版本来确认是否使用了正确的 GCC:
cd build
cmake -LA | grep "CMAKE_C.*COMPILER" # 查看 C/C++ 编译器配置
或者在 CMakeLists.txt
中添加以下代码:
message(STATUS "C 编译器: ${CMAKE_C_COMPILER}")
message(STATUS "C++ 编译器: ${CMAKE_CXX_COMPILER}")# 打印编译器版本
execute_process(COMMAND ${CMAKE_C_COMPILER} --versionOUTPUT_VARIABLE gcc_version
)
message(STATUS "GCC 版本: ${gcc_version}")
1.2.使用 RPATH/RUNPATH 直接指定库路径
通过在编译时设置 RPATH
或 RUNPATH
,让可执行程序直接从指定路径加载动态库。
示例步骤:
1.创建版本化的库目录
mkdir -p /opt/libfoo/v1 /opt/libfoo/v2
cp libfoo.so.1.0.0 /opt/libfoo/v1/
cp libfoo.so.2.0.0 /opt/libfoo/v2/
2.编译时指定 RPATH
# 程序 A 链接 v1
g++ -o program_a src/a.cpp -L/opt/libfoo/v1 -lfoo \-Wl,-rpath=/opt/libfoo/v1# 程序 B 链接 v2
g++ -o program_b src/b.cpp -L/opt/libfoo/v2 -lfoo \-Wl,-rpath=/opt/libfoo/v2
3.验证链接的库版本
ldd program_a # 应显示 /opt/libfoo/v1/libfoo.so.1
ldd program_b # 应显示 /opt/libfoo/v2/libfoo.so.2
1.3.使用符号链接和 LD_LIBRARY_PATH
通过环境变量 LD_LIBRARY_PATH
临时覆盖系统默认库搜索路径。
示例步骤:
1.创建版本化的库目录
mkdir -p ~/libfoo/v1 ~/libfoo/v2
cp libfoo.so.1.0.0 ~/libfoo/v1/
cp libfoo.so.2.0.0 ~/libfoo/v2/# 创建符号链接
cd ~/libfoo/v1 && ln -s libfoo.so.1.0.0 libfoo.so.1
cd ~/libfoo/v2 && ln -s libfoo.so.2.0.0 libfoo.so.2
2.编译程序(不指定 RPATH)
# 程序 A 链接 v1
g++ -o program_a src/a.cpp -L~/libfoo/v1 -lfoo# 程序 B 链接 v2
g++ -o program_b src/b.cpp -L~/libfoo/v2 -lfoo
3.运行时指定库路径
# 运行程序 A(使用 v1)
LD_LIBRARY_PATH=~/libfoo/v1 ./program_a# 运行程序 B(使用 v2)
LD_LIBRARY_PATH=~/libfoo/v2 ./program_b
1.4.使用 wrapper 脚本封装 LD_LIBRARY_PATH
创建脚本包装程序执行,自动设置正确的库路径。
示例步骤:
1.创建 wrapper 脚本
# program_a_wrapper.sh
#!/bin/bash
LD_LIBRARY_PATH=~/libfoo/v1:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
exec ~/bin/program_a "$@"
# program_b_wrapper.sh
#!/bin/bash
LD_LIBRARY_PATH=~/libfoo/v2:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
exec ~/bin/program_b "$@"
2.添加执行权限
chmod +x program_a_wrapper.sh program_b_wrapper.sh
1.5.使用符号版本控制(高级)
通过修改库的符号版本信息,让不同版本的符号共存于同一库文件中。
示例步骤:
1.创建版本脚本(libfoo.map)
LIBFOO_1.0 {global:func_v1;local:*;
};LIBFOO_2.0 {global:func_v2;local:*;
} LIBFOO_1.0;
2.编译库时指定版本脚本
# 编译 libfoo.so.2 包含两个版本的符号
g++ -shared -fPIC -o libfoo.so.2.0.0 src/foo.cpp \-Wl,--version-script=libfoo.map
3.程序选择性链接特定版本符号
# 程序 A 链接 v1 符号
g++ -o program_a src/a.cpp -L. -lfoo -Wl,-u,func_v1# 程序 B 链接 v2 符号
g++ -o program_b src/b.cpp -L. -lfoo -Wl,-u,func_v2
2.交叉编译
CMAKE_TOOLCHAIN_FILE
是 CMake 中用于配置交叉编译环境的核心变量。通过指定该变量,可覆盖默认的编译器、工具链和系统搜索路径,实现为不同平台(如嵌入式设备、移动平台)构建软件。
2.1.基本用法
1.定义工具链文件
创建一个 .cmake
文件(如 arm_toolchain.cmake
),内容示例:
# 指定目标系统
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)# 指定交叉编译工具
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_AR arm-linux-gnueabihf-ar)
set(CMAKE_RANLIB arm-linux-gnueabihf-ranlib)# 指定系统根目录(可选)
set(CMAKE_SYSROOT /path/to/arm-sysroot)# 仅搜索 sysroot 中的文件(避免主机系统污染)
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)
2.调用 CMake 时指定工具链文件:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=path/to/arm_toolchain.cmake
2.2.工具链文件关键配置
1.必选配置项
变量名 | 描述 |
---|---|
CMAKE_SYSTEM_NAME | 目标系统名称(如 Linux 、Windows 、Darwin ) |
CMAKE_SYSTEM_PROCESSOR | 目标处理器架构(如 arm 、aarch64 、x86_64 ) |
CMAKE_C_COMPILER | C 编译器路径 |
CMAKE_CXX_COMPILER | C++ 编译器路径 |
2.可选配置项
变量名 | 描述 |
---|---|
CMAKE_SYSROOT | 目标系统根目录(包含 include 、lib 等目录) |
CMAKE_FIND_ROOT_PATH | 自定义搜索路径(覆盖 CMAKE_SYSROOT ) |
CMAKE_AR /CMAKE_RANLIB | 归档工具和索引工具(用于静态库) |
CMAKE_STRIP | strip 工具(用于去除调试信息) |
CMAKE_EXE_LINKER_FLAGS | 链接器选项(如 -static ) |
2.3.多平台工具链示例
1.ARM 嵌入式 Linux 工具链
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)# 编译器路径(根据实际工具链调整)
set(CMAKE_C_COMPILER /opt/gcc-arm-none-eabi/bin/arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER /opt/gcc-arm-none-eabi/bin/arm-none-eabi-g++)# 不使用系统库(裸机开发)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
2.Android NDK 工具链
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_VERSION 21) # API 级别
set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) # 架构# 指定 NDK 路径
set(CMAKE_ANDROID_NDK /path/to/android-ndk)# 使用 NDK 自带的 CMake 工具链
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake)
3.Windows 交叉编译(从 Linux 到 Windows)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)# 使用 mingw-w64 工具链
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) # Windows 资源编译器# 生成 .exe 后缀
set(CMAKE_EXECUTABLE_SUFFIX .exe)
2.4.注意事项
1.集中管理工具链文件:
将工具链文件放在项目根目录的 cmake/toolchains/
目录下,便于版本控制。
2.使用环境变量简化调用:
export MY_ARM_TOOLCHAIN=/path/to/arm_toolchain.cmake
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$MY_ARM_TOOLCHAIN
3.工具链文件可继承:
复杂项目可创建基础工具链文件,再通过 include()
包含特定平台的配置。
4.测试工具链有效性:
使用 try_compile()
在工具链文件中验证编译环境是否正常:
try_compile(COMPILER_WORKS ${CMAKE_BINARY_DIR}/test_compileSOURCES ${CMAKE_CURRENT_LIST_DIR}/test.cOUTPUT_VARIABLE COMPILE_OUTPUT)if(NOT COMPILER_WORKS)message(FATAL_ERROR "编译测试失败: ${COMPILE_OUTPUT}")
endif()
2.5.相关资源
-
CMake 官方工具链文档:
cmake-toolchains(7) — CMake 4.0.3 Documentation -
Android NDK 工具链示例:
https://developer.android.com/ndk/guides/cmake -
跨平台工具链集合:
https://github.com/leetal/ios-cmake(iOS 交叉编译)
https://github.com/lebedevRI/cmake-toolchains(各种平台工具链)
相关链接
- CMake 官网 CMake - Upgrade Your Software Build System
- CMake 官方文档:CMake Tutorial — CMake 4.0.3 Documentation
- CMake 源码:https://github.com/Kitware/CMake
- CMake 源码:CMake · GitLab
- 中文版基础介绍: CMake 入门实战 | HaHack
- wiki: Home · Wiki · CMake / Community · GitLab
- Modern CMake 简体中文版: Introduction · Modern CMake