1#!/usr/bin/env sh
2
3# MIT License
4#
5# Copyright (c) 2021 Maxim Biro <nurupo.contributions@gmail.com>
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23# THE SOFTWARE.
24
25# Script for building a minimal statically compiled Toxic. While it doesn't
26# support X11 integration, video/audio calls, desktop & sound notifications, QR
27# codes and Python scripting, it is rather portable.
28#
29# Run as:
30#
31#    sudo docker run -it --rm \
32#         -v /tmp/artifact:/artifact \
33#         -v /home/jfreegman/git/toxic:/toxic \
34#         amd64/alpine:latest \
35#         /bin/sh /toxic/script/build-minimal-static-toxic.sh
36#
37# that would use Toxic code from /home/jfreegman/git/toxic and place the build
38# artifact at /tmp/artifact.
39# You can change between amd64/alpine:latest and i386/alpine:latest, for 64-bit
40# and 32-bit builds.
41#
42# To debug, run:
43#
44#    sudo docker run -it --rm \
45#         -v /tmp/artifact:/artifact \
46#         -v /home/jfreegman/git/toxic:/toxic \
47#         amd64/alpine:latest \
48#         /bin/sh
49#    # sh /toxic/script/build-minimal-static-toxic.sh
50
51set -eu
52
53ARTIFACT_DIR="/artifact"
54TOXIC_SRC_DIR="/toxic"
55
56# Make sure we run in the expected environment
57if ! grep -q 'docker' /proc/1/cgroup
58then
59  echo "Error: This script should be run inside a disposable Docker container as it might modify system files in ways that would break a real system."
60  exit 1
61fi
62
63if [ ! -f /etc/os-release ] || ! grep -qi 'Alpine Linux' /etc/os-release
64then
65  echo "Error: This script expects to be run on Alpine Linux."
66  exit 1
67fi
68
69if [ ! -d "$ARTIFACT_DIR" ] || [ ! -d "$TOXIC_SRC_DIR" ]
70then
71  echo "Error: At least one of $ARTIFACT_DIR or $TOXIC_SRC_DIR directories inside the container is missing."
72  exit 1
73fi
74
75if [ "$(id -u)" != "0" ]
76then
77  echo "Error: This script expects to be run as root."
78  exit 1
79fi
80
81set -x
82
83# Use all cores for building
84MAKEFLAGS=j$(nproc)
85export MAKEFLAGS
86
87check_sha256()
88{
89  if ! ( echo "$1  $2" | sha256sum -cs - )
90  then
91    echo "Error: sha256 of $2 doesn't match the known one."
92    echo "Expected: $1  $2"
93    echo "Got: $(sha256sum "$2")"
94    exit 1
95  else
96    echo "sha256 matches the expected one: $1"
97  fi
98}
99
100apk update
101apk upgrade
102apk add \
103    brotli-dev \
104    brotli-static \
105    build-base \
106    cmake \
107    git \
108    libconfig-dev \
109    libconfig-static \
110    libsodium-dev \
111    libsodium-static \
112    linux-headers \
113    ncurses-dev \
114    ncurses-static \
115    ncurses-terminfo \
116    ncurses-terminfo-base \
117    nghttp2-dev \
118    nghttp2-static \
119    openssl-dev \
120    openssl-libs-static \
121    pkgconf \
122    wget \
123    xz \
124    zlib-dev \
125    zlib-static
126
127BUILD_DIR="/tmp/build"
128mkdir -p "$BUILD_DIR"
129
130
131# Build Toxcore
132cd "$BUILD_DIR"
133
134# The git hash of the c-toxcore version we're using
135TOXCORE_VERSION="v0.2.13"
136
137# The sha256sum of the c-toxcore tarball for TOXCORE_VERSION
138TOXCORE_HASH="67114fa57504c58b695f5dce8ef85124d555f2c3c353d0d2615e6d4845114ab8"
139
140TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
141
142wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/$TOXCORE_VERSION.tar.gz"
143check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
144tar -o -xf "$TOXCORE_FILENAME"
145rm "$TOXCORE_FILENAME"
146cd c-toxcore*
147
148cmake -B_build -H. \
149      -DENABLE_STATIC=ON \
150      -DENABLE_SHARED=OFF \
151      -DCMAKE_BUILD_TYPE=Release \
152      -DBUILD_TOXAV=OFF \
153      -DBOOTSTRAP_DAEMON=OFF \
154      -DDHT_BOOTSTRAP=OFF \
155      -DCMAKE_INSTALL_PREFIX="$BUILD_DIR/prefix-toxcore"
156cmake --build _build --target install
157
158
159# Build cURL
160# While Alpine does provide a static cURL build, it's not built with
161# --with-ca-fallback, which is needed for better cross-distro portability.
162# Basically, some distros put their ca-certificates in different places, and
163# with --with-ca-fallback we or the user can provide the cert bundle file
164# location with SSL_CERT_FILE env variable.
165cd "$BUILD_DIR"
166
167CURL_VERSION="7.80.0"
168CURL_HASH="dab997c9b08cb4a636a03f2f7f985eaba33279c1c52692430018fae4a4878dc7"
169CURL_FILENAME="curl-$CURL_VERSION.tar.gz"
170
171wget --timeout=10 -O "$CURL_FILENAME" "https://curl.haxx.se/download/$CURL_FILENAME"
172check_sha256 "$CURL_HASH" "$CURL_FILENAME"
173tar -xf curl*.tar.gz
174rm curl*.tar.gz
175cd curl*
176
177./configure \
178  --prefix="$BUILD_DIR/prefix-curl" \
179  --disable-shared \
180  --enable-static \
181  --without-ca-bundle \
182  --without-ca-path \
183  --with-ca-fallback \
184  --with-nghttp2 \
185  --with-brotli \
186  --with-openssl
187make
188make install
189sed -i 's|-lbrotlidec |-lbrotlidec-static -lbrotlicommon-static |g' $BUILD_DIR/prefix-curl/lib/pkgconfig/libcurl.pc
190
191# Build Toxic
192cd "$BUILD_DIR"
193cp -a "$TOXIC_SRC_DIR" toxic
194cd toxic
195
196if [ -z "$(git describe --tags --exact-match HEAD)" ]
197then
198  set +x
199  echo "Didn't find a git tag on the HEAD commit. You seem to be building an in-development release of Toxic rather than a release version." | fold -sw 80
200  printf "Do you wish to proceed? (y/N): "
201  read -r answer
202  if echo "$answer" | grep -v -iq "^y" ; then
203    echo "Exiting."
204    exit 1
205  fi
206  set -x
207fi
208
209sed -i 's|pkg-config|pkg-config --static|' cfg/global_vars.mk
210sed -i 's|<limits.h|<linux/limits.h|' src/*
211
212CFLAGS="-static" PKG_CONFIG_PATH="$BUILD_DIR/prefix-toxcore/lib64/pkgconfig:$BUILD_DIR/prefix-toxcore/lib/pkgconfig:$BUILD_DIR/prefix-curl/lib/pkgconfig" PREFIX="$BUILD_DIR/prefix-toxic" make \
213  DISABLE_X11=1 \
214  DISABLE_AV=1 \
215  DISABLE_SOUND_NOTIFY=1 \
216  DISABLE_QRCODE=1 \
217  DISABLE_QRPNG=1 \
218  DISABLE_DESKTOP_NOTIFY=1 \
219  ENABLE_PYTHON=0 \
220  ENABLE_RELEASE=1 \
221  ENABLE_ASAN=0 \
222  DISABLE_GAMES=0 \
223  install
224
225
226# Prepare the build artifact
227PREPARE_ARTIFACT_DIR="$BUILD_DIR/artifact"
228cp -a "$BUILD_DIR/prefix-toxic/bin" "$PREPARE_ARTIFACT_DIR"
229strip "$PREPARE_ARTIFACT_DIR"/*
230
231cp -a "$BUILD_DIR/toxic/misc"/* "$PREPARE_ARTIFACT_DIR"
232mv "$PREPARE_ARTIFACT_DIR/toxic.conf.example" "$PREPARE_ARTIFACT_DIR/toxic.conf"
233
234cp -aL /usr/share/terminfo "$PREPARE_ARTIFACT_DIR"
235
236echo "A minimal statically compiled Toxic.
237Doesn't support X11 integration, video/audio calls, desktop & sound
238notifications, QR codes and Python scripting.
239However, it is rather portable.
240
241Toxic $(git -C "$BUILD_DIR/toxic" describe --tags --exact-match HEAD) ($(git -C "$BUILD_DIR/toxic" rev-parse HEAD))
242
243Build date time: $(TZ=UTC date +"%Y-%m-%dT%H:%M:%S%z")
244
245OS:
246$(cat /etc/os-release)
247
248List of self-built software statically compiled into Toxic:
249libcurl $CURL_VERSION
250libtoxcore $TOXCORE_VERSION
251
252List of OS-packaged software statically compiled into Toxic:
253$(apk list -I | grep 'static' | sort -i)
254
255List of all packages installed during the build:
256$(apk list -I | sort -i)" > "$PREPARE_ARTIFACT_DIR/build_info"
257
258echo '#!/usr/bin/env sh
259
260DEBIAN_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
261RHEL_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
262OPENSUSE_CERT_FILE=/etc/ssl/ca-bundle.pem
263
264if [ ! -f "$SSL_CERT_FILE" ] ; then
265  if [ -f "$DEBIAN_SSL_CERT_FILE" ] ; then
266    SSL_CERT_FILE="$DEBIAN_SSL_CERT_FILE"
267  elif [ -f "$RHEL_SSL_CERT_FILE" ] ; then
268    SSL_CERT_FILE="$RHEL_SSL_CERT_FILE"
269  elif [ -f "$OPENSUSE_CERT_FILE" ] ; then
270    SSL_CERT_FILE="$OPENSUSE_CERT_FILE"
271  fi
272fi
273
274if [ -z "$SSL_CERT_FILE" ] ; then
275  echo "Warning: Couldn'\''t find the SSL CA certificate store file." | fold -sw 80
276  echo
277  echo "Toxic uses HTTPS to download a list of DHT bootstrap nodes in order to connect to the Tox DHT. This functionality is optional, you should be able to use Toxic without it. If you choose to use Toxic without it, you might need to manually enter DHT bootstrap node information using the '\''/connect'\'' command in order to come online." | fold -sw 80
278  echo
279  echo "To fix this issue, install SSL CAs as provided by your Linux distribution, e.g. '\''ca-certificates'\'' package on Debian/Ubuntu. If it'\''s already installed and you still see this message, run this script with SSL_CERT_FILE variable set to point to the SSL CA certificate store file location. The file is usually named '\''ca-certificates.crt'\'' or '\''ca-bundle.pem'\''." | fold -sw 80
280  echo
281  printf "Do you wish to run Toxic without SSL CA certificate store file found? (y/N): "
282  read -r answer
283  if echo "$answer" | grep -v -iq "^y" ; then
284    echo "Exiting."
285    exit
286  fi
287fi
288
289cd "$(dirname -- $0)"
290
291SSL_CERT_FILE="$SSL_CERT_FILE" TERMINFO=./terminfo ./toxic -c toxic.conf' > "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
292chmod a+x "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
293
294
295# Tar it
296cd "$PREPARE_ARTIFACT_DIR"
297cd ..
298ARCH="$(tr '_' '-' < /etc/apk/arch)"
299ARTIFACT_NAME="toxic-minimal-static-musl_linux_$ARCH"
300mv "$PREPARE_ARTIFACT_DIR" "$PREPARE_ARTIFACT_DIR/../$ARTIFACT_NAME"
301tar -cJf "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_NAME"
302mv "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_DIR"
303chmod 777 -R "$ARTIFACT_DIR"
304
305