CMake实践:指定gcc版本编译和交叉编译

发布于:2025-06-20 ⋅ 阅读:(24) ⋅ 点赞:(0)

目录

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.0

cmake --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} --version
    OUTPUT_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 目标系统名称(如 LinuxWindowsDarwin
CMAKE_SYSTEM_PROCESSOR 目标处理器架构(如 armaarch64x86_64
CMAKE_C_COMPILER C 编译器路径
CMAKE_CXX_COMPILER C++ 编译器路径

2.可选配置项

变量名 描述
CMAKE_SYSROOT 目标系统根目录(包含 includelib 等目录)
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_compile
    SOURCES ${CMAKE_CURRENT_LIST_DIR}/test.c
    OUTPUT_VARIABLE COMPILE_OUTPUT)

if(NOT COMPILER_WORKS)
    message(FATAL_ERROR "编译测试失败: ${COMPILE_OUTPUT}")
endif()

2.5.相关资源

相关链接


网站公告

今日签到

点亮在社区的每一天
去签到