Linux 平台¶
使用 CCGO 为 Linux 构建 C++ 库的完整指南。
概述¶
CCGO 提供全面的 Linux 支持:
- 多编译器:GCC 和 Clang
- 多架构:x86_64、ARM64 (aarch64)、ARMv7
- 输出格式:静态库 (.a)、共享库 (.so)
- 构建方式:本地构建或 Docker(跨平台)
- IDE 支持:CodeLite 项目生成
- 发行版兼容:Ubuntu、Debian、CentOS、Fedora、Alpine
- C 库变体:支持 glibc 和 musl
前置条件¶
方式 1:本地构建(需要 Linux)¶
Ubuntu/Debian:
# 安装 GCC 工具链
sudo apt update
sudo apt install -y build-essential cmake pkg-config
# 安装 Clang(可选)
sudo apt install -y clang
# 验证安装
gcc --version
g++ --version
cmake --version
CentOS/Fedora/RHEL:
# 安装 GCC 工具链
sudo yum groupinstall -y "Development Tools"
sudo yum install -y cmake
# 或在 Fedora 上
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y cmake
# 安装 Clang(可选)
sudo yum install -y clang
# 或在 Fedora 上
sudo dnf install -y clang
# 验证
gcc --version
g++ --version
cmake --version
Alpine Linux:
# 安装 GCC 工具链(musl libc)
apk add --no-cache build-base cmake
# 安装 Clang(可选)
apk add --no-cache clang
# 验证
gcc --version
g++ --version
cmake --version
方式 2:Docker 构建(任意操作系统)¶
在 macOS 或 Windows 上使用 Docker 构建 Linux 库。
所需条件: - 已安装并运行 Docker Desktop - 3GB+ 磁盘空间用于 Docker 镜像
优势: - 可在任何操作系统上构建 - 一致的构建环境 - 无需 Linux 依赖 - 支持多个 Linux 发行版
限制: - 无法运行/测试应用程序(除非使用 Docker shell) - 首次下载较大(~800MB 镜像)
详见 Docker 构建 部分。
快速开始¶
基础构建¶
# 使用默认编译器为 x86_64 构建
ccgo build linux
# 使用 Docker 构建(从任意操作系统交叉编译)
ccgo build linux --docker
# 指定编译器
ccgo build linux --compiler gcc # GCC(默认)
ccgo build linux --compiler clang # Clang
ccgo build linux --compiler auto # 两个编译器都构建
# 指定架构
ccgo build linux --arch x86_64 # 64位 Intel/AMD(默认)
ccgo build linux --arch arm64 # 64位 ARM (aarch64)
ccgo build linux --arch armv7 # 32位 ARM
# 构建类型
ccgo build linux --build-type debug # Debug 构建
ccgo build linux --build-type release # Release 构建(默认)
# 链接类型
ccgo build linux --build-as static # 仅静态库
ccgo build linux --build-as shared # 仅共享库
ccgo build linux --build-as both # 两种类型(默认)
生成 CodeLite 项目¶
# 生成 CodeLite 工作区
ccgo build linux --ide-project
# 在 CodeLite 中打开
codelite cmake_build/linux/MyLib.workspace
输出结构¶
默认输出 (target/linux/)¶
target/linux/
├── MyLib_Linux_SDK-1.0.0.zip # 主包
│ ├── lib/
│ │ ├── static/
│ │ │ ├── gcc/
│ │ │ │ └── libmylib.a # GCC 静态库
│ │ │ └── clang/
│ │ │ └── libmylib.a # Clang 静态库
│ │ └── shared/
│ │ ├── gcc/
│ │ │ ├── libmylib.so # GCC 共享库
│ │ │ └── libmylib.so.1.0.0 # 版本化库
│ │ └── clang/
│ │ ├── libmylib.so
│ │ └── libmylib.so.1.0.0
│ ├── include/
│ │ └── mylib/ # 头文件
│ │ ├── mylib.h
│ │ └── version.h
│ └── build_info.json # 构建元数据
│
└── MyLib_Linux_SDK-1.0.0-SYMBOLS.zip # 调试符号
└── symbols/
├── gcc/
│ └── libmylib.so.debug # GCC 调试符号
└── clang/
└── libmylib.so.debug # Clang 调试符号
库类型¶
静态库 (.a): - 目标文件的归档 - 编译时链接 - 可执行文件较大 - 无运行时依赖 - 包含所有符号
共享库 (.so):
- 运行时动态链接
- 可执行文件较小
- 进程间共享
- 版本化(libmylib.so.1.0.0)
- 兼容性符号链接:
- libmylib.so → libmylib.so.1 → libmylib.so.1.0.0
构建元数据¶
build_info.json 包含:
{
"project": {
"name": "MyLib",
"version": "1.0.0",
"description": "My Linux library"
},
"build": {
"platform": "linux",
"architectures": ["x86_64"],
"compilers": ["gcc", "clang"],
"build_type": "release",
"link_types": ["static", "shared"],
"timestamp": "2024-01-15T10:30:00Z",
"ccgo_version": "0.1.0",
"gcc_version": "11.4.0",
"clang_version": "14.0.0",
"libc": "glibc-2.35"
},
"outputs": {
"libraries": {
"gcc": {
"static": "lib/static/gcc/libmylib.a",
"shared": "lib/shared/gcc/libmylib.so"
},
"clang": {
"static": "lib/static/clang/libmylib.a",
"shared": "lib/shared/clang/libmylib.so"
}
},
"headers": "include/mylib/",
"symbols": {
"gcc": "symbols/gcc/libmylib.so.debug",
"clang": "symbols/clang/libmylib.so.debug"
}
}
}
GCC vs Clang¶
GCC (GNU 编译器集合)¶
优点: - 大多数 Linux 发行版的默认编译器 - 优秀的优化 - 广泛的架构支持 - 更好的 C++20/C++23 支持(最新版本) - 更大的社区
缺点: - 编译速度比 Clang 慢 - 错误消息不如 Clang 友好 - 有时生成的二进制文件更大
何时使用: - 标准 Linux 开发 - 最大兼容性 - 最新的 C++ 标准 - 最佳优化
Clang (LLVM)¶
优点: - 更快的编译速度 - 更好的错误消息和警告 - 优秀的静态分析 - 更适合开发 - 模块化架构
缺点: - 可能生成稍慢的代码 - 不太常见作为默认编译器 - 某些架构优化较少
何时使用: - 开发和调试 - 需要更好的诊断信息 - 使用 LLVM 生态系统 - 需要静态分析
在 C++ 中使用库¶
链接静态库¶
CMakeLists.txt:
# 查找库
find_library(MYLIB_LIBRARY
NAMES mylib libmylib.a
PATHS "/path/to/lib/static/gcc"
)
# 链接到目标
target_link_libraries(myapp PRIVATE ${MYLIB_LIBRARY})
target_include_directories(myapp PRIVATE "/path/to/include")
手动编译:
# 使用 GCC
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static/gcc -lmylib
# 使用 Clang
clang++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static/clang -lmylib
链接共享库¶
CMakeLists.txt:
# 查找共享库
find_library(MYLIB_LIBRARY
NAMES mylib
PATHS "/path/to/lib/shared/gcc"
)
target_link_libraries(myapp PRIVATE ${MYLIB_LIBRARY})
target_include_directories(myapp PRIVATE "/path/to/include")
# 设置 RPATH 以便运行时找到库
set_target_properties(myapp PROPERTIES
BUILD_RPATH "/path/to/lib/shared/gcc"
INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib"
)
手动编译:
# 使用 GCC
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/shared/gcc -lmylib \
-Wl,-rpath,/path/to/lib/shared/gcc
# 使用 LD_LIBRARY_PATH 运行
LD_LIBRARY_PATH=/path/to/lib/shared/gcc ./myapp
在代码中使用:
Docker 构建¶
在任何操作系统上使用 Docker 构建 Linux 库:
前置条件¶
使用 Docker 构建¶
# 首次构建会下载预构建镜像(~800MB)
ccgo build linux --docker
# 后续构建很快
ccgo build linux --docker --arch x86_64
# 所有标准选项都可用
ccgo build linux --docker --compiler gcc --build-as static
工作原理¶
- CCGO 使用 Docker Hub 的预构建
ccgo-builder-linux镜像 - 项目目录挂载到容器中
- 使用 Ubuntu 22.04 + GCC/Clang 构建
- 输出写入主机文件系统
限制¶
- 无法运行:Docker 中没有 X11 显示
- 无法测试:GUI 应用程序无法工作
- 使用
docker exec -it <container> bash运行 CLI 应用
发行版兼容性¶
glibc vs musl¶
glibc (GNU C 库): - 大多数发行版的标准 - 更好的性能 - 更广泛的兼容性 - 二进制文件更大
musl: - 用于 Alpine Linux - 更小更简单 - 静态链接友好 - 严格符合 POSIX
ABI 兼容性¶
在较旧的发行版上构建的库通常可在较新的发行版上工作:
# 在 Ubuntu 18.04 上构建(glibc 2.27)
# 可在 Ubuntu 20.04、22.04、24.04 上工作
# 在 Ubuntu 22.04 上构建(glibc 2.35)
# 可能无法在 Ubuntu 18.04、20.04 上工作
最佳实践:在您需要支持的最旧发行版上构建。
版本化共享库¶
CCGO 自动创建版本化的共享库:
# 创建的文件
libmylib.so.1.0.0 # 带完整版本的实际库
libmylib.so.1 # SONAME 符号链接
libmylib.so # 开发符号链接
# 检查 SONAME
objdump -p libmylib.so.1.0.0 | grep SONAME
# 输出:SONAME libmylib.so.1
平台配置¶
CCGO.toml 设置¶
[package]
name = "mylib"
version = "1.0.0"
[library]
type = "both" # static、shared 或 both
[build]
cpp_standard = "17" # C++ 标准
[linux]
compiler = "gcc" # gcc、clang 或 auto
libc = "glibc" # glibc 或 musl
position_independent_code = true # 共享库的 PIC
strip_symbols = false # 剥离调试符号
CMake 变量¶
为 Linux 构建时:
${PLATFORM} # "linux"
${ARCHITECTURE} # "x86_64"、"arm64" 或 "armv7"
${BUILD_TYPE} # "Debug" 或 "Release"
${LINK_TYPE} # "static"、"shared" 或 "both"
${COMPILER} # "gcc" 或 "clang"
${LINUX_LIBC} # "glibc" 或 "musl"
条件编译¶
// 平台检测
#ifdef __linux__
// Linux 特定代码
#include <unistd.h>
#ifdef __x86_64__
// x86_64 特定代码
#elif defined(__aarch64__)
// ARM64 特定代码
#elif defined(__arm__)
// ARMv7 特定代码
#endif
#endif
// 编译器检测
#ifdef __GNUC__
// GCC 或 Clang
#define MYLIB_API __attribute__((visibility("default")))
#ifdef __clang__
// Clang 特定代码
#else
// GCC 特定代码
#endif
#endif
// glibc 检测
#ifdef __GLIBC__
// glibc 特定代码
#include <gnu/libc-version.h>
#endif
// 使用
class MYLIB_API MyClass {
public:
void do_work();
};
符号可见性¶
控制共享库中导出的符号:
// mylib_export.h
#ifdef __linux__
#ifdef MYLIB_EXPORTS
#define MYLIB_API __attribute__((visibility("default")))
#else
#define MYLIB_API
#endif
#define MYLIB_LOCAL __attribute__((visibility("hidden")))
#else
#define MYLIB_API
#define MYLIB_LOCAL
#endif
// 公共 API
class MYLIB_API PublicClass {
public:
void public_method();
};
// 内部实现(不导出)
class MYLIB_LOCAL InternalClass {
public:
void internal_method();
};
最佳实践¶
1. 控制符号可见性¶
隐藏内部符号以减小库大小并提高加载速度:
2. 使用 RPATH 进行分发¶
设置 RPATH 以便应用程序可以找到共享库:
# 将库安装到相对于二进制文件的 lib/ 目录
set_target_properties(myapp PROPERTIES
INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib"
)
目录结构:
3. 版本化共享库¶
为共享库遵循语义版本控制:
4. 静态链接以简化部署¶
为了更简单的分发而无需依赖:
# 仅构建静态库
ccgo build linux --build-as static
# 所有代码都嵌入在可执行文件中
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static -lmylib
5. 在目标发行版上测试¶
始终在实际目标发行版上测试: - Ubuntu 20.04、22.04、24.04 - Debian 11、12 - CentOS 7、8、9 - Fedora(最新版本) - Alpine(用于 musl)
6. 使用位置无关代码¶
始终为共享库启用 PIC:
7. 剥离发布二进制文件¶
减小发布构建的库大小:
故障排除¶
找不到编译器¶
解决方案:
# Ubuntu/Debian
sudo apt install -y build-essential
# CentOS/Fedora
sudo yum groupinstall -y "Development Tools"
# 验证
which g++
g++ --version
运行时找不到库¶
解决方案:
-
添加到 LD_LIBRARY_PATH:
-
安装到系统目录:
-
使用 RPATH(推荐):
-
检查库路径:
找不到符号¶
解决方案:
-
检查符号是否存在:
-
验证库已链接:
-
检查符号可见性:
版本不匹配¶
解决方案:
在较旧的发行版上构建或使用静态链接:
# 检查所需的 glibc 版本
objdump -T libmylib.so | grep GLIBC
# 检查系统 glibc 版本
ldd --version
# 在较旧的系统上构建或使用较旧基础镜像的 Docker
CMake 配置失败¶
解决方案:
确保 CMake 可以找到库:
# 设置 CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH "/path/to/MyLib/lib/cmake")
find_package(MyLib REQUIRED)
# 或设置为环境变量
export CMAKE_PREFIX_PATH=/path/to/MyLib/lib/cmake
性能提示¶
1. 使用链接时优化¶
2. 启用优化¶
3. 配置文件引导优化¶
# 1. 使用 profiling 构建
CXXFLAGS="-fprofile-generate" ccgo build linux
# 2. 使用典型工作负载运行
./benchmark
# 3. 使用 profile 数据重新构建
CXXFLAGS="-fprofile-use" ccgo build linux
4. 静态链接以提高性能¶
由于更好的优化,静态链接可能更快:
5. 禁用异常(如果不需要)¶
打包和分发¶
系统包集成¶
Debian/Ubuntu (.deb):
# 安装 checkinstall
sudo apt install checkinstall
# 创建 .deb 包
cd target/linux
sudo checkinstall --pkgname=mylib --pkgversion=1.0.0 \
--provides=mylib make install
基于 RPM (.rpm):
AppImage(便携式):
Snap 包¶
# snapcraft.yaml
name: mylib
version: '1.0.0'
summary: My Linux library
description: Complete C++ library for Linux
parts:
mylib:
plugin: cmake
source: .
Flatpak¶
{
"app-id": "com.example.mylib",
"runtime": "org.freedesktop.Platform",
"sdk": "org.freedesktop.Sdk",
"command": "myapp"
}
迁移指南¶
从 Makefile¶
之前:
CC = gcc
CFLAGS = -O2 -Wall
TARGET = libmylib.so
$(TARGET): mylib.o
$(CC) -shared -o $(TARGET) mylib.o
之后:
-
创建 CCGO 项目:
-
将源文件复制到
src/ -
配置 CCGO.toml:
-
构建:
从 CMake¶
CMakeLists.txt:
project(mylib)
add_library(mylib SHARED src/mylib.cpp)
target_include_directories(mylib PUBLIC include)
CCGO.toml:
然后:ccgo build linux
从 Autotools¶
之前:
之后:
- 从
src/目录提取源文件 - 使用
ccgo new创建 CCGO 项目 - 将源文件复制到新项目结构
- 在 CCGO.toml 中配置依赖项
- 使用
ccgo build linux构建
高级主题¶
交叉编译¶
为不同架构构建:
# 在 x86_64 上为 ARM64 构建
ccgo build linux --arch arm64 --docker
# 为 ARMv7 构建
ccgo build linux --arch armv7 --docker
静态 musl 构建¶
用于真正可移植的静态二进制文件:
清理器(Sanitizers)¶
启用地址清理器进行调试:
[build]
cxxflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
ldflags = ["-fsanitize=address"]
覆盖率分析¶
# 使用覆盖率构建
CXXFLAGS="-fprofile-arcs -ftest-coverage" ccgo build linux
# 运行测试
ccgo test
# 生成报告
gcov src/mylib.cpp
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage_html