mirror of
https://github.com/imjasonh/krust
synced 2026-07-06 22:12:32 +00:00
Use cargo-zigbuild for cross-compilation, add build caching
- Integrate cargo-zigbuild as the default build backend, falling back to cargo build with best-effort linker detection if not installed - Auto-install rustup targets when missing (rustup target add) - Use persistent target/krust/ directory instead of temp dirs so incremental compilation works across runs - Remove .cargo/config.toml from repo (add to .gitignore) since zigbuild handles cross-linker configuration - Improve error messages: suggest cargo-zigbuild when linker not found - Simplify CI to use zigbuild instead of per-target system linkers - Simplify Makefile by removing setup-cross-compile/verify targets - Update README and CLAUDE.md to reflect new approach https://claude.ai/code/session_01EcsZDNeWn56wFqryb4Wq7r
This commit is contained in:
parent
68a9a86e1c
commit
bb76a120c3
8 changed files with 181 additions and 244 deletions
|
|
@ -1,5 +0,0 @@
|
|||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "x86_64-linux-musl-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
|
|
@ -25,10 +25,10 @@ jobs:
|
|||
toolchain: ${{ matrix.rust }}
|
||||
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
|
||||
components: rustfmt,clippy
|
||||
- name: Install cross-compilation tools for both architectures
|
||||
- name: Install cargo-zigbuild and zig
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y musl-tools gcc-aarch64-linux-gnu gcc-x86-64-linux-gnu
|
||||
sudo snap install zig --classic --beta || sudo apt-get update && sudo apt-get install -y ziglang
|
||||
cargo install cargo-zigbuild
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4
|
||||
with:
|
||||
|
|
@ -47,19 +47,6 @@ jobs:
|
|||
|
||||
- run: make check-fmt
|
||||
- run: make lint
|
||||
|
||||
- name: Setup cargo config for CI cross-compilation
|
||||
run: |
|
||||
mkdir -p .cargo
|
||||
cat > .cargo/config.toml << 'EOF'
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "musl-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
EOF
|
||||
|
||||
- run: make verify-cross-compile
|
||||
- run: make build
|
||||
- run: make test
|
||||
- run: make test-e2e
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -16,6 +16,7 @@ Thumbs.db
|
|||
|
||||
# Cargo
|
||||
Cargo.lock
|
||||
.cargo/
|
||||
|
||||
# Coverage
|
||||
tarpaulin-report.html
|
||||
|
|
|
|||
22
CLAUDE.md
22
CLAUDE.md
|
|
@ -90,16 +90,13 @@ GAR has special handling for blob uploads that differs from the standard OCI spe
|
|||
- Blob uploads require `.to_vec()` due to reqwest's `'static` requirement for request bodies
|
||||
- This is acceptable as reqwest streams the data internally
|
||||
|
||||
### Cross-Compilation on macOS
|
||||
### Cross-Compilation
|
||||
|
||||
For Linux targets from macOS, you need:
|
||||
1. Target toolchain: `rustup target add x86_64-unknown-linux-musl`
|
||||
2. Cross-linker: `brew install filosottile/musl-cross/musl-cross`
|
||||
3. Cargo config to specify the linker:
|
||||
```toml
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "x86_64-linux-musl-gcc"
|
||||
```
|
||||
krust uses `cargo-zigbuild` for cross-compilation by default. This eliminates the need for
|
||||
per-target system linkers and `.cargo/config.toml` linker configuration. If zigbuild is not
|
||||
available, krust falls back to `cargo build` with best-effort system linker detection.
|
||||
|
||||
Required targets are auto-installed via `rustup target add` when needed.
|
||||
|
||||
### Rust Static Linking
|
||||
|
||||
|
|
@ -137,6 +134,7 @@ Key crates chosen:
|
|||
- `tar` + `flate2` - Layer creation
|
||||
- `sha256` - Digest calculation
|
||||
- `tracing` - Structured logging
|
||||
- `cargo-zigbuild` - Cross-compilation backend (external tool, not a crate dep)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
|
|
@ -207,7 +205,7 @@ Potential enhancements identified:
|
|||
1. ~~Registry authentication support~~ ✓ Implemented (supports Docker credential helpers)
|
||||
2. ~~YAML resolution for Kubernetes deployments~~ ✓ Implemented (`krust resolve`)
|
||||
3. Multi-platform image manifests
|
||||
4. Build caching
|
||||
4. ~~Build caching~~ ✓ Implemented (persistent `target/krust/` directory)
|
||||
5. Image layer optimization
|
||||
6. Support for custom Dockerfile-like configs
|
||||
7. SBOM (Software Bill of Materials) generation
|
||||
|
|
@ -232,8 +230,8 @@ krust build example/hello-krust
|
|||
krust build -v 2>&1 | less
|
||||
|
||||
# Check static linking
|
||||
file target/x86_64-unknown-linux-musl/release/binary
|
||||
ldd target/x86_64-unknown-linux-musl/release/binary # should say "not a dynamic executable"
|
||||
file target/krust/x86_64-unknown-linux-musl/release/binary
|
||||
ldd target/krust/x86_64-unknown-linux-musl/release/binary # should say "not a dynamic executable"
|
||||
|
||||
# Verify pushed image
|
||||
crane manifest $(krust build --no-push example/hello-krust)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||
dirs = "6.0"
|
||||
toml = "0.9"
|
||||
which = "8.0"
|
||||
tempfile = "3.9"
|
||||
base64 = "0.22"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
|||
40
Makefile
40
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: test test-unit test-e2e test-testscript build run clean fmt lint check-fmt check install setup-cross-compile verify-cross-compile
|
||||
.PHONY: test test-unit test-e2e test-testscript build run clean fmt lint check-fmt check install
|
||||
|
||||
# Test flags - run single-threaded to avoid env var races
|
||||
TEST_FLAGS := -- --test-threads=1
|
||||
|
|
@ -7,44 +7,6 @@ TEST_FLAGS := -- --test-threads=1
|
|||
build:
|
||||
cargo build --verbose
|
||||
|
||||
# Setup cargo config for cross-compilation
|
||||
setup-cross-compile:
|
||||
@mkdir -p .cargo
|
||||
@cat > .cargo/config.toml <<'EOF'
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "musl-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
EOF
|
||||
@echo "Created .cargo/config.toml for cross-compilation"
|
||||
|
||||
# Verify cross-compilation setup
|
||||
verify-cross-compile:
|
||||
@echo "=== Rust Installation ==="
|
||||
@echo "Installed targets:"
|
||||
@rustup target list --installed
|
||||
@echo ""
|
||||
@echo "Cargo version:"
|
||||
@cargo --version
|
||||
@echo ""
|
||||
@echo "Rustc version:"
|
||||
@rustc --version
|
||||
@echo ""
|
||||
@echo "=== Available Linkers ==="
|
||||
@which x86_64-unknown-linux-musl-gcc 2>/dev/null && echo "✓ x86_64-unknown-linux-musl-gcc found" || echo "✗ x86_64-unknown-linux-musl-gcc not found"
|
||||
@which x86_64-linux-musl-gcc 2>/dev/null && echo "✓ x86_64-linux-musl-gcc found" || echo "✗ x86_64-linux-musl-gcc not found"
|
||||
@which musl-gcc 2>/dev/null && echo "✓ musl-gcc found" || echo "✗ musl-gcc not found"
|
||||
@which aarch64-linux-gnu-gcc 2>/dev/null && echo "✓ aarch64-linux-gnu-gcc found" || echo "✗ aarch64-linux-gnu-gcc not found"
|
||||
@which rust-lld 2>/dev/null && echo "✓ rust-lld found" || echo "✗ rust-lld not found"
|
||||
@echo ""
|
||||
@echo "=== Cargo Config ==="
|
||||
@if [ -f .cargo/config.toml ]; then \
|
||||
cat .cargo/config.toml; \
|
||||
else \
|
||||
echo "No cargo config found at .cargo/config.toml"; \
|
||||
fi
|
||||
|
||||
# Run the project
|
||||
run:
|
||||
cargo run
|
||||
|
|
|
|||
109
README.md
109
README.md
|
|
@ -33,52 +33,20 @@ cargo install --path .
|
|||
|
||||
### Prerequisites
|
||||
|
||||
Install the Linux musl targets for static binary cross-compilation:
|
||||
Install [`cargo-zigbuild`](https://github.com/rust-cross/cargo-zigbuild) for cross-compilation (recommended):
|
||||
|
||||
```bash
|
||||
# For linux/amd64 (most common)
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
# Install zig (cargo-zigbuild's cross-compilation backend)
|
||||
# macOS:
|
||||
brew install zig
|
||||
# Linux:
|
||||
sudo snap install zig --classic --beta # or via your package manager
|
||||
|
||||
# For linux/arm64
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
|
||||
# Or install all supported targets at once
|
||||
rustup target add \
|
||||
x86_64-unknown-linux-musl \
|
||||
aarch64-unknown-linux-musl \
|
||||
armv7-unknown-linux-musleabihf \
|
||||
arm-unknown-linux-musleabihf \
|
||||
i686-unknown-linux-musl \
|
||||
powerpc64le-unknown-linux-musl \
|
||||
s390x-unknown-linux-musl \
|
||||
riscv64gc-unknown-linux-musl
|
||||
# Install cargo-zigbuild
|
||||
cargo install cargo-zigbuild
|
||||
```
|
||||
|
||||
#### macOS Cross-compilation Setup
|
||||
|
||||
On macOS, you'll need a cross-compilation toolchain:
|
||||
|
||||
```bash
|
||||
# Install musl cross-compilation tools
|
||||
brew install filosottile/musl-cross/musl-cross
|
||||
|
||||
# Note: The musl-cross formula typically only includes x86_64 and aarch64 toolchains.
|
||||
# For other architectures, you may need additional toolchains or use Docker/remote builders.
|
||||
|
||||
# Create a .cargo/config.toml in your project with:
|
||||
cat > .cargo/config.toml << 'EOF'
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "x86_64-linux-musl-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
|
||||
# For other architectures, you'll need to install the appropriate cross-compiler
|
||||
# or use cargo-zigbuild which can target all platforms:
|
||||
# cargo install cargo-zigbuild
|
||||
# Then build with: cargo zigbuild --target <target>
|
||||
EOF
|
||||
```
|
||||
krust will automatically install the required rustup targets when building. If `cargo-zigbuild` is not installed, krust falls back to `cargo build` and will attempt to find a system cross-linker.
|
||||
|
||||
Note: krust builds fully static binaries by default using musl libc, ensuring maximum portability across different Linux distributions and container environments.
|
||||
|
||||
|
|
@ -166,19 +134,14 @@ This intelligent platform detection ensures your images support the same platfor
|
|||
|
||||
## Build Process
|
||||
|
||||
krust builds your Rust application in an isolated environment:
|
||||
krust builds your Rust application and packages it into a container image:
|
||||
|
||||
1. **Temporary build directory** - Each build uses a unique temporary directory via `--target-dir`
|
||||
1. **Target installation** - Automatically installs the required rustup target if missing
|
||||
2. **Static compilation** - Builds with `RUSTFLAGS="-C target-feature=+crt-static"` for musl targets
|
||||
3. **Cross-compilation** - Automatically configures the appropriate linker for the target platform
|
||||
4. **Binary extraction** - Copies the built binary from the temp directory for packaging
|
||||
3. **Cross-compilation** - Uses `cargo-zigbuild` for seamless cross-compilation (falls back to `cargo build` with system linkers)
|
||||
4. **Cached builds** - Uses `target/krust/` as the build directory, so incremental compilation works across runs
|
||||
5. **Container creation** - Packages the binary into a minimal OCI image
|
||||
|
||||
This approach ensures:
|
||||
- No conflicts between concurrent builds
|
||||
- Clean builds without interference from previous compilations
|
||||
- Safe parallel execution of multiple krust instances
|
||||
|
||||
## Static Binaries
|
||||
|
||||
krust builds fully static binaries by default using:
|
||||
|
|
@ -438,20 +401,15 @@ krust resolve -f deployment.yaml | kubectl apply -f -
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### macOS: "linking with `cc` failed"
|
||||
### "linking with `cc` failed" or linker errors
|
||||
|
||||
This error occurs when the cross-compilation toolchain is not properly configured. Make sure you:
|
||||
|
||||
1. Install musl-cross: `brew install filosottile/musl-cross/musl-cross`
|
||||
2. Create `.cargo/config.toml` in your project with the appropriate linker configuration
|
||||
|
||||
### "target may not be installed"
|
||||
|
||||
Install the required target with rustup:
|
||||
Install `cargo-zigbuild` for seamless cross-compilation:
|
||||
```bash
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
cargo install cargo-zigbuild
|
||||
```
|
||||
|
||||
If you prefer not to use zigbuild, install the appropriate system cross-linker for your target platform.
|
||||
|
||||
### Platform mismatch warning when running images
|
||||
|
||||
This is normal when building linux/amd64 images on Apple Silicon. The images will still run correctly under emulation.
|
||||
|
|
@ -465,32 +423,11 @@ This is normal when building linux/amd64 images on Apple Silicon. The images wil
|
|||
git clone https://github.com/imjasonh/krust.git
|
||||
cd krust
|
||||
|
||||
# Install cross-compilation toolchain (required for tests)
|
||||
# On macOS:
|
||||
brew install messense/macos-cross-toolchains/x86_64-unknown-linux-musl
|
||||
brew install messense/macos-cross-toolchains/aarch64-unknown-linux-musl
|
||||
|
||||
# For full platform support, consider using cargo-zigbuild:
|
||||
# Install cargo-zigbuild for cross-compilation
|
||||
cargo install cargo-zigbuild
|
||||
|
||||
# Install Rust targets (at minimum for tests)
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
|
||||
# For full platform support, add all targets:
|
||||
rustup target add \
|
||||
armv7-unknown-linux-musleabihf \
|
||||
arm-unknown-linux-musleabihf \
|
||||
i686-unknown-linux-musl \
|
||||
powerpc64le-unknown-linux-musl \
|
||||
s390x-unknown-linux-musl \
|
||||
riscv64gc-unknown-linux-musl
|
||||
|
||||
# Install pre-commit hooks
|
||||
pip install pre-commit
|
||||
or
|
||||
brew install pre-commit
|
||||
|
||||
pip install pre-commit # or: brew install pre-commit
|
||||
pre-commit install
|
||||
|
||||
# Build and test
|
||||
|
|
@ -505,23 +442,17 @@ The project includes a comprehensive Makefile for common development tasks:
|
|||
```bash
|
||||
# Building
|
||||
make build # Build the project
|
||||
make build-verbose # Build with verbose output
|
||||
|
||||
# Testing (runs single-threaded to avoid env var races)
|
||||
make test # Run all tests
|
||||
make test-unit # Run unit tests only
|
||||
make test-e2e # Run end-to-end tests only
|
||||
make test-verbose # Run all tests with verbose output
|
||||
|
||||
# Code quality
|
||||
make fmt # Format code
|
||||
make lint # Run clippy linter
|
||||
make check-fmt # Check formatting without fixing
|
||||
make check # Run all checks (format, lint, test)
|
||||
|
||||
# Cross-compilation
|
||||
make setup-cross-compile # Set up cargo config for cross-compilation
|
||||
make verify-cross-compile # Verify cross-compilation setup
|
||||
```
|
||||
|
||||
### Pre-commit hooks
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use anyhow::{Context, Result};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use tempfile::TempDir;
|
||||
use tracing::{debug, error, info};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
@ -15,7 +14,6 @@ pub struct RustBuilder {
|
|||
|
||||
pub struct BuildResult {
|
||||
pub binary_path: PathBuf,
|
||||
_temp_dir: TempDir, // Keep temp dir alive until BuildResult is dropped
|
||||
}
|
||||
|
||||
impl RustBuilder {
|
||||
|
|
@ -32,94 +30,91 @@ impl RustBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Check if cargo-zigbuild is available.
|
||||
fn has_zigbuild() -> bool {
|
||||
Command::new("cargo")
|
||||
.args(["zigbuild", "--version"])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if the rustup target is installed, and install it if not.
|
||||
fn ensure_target_installed(target: &str) -> Result<()> {
|
||||
let output = Command::new("rustup")
|
||||
.args(["target", "list", "--installed"])
|
||||
.output()
|
||||
.context("Failed to run rustup. Is rustup installed?")?;
|
||||
|
||||
let installed = String::from_utf8_lossy(&output.stdout);
|
||||
if installed.lines().any(|line| line.trim() == target) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Installing rustup target: {}", target);
|
||||
let status = Command::new("rustup")
|
||||
.args(["target", "add", target])
|
||||
.status()
|
||||
.context("Failed to run rustup target add")?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!(
|
||||
"Failed to install target '{}'. Run: rustup target add {}",
|
||||
target,
|
||||
target
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the persistent target directory for krust builds.
|
||||
/// Uses `<project>/target/krust/` so cargo can reuse build caches.
|
||||
fn target_dir(&self) -> PathBuf {
|
||||
self.project_path.join("target").join("krust")
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<BuildResult> {
|
||||
info!("Building Rust project at {:?}", self.project_path);
|
||||
|
||||
// Use a unique target directory to avoid conflicts between concurrent builds
|
||||
let temp_target_dir =
|
||||
tempfile::tempdir().context("Failed to create temporary directory")?;
|
||||
let target_dir = temp_target_dir.path();
|
||||
// Ensure the target is installed via rustup
|
||||
Self::ensure_target_installed(&self.target)?;
|
||||
|
||||
let target_dir = self.target_dir();
|
||||
let use_zigbuild = Self::has_zigbuild();
|
||||
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.arg("build")
|
||||
.arg("--release")
|
||||
if use_zigbuild {
|
||||
info!("Using cargo-zigbuild for cross-compilation");
|
||||
cmd.arg("zigbuild");
|
||||
} else {
|
||||
warn!(
|
||||
"cargo-zigbuild not found, falling back to cargo build. \
|
||||
Install it for easier cross-compilation: cargo install cargo-zigbuild"
|
||||
);
|
||||
cmd.arg("build");
|
||||
}
|
||||
|
||||
cmd.arg("--release")
|
||||
.arg("--target")
|
||||
.arg(&self.target)
|
||||
.arg("--target-dir")
|
||||
.arg(target_dir)
|
||||
.arg(&target_dir)
|
||||
.current_dir(&self.project_path);
|
||||
|
||||
// Set RUSTFLAGS for static linking
|
||||
let rustflags = if self.target.contains("musl") {
|
||||
// For musl targets, ensure fully static linking
|
||||
"-C target-feature=+crt-static"
|
||||
} else {
|
||||
// For GNU targets, link statically where possible
|
||||
"-C target-feature=+crt-static -C link-arg=-static-libgcc"
|
||||
};
|
||||
cmd.env("RUSTFLAGS", rustflags);
|
||||
|
||||
// For cross-compilation on non-Linux platforms, set linker if available
|
||||
if cfg!(not(target_os = "linux")) && self.target.contains("linux") {
|
||||
// Check if we have a musl cross-compiler available
|
||||
if self.target.contains("x86_64-unknown-linux-musl") {
|
||||
// On Windows, use rust-lld with full path
|
||||
if cfg!(target_os = "windows") {
|
||||
// Get the rust sysroot to find rust-lld
|
||||
let sysroot_output = Command::new("rustc")
|
||||
.arg("--print")
|
||||
.arg("sysroot")
|
||||
.output()
|
||||
.context("Failed to get rustc sysroot")?;
|
||||
|
||||
if sysroot_output.status.success() {
|
||||
let sysroot = String::from_utf8_lossy(&sysroot_output.stdout)
|
||||
.trim()
|
||||
.to_string();
|
||||
let rust_lld = PathBuf::from(&sysroot)
|
||||
.join("lib")
|
||||
.join("rustlib")
|
||||
.join("x86_64-pc-windows-msvc")
|
||||
.join("bin")
|
||||
.join("rust-lld.exe");
|
||||
|
||||
if rust_lld.exists() {
|
||||
cmd.env(
|
||||
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER",
|
||||
rust_lld.to_string_lossy().to_string(),
|
||||
);
|
||||
debug!("Using linker: {}", rust_lld.display());
|
||||
} else {
|
||||
// Fallback to just "rust-lld" and hope it's in PATH
|
||||
cmd.env("CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER", "rust-lld");
|
||||
debug!("Using linker: rust-lld (in PATH)");
|
||||
}
|
||||
} else {
|
||||
// Fallback to just "rust-lld"
|
||||
cmd.env("CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER", "rust-lld");
|
||||
debug!("Using linker: rust-lld (fallback)");
|
||||
}
|
||||
} else {
|
||||
// Try common linker names on other platforms
|
||||
let linkers = if cfg!(target_os = "macos") {
|
||||
vec![
|
||||
"x86_64-unknown-linux-musl-gcc",
|
||||
"x86_64-linux-musl-gcc",
|
||||
"musl-gcc",
|
||||
]
|
||||
} else {
|
||||
vec!["x86_64-linux-musl-gcc", "musl-gcc", "x86_64-linux-gnu-gcc"]
|
||||
};
|
||||
|
||||
for linker in &linkers {
|
||||
if which::which(linker).is_ok() {
|
||||
cmd.env("CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER", linker);
|
||||
debug!("Using linker: {}", linker);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// When not using zigbuild, try to find a cross-linker for non-native targets
|
||||
if !use_zigbuild {
|
||||
self.configure_cross_linker(&mut cmd);
|
||||
}
|
||||
|
||||
for arg in &self.cargo_args {
|
||||
|
|
@ -133,11 +128,20 @@ impl RustBuilder {
|
|||
let output = cmd.output().context("Failed to execute cargo build")?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("Cargo build failed!");
|
||||
error!("stdout:\n{}", stdout);
|
||||
error!("stderr:\n{}", stderr);
|
||||
|
||||
// Provide actionable error messages
|
||||
let stderr_str = stderr.to_string();
|
||||
if stderr_str.contains("linker") && stderr_str.contains("not found") {
|
||||
anyhow::bail!(
|
||||
"Cross-compilation linker not found for target '{}'.\n\
|
||||
Install cargo-zigbuild for easy cross-compilation:\n\
|
||||
\n cargo install cargo-zigbuild\n\
|
||||
\nOr install the appropriate system linker for your platform.",
|
||||
self.target
|
||||
);
|
||||
}
|
||||
|
||||
anyhow::bail!("Cargo build failed: {}", stderr);
|
||||
}
|
||||
|
||||
|
|
@ -163,11 +167,71 @@ impl RustBuilder {
|
|||
|
||||
info!("Successfully built binary at {:?}", binary_path);
|
||||
|
||||
// Return the build result with the temp directory to keep it alive
|
||||
Ok(BuildResult {
|
||||
binary_path,
|
||||
_temp_dir: temp_target_dir,
|
||||
})
|
||||
Ok(BuildResult { binary_path })
|
||||
}
|
||||
|
||||
/// Configure cross-compilation linker when not using zigbuild.
|
||||
/// This is a best-effort fallback for users who don't have zigbuild installed.
|
||||
fn configure_cross_linker(&self, cmd: &mut Command) {
|
||||
// Only needed when cross-compiling
|
||||
let is_cross = if cfg!(target_os = "linux") {
|
||||
// On Linux, check if we're building for a different arch
|
||||
let host_arch = std::env::consts::ARCH;
|
||||
let target_arch = self.target.split('-').next().unwrap_or("");
|
||||
host_arch != target_arch
|
||||
&& !(host_arch == "x86_64" && target_arch == "x86_64")
|
||||
&& !(host_arch == "aarch64" && target_arch == "aarch64")
|
||||
} else {
|
||||
// On non-Linux hosts, always need cross-linker for Linux targets
|
||||
self.target.contains("linux")
|
||||
};
|
||||
|
||||
if !is_cross {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the CARGO_TARGET_<triple>_LINKER env var name
|
||||
let env_var = format!(
|
||||
"CARGO_TARGET_{}_LINKER",
|
||||
self.target.to_uppercase().replace('-', "_")
|
||||
);
|
||||
|
||||
// Try common linker names based on target
|
||||
let linkers: Vec<&str> = if self.target.contains("x86_64") && self.target.contains("musl") {
|
||||
vec!["x86_64-linux-musl-gcc", "musl-gcc"]
|
||||
} else if self.target.contains("aarch64") && self.target.contains("musl") {
|
||||
vec!["aarch64-linux-musl-gcc", "aarch64-linux-gnu-gcc"]
|
||||
} else if self.target.contains("armv7") {
|
||||
vec!["arm-linux-gnueabihf-gcc", "armv7-linux-musleabihf-gcc"]
|
||||
} else if self.target.contains("arm-") {
|
||||
vec!["arm-linux-gnueabihf-gcc", "arm-linux-musleabihf-gcc"]
|
||||
} else if self.target.contains("i686") {
|
||||
vec!["i686-linux-musl-gcc", "i686-linux-gnu-gcc"]
|
||||
} else if self.target.contains("powerpc64le") {
|
||||
vec!["powerpc64le-linux-gnu-gcc"]
|
||||
} else if self.target.contains("s390x") {
|
||||
vec!["s390x-linux-gnu-gcc"]
|
||||
} else if self.target.contains("riscv64") {
|
||||
vec!["riscv64-linux-gnu-gcc"]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
for linker in &linkers {
|
||||
if which::which(linker).is_ok() {
|
||||
cmd.env(&env_var, linker);
|
||||
debug!("Using linker: {}", linker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !linkers.is_empty() {
|
||||
debug!(
|
||||
"No cross-linker found for target '{}'. Build may fail. \
|
||||
Consider installing cargo-zigbuild: cargo install cargo-zigbuild",
|
||||
self.target
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_binary_name(&self) -> Result<String> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue