
Cross OpenSSL
Problem Background
When using Rust Cross for Linux cross-compilation, we often encounter openssl-sys
related compilation errors, especially when compiling to musl
targets. This is because OpenSSL depends on system libraries, and the development packages may be missing in the cross-compilation environment.
Common Error Messages
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
Root Cause Analysis
- Missing System Dependencies: OpenSSL requires system development libraries and header files
- Package Manager Mismatch: musl images use
apk
, notapt-get
- Complex Cross-Compilation Environment Setup: Different target platforms require different dependency configurations
- Conditional Compilation Configuration Errors:
[target.'cfg(linux)']
does not work during cross-compilation
Solutions
Solution 1: Use Vendored OpenSSL (Recommended)
This is the simplest and most reliable solution, statically compiling OpenSSL into the binary file.
1. Target-Specific Configuration
[package]
name = "your_project"
version = "0.1.0"
edition = "2021"
[dependencies]
# Base dependencies
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Configure vendored OpenSSL for Linux musl targets
[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"] }
# Configure vendored OpenSSL for Linux GNU targets
[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"] }
# Configure standard OpenSSL for Windows (to avoid compilation issues)
[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. Global Configuration (if only compiling for Linux)
[dependencies]
# Global use of vendored OpenSSL
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# Other dependencies
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
3. Using Correct Conditional Compilation
[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Configure vendored OpenSSL for Linux targets
[target.'cfg(target_os = "linux")'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11", features = ["native-tls-vendored", "json"] }
# Configure standard version for other platforms
[target.'cfg(not(target_os = "linux"))'.dependencies]
openssl = { version = "0.10" }
reqwest = { version = "0.11", features = ["native-tls", "json"] }
Solution 2: Use rustls Instead of OpenSSL
Completely avoid OpenSSL dependencies:
[dependencies]
# Use rustls instead of 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"]
}
# Other dependencies
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Solution 3: Configure Cross.toml (Not Recommended)
If you must solve it through system packages, note that musl and glibc use different package managers:
[build.env]
passthrough = [
"CARGO_HTTP_MULTIPLEXING",
"CARGO_NET_GIT_FETCH_WITH_CLI"
]
# musl targets use 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 targets use 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"
]
Common Errors and Solutions
1. [target.'cfg(linux)']
Configuration Does Not Work
Error Cause: Syntax error in conditional compilation or not recognized during cross-compilation
Solution:
# ❌ Incorrect way
[target.'cfg(linux)']
openssl = { version = "0.10", features = ["vendored"] }
# ✅ Correct way
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
2. dpkg --add-architecture
Fails in musl Image
Error Cause: musl images are based on Alpine Linux, which uses apk
instead of apt
Solution:
# ❌ Using apt for musl targets
pre-build = [
"apt-get update && apt-get install libssl-dev"
]
# ✅ Using apk for musl targets
pre-build = [
"apk add --no-cache openssl-dev openssl-libs-static"
]
3. buildkit Related Errors
Solution:
# Disable buildkit
export CROSS_CONTAINER_ENGINE_NO_BUILDKIT=1
# Or
$env:CROSS_CONTAINER_ENGINE_NO_BUILDKIT = "1"
4. pkg-config Cannot Find OpenSSL
Solution:
# Set environment variables
export OPENSSL_STATIC=1
export OPENSSL_VENDORED=1
Best Practices
1. Recommended Project Structure
your_project/
├── Cargo.toml # Configure vendored OpenSSL
├── Cross.toml # Simplified cross-compilation configuration (optional)
├── build.sh # Linux/macOS build script
├── build.ps1 # Windows build script
└── src/
└── main.rs
2. Simplified Cross.toml
# Keep it simple, mainly rely on vendored configuration in Cargo.toml
[build.env]
passthrough = [
"CARGO_HTTP_MULTIPLEXING",
"CARGO_NET_GIT_FETCH_WITH_CLI"
]
3. Build Script Examples
PowerShell (build.ps1)
# Set environment variables
$env:OPENSSL_STATIC = "1"
$env:OPENSSL_VENDORED = "1"
$env:CROSS_CONTAINER_ENGINE_NO_BUILDKIT = "1"
# Clean previous builds
Write-Host "Cleaning previous builds..."
cargo clean
# Build for different targets
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
# Set environment variables
export OPENSSL_STATIC=1
export OPENSSL_VENDORED=1
export CROSS_CONTAINER_ENGINE_NO_BUILDKIT=1
# Clean previous builds
echo "Cleaning previous builds..."
cargo clean
# Build for different targets
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!"
Verification and Testing
1. Verify Configuration
# Check dependency tree
cargo tree --target x86_64-unknown-linux-musl | grep openssl
# Check binary dependencies
file target/x86_64-unknown-linux-musl/release/your_binary
ldd target/x86_64-unknown-linux-musl/release/your_binary
2. Test Compilation
# Create a test project
cargo new --bin openssl-test
cd openssl-test
# Add dependencies to Cargo.toml
# Then test compilation
cross build --release --target x86_64-unknown-linux-musl
Performance and Size Optimization
1. Optimize Compilation Configuration
[profile.release]
strip = true # Remove debug symbols
lto = true # Link-time optimization
codegen-units = 1 # Single code generation unit
panic = "abort" # Terminate directly on crash
opt-level = "z" # Optimize for size
2. Choose Appropriate TLS Implementation
Solution | Size | Performance | Compatibility | Recommended For |
---|---|---|---|---|
vendored OpenSSL | Large | High | Good | Production environment |
rustls | Medium | High | Good | Modern applications |
System OpenSSL | Small | High | Medium | Container environment |
Summary
- Prioritize vendored OpenSSL: The simplest and most reliable solution
- Avoid complex Cross.toml configurations: Prone to errors and difficult to maintain
- Use specific target triples: Rather than conditional compilation
- Consider using rustls: If you don't need specific OpenSSL functionality
- Keep configurations simple: Complex configurations increase the probability of errors
By correctly configuring [target.x86_64-unknown-linux-musl.dependencies]
, you can avoid most OpenSSL-related cross-compilation issues.