Here’s a quick example of a flake.nix that sets up an environment suitable to cross-compile Rust from a Linux host to a FreeBSD target.
The example project uses Tokio and around 100 dependencies overall, but it likely requires additional work to support building
against and linking to arbitrary C libraries.
This was tested on a x86_64 Linux host. The target triple is x86_64-unknown-freebsd. The flake.nix builds a dev shell with:
- clang cross compiler
- FreeBSD 15.0 sysroot
See below for the flake.nix. With this file in place, and a Cargo.toml and some Rust source, you get both
a normal dev shell using nix develop for local development, and a dev shell for cross-compiling to FreeBSD
using a command like:
For the example, I’m using the
static-files example from Poem.
With that in place, the following should work:
$ nix develop .#freebsd -c cargo build --target=x86_64-unknown-freebsd --release
$ file target/x86_64-unknown-freebsd/debug/example-static-files
target/x86_64-unknown-freebsd/debug/example-static-files:
ELF 64-bit LSB pie executable, x86-64, version 1 (FreeBSD),
dynamically linked, interpreter /libexec/ld-elf.so.1,
for FreeBSD 15.0 (1500068), FreeBSD-style,
with debug_info, not stripped
The binary can be copied and launched from an x86_64 FreeBSD system.
See this nixos wiki for additional ideas.
# flake.nix
{
description = "Dev shells for Rust development on Linux with FreeBSD cross compilation";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default =
with pkgs;
mkShell {
name = "dev-shell";
packages = [
stdenv
# Rust toolchain
cargo
rustc
rust-analyzer
rustfmt
clippy
];
};
# Shell for cross-compiling to FreeBSD from Linux.
#
# Uses pkgsCross.cargo to get a cross rustc wrapper, and host clang
# (via pkgs.llvmPackages.clang-unwrapped) with explicit --target/--sysroot.
#
# Usage: nix develop .#freebsd -c cargo build --target=x86_64-unknown-freebsd --release
devShells.freebsd = let
pkgsCross = pkgs.pkgsCross.x86_64-freebsd;
target = "x86_64-unknown-freebsd";
sysroot = pkgs.fetchzip {
url = "https://download.freebsd.org/releases/amd64/15.0-RELEASE/base.txz";
hash = "sha256-OLnHge+hTwjvMNTiChI7YWOhkFUJKj2WQ33SaCa7h4E=";
stripRoot = false;
};
in pkgs.mkShell {
name = "freebsd-cross";
nativeBuildInputs = [
pkgsCross.cargo
pkgs.llvmPackages.clang-unwrapped
pkgs.llvmPackages.lld
];
CC_x86_64_unknown_freebsd = "${pkgs.llvmPackages.clang-unwrapped}/bin/clang";
CXX_x86_64_unknown_freebsd = "${pkgs.llvmPackages.clang-unwrapped}/bin/clang++";
RUSTFLAGS = [
"-Clinker=${pkgs.llvmPackages.clang-unwrapped}/bin/clang"
"-Clink-arg=--target=${target}"
"-Clink-arg=--sysroot=${sysroot}"
"-Clink-arg=-fuse-ld=lld"
];
CFLAGS_x86_64_unknown_freebsd = "--target=${target} --sysroot=${sysroot}";
CXXFLAGS_x86_64_unknown_freebsd = "--target=${target} --sysroot=${sysroot}";
shellHook = ''
echo "FreeBSD cross-compilation environment ready. Target: ${target}"
echo "Run: cargo build --target=${target} --release"
'';
};
}
);
}