diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index cf3f595b3..54dd540e3 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,56 +1,103 @@ -FROM alpine:3.17 AS build -ARG VERSION=master +#' Define default build variables +ARG ALPINE_VSN='3.17' +ARG UID='9000' +ARG USER='ejabberd' +ARG HOME="opt/$USER" +ARG METHOD='direct' +ARG BUILD_DIR="/$USER" +ARG VERSION='master' -RUN apk upgrade --update musl \ - && apk add \ - autoconf \ - automake \ - bash \ - build-base \ - curl \ - elixir \ - erlang-odbc \ - erlang-reltool \ - expat-dev \ - file \ - gd-dev \ - git \ - jpeg-dev \ - libpng-dev \ - libwebp-dev \ - linux-pam-dev \ - openssl \ - openssl-dev \ - sqlite-dev \ - yaml-dev \ - zlib-dev +################################################################################ +#' METHOD='direct' - build and install ejabberd directly from source +FROM alpine:${ALPINE_VSN} AS direct + +RUN apk -U add --no-cache \ + autoconf \ + automake \ + bash \ + build-base \ + curl \ + elixir \ + erlang-odbc \ + erlang-reltool \ + expat-dev \ + file \ + gd-dev \ + git \ + jpeg-dev \ + libpng-dev \ + libwebp-dev \ + linux-pam-dev \ + openssl-dev \ + sqlite-dev \ + yaml-dev \ + zlib-dev RUN mix local.hex --force \ && mix local.rebar --force -COPY . ./ejabberd - -WORKDIR ejabberd +ARG BUILD_DIR +COPY / $BUILD_DIR/ +WORKDIR $BUILD_DIR RUN mv .github/container/ejabberdctl.template . \ && ./autogen.sh \ && ./configure --with-rebar=mix --enable-all \ && make deps \ && make rel -RUN cp -r _build/prod/rel/ejabberd/ /opt/ejabberd-$VERSION \ - && mkdir -p /opt/ejabberd \ - && mv /opt/ejabberd-$VERSION/conf /opt/ejabberd/conf +WORKDIR /rootfs +ARG VERSION +ARG HOME +RUN mkdir -p $HOME $HOME-$VERSION \ + && cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \ + && mv $HOME-$VERSION/conf $HOME/conf -RUN cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib" +RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib -RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/ +RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ + && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete -RUN find "/opt/ejabberd-$VERSION/bin" -name 'ejabberd' -delete \ - && find "/opt/ejabberd-$VERSION/releases" -name 'COOKIE' -delete +RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ + && sed -i '/^loglevel:/a \ \ + \nca_file: /opt/ejabberd/conf/cacert.pem \ + \ncertfiles: \ + \n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml" -RUN export PEM=/opt/ejabberd/conf/server.pem \ - && curl -o "/opt/ejabberd/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ +################################################################################ +#' METHOD='package' - install ejabberd from binary tarball package +FROM alpine:${ALPINE_VSN} AS package +COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/ +WORKDIR /rootfs +ARG HOME +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && mkdir -p $home_root_dir \ + && ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \ + && tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir + +################################################################################ +#' Prepare ejabberd for runtime +FROM ${METHOD} AS ejabberd +RUN apk -U add --no-cache \ + git \ + libcap-utils \ + openssl + +WORKDIR /rootfs +ARG HOME +RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload + +ARG BUILD_DIR +RUN if [ ! -d $HOME/.ejabberd-modules ]; \ + then \ + if [ -d $BUILD_DIR/.ejabberd-modules ]; \ + then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \ + else git clone https://github.com/processone/ejabberd-contrib --depth 1 \ + $HOME/.ejabberd-modules/sources/ejabberd-contrib; \ + fi \ + fi + +RUN export PEM=$HOME/conf/server.pem \ && openssl req -x509 \ -batch \ -nodes \ @@ -58,63 +105,81 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \ -keyout $PEM \ -out $PEM \ -days 3650 \ - -subj "/CN=localhost" \ - && sed -i '/^loglevel:/a \ \ - \nca_file: /opt/ejabberd/conf/cacert.pem \ - \ncertfiles: \ - \n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml" + -subj "/CN=localhost" -FROM alpine:3.17 -ENV HOME=/opt/ejabberd -ARG VERSION=master +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \ + && echo -e \ + "#!/bin/sh \ + \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ + \nexport CONFIG_DIR=/$HOME/conf \ + \nexport LOGS_DIR=/$HOME/logs \ + \nexport SPOOL_DIR=/$HOME/database \ + \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \ + > usr/local/bin/ejabberdctl \ + && chmod +x usr/local/bin/* -RUN apk upgrade --update musl \ - && apk add \ - expat \ - freetds \ - gd \ - jpeg \ - libgd \ - libpng \ - libstdc++ \ - libwebp \ - linux-pam \ - ncurses-libs \ - sqlite \ - sqlite-libs \ - tini \ - unixodbc \ - yaml \ - zlib \ - && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so \ - && rm -rf /var/cache/apk/* +ARG UID +RUN chown -R $UID:$UID $HOME -COPY --from=build /opt /opt -RUN echo -e \ - "#!/bin/sh \ - \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ - \nexport CONFIG_DIR=/opt/ejabberd/conf \ - \nexport LOGS_DIR=/opt/ejabberd/logs \ - \nexport SPOOL_DIR=/opt/ejabberd/database \ - \nexec /opt/ejabberd-$VERSION/bin/ejabberdctl \"\$@\"" > /usr/local/bin/ejabberdctl \ - && chmod +x /usr/local/bin/ejabberdctl +################################################################################ +#' METHOD='package' - install runtime dependencies +FROM alpine:${ALPINE_VSN} AS runtime-package +RUN apk -U upgrade --available --no-cache \ + && apk add --no-cache \ + libcap2 \ + tini -RUN addgroup ejabberd -g 9000 \ - && adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \ - && mkdir -p $HOME/conf $HOME/database $HOME/logs $HOME/upload \ - && chown -R ejabberd:ejabberd $HOME +################################################################################ +#' METHOD='direct' - install runtime dependencies +FROM runtime-package AS runtime-direct +RUN apk add --no-cache \ + expat \ + freetds \ + gd \ + jpeg \ + libgd \ + libpng \ + libstdc++ \ + libwebp \ + linux-pam \ + ncurses-libs \ + sqlite \ + sqlite-libs \ + unixodbc \ + yaml \ + zlib \ + && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so + +################################################################################ +#' Finalize runtime environment +FROM runtime-${METHOD} AS runtime +ARG USER +ARG UID +ARG HOME +RUN addgroup $USER -g $UID \ + && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER + +################################################################################ +#' Build together production image +FROM scratch AS prod +ARG USER +ARG HOME + +COPY --from=runtime / / +COPY --from=ejabberd /rootfs / HEALTHCHECK \ --interval=1m \ --timeout=5s \ --start-period=5s \ --retries=10 \ - CMD /usr/local/bin/ejabberdctl status + CMD ejabberdctl status -WORKDIR $HOME -USER ejabberd -VOLUME ["$HOME"] +WORKDIR /$HOME +USER $USER +VOLUME ["/$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 -ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"] -CMD ["foreground"] +ENTRYPOINT ["/sbin/tini","--"] +CMD ["ejabberdctl", "foreground"] diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index dbae22f2d..3663d74a8 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,6 +1,8 @@ name: Container on: + schedule: + - cron: '22 2 */6 * *' # every 6 days to avoid gha cache being evicted push: paths-ignore: - '.devcontainer/**' @@ -21,12 +23,49 @@ jobs: permissions: packages: write steps: - - name: Check out repository code uses: actions/checkout@v3 with: fetch-depth: 0 + - name: Cache build directory + uses: actions/cache@v3 + with: + path: ~/build/ + key: ${{runner.os}}-ctr-ct-ng-1.25.0 + + - name: Get erlang/OTP version for bootstrapping + run: | + echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV + echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV + + - name: Install prerequisites + run: | + sudo apt-get -qq update + sudo apt-get -qq install makeself + # https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile + sudo apt-get -qq install build-essential autoconf bison flex gawk + sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin + sudo apt-get -qq install python3-dev texinfo unzip + + - name: Install erlang/OTP + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VSN }} + elixir-version: ${{ env.ELIXIR_VSN }} + version-type: strict + + - name: Build musl-libc based binary archives + run: | + sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries + mv .github/container/ejabberdctl.template . + CHECK_DEPS=false tools/make-binaries + + - name: Collect packages + run: | + mkdir tarballs + mv ejabberd-*.tar.gz tarballs + - name: Checkout ejabberd-contrib uses: actions/checkout@v3 with: @@ -64,6 +103,7 @@ jobs: uses: docker/build-push-action@v4 with: build-args: | + METHOD=package VERSION=${{ steps.gitdescribe.outputs.ver }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/CONTAINER.md b/CONTAINER.md index 697084442..869d1a2cd 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -262,6 +262,18 @@ Example using environment variables (see full example [docker-compose.yml](https Generating a Container Image ============================ +> By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, +> it is a fast and direct method. +> +> However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25 +> for the `arm64` architecture. +> +> Providing `build-args: METHOD=package` is an alternate method to build the container +> used by the Github Actions workflow that provides `amd64` and `arm64` container images. +> It first builds an ejabberd binary package, and later installs it in the image. +> That method avoids using QEMU, so it can build `arm64` container images, but is extremely +> slow the first time it's used, and consequently not recommended for general use. + This container image includes ejabberd as a standalone OTP release built using Elixir. That OTP release is configured with: diff --git a/tools/make-binaries b/tools/make-binaries index c124db01d..273ad117f 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -117,7 +117,8 @@ odbc_tar="$odbc_dir.tar.gz" rel_tar="$rel_name-$mix_vsn.tar.gz" ct_jobs=$(nproc) src_dir="$root_dir/src" -platform='x86_64-pc-linux-gnu' +platform=$(gcc -dumpmachine) +platform_libc=$(echo $platform | sed "s/\-/\ /g" | awk '{print $NF}') targets='x86_64-linux-gnu aarch64-linux-gnu' build_start=$(date '+%F %T') have_current_deps='false' @@ -257,10 +258,37 @@ create_common_config() CT_CC_LANG_CXX=y CT_ARCH_64=y CT_KERNEL_LINUX=y + CT_LOG_PROGRESS_BAR=n + EOF +} +#. + +#' Create Crosstool-NG configuration file for glibc. +create_gnu_config() +{ + local file="$1" + + create_common_config "$file" + + cat >>"$file" <<-'EOF' CT_LINUX_V_3_16=y CT_GLIBC_V_2_19=y CT_GLIBC_KERNEL_VERSION_NONE=y - CT_LOG_PROGRESS_BAR=n + EOF +} +#. + +#' Create Crosstool-NG configuration file for musl. +create_musl_config() +{ + local file="$1" + + create_common_config "$file" + + cat >>"$file" <<-'EOF' + CT_EXPERIMENTAL=y + CT_LIBC_MUSL=y + CT_MUSL_V_1_2_2=y EOF } #. @@ -269,8 +297,10 @@ create_common_config() create_x64_config() { local file="$1" + local libc="$2" create_common_config "$file" + create_${libc}_config "$file" cat >>"$file" <<-'EOF' CT_ARCH_X86=y @@ -282,8 +312,10 @@ create_x64_config() create_arm64_config() { local file="$1" + local libc="$2" create_common_config "$file" + create_${libc}_config "$file" cat >>"$file" <<-'EOF' CT_ARCH_ARM=y @@ -319,6 +351,13 @@ add_otp_path() if [ "$mode" = 'native' ] then native_otp_bin="$prefix/bin" + # for github runners to build for non-native systems + # https://github.com/marketplace/actions/setup-erlang-otp-with-optional-elixir-and-mix-and-or-rebar3 + elif [ ! -z "${INSTALL_DIR_FOR_OTP-}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR-}" ] + then + native_otp_bin="$INSTALL_DIR_FOR_OTP/bin" + native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin" + export PATH="$native_elixir_bin:$PATH" fi export PATH="$native_otp_bin:$PATH" } @@ -437,8 +476,9 @@ build_toolchain() { local target="$1" local prefix="$2" + local libc="$3" local arch=$(arch_name "$target") - + if [ -d "$prefix" ] then info "Using existing toolchain in $prefix ..." @@ -458,9 +498,9 @@ build_toolchain() cd "$OLDPWD" fi - info "Building toolchain for $arch ..." + info "Building toolchain for $arch-$libc ..." cd "$root_dir" - create_${arch}_config 'defconfig' + create_${arch}_config 'defconfig' "$libc" ct-ng defconfig sed -i -e "s|^CT_ZLIB_VERSION=.*|CT_ZLIB_VERSION=\"$zlib_vsn\"|" .config sed -i -e 's|^CT_ZLIB_MIRRORS=.*|CT_ZLIB_MIRRORS="https://github.com/madler/zlib/releases/download/v${CT_ZLIB_VERSION} https://www.zlib.net/ https://www.zlib.net/fossils/"|' .config @@ -477,6 +517,7 @@ build_deps() local mode="$1" local target="$2" local prefix="$3" + local libc="$4" local arch="$(arch_name "$target")" local target_src_dir="$prefix/src" local saved_path="$PATH" @@ -507,7 +548,7 @@ build_deps() tar -xJf "$src_dir/$pam_tar" cd "$OLDPWD" - info "Building Termcap $termcap_vsn for $arch ..." + info "Building Termcap $termcap_vsn for $arch-$libc ..." cd "$target_src_dir/$termcap_dir" $configure --prefix="$prefix" cat >'config.h' <<-'EOF' @@ -535,24 +576,24 @@ build_deps() make install cd "$OLDPWD" - info "Building zlib $zlib_vsn for $arch ..." + info "Building zlib $zlib_vsn for $arch-$libc ..." cd "$target_src_dir/$zlib_dir" CFLAGS="$CFLAGS -O3 -fPIC" ./configure --prefix="$prefix" --static make make install cd "$OLDPWD" - info "Building OpenSSL $ssl_vsn for $arch ..." + info "Building OpenSSL $ssl_vsn for $arch-$libc ..." cd "$target_src_dir/$ssl_dir" CFLAGS="$CFLAGS -O3 -fPIC" ./Configure no-shared no-ui-console \ --prefix="$prefix" \ --openssldir="$prefix" \ - "linux-${target%-linux-gnu}" + "linux-${target%-linux-*}" make build_libs make install_dev cd "$OLDPWD" - info "Building Expat $expat_vsn for $arch ..." + info "Building Expat $expat_vsn for $arch-$libc ..." cd "$target_src_dir/$expat_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ --without-docbook \ @@ -561,7 +602,7 @@ build_deps() make install cd "$OLDPWD" - info "Building LibYAML $yaml_vsn for $arch ..." + info "Building LibYAML $yaml_vsn for $arch-$libc ..." cd "$target_src_dir/$yaml_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -569,7 +610,7 @@ build_deps() make install cd "$OLDPWD" - info "Building SQLite $sqlite_vsn for $arch ..." + info "Building SQLite $sqlite_vsn for $arch-$libc ..." cd "$target_src_dir/$sqlite_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -577,7 +618,7 @@ build_deps() make install cd "$OLDPWD" - info "Building ODBC $odbc_vsn for $arch ..." + info "Building ODBC $odbc_vsn for $arch-$libc ..." cd "$target_src_dir/$odbc_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -594,7 +635,7 @@ build_deps() make install cd "$OLDPWD" - info "Building libpng $png_vsn for $arch ..." + info "Building libpng $png_vsn for $arch-$libc ..." cd "$target_src_dir/$png_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -602,7 +643,7 @@ build_deps() make install cd "$OLDPWD" - info "Building JPEG $jpeg_vsn for $arch ..." + info "Building JPEG $jpeg_vsn for $arch-$libc ..." cd "$target_src_dir/$jpeg_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -610,7 +651,7 @@ build_deps() make install cd "$OLDPWD" - info "Building WebP $webp_vsn for $arch ..." + info "Building WebP $webp_vsn for $arch-$libc ..." cd "$target_src_dir/$webp_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -618,7 +659,7 @@ build_deps() make install cd "$OLDPWD" - info "Building LibGD $gd_vsn for $arch ..." + info "Building LibGD $gd_vsn for $arch-$libc ..." cd "$target_src_dir/$gd_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ --with-zlib="$prefix" \ @@ -640,7 +681,7 @@ build_deps() make install cd "$OLDPWD" - info "Building Erlang/OTP $otp_vsn for $arch ..." + info "Building Erlang/OTP $otp_vsn for $arch-$libc ..." if [ "$mode" = 'cross' ] then add_otp_path "$mode" "$prefix" @@ -669,7 +710,7 @@ build_deps() fi cd "$OLDPWD" - info "Building Elixir $elixir_vsn for $arch ..." + info "Building Elixir $elixir_vsn for $arch-$libc ..." cd "$target_src_dir/$elixir_dir" make install PREFIX="$prefix" cd "$OLDPWD" @@ -684,11 +725,12 @@ build_rel() local mode="$1" local target="$2" local prefix="$3" + local libc="$4" local arch="$(arch_name "$target")" local rel_dir="$PWD/_build/prod" local target_data_dir="$prefix/$rel_name" local target_dst_dir="$prefix/$rel_name-$rel_vsn" - local target_dst_tar="$rel_name-$rel_vsn-linux-$arch.tar.gz" + local target_dst_tar="$rel_name-$rel_vsn-linux-$libc-$arch.tar.gz" local saved_path="$PATH" # @@ -723,7 +765,7 @@ build_rel() fi if [ $have_current_deps = false ] - then build_deps "$mode" "$target" "$prefix" + then build_deps "$mode" "$target" "$prefix" "$libc" fi add_otp_path "$mode" "$prefix" @@ -738,7 +780,7 @@ build_rel() info "Removing old $rel_name builds" rm -rf '_build' 'deps' - info "Building $rel_name $rel_vsn for $arch ..." + info "Building $rel_name $rel_vsn for $arch-$libc ..." ./autogen.sh eimp_cflags='-fcommon' eimp_libs='-lwebp -ljpeg -lpng -lz -lm' @@ -782,7 +824,7 @@ build_rel() unset host_alias build_alias fi - info "Putting together $rel_name $rel_vsn archive for $arch ..." + info "Putting together $rel_name $rel_vsn archive for $arch-$libc ..." mkdir "$target_dst_dir" tar -C "$target_dst_dir" -xzf "$rel_dir/$rel_tar" create_data_dir "$target_dst_dir" "$target_data_dir" @@ -870,15 +912,16 @@ export LC_ALL='C.UTF-8' # Elixir insists on a UTF-8 environment. for target in $targets do - prefix="$build_dir/$(arch_name "$target")" + libc="$(echo $target | sed "s/\-/\ /g" | awk '{print $NF}')" + prefix="$build_dir/$(arch_name "$target")-$libc" toolchain_dir="$ct_prefix_dir/$target" - if [ "$(uname -m)-linux-gnu" = "$target" ] + if [ "$(uname -m)-linux-$platform_libc" = "$target" ] then mode='native' else mode='cross' fi - build_toolchain "$target" "$toolchain_dir" - build_rel "$mode" "$target" "$prefix" + build_toolchain "$target" "$toolchain_dir" "$libc" + build_rel "$mode" "$target" "$prefix" "$libc" done save_built_dep_vsns diff --git a/tools/make-installers b/tools/make-installers index d54a90bd8..ee0ee1ed1 100755 --- a/tools/make-installers +++ b/tools/make-installers @@ -80,7 +80,7 @@ create_help_file() local file="$1" cat >"$file" <<-EOF - This is the $rel_name $rel_vsn-$iteration installer for linux-$arch + This is the $rel_name $rel_vsn-$iteration installer for linux-gnu-$arch Visit: $home_url @@ -354,7 +354,7 @@ create_setup_script() for arch in $architectures do - tar_name="$rel_name-$rel_vsn-linux-$arch.tar.gz" + tar_name="$rel_name-$rel_vsn-linux-gnu-$arch.tar.gz" installer_name="$rel_name-$rel_vsn-$iteration-linux-$arch.run" test -e "$tar_name" || tools/make-binaries