Often we need to distribute binaries across a variety of platforms where they need to operate past various updates with little to no maintenance. Perhaps its for an esoteric software environment, like termux. Sometimes its just the harsh reality that you might be writing a program nobody will maintain after you move to the next project, and you want the software to remain operational as long as possible.
Essential Steps
Straight to the point, here are the main series of steps required to achieve this as fast as possible:
- Create your cargo project
- Install cross-rs. Note this is gonna include an installation of docker or podman as a substep.
-
Make a file in the project directory under
.cargo/config.toml
:[build] target = "x86_64-unknown-linux-gnu" rustflags = "-C target-feature=+crt-static"
-
Use vendored openssl, in
Cargo.toml
this is a line that could looks like:openssl = { version = "*", features = ["vendored"] }
-
Build with
cross build --target x86_64-unknown-linux-gnu --release
-
VoilĂ ! Now
ldd
should tell you that the resultant binary isnot a dynamic executable
.
A possibly better way
I often build both not static and static versions of things. This is because cross-rs has a lot of limitation, such as being unable to compile tests in parallel, that make it a pain to use for every debug build. However, vendored openssl won't compile correctly for me outside cross-rs. So I use this script to manage static vs non-static:
#!/bin/bash
FILE="Cargo.toml"
if [ ! -f "$FILE" ]; then
echo "Error: File '$FILE' not found."
exit 1
fi
# Check if the line exists (in commented form)
if grep -q "^# openssl = { version = \"\*\", features = \[\"vendored\"\] }" "$FILE"; then
echo "Uncommenting openssl vendored line in $FILE..."
sed -i 's/^# \(openssl = { version = "\*", features = \["vendored"\] }\)/\1/' "$FILE"
echo "Building with vendored openssl..."
RUSTFLAGS='-C target-feature=+crt-static' cross build --target x86_64-unknown-linux-gnu --release
echo "Commenting openssl vendored line again..."
sed -i 's/^\(openssl = { version = "\*", features = \["vendored"\] }\)/# \1/' "$FILE"
echo "Build process completed."
else
echo "The openssl vendored line was not found in Cargo.toml."
exit 1
fi
The following changes are required in order for this script to work (because it handles this for you)
-
Don't have
.cargo/config.toml
. This script passes the RUSTFLAGS environment variable instead -
Have the vendored openssl line in
Cargo.toml
commented, as this script will uncomment/comment it for the build. It is done this way because Cargo doesn't currently let you specify dependencies by debug vs release.
Troubleshooting
Note these use my directories from real examples, but the specific location depends on where your cargo project is
-
Message:
error: cannot produce proc-macro for `actix-macros v0.2.4` as the target `x86_64-unknown-linux-gnu` does not support these crate types
Cause: You didn't specify a target triplet. In the build script or.cargo/config.toml
you need atarget = "x86_64-unknown-linux-gnu"
line. -
Message:
Cause: Your system doesn't provide a static build of openssl (which is where all my stuff about cross-rs and vendored openssl comes from). You need to make sure you specify vendored openssl in= note: /usr/bin/ld: cannot find -lssl: No such file or directory /usr/bin/ld: have you installed the static version of the ssl library ? /usr/bin/ld: cannot find -lcrypto: No such file or directory /usr/bin/ld: have you installed the static version of the crypto library ? collect2: error: ld returned 1 exit status
Cargo.toml
-
Message:
Cause: Your system can't build vendored openssl (I found this to be the case with mine). This is what cross-rs solves.Error installing OpenSSL: 'make' reported failure with exit status: 2 Command failed: cd "/home/thekingofravens/Programming/Poker Palace/unified_reporting/target/x86_64-unknown-linux-gnu/debug/build/openssl-sys-3d2ab0c7d5d9c197/out/openssl-build/build/src" && "make" "install_dev"
About Termux
I use termux as a test environment to try and create bad server
conditions. You can't run the binaries directly on termux as is,
because you run into issues like
this one. I
won't go into detail here, but broadly, you use
proot-distro
within termux to install a debian installation, which can then
be targeted. You use the same steps used with
cross-rs
, but change to target triplet to something
relevant like aarch64-unknown-linux-gnu