
Cross OpenSSL
大约 5 分钟
问题背景
在使用 Rust Cross 进行 Linux 交叉编译时,经常遇到 openssl-sys
相关的编译错误,特别是在编译到 musl
目标时。这是因为 OpenSSL 依赖系统库,而交叉编译环境中可能缺少相应的开发包。
常见错误信息
error: failed to run custom build command for `openssl-sys v0.9.109`
Dockerfile.x86_64-unknown-linux-musl-custom:5
ERROR: failed to build: failed to solve: process "/bin/sh -c eval \"${CROSS_CMD}\"" did not complete successfully: exit code: 100
ERROR: failed to build: failed to solve: process "/bin/sh -c eval \"${CROSS_CMD}\"" did not complete successfully: exit code: 127
根本原因分析
- 系统依赖缺失:OpenSSL 需要系统的开发库和头文件
- 包管理器不匹配:musl 镜像使用
apk
,而不是apt-get
- 交叉编译环境配置复杂:需要为不同目标平台配置不同的依赖
- 条件编译配置错误:
[target.'cfg(linux)']
在交叉编译时不生效
解决方案
方案一:使用 vendored OpenSSL(推荐)
这是最简单且最可靠的解决方案,让 OpenSSL 静态编译到二进制文件中。
1. 针对特定目标配置
[package]
name = "your_project"
version = "0.1.0"
edition = "2021"
[dependencies]
# 基础依赖
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# 为 Linux musl 目标配置 vendored OpenSSL
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
[target.aarch64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# 为 Linux GNU 目标配置 vendored OpenSSL
[target.x86_64-unknown-linux-gnu.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
[target.aarch64-unknown-linux-gnu.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# 为 Windows 配置标准 OpenSSL(避免编译问题)
[target.x86_64-pc-windows-msvc.dependencies]
openssl = { version = "0.10" }
reqwest = { version = "0.11", features = ["native-tls", "json"] }
[target.x86_64-pc-windows-gnu.dependencies]
openssl = { version = "0.10" }
reqwest = { version = "0.11", features = ["native-tls", "json"] }
2. 全局配置(如果只编译 Linux)
[dependencies]
# 全局使用 vendored OpenSSL
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# 其他依赖
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
3. 使用正确的条件编译
[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# 为 Linux 目标配置 vendored OpenSSL
[target.'cfg(target_os = "linux")'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# 为其他平台配置标准版本
[target.'cfg(not(target_os = "linux"))'.dependencies]
openssl = { version = "0.10" }
reqwest = { version = "0.11", features = ["native-tls", "json"] }
方案二:使用 rustls 替代 OpenSSL
完全避免 OpenSSL 依赖:
[dependencies]
# 使用 rustls 替代 OpenSSL
reqwest = {
version = "0.11",
default-features = false,
features = ["rustls-tls", "json", "stream"]
}
tokio-tungstenite = {
version = "0.20",
default-features = false,
features = ["rustls-tls-webpki-roots"]
}
# 其他依赖
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
方案三:配置 Cross.toml(不推荐)
如果必须通过系统包解决,需要注意 musl 和 glibc 使用不同的包管理器:
[build.env]
passthrough = [
"CARGO_HTTP_MULTIPLEXING",
"CARGO_NET_GIT_FETCH_WITH_CLI"
]
# musl 目标使用 apk
[target.x86_64-unknown-linux-musl]
image = "ghcr.io/cross-rs/x86_64-unknown-linux-musl:main"
pre-build = [
"apk add --no-cache openssl-dev openssl-libs-static pkgconfig musl-dev"
]
# glibc 目标使用 apt
[target.x86_64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main"
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH"
]
常见错误及解决方法
1. [target.'cfg(linux)']
配置不生效
错误原因:条件编译语法错误或在交叉编译时不被识别
解决方法:
# ❌ 错误写法
[target.'cfg(linux)']
openssl = { version = "0.10", features = ["vendored"] }
# ✅ 正确写法
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
2. dpkg --add-architecture
在 musl 镜像中失败
错误原因:musl 镜像基于 Alpine Linux,使用 apk
而不是 apt
解决方法:
# ❌ 在 musl 目标使用 apt
pre-build = [
"apt-get update && apt-get install libssl-dev"
]
# ✅ 在 musl 目标使用 apk
pre-build = [
"apk add --no-cache openssl-dev openssl-libs-static"
]
3. buildkit 相关错误
解决方法:
# 禁用 buildkit
export CROSS_CONTAINER_ENGINE_NO_BUILDKIT=1
# 或者
$env:CROSS_CONTAINER_ENGINE_NO_BUILDKIT = "1"
4. pkg-config 找不到 OpenSSL
解决方法:
# 设置环境变量
export OPENSSL_STATIC=1
export OPENSSL_VENDORED=1
最佳实践
1. 推荐的项目结构
your_project/
├── Cargo.toml # 配置 vendored OpenSSL
├── Cross.toml # 简化的交叉编译配置(可选)
├── build.sh # Linux/macOS 构建脚本
├── build.ps1 # Windows 构建脚本
└── src/
└── main.rs
2. 简化的 Cross.toml
# 保持简单,主要依赖 Cargo.toml 中的 vendored 配置
[build.env]
passthrough = [
"CARGO_HTTP_MULTIPLEXING",
"CARGO_NET_GIT_FETCH_WITH_CLI"
]
3. 构建脚本示例
PowerShell (build.ps1)
# 设置环境变量
$env:OPENSSL_STATIC = "1"
$env:OPENSSL_VENDORED = "1"
$env:CROSS_CONTAINER_ENGINE_NO_BUILDKIT = "1"
# 清理之前的构建
Write-Host "Cleaning previous builds..."
cargo clean
# 构建不同目标
Write-Host "Building for x86_64-unknown-linux-musl..."
cross build --release --target x86_64-unknown-linux-musl
Write-Host "Building for x86_64-unknown-linux-gnu..."
cross build --release --target x86_64-unknown-linux-gnu
Write-Host "Build completed!"
Bash (build.sh)
#!/bin/bash
# 设置环境变量
export OPENSSL_STATIC=1
export OPENSSL_VENDORED=1
export CROSS_CONTAINER_ENGINE_NO_BUILDKIT=1
# 清理之前的构建
echo "Cleaning previous builds..."
cargo clean
# 构建不同目标
echo "Building for x86_64-unknown-linux-musl..."
cross build --release --target x86_64-unknown-linux-musl
echo "Building for x86_64-unknown-linux-gnu..."
cross build --release --target x86_64-unknown-linux-gnu
echo "Build completed!"
验证和测试
1. 验证配置
# 检查依赖树
cargo tree --target x86_64-unknown-linux-musl | grep openssl
# 检查二进制文件依赖
file target/x86_64-unknown-linux-musl/release/your_binary
ldd target/x86_64-unknown-linux-musl/release/your_binary
2. 测试编译
# 创建测试项目
cargo new --bin openssl-test
cd openssl-test
# 添加依赖到 Cargo.toml
# 然后测试编译
cross build --release --target x86_64-unknown-linux-musl
性能和大小优化
1. 优化编译配置
[profile.release]
strip = true # 移除调试符号
lto = true # 链接时优化
codegen-units = 1 # 单个代码生成单元
panic = "abort" # 崩溃时直接终止
opt-level = "z" # 优化大小
2. 选择合适的 TLS 实现
方案 | 大小 | 性能 | 兼容性 | 推荐场景 |
---|---|---|---|---|
vendored OpenSSL | 大 | 高 | 好 | 生产环境 |
rustls | 中 | 高 | 好 | 现代应用 |
系统 OpenSSL | 小 | 高 | 中 | 容器环境 |
总结
- 优先使用 vendored OpenSSL:最简单可靠的解决方案
- 避免复杂的 Cross.toml 配置:容易出错且难以维护
- 使用具体的目标三元组:而不是条件编译
- 考虑使用 rustls:如果不需要 OpenSSL 的特定功能
- 保持配置简单:复杂的配置增加出错概率
通过正确配置 [target.x86_64-unknown-linux-musl.dependencies]
,可以避免大部分 OpenSSL 相关的交叉编译问题。