Add support for static linking of sqlx

`sqlx` requires linking against OpenSSL twice:

- `sqlx-macros` needs to link against OpenSSL as a shared libary for use
  at compile time.
- `sqlx` needs to link against OpenSSL a static library for use at
  runtime.

You can find the details here:

https://github.com/launchbadge/sqlx/issues/670
https://github.com/sfackler/rust-openssl/issues/1337

We go with the fix proposed by @sfackler, and add a test program that we
can use to verify everything works correctly.

We remove a few `OPENSSL`-related variables that were added 4 years ago,
and that no longer appear to be needed.
This commit is contained in:
Eric Kidd 2020-09-03 16:18:59 -04:00
parent 9f6efa542f
commit 727d912b3b
13 changed files with 1386 additions and 11 deletions

View file

@ -67,7 +67,7 @@ RUN apt-get update && \
curl -fLO https://github.com/EmbarkStudios/cargo-deny/releases/download/$CARGO_DENY_VERSION/cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz && \ curl -fLO https://github.com/EmbarkStudios/cargo-deny/releases/download/$CARGO_DENY_VERSION/cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz && \
tar xf cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz && \ tar xf cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz && \
mv cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl/cargo-deny /usr/local/bin/ && \ mv cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl/cargo-deny /usr/local/bin/ && \
rm -rf cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz rm -rf cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz
# Static linking for C++ code # Static linking for C++ code
RUN sudo ln -s "/usr/bin/g++" "/usr/bin/musl-g++" RUN sudo ln -s "/usr/bin/g++" "/usr/bin/musl-g++"
@ -142,11 +142,8 @@ RUN echo "Building libpq" && \
cd ../../bin/pg_config && make && sudo make install && \ cd ../../bin/pg_config && make && sudo make install && \
rm -r /tmp/* rm -r /tmp/*
ENV OPENSSL_DIR=/usr/local/musl/ \ ENV X86_64_UNKNOWN_LINUX_MUSL_OPENSSL_DIR=/usr/local/musl/ \
OPENSSL_INCLUDE_DIR=/usr/local/musl/include/ \ X86_64_UNKNOWN_LINUX_MUSL_OPENSSL_STATIC=1 \
DEP_OPENSSL_INCLUDE=/usr/local/musl/include/ \
OPENSSL_LIB_DIR=/usr/local/musl/lib/ \
OPENSSL_STATIC=1 \
PQ_LIB_STATIC_X86_64_UNKNOWN_LINUX_MUSL=1 \ PQ_LIB_STATIC_X86_64_UNKNOWN_LINUX_MUSL=1 \
PG_CONFIG_X86_64_UNKNOWN_LINUX_GNU=/usr/bin/pg_config \ PG_CONFIG_X86_64_UNKNOWN_LINUX_GNU=/usr/bin/pg_config \
PKG_CONFIG_ALLOW_CROSS=true \ PKG_CONFIG_ALLOW_CROSS=true \

View file

@ -2,6 +2,8 @@
[![Docker Image](https://img.shields.io/docker/pulls/ekidd/rust-musl-builder.svg?maxAge=2592000)](https://hub.docker.com/r/ekidd/rust-musl-builder/) [![Docker Image](https://img.shields.io/docker/pulls/ekidd/rust-musl-builder.svg?maxAge=2592000)](https://hub.docker.com/r/ekidd/rust-musl-builder/)
[Source on GitHub](https://github.com/emk/rust-musl-builder)
## What is this? ## What is this?
Do you want to compile a completely static Rust binary with no external dependencies? If so, try: Do you want to compile a completely static Rust binary with no external dependencies? If so, try:
@ -13,7 +15,7 @@ rust-musl-builder cargo build --release
This command assumes that `$(pwd)` is readable and writable by uid 1000, gid 1000. At the moment, it doesn't attempt to cache libraries between builds, so this is best reserved for making final release builds. This command assumes that `$(pwd)` is readable and writable by uid 1000, gid 1000. At the moment, it doesn't attempt to cache libraries between builds, so this is best reserved for making final release builds.
For a more realistic example, see the `Dockerfile` for [examples/using-diesel](./examples/using-diesel). For a more realistic example, see the `Dockerfile` for [examples/using-diesel](./examples/using-diesel) [examples/using-sqlx](./examples/using-sqlx).
## Deploying your Rust application ## Deploying your Rust application
@ -36,7 +38,7 @@ In general, we provide the following tagged Docker images:
Rust, please file an issue. Rust, please file an issue.
At a minimum, each of these images should be able to At a minimum, each of these images should be able to
compile [examples/using-diesel](./examples/using-diesel). compile [examples/using-diesel](./examples/using-diesel) and [examples/using-sqlx](./examples/using-sqlx).
[comp]: https://rust-lang.github.io/rustup-components-history/index.html [comp]: https://rust-lang.github.io/rustup-components-history/index.html
@ -94,8 +96,6 @@ This image also supports the following extra goodies:
If your application uses OpenSSL, you will also need to take a few extra steps to make sure that it can find OpenSSL's list of trusted certificates, which is stored in different locations on different Linux distributions. You can do this using [`openssl-probe`](https://crates.io/crates/openssl-probe) as follows: If your application uses OpenSSL, you will also need to take a few extra steps to make sure that it can find OpenSSL's list of trusted certificates, which is stored in different locations on different Linux distributions. You can do this using [`openssl-probe`](https://crates.io/crates/openssl-probe) as follows:
```rust ```rust
extern crate openssl_probe;
fn main() { fn main() {
openssl_probe::init_ssl_cert_env_vars(); openssl_probe::init_ssl_cert_env_vars();
//... your code //... your code

View file

@ -9,3 +9,4 @@ diesel = { version = "1", features = ["postgres", "sqlite"] }
libsqlite3-sys = { version = "*", features = ["bundled"] } libsqlite3-sys = { version = "*", features = ["bundled"] }
# Needed for Postgres. # Needed for Postgres.
openssl = "*" openssl = "*"
openssl-probe = "0.1.2"

View file

@ -46,6 +46,8 @@ where
} }
fn main() { fn main() {
openssl_probe::init_ssl_cert_env_vars();
println!("Hello, world!"); println!("Hello, world!");
// Only run our database example if we have a database. Otherwise, we just // Only run our database example if we have a database. Otherwise, we just

View file

@ -0,0 +1 @@
target

1265
examples/using-sqlx/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
[package]
name = "using-sqlx"
version = "0.1.0"
authors = ["Eric Kidd <git@randomhacks.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
openssl-probe = "0.1.2"
sqlx = { version = "0.4.0-beta.1", default-features = false, features = ["runtime-tokio", "macros", "offline", "postgres"] }
tokio = { version = "0.2", features = ["macros"] }

View file

@ -0,0 +1,25 @@
# -*- mode: dockerfile -*-
#
# An example Dockerfile showing how to build a Rust executable using this
# image, and deploy it with a tiny Alpine Linux container.
# You can override this `--build-arg BASE_IMAGE=...` to use different
# version of Rust or OpenSSL.
ARG BASE_IMAGE=ekidd/rust-musl-builder:latest
# Our first FROM statement declares the build environment.
FROM ${BASE_IMAGE} AS builder
# Add our source code.
ADD --chown=rust:rust . ./
# Build our application.
RUN cargo build --release
# Now, we need to build our _real_ Docker container, copying in `using-sqlx`.
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder \
/home/rust/src/target/x86_64-unknown-linux-musl/release/using-sqlx \
/usr/local/bin/
CMD /usr/local/bin/using-sqlx

View file

@ -0,0 +1,12 @@
version: "2.0"
services:
app:
build: .
volumes:
- cargo:/home/rust/.cargo
- target:/home/rust/src/target
volumes:
cargo: {}
target: {}

View file

@ -0,0 +1,23 @@
{
"db": "PostgreSQL",
"bf54a84704792059fc3a494889dce274bb09f5cadc8c7be61a9b85abbceb001f": {
"query": "SELECT $1::INTEGER AS value",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
null
]
}
}
}

View file

@ -0,0 +1,23 @@
use std::env;
use sqlx::{Connection, postgres::PgConnection};
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
openssl_probe::init_ssl_cert_env_vars();
let url = env::var("POSTGRES_URL")
.unwrap_or_else(|_| "postgresql://postgres@localhost/postgres".to_owned());
// Create a connection.
let mut conn = PgConnection::connect(&url).await?;
// Make a simple query using the `query!` macro.
let row = sqlx::query!("SELECT $1::INTEGER AS value", 2i32)
.fetch_one(&mut conn)
.await?;
assert_eq!(row.value, Some(2));
Ok(())
}

View file

@ -4,7 +4,7 @@
set -euo pipefail set -euo pipefail
# Make sure we can build some of our more important test images. # Make sure we can build some of our more important test images.
for EXAMPLE in using-diesel; do for EXAMPLE in using-diesel using-sqlx; do
docker build \ docker build \
--build-arg BASE_IMAGE="$IMAGE_NAME" \ --build-arg BASE_IMAGE="$IMAGE_NAME" \
-t rust-musl-builder-"$EXAMPLE" \ -t rust-musl-builder-"$EXAMPLE" \

View file

@ -41,6 +41,20 @@ fi
echo -e '[PASS] ARMhf binary is statically linked.\n' echo -e '[PASS] ARMhf binary is statically linked.\n'
" "
# Make sure we can build a static executable using `sqlx`.
docker build -t rust-musl-builder-using-sqlx examples/using-sqlx
docker run --rm rust-musl-builder-using-sqlx sh -c "
set -euo pipefail
echo -e '--- Test case for sqlx:'
echo 'ldd says:'
if ldd /usr/local/bin/using-sqlx; then
echo '[FAIL] Executable is not static!' 1>&2
exit 1
fi
echo -e '[PASS] using-sqlx binary is statically linked.\n'
"
# Make sure we can build a static executable using `git2`. # Make sure we can build a static executable using `git2`.
docker build -t rust-musl-builder-linking-with-git2 examples/linking-with-git2 docker build -t rust-musl-builder-linking-with-git2 examples/linking-with-git2
docker run --rm rust-musl-builder-linking-with-git2 bash -c " docker run --rm rust-musl-builder-linking-with-git2 bash -c "