1#!/bin/bash
2#
3# Example:
4#         env BUILD_TARGET=mips ./scripts/cross-build.sh
5#
6set -eo pipefail
7
8CROSS_ROOT="${CROSS_ROOT:-/tmp/cross}"
9STAGE_ROOT="${STAGE_ROOT:-/tmp/stage}"
10BUILD_ROOT="${BUILD_ROOT:-/tmp/build}"
11BUILD_TARGET="${BUILD_TARGET:-x86_64}"
12
13ZLIB_VERSION="${ZLIB_VERSION:-1.3.1}"
14JSON_C_VERSION="${JSON_C_VERSION:-0.17}"
15MBEDTLS_VERSION="${MBEDTLS_VERSION:-2.28.5}"
16LIBUV_VERSION="${LIBUV_VERSION:-1.44.2}"
17LIBWEBSOCKETS_VERSION="${LIBWEBSOCKETS_VERSION:-4.3.3}"
18
19build_zlib() {
20    echo "=== Building zlib-${ZLIB_VERSION} (${TARGET})..."
21    curl -fSsLo- "https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
22    pushd "${BUILD_DIR}"/zlib-"${ZLIB_VERSION}"
23        env CHOST="${TARGET}" ./configure --static --archs="-fPIC" --prefix="${STAGE_DIR}"
24        make -j"$(nproc)" install
25    popd
26}
27
28build_json-c() {
29    echo "=== Building json-c-${JSON_C_VERSION} (${TARGET})..."
30    curl -fSsLo- "https://s3.amazonaws.com/json-c_releases/releases/json-c-${JSON_C_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
31    pushd "${BUILD_DIR}/json-c-${JSON_C_VERSION}"
32        rm -rf build && mkdir -p build && cd build
33        cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
34            -DCMAKE_BUILD_TYPE=RELEASE \
35            -DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
36            -DBUILD_SHARED_LIBS=OFF \
37            -DBUILD_TESTING=OFF \
38            -DDISABLE_THREAD_LOCAL_STORAGE=ON \
39            ..
40        make -j"$(nproc)" install
41    popd
42}
43
44build_mbedtls() {
45    echo "=== Building mbedtls-${MBEDTLS_VERSION} (${TARGET})..."
46    curl -fSsLo- "https://github.com/ARMmbed/mbedtls/archive/v${MBEDTLS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
47    pushd "${BUILD_DIR}/mbedtls-${MBEDTLS_VERSION}"
48        rm -rf build && mkdir -p build && cd build
49        cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
50            -DCMAKE_BUILD_TYPE=RELEASE \
51            -DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
52            -DENABLE_TESTING=OFF \
53            ..
54        make -j"$(nproc)" install
55    popd
56}
57
58build_libuv() {
59    echo "=== Building libuv-${LIBUV_VERSION} (${TARGET})..."
60    curl -fSsLo- "https://dist.libuv.org/dist/v${LIBUV_VERSION}/libuv-v${LIBUV_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
61    pushd "${BUILD_DIR}/libuv-v${LIBUV_VERSION}"
62        ./autogen.sh
63        env CFLAGS=-fPIC ./configure --disable-shared --enable-static --prefix="${STAGE_DIR}" --host="${TARGET}"
64        make -j"$(nproc)" install
65    popd
66}
67
68install_cmake_cross_file() {
69    cat << EOF > "${BUILD_DIR}/cross-${TARGET}.cmake"
70SET(CMAKE_SYSTEM_NAME $1)
71
72set(CMAKE_C_COMPILER "${TARGET}-gcc")
73set(CMAKE_CXX_COMPILER "${TARGET}-g++")
74
75set(CMAKE_FIND_ROOT_PATH "${STAGE_DIR}")
76set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
77set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
78set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
79
80set(OPENSSL_USE_STATIC_LIBS TRUE)
81EOF
82}
83
84build_libwebsockets() {
85    echo "=== Building libwebsockets-${LIBWEBSOCKETS_VERSION} (${TARGET})..."
86    curl -fSsLo- "https://github.com/warmcat/libwebsockets/archive/v${LIBWEBSOCKETS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
87    cp "$(dirname $0)/client_cert.patch" ${BUILD_DIR}/libwebsockets-${LIBWEBSOCKETS_VERSION}
88    pushd "${BUILD_DIR}/libwebsockets-${LIBWEBSOCKETS_VERSION}"
89        patch -p1 < client_cert.patch
90        sed -i 's/ websockets_shared//g' cmake/libwebsockets-config.cmake.in
91        sed -i 's/ OR PC_OPENSSL_FOUND//g' lib/tls/CMakeLists.txt
92        sed -i '/PC_OPENSSL/d' lib/tls/CMakeLists.txt
93        rm -rf build && mkdir -p build && cd build
94        cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
95            -DCMAKE_BUILD_TYPE=RELEASE \
96            -DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
97            -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
98            -DCMAKE_EXE_LINKER_FLAGS="-static" \
99            -DLWS_WITHOUT_TESTAPPS=ON \
100            -DLWS_WITH_MBEDTLS=ON \
101            -DLWS_WITH_LIBUV=ON \
102            -DLWS_STATIC_PIC=ON \
103            -DLWS_WITH_SHARED=OFF \
104            -DLWS_UNIX_SOCK=ON \
105            -DLWS_IPV6=ON \
106            -DLWS_ROLE_RAW_FILE=OFF \
107            -DLWS_WITH_HTTP2=ON \
108            -DLWS_WITH_HTTP_BASIC_AUTH=OFF \
109            -DLWS_WITH_UDP=OFF \
110            -DLWS_WITHOUT_CLIENT=ON \
111            -DLWS_WITHOUT_EXTENSIONS=OFF \
112            -DLWS_WITH_LEJP=OFF \
113            -DLWS_WITH_LEJP_CONF=OFF \
114            -DLWS_WITH_LWSAC=OFF \
115            -DLWS_WITH_SEQUENCER=OFF \
116            ..
117        make -j"$(nproc)" install
118    popd
119}
120
121build_ttyd() {
122    echo "=== Building ttyd (${TARGET})..."
123    # TODO: Remove this when https://github.com/xtermjs/xterm.js/pull/5221 is
124    # deployed
125    patch -p1 < $(dirname $0)/xtermjs_a11y.patch
126    rm -rf build && mkdir -p build && cd build
127    cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
128        -DCMAKE_INSTALL_PREFIX="${STAGE_DIR}" \
129        -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
130        -DCMAKE_C_FLAGS="-Os -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -flto" \
131        -DCMAKE_EXE_LINKER_FLAGS="-static -no-pie -Wl,-s -Wl,-Bsymbolic -Wl,--gc-sections" \
132        -DCMAKE_BUILD_TYPE=RELEASE \
133        ..
134    make install
135}
136
137build() {
138    TARGET="$1"
139    ALIAS="$2"
140    STAGE_DIR="${STAGE_ROOT}/${TARGET}"
141    BUILD_DIR="${BUILD_ROOT}/${TARGET}"
142    MUSL_CC_URL="https://github.com/tsl0922/musl-toolchains/releases/download/2021-11-23"
143    COMPONENTS="1"
144    SYSTEM="Linux"
145
146    if [ "$ALIAS" = "win32" ]; then
147        COMPONENTS=2
148        SYSTEM="Windows"
149    fi
150
151    echo "=== Installing toolchain ${ALIAS} (${TARGET})..."
152
153    mkdir -p "${CROSS_ROOT}" && export PATH="${PATH}:${CROSS_ROOT}/bin"
154    curl -fSsLo- "${MUSL_CC_URL}/${TARGET}-cross.tgz" | tar xz -C "${CROSS_ROOT}" --strip-components=${COMPONENTS}
155
156    echo "=== Building target ${ALIAS} (${TARGET})..."
157
158    rm -rf "${STAGE_DIR}" "${BUILD_DIR}"
159    mkdir -p "${STAGE_DIR}" "${BUILD_DIR}"
160    export PKG_CONFIG_PATH="${STAGE_DIR}/lib/pkgconfig"
161
162    install_cmake_cross_file ${SYSTEM}
163
164    build_zlib
165    build_json-c
166    build_libuv
167    build_mbedtls
168    build_libwebsockets
169    build_ttyd
170}
171
172case ${BUILD_TARGET} in
173    amd64) BUILD_TARGET="x86_64" ;;
174    arm64) BUILD_TARGET="aarch64" ;;
175    armv7) BUILD_TARGET="armv7l" ;;
176esac
177
178case ${BUILD_TARGET} in
179    i686|x86_64|aarch64|mips|mipsel|mips64|mips64el|s390x)
180        build "${BUILD_TARGET}-linux-musl" "${BUILD_TARGET}"
181        ;;
182    arm)
183        build "${BUILD_TARGET}-linux-musleabi" "${BUILD_TARGET}"
184        ;;
185    armhf)
186        build arm-linux-musleabihf "${BUILD_TARGET}"
187        ;;
188    armv7l)
189        build armv7l-linux-musleabihf "${BUILD_TARGET}"
190        ;;
191    win32)
192        build x86_64-w64-mingw32 "${BUILD_TARGET}"
193        ;;
194    *)
195        echo "unknown cross target: ${BUILD_TARGET}" && exit 1
196esac
197