Skip to content

Instantly share code, notes, and snippets.

@zenofile
Last active October 23, 2024 18:58
Show Gist options
  • Save zenofile/d2acef1a0423e5081e74162cd5a0ae2d to your computer and use it in GitHub Desktop.
Save zenofile/d2acef1a0423e5081e74162cd5a0ae2d to your computer and use it in GitHub Desktop.
Building a static tmux binary with musl
#!/usr/bin/env bash
set -o errexit
MUSL_VERSION=1.2.5
LIBEVENT_VERSION=2.1.12
NCURSES_VERSION=6.5
TMUX_VERSION=3.5a
TARGETDIR=$1
if [[ "${TARGETDIR}" = "" ]]; then
TARGETDIR="${PWD}/out"
fi
mkdir -p "${TARGETDIR}"
TASKS=$(nproc)
CC=( "${TARGETDIR}/bin/musl-gcc" -static )
_CFLAGS=( -Os -ffunction-sections -fdata-sections -no-pie)
_LDFLAGS=( "-Wl,--gc-sections" -flto )
_musl() {
if [[ ! -e musl-${MUSL_VERSION}.tar.gz ]]; then
curl -LO https://www.musl-libc.org/releases/musl-${MUSL_VERSION}.tar.gz
fi
tar zxf musl-${MUSL_VERSION}.tar.gz --skip-old-files
pushd .
cd musl-${MUSL_VERSION}
CFLAGS="${_CFLAGS[*]}" LDFLAGS="${_LDFLAGS[*]}" ./configure --prefix="${TARGETDIR}" --disable-shared --enable-wrapper=gcc
make -j $TASKS
make install
make clean
popd
}
_libevent() {
if [[ ! -e libevent-${LIBEVENT_VERSION}-stable.tar.gz ]]; then
curl -LO https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}-stable/libevent-${LIBEVENT_VERSION}-stable.tar.gz
fi
tar zxf libevent-${LIBEVENT_VERSION}-stable.tar.gz --skip-old-files
pushd .
cd libevent-${LIBEVENT_VERSION}-stable
_cflags=("${_CFLAGS[@]}" -flto)
CC="${CC[*]}" CFLAGS="${_cflags[*]}" LDFLAGS="${_LDFLAGS[*]}" ./configure --prefix="${TARGETDIR}" --disable-shared --disable-openssl --disable-samples
make -j $TASKS
make install
make clean
popd
}
_ncurses() {
if [[ ! -e ncurses-${NCURSES_VERSION}.tar.gz ]]; then
curl -LO https://ftp.gnu.org/pub/gnu/ncurses/ncurses-${NCURSES_VERSION}.tar.gz
fi
tar zxvf ncurses-${NCURSES_VERSION}.tar.gz --skip-old-files
pushd .
cd ncurses-${NCURSES_VERSION}
_cflags=( "${_CFLAGS[@]}" -flto )
CC="${CC[*]}" CFLAGS="${_cflags[*]}" LDFLAGS="${_LDFLAGS[*]}" ./configure --prefix $TARGETDIR \
--with-default-terminfo-dir=/usr/share/terminfo \
--with-terminfo-dirs="/etc/terminfo:/lib/terminfo:/usr/share/terminfo" \
--enable-pc-files \
--with-pkg-config-libdir="${TARGETDIR}/lib/pkgconfig" \
--without-ada \
--without-debug \
--with-termlib \
--without-cxx \
--without-progs \
--without-manpages \
--disable-db-install \
--without-tests
make -j $TASKS
make install
make clean
popd
}
_tmux() {
if [[ ! -e tmux-${TMUX_VERSION}.tar.gz ]]; then
curl -LO https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/tmux-${TMUX_VERSION}.tar.gz
fi
tar zxf tmux-${TMUX_VERSION}.tar.gz --skip-old-files
pushd .
cd tmux-${TMUX_VERSION}
_cflags=( "${_CFLAGS[@]}" -flto )
CC="${CC[*]}" CFLAGS="${_cflags[*]}" LDFLAGS="${_LDFLAGS[*]}" PKG_CONFIG_PATH="${TARGETDIR}/lib/pkgconfig" ./configure --enable-static --prefix="${TARGETDIR}"
make -j $TASKS
make install
make clean
popd
cp "${TARGETDIR}/bin/tmux" .
strip --strip-all ./tmux
if command -v upx &> /dev/null; then
upx -k --best ./tmux
fi
}
rm -rf "${TARGETDIR}/out"
if [[ ! -x "${TARGETDIR}/bin/musl-gcc" ]]; then
_musl
fi
_libevent
_ncurses
_tmux
@zenofile
Copy link
Author

zenofile commented Jun 3, 2021

That's awesome, thanks!

I was building it in an Archlinux container using podman and didn't test any other build environment.

podman run --rm -it --hostname archns --userns=host --tmpfs /tmp:exec,rw -v ./build.sh:/tmp/build/build.sh -v /tmp/build-tmux:/tmp/build --workdir=/tmp/build --security-opt label=disable gitlab.archlinux.org:5050/archlinux/archlinux-docker:base-devel-master /bin/bash /tmp/build/build.sh

For anyone that might be preferring podman or docker.

@asdffdsazqqq
Copy link

hello, thanks so much,

i am using a shared linux vm, no root access, spent days trying replace the very old tmux, lacked the skills.

wanted to let you know your script works with tmux 3.2a,
TMUX_VERSION=3.2a

really, thanks!

@ursetto
Copy link

ursetto commented Mar 25, 2022

Fantastic. In order to build on older systems, with a gcc that doesn't support -no-pie, add the following patch:

diff --git a/build.sh b/build.sh
index e7f6c33..720226e 100755
--- a/build.sh
+++ b/build.sh
@@ -15,7 +15,10 @@ mkdir -p "${TARGETDIR}"

 TASKS=2
 CC=( "${TARGETDIR}/bin/musl-gcc" -static )
-_CFLAGS=( -Os -ffunction-sections -fdata-sections -no-pie)
+_CFLAGS=( -Os -ffunction-sections -fdata-sections )
+if "${REALGCC:-gcc}" -v 2>&1 | grep -q -- --enable-default-pie; then
+    _CFLAGS+=( -no-pie )
+fi
 _LDFLAGS=( "-Wl,--gc-sections" -flto )

The -no-pie flag is illegal if gcc wasn't compiled with --enable-default-pie; in this case omitting it has the same effect.

With this change, it builds perfectly on CentOS 7. (For example in a centos:7 Dockerfile, use yum install -y gcc make autoconf and run build.sh.)

Adding @meermanr's patch above, it also builds in an ubuntu:20.04 container. In theory, it should build in a standard GitHub Actions runner.

@zenofile
Copy link
Author

I applied some of the suggested patches and bumped the versions. Also smoke tested with gcc-11 on aarch64 and x86_64.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment