#!/usr/bin/env bash # Copyright (c) the JPEG XL Project Authors. All rights reserved. # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # Main entry point for all the Dockerfile for jpegxl-builder. This centralized # file helps sharing code and configuration between Dockerfiles. set -eux MYDIR=$(dirname $(realpath "$0")) # libjpeg-turbo. JPEG_TURBO_RELEASE="2.0.4" JPEG_TURBO_URL="https://github.com/libjpeg-turbo/libjpeg-turbo/archive/${JPEG_TURBO_RELEASE}.tar.gz" JPEG_TURBO_SHA256="7777c3c19762940cff42b3ba4d7cd5c52d1671b39a79532050c85efb99079064" # zlib (dependency of libpng) ZLIB_RELEASE="1.2.11" ZLIB_URL="https://www.zlib.net/zlib-${ZLIB_RELEASE}.tar.gz" ZLIB_SHA256="c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1" # The name in the .pc and the .dll generated don't match in zlib for Windows # because they use different .dll names in Windows. We avoid that by defining # UNIX=1. We also install all the .dll files to ${prefix}/lib instead of the # default ${prefix}/bin. ZLIB_FLAGS='-DUNIX=1 -DINSTALL_PKGCONFIG_DIR=/${CMAKE_INSTALL_PREFIX}/lib/pkgconfig -DINSTALL_BIN_DIR=/${CMAKE_INSTALL_PREFIX}/lib' # libpng LIBPNG_RELEASE="1.6.37" LIBPNG_URL="https://github.com/glennrp/libpng/archive/v${LIBPNG_RELEASE}.tar.gz" LIBPNG_SHA256="ca74a0dace179a8422187671aee97dd3892b53e168627145271cad5b5ac81307" # giflib GIFLIB_RELEASE="5.2.1" GIFLIB_URL="https://netcologne.dl.sourceforge.net/project/giflib/giflib-${GIFLIB_RELEASE}.tar.gz" GIFLIB_SHA256="31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd" # A patch needed to compile GIFLIB in mingw. GIFLIB_PATCH_URL="https://github.com/msys2/MINGW-packages/raw/3afde38fcee7b3ba2cafd97d76cca8f06934504f/mingw-w64-giflib/001-mingw-build.patch" GIFLIB_PATCH_SHA256="2b2262ddea87fc07be82e10aeb39eb699239f883c899aa18a16e4d4e40af8ec8" # webp WEBP_RELEASE="1.0.2" WEBP_URL="https://codeload.github.com/webmproject/libwebp/tar.gz/v${WEBP_RELEASE}" WEBP_SHA256="347cf85ddc3497832b5fa9eee62164a37b249c83adae0ba583093e039bf4881f" # Google benchmark BENCHMARK_RELEASE="1.5.2" BENCHMARK_URL="https://github.com/google/benchmark/archive/v${BENCHMARK_RELEASE}.tar.gz" BENCHMARK_SHA256="dccbdab796baa1043f04982147e67bb6e118fe610da2c65f88912d73987e700c" BENCHMARK_FLAGS="-DGOOGLETEST_PATH=${MYDIR}/../../third_party/googletest" # attribute(format(__MINGW_PRINTF_FORMAT, ...)) doesn't work in our # environment, so we disable the warning. BENCHMARK_FLAGS="-DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_TESTING=OFF \ -DCMAKE_CXX_FLAGS=-Wno-ignored-attributes \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON" # V8 V8_VERSION="9.3.22" # Temporary files cleanup hooks. CLEANUP_FILES=() cleanup() { if [[ ${#CLEANUP_FILES[@]} -ne 0 ]]; then rm -fr "${CLEANUP_FILES[@]}" fi } trap "{ set +x; } 2>/dev/null; cleanup" INT TERM EXIT # List of Ubuntu arch names supported by the builder (such as "i386"). LIST_ARCHS=( amd64 i386 arm64 armhf ) # List of target triplets supported by the builder. LIST_TARGETS=( x86_64-linux-gnu i686-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu ) LIST_MINGW_TARGETS=( i686-w64-mingw32 x86_64-w64-mingw32 ) LIST_WASM_TARGETS=( wasm32 ) # Setup the apt repositories and supported architectures. setup_apt() { apt-get update -y apt-get install -y curl gnupg ca-certificates apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F # node sources. cat >/etc/apt/sources.list.d/nodesource.list <>"${newlist}" fi main_list=$(echo "${main_list[@]}" | tr ' ' ,) grep -v -E '^#' "${bkplist}" | sed -E "s;^deb (http[^ ]+) (.*)\$;deb [arch=${main_list}] \\1 \\2\ndeb-src [arch=${main_list}] \\1 \\2;" \ >>"${newlist}" mv "${newlist}" /etc/apt/sources.list } install_pkgs() { packages=( # Native compilers (minimum for SIMD is clang-7) clang-7 clang-format-7 clang-tidy-7 # TODO: Consider adding clang-8 to every builder: # clang-8 clang-format-8 clang-tidy-8 # For cross-compiling to Windows with mingw. mingw-w64 wine64 wine-binfmt # Native tools. bsdmainutils cmake extra-cmake-modules git llvm nasm ninja-build parallel pkg-config # For compiling / testing JNI wrapper. JDK8 is almost 2x smaller than JDK11 # openjdk-8-jdk-headless would be 50MB smaller, unfortunately, CMake # does mistakenly thinks it does not contain JNI feature. openjdk-8-jdk # These are used by the ./ci.sh lint in the native builder. clang-format-7 clang-format-8 # For coverage builds gcovr # For compiling giflib documentation. xmlto # Common libraries. libstdc++-8-dev # We don't use tcmalloc on archs other than amd64. This installs # libgoogle-perftools4:amd64. google-perftools # NodeJS for running WASM tests nodejs # To generate API documentation. doxygen # Freezes version that builds (passes tests). Newer version # (2.30-21ubuntu1~18.04.4) claims to fix "On Intel Skylake # (-march=native) generated avx512 instruction can be wrong", # but newly added tests does not pass. Perhaps the problem is # that mingw package is not updated. binutils-source=2.30-15ubuntu1 ) # Install packages that are arch-dependent. local ubarch for ubarch in "${LIST_ARCHS[@]}"; do packages+=( # Library dependencies. These normally depend on the target architecture # we are compiling for and can't usually be installed for multiple # architectures at the same time. libgif7:"${ubarch}" libjpeg-dev:"${ubarch}" libpng-dev:"${ubarch}" libqt5x11extras5-dev:"${ubarch}" libstdc++-8-dev:"${ubarch}" qtbase5-dev:"${ubarch}" # For OpenEXR: libilmbase12:"${ubarch}" libopenexr22:"${ubarch}" # TCMalloc dependency libunwind-dev:"${ubarch}" # Cross-compiling tools per arch. libc6-dev-"${ubarch}"-cross libstdc++-8-dev-"${ubarch}"-cross ) done local target for target in "${LIST_TARGETS[@]}"; do # Per target cross-compiling tools. if [[ "${target}" != "x86_64-linux-gnu" ]]; then packages+=( binutils-"${target}" gcc-"${target}" ) fi done # Install all the manual packages via "apt install" for the main arch. These # will be installed for other archs via manual download and unpack. apt install -y "${packages[@]}" "${UNPACK_PKGS[@]}" } # binutils <2.32 need a patch. install_binutils() { local workdir=$(mktemp -d --suffix=_install) CLEANUP_FILES+=("${workdir}") pushd "${workdir}" apt source binutils-mingw-w64 apt -y build-dep binutils-mingw-w64 cd binutils-mingw-w64-8ubuntu1 cp "${MYDIR}/binutils_align_fix.patch" debian/patches echo binutils_align_fix.patch >> debian/patches/series dpkg-buildpackage -b cd .. dpkg -i *deb popd } # Install a library from the source code for multiple targets. # Usage: install_from_source [] install_from_source() { local package="$1" shift local url eval "url=\${${package}_URL}" local sha256 eval "sha256=\${${package}_SHA256}" # Optional package flags local pkgflags eval "pkgflags=\${${package}_FLAGS:-}" local workdir=$(mktemp -d --suffix=_install) CLEANUP_FILES+=("${workdir}") local tarfile="${workdir}"/$(basename "${url}") curl -L --output "${tarfile}" "${url}" if ! echo "${sha256} ${tarfile}" | sha256sum -c --status -; then echo "SHA256 mismatch for ${url}: expected ${sha256} but found:" sha256sum "${tarfile}" exit 1 fi local target for target in "$@"; do echo "Installing ${package} for target ${target} from ${url}" local srcdir="${workdir}/source-${target}" mkdir -p "${srcdir}" tar -zxf "${tarfile}" -C "${srcdir}" --strip-components=1 local prefix="/usr" if [[ "${target}" != "x86_64-linux-gnu" ]]; then prefix="/usr/${target}" fi # Apply patches to buildfiles. if [[ "${package}" == "GIFLIB" && "${target}" == *mingw32 ]]; then # GIFLIB Makefile has several problems so we need to fix them here. We are # using a patch from MSYS2 that already fixes the compilation for mingw. local make_patch="${srcdir}/libgif.patch" curl -L "${GIFLIB_PATCH_URL}" -o "${make_patch}" echo "${GIFLIB_PATCH_SHA256} ${make_patch}" | sha256sum -c --status - patch "${srcdir}/Makefile" < "${make_patch}" elif [[ "${package}" == "LIBPNG" && "${target}" == wasm* ]]; then # Cut the dependency to libm; there is pull request to fix it, so this # might not be needed in the future. sed -i 's/APPLE/EMSCRIPTEN/g' "${srcdir}/CMakeLists.txt" fi local cmake_args=() local export_args=("CC=clang-7" "CXX=clang++-7") local cmake="cmake" local make="make" local system_name="Linux" if [[ "${target}" == *mingw32 ]]; then system_name="Windows" # When compiling with clang, CMake doesn't detect that we are using mingw. cmake_args+=( -DMINGW=1 # Googletest needs this when cross-compiling to windows -DCMAKE_CROSSCOMPILING=1 -DHAVE_STD_REGEX=0 -DHAVE_POSIX_REGEX=0 -DHAVE_GNU_POSIX_REGEX=0 ) local windres=$(which ${target}-windres || true) if [[ -n "${windres}" ]]; then cmake_args+=(-DCMAKE_RC_COMPILER="${windres}") fi fi if [[ "${target}" == wasm* ]]; then system_name="WASM" cmake="emcmake cmake" make="emmake make" export_args=() cmake_args+=( -DCMAKE_FIND_ROOT_PATH="${prefix}" -DCMAKE_PREFIX_PATH="${prefix}" ) # Static and shared library link to the same file -> race condition. nproc=1 else nproc=`nproc --all` fi cmake_args+=(-DCMAKE_SYSTEM_NAME="${system_name}") if [[ "${target}" != "x86_64-linux-gnu" ]]; then # Cross-compiling. cmake_args+=( -DCMAKE_C_COMPILER_TARGET="${target}" -DCMAKE_CXX_COMPILER_TARGET="${target}" -DCMAKE_SYSTEM_PROCESSOR="${target%%-*}" ) fi if [[ -e "${srcdir}/CMakeLists.txt" ]]; then # Most packages use cmake for building which is easier to configure for # cross-compiling. if [[ "${package}" == "JPEG_TURBO" && "${target}" == wasm* ]]; then # JT erroneously detects WASM CPU as i386 and tries to use asm. # Wasm/Emscripten support for dynamic linking is incomplete; disable # to avoid CMake warning. cmake_args+=(-DWITH_SIMD=0 -DENABLE_SHARED=OFF) fi ( cd "${srcdir}" export ${export_args[@]} ${cmake} \ -DCMAKE_INSTALL_PREFIX="${prefix}" \ "${cmake_args[@]}" ${pkgflags} ${make} -j${nproc} ${make} install ) elif [[ "${package}" == "GIFLIB" ]]; then # GIFLIB doesn't yet have a cmake build system. There is a pull # request in giflib for adding CMakeLists.txt so this might not be # needed in the future. ( cd "${srcdir}" local giflib_make_flags=( CFLAGS="-O2 --target=${target} -std=gnu99" PREFIX="${prefix}" ) if [[ "${target}" != wasm* ]]; then giflib_make_flags+=(CC=clang-7) fi # giflib make dependencies are not properly set up so parallel building # doesn't work for everything. ${make} -j${nproc} libgif.a "${giflib_make_flags[@]}" ${make} -j${nproc} all "${giflib_make_flags[@]}" ${make} install "${giflib_make_flags[@]}" ) else echo "Don't know how to install ${package}" exit 1 fi # CMake mistakenly uses ".so" libraries and EMCC fails to link properly. if [[ "${target}" == wasm* ]]; then rm -f "${prefix}/lib"/*.so* fi done } # Packages that are manually unpacked for each architecture. UNPACK_PKGS=( libgif-dev libclang-common-7-dev # For OpenEXR: libilmbase-dev libopenexr-dev # TCMalloc libgoogle-perftools-dev libtcmalloc-minimal4 libgoogle-perftools4 ) # Main script entry point. main() { cd "${MYDIR}" # Configure the repositories with the sources for multi-arch cross # compilation. setup_apt apt-get update -y apt-get dist-upgrade -y install_pkgs install_binutils apt clean # Remove prebuilt Java classes cache. rm /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/classes.jsa # Manually extract packages for the target arch that can't install it directly # at the same time as the native ones. local ubarch for ubarch in "${LIST_ARCHS[@]}"; do if [[ "${ubarch}" != "amd64" ]]; then local pkg for pkg in "${UNPACK_PKGS[@]}"; do apt download "${pkg}":"${ubarch}" dpkg -x "${pkg}"_*_"${ubarch}".deb / done fi done # TODO: Add clang from the llvm repos. This is problematic since we are # installing libclang-common-7-dev:"${ubarch}" from the ubuntu ports repos # which is not available in the llvm repos so it might have a different # version than the ubuntu ones. # Remove the win32 libgcc version. The gcc-mingw-w64-x86-64 (and i686) # packages install two libgcc versions: # /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix # /usr/lib/gcc/x86_64-w64-mingw32/7.3-win32 # (exact libgcc version number depends on the package version). # # Clang will pick the best libgcc, sorting by version, but it doesn't # seem to be a way to specify one or the other one, except by passing # -nostdlib and setting all the include paths from the command line. # To check which one is being used you can run: # clang++-7 --target=x86_64-w64-mingw32 -v -print-libgcc-file-name # We need to use the "posix" versions for thread support, so here we # just remove the other one. local target for target in "${LIST_MINGW_TARGETS[@]}"; do update-alternatives --set "${target}-gcc" $(which "${target}-gcc-posix") local gcc_win32_path=$("${target}-cpp-win32" -print-libgcc-file-name) rm -rf $(dirname "${gcc_win32_path}") done # TODO: Add msan for the target when cross-compiling. This only installs it # for amd64. ./msan_install.sh # Build and install qemu user-linux targets. ./qemu_install.sh # Install emscripten SDK. ./emsdk_install.sh # Setup environment for building WASM libraries from sources. source /opt/emsdk/emsdk_env.sh # Install some dependency libraries manually for the different targets. install_from_source JPEG_TURBO "${LIST_MINGW_TARGETS[@]}" "${LIST_WASM_TARGETS[@]}" install_from_source ZLIB "${LIST_MINGW_TARGETS[@]}" "${LIST_WASM_TARGETS[@]}" install_from_source LIBPNG "${LIST_MINGW_TARGETS[@]}" "${LIST_WASM_TARGETS[@]}" install_from_source GIFLIB "${LIST_MINGW_TARGETS[@]}" "${LIST_WASM_TARGETS[@]}" # webp in Ubuntu is relatively old so we install it from source for everybody. install_from_source WEBP "${LIST_TARGETS[@]}" "${LIST_MINGW_TARGETS[@]}" install_from_source BENCHMARK "${LIST_TARGETS[@]}" "${LIST_MINGW_TARGETS[@]}" # Install v8. v8 has better WASM SIMD support than NodeJS 14 (LTS). # First we need the installer to install v8. npm install jsvu -g # install specific version; HOME=/opt jsvu --os=linux64 "v8@${V8_VERSION}" ln -s "/opt/.jsvu/v8-${V8_VERSION}" "/opt/.jsvu/v8" # Cleanup. find /var/lib/apt/lists/ -mindepth 1 -delete } main "$@"