Kereszt-fordítás (cross-compilation) alatt azt értjük, amikor a fordítást futtató gép architektúrája és operációs rendszere eltér a végeredmény célplatformjától. Rust-ban ez szerencsére nem valami egzotikus hack, hanem a toolchain natív funkciója — de van néhány buktató, amit érdemes ismerni, mielőtt élesben nekiugrasz.
Mikor van szükség kereszt-fordításra
A leggyakoribb esetek:
- Embedded és IoT projektek: Raspberry Pi-re (aarch64 vagy armv7), mikrokontrollerekre (thumbv7em-none-eabihf) fordítasz, de fejlesztés közben x86_64-es laptopot használsz.
- CI/CD pipeline-ok: egy build szerveren szeretnél Linux, macOS és Windows binárisokat is generálni anélkül, hogy három különböző futtatót tartanál fenn.
- Docker image-ek: gyakran
x86_64-unknown-linux-musltarget-re fordítunk, hogy a statikusan linkelt bináris minimális Alpine-image-ekbe kerülhessen. - WASM célok:
wasm32-unknown-unknowntarget böngészőben futó kódhoz.
A natív fordítás (amikor a build gép és a célgép azonos) technikailag is egy "target", csak épp megegyezik a host triple-lel. A rustup ezt alapból már telepíti, ezért nem szoktunk vele foglalkozni.
A rustup target add használata és elérhető triple-ök
A Rust platformleírásokat úgynevezett target triple formában adja meg: architektúra-gyártó-operációsrendszer-abi. Például:
x86_64-unknown-linux-gnu— a legtöbb Linux disztribúció alap targetjex86_64-unknown-linux-musl— statikusan linkelt Linux binárisaarch64-unknown-linux-gnu— ARM64 szerverek, Raspberry Pi 4 (64 bites OS)armv7-unknown-linux-gnueabihf— Raspberry Pi 3 és korábbi, 32 bites OSx86_64-pc-windows-gnu/x86_64-pc-windows-msvc— Windowsaarch64-apple-darwin— Apple Silicon Macwasm32-unknown-unknown— WebAssembly
Ahhoz, hogy egy adott target-re fordíthass, előbb hozzá kell adnod a standard könyvtár prebuilt verzióját a toolchain-hez:
rustup target list
rustup target add aarch64-unknown-linux-gnu
A rustup target list kiírja az összes elérhető target-et, a telepítettek mellett (installed) jelzéssel. Fontos: ez csak a Rust std-t és a core komponenseket telepíti — a linkert és az esetleges C könyvtárakat neked kell biztosítanod (erről lentebb bővebben).
cargo build --target gyakorlati példákon
Ha a target telepítve van, a fordítás triviális:
cargo build --release --target aarch64-unknown-linux-gnu
A kimenet nem a szokásos target/release/ alá kerül, hanem target/<triple>/release/ alá, hogy a különböző platformok binárisai ne ütközzenek egymással.
Gyakori trükk, hogy a kódban cfg attribútumokkal ágazol el platform-specifikus logikára:
#[cfg(target_os = "linux")]
fn platform_info() -> &'static str {
"Linux rendszeren futunk"
}
#[cfg(target_os = "windows")]
fn platform_info() -> &'static str {
"Windows rendszeren futunk"
}
#[cfg(target_arch = "aarch64")]
fn arch_info() -> &'static str {
"ARM64 architektúra"
}
#[cfg(target_arch = "x86_64")]
fn arch_info() -> &'static str {
"x86_64 architektúra"
}
fn main() {
println!("{} - {}", platform_info(), arch_info());
}
Ez ugyanazzal a forráskóddal fordul minden target-en, csak épp más ág aktiválódik a cfg feltételek miatt.
Ha a linkerbeállítást is meg kell adnod (lásd lentebb), azt a .cargo/config.toml fájlban tudod projekt- vagy globális szinten testre szabni:
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
Ha csak egyszer akarsz egy adott target-re fordítani, a CARGO_BUILD_TARGET környezeti változóval is beállíthatod alapértelmezettnek, elkerülve a --target flag ismételt beírását.
A cross eszköz bemutatása Docker-alapú fordításhoz
A rustup target add + natív linker kombó sok esetben elég, de amint natív C függőségekkel (pl. OpenSSL, libpq, vagy egyéb dinamikus linkelt könyvtárral) dolgozó crate-eket használsz, a helyzet bonyolulttá válhat — a host gépeden ugyanis nem feltétlenül áll rendelkezésre a célplatform C toolchain-je és fejlesztői headerei.
Itt jön képbe a cross nevű community eszköz, amely Docker (vagy Podman) konténereken belül futtatja a fordítást, előre elkészített image-ekkel, amikben minden szükséges cross linker és header már benne van.
Telepítés és használat:
cargo install cross --git https://github.com/cross-rs/cross
cross build --release --target aarch64-unknown-linux-gnu
A cross a háttérben letölt egy megfelelő Docker image-et (pl. ghcr.io/cross-rs/aarch64-unknown-linux-gnu), abban futtatja a cargo build-et, és a bináris ugyanúgy megjelenik a target/<triple>/release/ mappában, mintha lokálisan fordítottad volna.
A cross használatához Docker (vagy Podman) daemon kell, hogy fusson a gépeden. CI környezetben (pl. GitHub Actions) ez általában adott, de lokális fejlesztésnél ellenőrizd, hogy a Docker szolgáltatás elindult-e, különben a cross build egy kapcsolódási hibával leáll.
Ha saját, testreszabott image-et akarsz (mondjuk extra system library-vel), a Cross.toml fájlban tudod megadni:
[target.aarch64-unknown-linux-gnu]
image = "my-registry/custom-cross-image:latest"
Gyakori buktatók: linker beállítások és natív függőségek
A kereszt-fordítás során a leggyakoribb hibaüzenetek valamelyike ezek közül szokott lenni:
-
"linker
ccnot found" — a rustup target hozzáadása nem telepít linkert, csak a Rust std-t. Linux-on a legtöbb esetben egy megfelelőgcccross toolchain csomagot kell telepítened (pl.gcc-aarch64-linux-gnuUbuntu-n), és beírnod a.cargo/config.toml-ba, hogy melyik binárist használja linkerként. -
OpenSSL és egyéb dinamikus C függőségek — az
opensslcrate klasszikus fájdalompont, mert natívan linkeli a rendszer OpenSSL könyvtárát. Kereszt-fordításnál ez szinte sosem elérhető a célplatform verziójában a host gépen. Két gyakori megoldás:- a
crosseszköz használata, ami a Docker image-ben már tartalmazza a megfelelő OpenSSL fejlesztői csomagokat, - vagy áttérés
rustls-alapú TLS implementációra (pl.reqwestesetén arustls-tlsfeature engedélyezése), amivel elkerülöd a natív OpenSSL linkelést teljesen.
- a
-
musl target és statikus linkelés —
x86_64-unknown-linux-musltarget esetén figyelj arra, hogy a musl toolchain telepítve legyen (apt install musl-toolsDebian-alapú rendszereken), különben a linker lépés hibázik. -
build.rs szkriptek platformfüggő logikával — ha a projektben van
build.rs, ami natív parancsokat futtat (pl.cccrate-tel C kódot fordít), figyelj arra, hogy a build.rs a host gépen fut, nem a target-en, de acccrate-nek is tudnia kell a megfelelő cross-toolchain-t megtalálni:
// build.rs
fn main() {
let target = std::env::var("TARGET").unwrap();
println!("cargo:warning=Fordítási cél: {target}");
if target.contains("windows") {
println!("cargo:rustc-link-lib=ws2_32");
}
}
Ez a minta build script a TARGET környezeti változó alapján dönt arról, hogy szükség van-e egy platformspecifikus natív könyvtár linkelésére — ez a Cargo által automatikusan beállított változó minden build.rs futásnál elérhető.
Ne feledd, hogy a TARGET és a HOST környezeti változó eltérhet egymástól kereszt-fordításnál! Ha a build script feltételezi, hogy a kettő megegyezik (например natív parancsokat futtat, amik csak a host-on futnak), az konténerben vagy CI-ban rejtett hibákhoz vezethet.
Összefoglalás
A Rust kereszt-fordítási támogatása igazából két rétegből áll: a rustup target add biztosítja a szükséges std komponenseket, a cargo build --target pedig kiválasztja, mire fordulunk. Egyszerű, natív függőség nélküli projekteknél ez elég is. Amint viszont C könyvtárak, OpenSSL vagy egyéb rendszer-specifikus binding kerül képbe, a cross eszköz Docker-alapú megközelítése rengeteg fejfájástól kímél meg — cserébe egy Docker daemon futtatásáért. Érdemes már a projekt elején eldönteni, hogy melyik útra lépsz, mert a natív függőségek utólagos migrálása (pl. OpenSSL → rustls) sokkal fájdalmasabb, mint elsőre jól megválasztani a stacket.