├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── BaseDockerfile.template ├── README.md ├── RustDockerfile.template ├── _build_base.sh ├── _build_rust.sh ├── build_manual.sh ├── build_nightly.sh └── build_releases.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker:latest 2 | 3 | services: 4 | - docker:dind 5 | 6 | build_base: 7 | stage: build 8 | script: 9 | - ./build_manual.sh -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | script: 7 | - ./build_manual.sh -------------------------------------------------------------------------------- /BaseDockerfile.template: -------------------------------------------------------------------------------- 1 | FROM debian:stretch 2 | MAINTAINER Pyry Kontio 3 | USER root 4 | 5 | # NOTES TO MYSELF AND OTHERS TOO. THEY MIGHT BE HELPFUL, 6 | # since this took me too long to figure out. 7 | 8 | # This image is for building apps in Rust nightly for musl target. 9 | # It contains the needed libraries to be able to build Diesel and Rocket. 10 | # Setting up the build environment isn't actually easy at all – because 11 | # Diesel and Rocket use code-generating compiler plugins, they plugins 12 | # must be able to run in the host environment, whereas we want the final build 13 | # to be a statically linked musl build. So we need to set up the environment 14 | # doubly and ensure that the libraries are compatible and the correct libraries 15 | # are selected. 16 | 17 | # This dockerfile is easy to build with associated build.sh script. 18 | # While building, it assembles the build environment for compiling static musl binaries. 19 | # The pain of static compiling is the native dependencies, 20 | # but with correct configuration, it should be all right. 21 | # You need to download the C libraries, build the sources with appropriate 22 | # settings and check from the appropriate Rust crates (usually named something-sys) 23 | # how they are going to present the linking flags to Cargo. 24 | # Read the build.rs files of those crates. Usually you need to set some 25 | # environment variables that affect how the build.rs build scripts work. 26 | 27 | # Note: if build.rs doesn't provide options for static linking 28 | # and/or cross-compiling, forking the crate, cargo-patching it 29 | # and adding that functionality is a thing! It helps everyone. 30 | # The build.rs scripts provide the linking flags to cargo by printing some 31 | # instructions like `println!("cargo:rustc-link-lib=static={}", lib);`, 32 | # where lib is the libname as passed to linker [pq, ssl, crypto etc.].) 33 | # Remember to contribute to upstream too! 34 | 35 | # Note about compiler plugins. 36 | # The compiler plugins run in the HOST environment alongside the compiler, 37 | # whereas the compiled binary is of the TARGET environment. 38 | # In this image, HOST is x86_64-unknown-linux-musl 39 | # and TARGET is x86_64-unknown-linux-gnu. 40 | # Additionally compiler plugins are dynamically linked. 41 | # This all means that you need to have installed both 42 | # dynamically linked HOST libraries for compiler plugins 43 | # and statically linked TARGET libraries for linking. 44 | 45 | # Note about the OpenSSL version: 46 | # The version is configurable. I tried using 1.1.0 briefly, 47 | # but it introduced some problems with its stricter cert checking, 48 | # so now the build.sh defaults to 1.0.2. This can be easily changed, however, 49 | # and the build environment fully supports building 1.1.0. 50 | 51 | # Note about the distro version: 52 | # Debian Stretch (9) introduced GCC that defaults to position-independent code. 53 | # The support for linking PIE code statically was added to Rust recently (2017-08-25). 54 | # Before that it used to link the libraries statically, but still produce 55 | # a dynamic looking executable (a binary with a reference to the dynamic loader), 56 | # which partially defeats the purpose. 57 | # No support is guaranteed for older Rust nightlies than that. 58 | 59 | # USAGE: 60 | # When run, this image compiles can be used to compile static musl builds, 61 | # presenting the --target=x86_64-unknown-linux-musl flag to cargo. (-vv) is 62 | # also recommended for debugging purposes, as static linking and 63 | # cross-compiling builds break easily in my experience. 64 | # It is recommended to mount not only the work directory, but also the 65 | # `~/.cargo/git` and `~/.cargo/registry` directories as it enables better caching. 66 | 67 | # Example of using the built container to build Rust executables: 68 | # (from command line in the project directory) 69 | # 70 | # docker run -it --rm \ 71 | # -v $PWD:/workdir \ 72 | # -v ~/.cargo/git:/root/.cargo/git \ 73 | # -v ~/.cargo/registry:/root/.cargo/registry \ 74 | # golddranks/rust_musl_docker:nightly-2017-08-21 \ 75 | # cargo build --release -vv --target=x86_64-unknown-linux-musl 76 | 77 | 78 | 79 | # Installing packages - WHY THESE PACKAGES? 80 | # musl itself: 81 | # musl-dev, musl-tools 82 | # optional utilities for debugging inside the container: 83 | # file - for inspecting built files (whether they are actually static) 84 | # nano - a text editor 85 | # git - because basically every Rust project uses it 86 | # zlib1g-dev, cmake, libssl-dev - needed for building cargo-tree which helps visualizing the build deps 87 | # libssl-dev - needed for dynamically linking (for diesel code generation) the libpq crate 88 | # needed utilities for downloading, installing and building everything 89 | # (diesel, rocket and their dependencies, mainly libpq and openssl): 90 | # curl, g++, make, pkgconf - these are needed by many libraries 91 | # linux-headers-amd64 - needed for building openssl 1.1 92 | # ca-certificates - for openssl & curl. 93 | # xutils-dev because it contains makedepend with is used by openssl build system 94 | # libpq-dev - needed for dynamically linking (for diesel code generation) the libpq crate 95 | 96 | RUN apt-get update && \ 97 | apt-get install -y \ 98 | musl-dev \ 99 | musl-tools \ 100 | file \ 101 | nano \ 102 | git \ 103 | zlib1g-dev \ 104 | cmake \ 105 | make \ 106 | g++ \ 107 | curl \ 108 | pkgconf \ 109 | linux-headers-amd64 \ 110 | ca-certificates \ 111 | xutils-dev \ 112 | libpq-dev \ 113 | libssl-dev \ 114 | --no-install-recommends && \ 115 | rm -rf /var/lib/apt/lists/* 116 | 117 | # Setting the musl prefix where the statically linked C libs are installed 118 | # Setting the workdir for building the C libs (we will later set another one for the Rust project) 119 | ENV MUSL_PREFIX=/musl 120 | RUN mkdir /workdir && mkdir $MUSL_PREFIX 121 | WORKDIR /libworkdir 122 | 123 | # OpenSSL 1.1 needs some linux headers to exists. They aren't installed by 124 | # default to the directory of musl includes, so we must link them. 125 | # OpenSSL 1.0 doesn't need these, but they won't do any harm. 126 | RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/include/x86_64-linux-musl/asm && \ 127 | ln -s /usr/include/asm-generic /usr/include/x86_64-linux-musl/asm-generic && \ 128 | ln -s /usr/include/linux /usr/include/x86_64-linux-musl/linux 129 | 130 | # Let's download the libraries so we don't have to do this everytime when 131 | # debugging failing builds. 132 | RUN curl -sL http://www.openssl.org/source/openssl-OPENSSL_VER.tar.gz | tar xz 133 | RUN curl -sL https://ftp.postgresql.org/pub/source/vPOSTGRES_VER/postgresql-POSTGRES_VER.tar.gz | tar xz 134 | RUN curl -sL https://zlib.net/zlib-1.2.11.tar.gz | tar xz 135 | 136 | # Note that since Debian Stretch, gcc builds position-independent executables. 137 | # The compilation will fail without flags "-fPIE" (which is for the compiler) 138 | # and "-pie" (which is for the linker). 139 | 140 | # The LDFLAGS and CFLAGS are set so that it finds the libraries we built earlier 141 | # with the prefix /musl. (System musl libraries don't a have common prefix, 142 | # they are found, for example in /usr/lib/x86_64-linux-musl, and includes in 143 | # /usr/include/x86_64-linux-musl) This allows the libraries we build later 144 | # to find the libraries we've built earlier. zlib doesn't depend on anything, 145 | # but we'll set the flags for every one for consistency. 146 | RUN cd zlib-1.2.11 && \ 147 | CC="musl-gcc -fPIE -pie" LDFLAGS="-L/musl/lib/" CFLAGS="-I/musl/include" ./configure --prefix=$MUSL_PREFIX && \ 148 | make -j$(nproc) && make install 149 | 150 | # OpenSSL needs to be built with no-async because otherwise the build is going 151 | # to crash with missing symbols. (Apparently it uses some stuff that musl doesn't have by default.) 152 | # Note that OpenSSL has their own build system, they don't use autotools. 153 | # This makes the settings a bit non-standard. 154 | # Again, we use "-fPIE -pie" and set the flags. 155 | RUN cd openssl-OPENSSL_VER && \ 156 | CC="musl-gcc -fPIE -pie" LDFLAGS="-L/musl/lib/" CFLAGS="-I/musl/include" ./Configure no-shared no-async --prefix=$MUSL_PREFIX --openssldir=$MUSL_PREFIX/ssl linux-x86_64 && \ 157 | make depend && make -j$(nproc) && make install 158 | 159 | # Adding out library path to /etc/ld-musl-x86_64.path allows the dynamic linker 160 | # to find musl libraries built by us. The configure script of postgres will 161 | # fail without this, since it tests dynamic linking even if ne don't need it. 162 | RUN echo "/musl/lib" >> /etc/ld-musl-x86_64.path 163 | 164 | # Again, we use "-fPIE -pie" and set the flags. 165 | # Readline is disabled, because we are too lazy to build musl-compatible one by ourselves. 166 | 167 | RUN cd postgresql-POSTGRES_VER && \ 168 | CC="musl-gcc -fPIE -pie" LDFLAGS="-L/musl/lib/" CFLAGS="-I/musl/include" ./configure --prefix=$MUSL_PREFIX --host=x86_64-unknown-linux-musl --without-readline && \ 169 | make -j$(nproc) && make install -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust_musl_docker – A well-documented container for building Rust crates for MUSL target. Supports dependencies for OpenSSL and Postgres. 2 | 3 | ## NOTE: This repository is moving to GitLab. We will provide freshly built nightly docker images there! https://gitlab.com/rust_musl_docker/image 4 | 5 | This docker image is primarily meant for building statically Rust crates that use **Diesel** and **Rocket** libraries. 6 | A combination of static linking, native (C) dependencies and crates that use heavily compiler plugins is hard to get to 7 | compile. (1) This Docker image is meant to help with that. Not only it supports Diesel and Rocket (and many other crates) 8 | directly, it also is fully commented to help for possible customisation needs. There exists other similar images too, 9 | but the lack of comments make them unhelpful if they don't happen to contain the exact things you need. 10 | 11 | `Dockerfile.template` is fully commented! Please read it for details. 12 | 13 | ## USAGE: 14 | 15 | Run this container (you can do that directly but I recommend writing a build script) from your project dir. 16 | 17 | Mount the current work dir (`-v $PWD:/workdir`) and Cargo cache dirs (`-v ~/.cargo/git:/root/.cargo/git` & 18 | `-v ~/.cargo/registry:/root/.cargo/registry`) to be able to reuse the cache. 19 | 20 | You can customise the build command; in this example, it builds the release build super-verbosely for musl target. 21 | I'll occasionally build new images for new nightlies, but you can do that yourself too, editing and running the build.sh 22 | script. 23 | 24 | 25 | ``` 26 | docker run -it --rm \ 27 | -v $PWD:/workdir \ 28 | -v ~/.cargo/git:/root/.cargo/git \ 29 | -v ~/.cargo/registry:/root/.cargo/registry \ 30 | golddranks/rust_musl_docker:nightly-2018-05-09_openssl-1.1.0h \ 31 | cargo build --release -vv --target=x86_64-unknown-linux-musl 32 | ``` 33 | 34 | (1) This has gotten even harder as of late, with Debian Stretch moving to position-independent-code by default and the Rust compiler enabling RELRO. Fortunately, after a lot of trial and error, the build enviroment works. 35 | -------------------------------------------------------------------------------- /RustDockerfile.template: -------------------------------------------------------------------------------- 1 | FROM BASE_IMAGE 2 | 3 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain RUST_TOOLCHAIN 4 | 5 | # .cargo/bin in PATH is needed for running cargo, rustup etc. 6 | ENV PATH=/root/.cargo/bin:$PATH 7 | 8 | # A tool for visualizing Rust build dependencies. Handy for debugging the build inside the container. 9 | RUN cargo install cargo-tree 10 | 11 | # Installing the musl target! 12 | RUN rustup target add x86_64-unknown-linux-musl 13 | 14 | # SETTING THE ENV VARS FOR MUSL BUILDS - WHY THESE? 15 | # PKG_CONFIG_PATH Some of the build.rs script of *-sys crates use pkg-config to probe for libs. 16 | # PKG_CONFIG_ALLOW_CROSS This tells the rust pkg-config crate to be enabled even when cross-compiling 17 | # PKG_CONFIG_ALL_STATIC This tells the rust pkg-config crate to statically link the native dependencies 18 | # The pq-sys crate doesn't use PKG_CONFIG, and we must manually pass PQ_LIB_STATIC for it to link statically. 19 | # PATH /musl/bin is needed because the build.rs of pq-sys runs a postgres binary pg_config from there that tells it the lib dir. 20 | 21 | ENV PATH=$MUSL_PREFIX/bin:$PATH \ 22 | PKG_CONFIG_ALLOW_CROSS=true \ 23 | PKG_CONFIG_ALL_STATIC=true \ 24 | PKG_CONFIG_PATH=$MUSL_PREFIX/lib/pkgconfig \ 25 | PQ_LIB_STATIC_X86_64_UNKNOWN_LINUX_MUSL=true \ 26 | PG_CONFIG_X86_64_UNKNOWN_LINUX_GNU=/usr/bin/pg_config \ 27 | OPENSSL_STATIC=true \ 28 | OPENSSL_DIR=$MUSL_PREFIX \ 29 | SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ 30 | SSL_CERT_DIR=/etc/ssl/certs \ 31 | LIBZ_SYS_STATIC=1 32 | 33 | WORKDIR /workdir 34 | -------------------------------------------------------------------------------- /_build_base.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | # PARAMS: OpenSSL version, PostgreSQL version 4 | OPENSSL_VER="$1" 5 | POSTGRES_VER="$2" 6 | 7 | # zlib seems to update so seldom that 8 | # it's version is hardcoded in the BaseDockerfile.template 9 | # as zlib 1.2.11 10 | 11 | # available openssl versions: "1.1.0i" and "1.1.1" 12 | 13 | # available postgresql versions: "9.6.10", "10.5" and "11.0" 14 | 15 | TEMP_DOCKERFILE="BaseDockerfile.$(date '+%s').tmp" 16 | REPO="golddranks/rust_musl_docker_base" 17 | TAG="openssl-${OPENSSL_VER}_postgres-${POSTGRES_VER}" 18 | 19 | cat BaseDockerfile.template | \ 20 | sed "s@OPENSSL_VER@$OPENSSL_VER@g" | \ 21 | sed "s@POSTGRES_VER@$POSTGRES_VER@g" > "$TEMP_DOCKERFILE" 22 | 23 | docker build -f "$TEMP_DOCKERFILE" -t "$REPO":"$TAG" . 24 | rm "$TEMP_DOCKERFILE" 25 | docker push "$REPO":"$TAG" -------------------------------------------------------------------------------- /_build_rust.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | 4 | # PARAMS: Rust version, OpenSSL version, PostgreSQL version, tag name 5 | RUST_VER="$1" 6 | OPENSSL_VER="$2" 7 | POSTGRES_VER="$3" 8 | TAG="$4" 9 | 10 | # available openssl versions: "1.1.0i" and "1.1.1" 11 | 12 | # available postgresql versions: "9.6.10", "10.5" and "11.0" 13 | 14 | TEMP_DOCKERFILE="RustDockerfile.$(date '+%s').tmp" 15 | REPO="registry.gitlab.com/rust_musl_docker" 16 | 17 | BASE_REPO="golddranks/rust_musl_docker_base" 18 | BASE_TAG="openssl-${OPENSSL_VER}_postgres-${POSTGRES_VER}" 19 | 20 | cat RustDockerfile.template | \ 21 | sed "s@BASE_IMAGE@$BASE_REPO:$BASE_TAG@g" | \ 22 | sed "s@RUST_TOOLCHAIN@$RUST_VER@g" > "$TEMP_DOCKERFILE" 23 | 24 | docker build -f "$TEMP_DOCKERFILE" -t "$REPO":"$TAG" . 25 | rm "$TEMP_DOCKERFILE" 26 | docker login registry.gitlab.com -u gitlab-ci-token -p $CI_BUILD_TOKEN 27 | docker push "$REPO":"$TAG" -------------------------------------------------------------------------------- /build_manual.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | 4 | # stable with an non-changing tag 5 | ./_build_rust.sh "1.29.2" "1.1.1" "11.0" "stable-1.29.2" 6 | -------------------------------------------------------------------------------- /build_nightly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | 4 | NIGHTLY_DATE=$(date -u '+%Y-%m-%d') 5 | 6 | # nightly with older version of libraries 7 | ./_build_rust.sh "nightly-$NIGHTLY_DATE" "1.1.0i" "9.6.10" "nightly-$NIGHTLY_DATE-legacy" 8 | 9 | # nightly 10 | ./_build_rust.sh "nightly-$NIGHTLY_DATE" "1.1.1" "11.0" "nightly-$NIGHTLY_DATE" 11 | -------------------------------------------------------------------------------- /build_releases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | 4 | # newest beta 5 | ./_build_rust.sh "beta" "1.1.1" "11.0" "beta-latest" 6 | 7 | # newest stable 8 | ./_build_rust.sh "stable" "1.1.1" "11.0" "stable-latest" 9 | --------------------------------------------------------------------------------