summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/docker/scripts/jpegxl_builder.sh
blob: 949c811eaebc61ced325b5b5a560e423acdfb426 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
#!/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 <<EOF
  deb https://deb.nodesource.com/node_14.x bionic main
  deb-src https://deb.nodesource.com/node_14.x bionic main
EOF
  curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -

  local port_list=()
  local main_list=()
  local ubarch
  for ubarch in "${LIST_ARCHS[@]}"; do
    if [[ "${ubarch}" != "amd64" && "${ubarch}" != "i386" ]]; then
      # other archs are not part of the main mirrors, but available in
      # ports.ubuntu.com.
      port_list+=("${ubarch}")
    else
      main_list+=("${ubarch}")
    fi
    # Add the arch to the system.
    if [[ "${ubarch}" != "amd64" ]]; then
      dpkg --add-architecture "${ubarch}"
    fi
  done

  # Update the sources.list with the split of supported architectures.
  local bkplist="/etc/apt/sources.list.bkp"
  [[ -e "${bkplist}" ]] || \
    mv /etc/apt/sources.list "${bkplist}"

  local newlist="/etc/apt/sources.list.tmp"
  rm -f "${newlist}"
  port_list=$(echo "${port_list[@]}" | tr ' ' ,)
  if [[ -n "${port_list}" ]]; then
    local port_url="http://ports.ubuntu.com/ubuntu-ports/"
    grep -v -E '^#' "${bkplist}" |
      sed -E "s;^deb (http[^ ]+) (.*)\$;deb [arch=${port_list}] ${port_url} \\2;" \
      >>"${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}"

      libstdc++-8-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 <tar_url> <sha256> <target> [<target...>]
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 "$@"