Cross compile macOS -> Linux server... really need docker?

I was able to cross compile from my laptop (macOS) to iOS and Android devices without much problem. I just used the usual cargo build but with a different --target argument. But for some reason that doesn’t work with Linux. I want to deploy the binary to my Linux server (x86_64). I tried the following, and it failed:

% cargo build --target x86_64-unknown-linux-gnu -p hello-cross
   Compiling hello-cross v0.1.0 (/Users/rob/Workspace/MonoRepo3/learning/hello-cross)
error: linking with `cc` failed: exit status: 1
  |
  = note:  "cc" "-m64" "/var/folders/zm/mzv_wcgn0gn4xcj3z6vl4_cm0000gn/T/rustclPzqtP/symbols.o" "<7 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,libcfg_if-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/var/folders/zm/mzv_wcgn0gn4xcj3z6vl4_cm0000gn/T/rustclPzqtP/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "<sysroot>/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/Users/rob/Workspace/MonoRepo3/target/x86_64-unknown-linux-gnu/debug/deps/hello_cross-5c3b0cdd7be55a39" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
          ld: unknown options: --as-needed -Bstatic -Bdynamic --eh-frame-hdr -z --gc-sections -z -z 
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          

error: could not compile `hello-cross` (bin "hello-cross") due to 1 previous error

I thought Rust was based on LLVM and that LLVM would have enough to cross compile binaries without other tools.

Someone recommend cross. I installed it with cargo. It appears to want docker. I don’t want to have to learn about docker if I can avoid it, so I wanted to check. Is all this really needed to cross compile to Linux? Why doesn’t a simple cargo build --target ... work like it did for Android and iOS?

 % cross build --target x86_64-unknown-linux-gnu -p hello-cross
ERROR: Cannot connect to the Docker daemon at unix:///Users/rob/.docker/run/docker.sock. Is the docker daemon running?
Error: 
   0: could not run container
   1: when building custom image
   2: when pre-building
   3: `docker build --label 'org.cross-rs.for-cross-target=x86_64-unknown-linux-gnu' --label 'org.cross-rs.workspace_root=/Users/rob/Workspace/MonoRepo3' --tag cross-custom-monorepo:x86_64-unknown-linux-gnu-8a8d0-pre-build --build-arg 'CROSS_CMD=dpkg --add-architecture $CROSS_DEB_ARCH
      apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH' --build-arg 'CROSS_DEB_ARCH=amd64' --file /Users/rob/Workspace/MonoRepo3/target/x86_64-unknown-linux-gnu/Dockerfile.x86_64-unknown-linux-gnu-custom /Users/rob/Workspace/MonoRepo3` failed with exit status: 1

Note: CROSS_CMD=dpkg --add-architecture $CROSS_DEB_ARCH
apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH

Shoot… I just realized that for iOS and Android I was building static libraries and using Xcode and Android Studio to make the binary. So maybe I can try that tactic here. Maybe I could run the last linker step on the Linux server, and just use plain cargo build on my laptop to make a static library for Linux.

Yup. That works. Sorry for the noise, but it won’t let me delete the post now. For those that might be searching for same thing, you can cross compile a static library, and then do the final linking step on the linux server. So on my laptop:

// lib.rs
#[unsafe(no_mangle)]
pub extern "C" fn hello_world() {
    println!("Hello, world! (cross compiled, hopefully)");
}
# Cargo.toml
[package]
name = "hello-cross"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["staticlib"]

[dependencies]

Build:

% cargo build --target x86_64-unknown-linux-gnu -p hello-cross --lib --release
% scp target/x86_64-unknown-linux-gnu/release/libhello_cross.a my-cloud-server:

Then on the Linux server:

// main.c
void hello_world();
int main() {
  hello_world();
}

And:

% gcc main.c libhello_cross.a -lpthread -ldl
% ./a.out 
Hello, world! (cross compiled, hopefully)

This works for me. My main goal was to run things on Linux without uploading intellectual property like source code to the server.

1 Like