Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build an executable that depends on curl for x86_64-unknown-linux-musl

Tags:

rust

musl

I am on an amd64 Debian machine, and am trying to build a x86_64-unknown-linux-musl executable. I have this in my Cargo.toml:

[dependencies]
curl = "0.4"

When I run cargo build --target=x86_64-unknown-linux-musl I get this:

error: failed to run custom build command for `libz-sys v1.0.10`
process didn't exit successfully: `/tmp/foo/target/debug/build/libz-sys-c20da5f29c41e515/build-script-build` (exit code: 101)
--- stdout
OPT_LEVEL = Some("0")
PROFILE = Some("debug")
TARGET = Some("x86_64-unknown-linux-musl")
debug=true opt-level=0
HOST = Some("x86_64-unknown-linux-gnu")
TARGET = Some("x86_64-unknown-linux-musl")
TARGET = Some("x86_64-unknown-linux-musl")
HOST = Some("x86_64-unknown-linux-gnu")
CC_x86_64-unknown-linux-musl = None
CC_x86_64_unknown_linux_musl = None
TARGET_CC = None
CC = None
HOST = Some("x86_64-unknown-linux-gnu")
CROSS_COMPILE = None
TARGET = Some("x86_64-unknown-linux-musl")
HOST = Some("x86_64-unknown-linux-gnu")
CFLAGS_x86_64-unknown-linux-musl = None
CFLAGS_x86_64_unknown_linux_musl = None
TARGET_CFLAGS = None
CFLAGS = None
running: "./configure" "--prefix=/tmp/foo/target/x86_64-unknown-linux-musl/debug/build/libz-sys-e109627694e9981e/out"
Compiler error reporting is too harsh for ./configure (perhaps remove -Werror).
** ./configure aborting.

--- stderr
thread 'main' panicked at 'failed to run successfully: exit code: 1', /home/tshepang/.cargo/registry/src/github.com-1ecc6299db9ec823/libz-sys-1.0.10/build.rs:189

When I re-run it:

error: failed to run custom build command for `openssl-sys v0.9.6`
process didn't exit successfully: `/tmp/foo/target/debug/build/openssl-sys-ac9c042b062dad1d/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at '

Could not find directory of OpenSSL installation, and this `-sys` crate cannot
proceed without this knowledge. If OpenSSL is installed and this crate had
trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
compilation process.

If you're in a situation where you think the directory *should* be found
automatically, please open a bug at https://github.com/sfackler/rust-openssl
and include information about your system as well as this message.

    $HOST = x86_64-unknown-linux-gnu
    $TARGET = x86_64-unknown-linux-musl
    openssl-sys = 0.9.6

All works well when I build natively, i.e. cargo build --target=x86_64-unknown-linux-gnu.

Searching around, I learned about an environment variable, PKG_CONFIG_ALLOW_CROSS:

PKG_CONFIG_ALLOW_CROSS=true cargo build --target=x86_64-unknown-linux-musl

In doing that, I also found that I was missing the Debian package named libcurl4-openssl-dev.

Running ldd target/target/x86_64-unknown-linux-musl/debug/foo indicated the executable is dynamically linked, then searching further, I learned about another environment variable, PKG_CONFIG_ALL_STATIC:

PKG_CONFIG_ALL_STATIC=true PKG_CONFIG_ALLOW_CROSS=true cargo build --target=x86_64-unknown-linux-musl

That revealed a whole bunch of missing deps, all of which (luckily) had Debian dependencies. But installing all of them did not help, as, in the end, I was still sitting with an executable that wasn't statically linked .

like image 204
tshepang Avatar asked Jan 18 '17 15:01

tshepang


2 Answers

I gave in and ended up using cross:

cargo install cross
cross build --target=x86_64-unknown-linux-musl

This was just too easy, and you will find the executable in target/x86_64-unknown-linux-musl/debug.

like image 178
tshepang Avatar answered Nov 09 '22 02:11

tshepang


The curl crate depends (directly or indirectly) on the two crates libz-sys and openssl-sys. A crate whose name ends in "-sys" is generally a set of FFI (foreign function interface) bindings to a native C library.

Building such a "-sys" crate requires linking to the native library. If your target is x86_64-unknown-linux-musl, then you must link to a native library built against musl, not glic. However, most of the packages you will find in the repositories of your distribution provide libraries built against glibc.

The solution is to build yourself the libraries you need, linking to musl instead of glibc.

I don't have access to a Debian installation, but on Ubuntu 16.04 this looks like this for OpenSSL:

# this package provides the "musl-gcc" wrapper
apt-get install musl-tools 
# you will also need these, if they are not installed yet
apt-get install pkg-config xutils-dev build-essential

# Download and build OpenSSL against musl
VERS=1.0.2j
export CC=musl-gcc
export MUSL_PREFIX=/usr/local/musl
export C_INCLUDE_PATH="$MUSL_PREFIX/include/"
curl -O https://www.openssl.org/source/openssl-$VERS.tar.gz
tar xvzf openssl-$VERS.tar.gz
cd openssl-$VERS

./config --prefix "$MUSL_PREFIX"
make depend
make
sudo make install

export OPENSSL_DIR=/usr/local/musl/
export OPENSSL_STATIC=1

Once you have one the same for libz (I haven't tried to built it), you should then be able to build your crate:

 cargo build --target=x86_64-unknown-linux-musl

and the resulting binary will be in target/x86_64-unknown-linux-musl/debug/<binary_name>

The cross tool does basically this, but inside a Docker container as to keep your host machine clean.

The binary produced by this build should be statically linked, and not depend even on glibc. This also means that it will be bigger* and that you will need to take care yourself of upgrading any dependency (especially OpenSSL) if a security issue is found in one of them.

*You may want to use strip on the released binary.

like image 2
Benoît Faucon Avatar answered Nov 09 '22 01:11

Benoît Faucon